blob: cf6124fe856d1c82d3490eedff5d647a6055e39a [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
#include "precompiled_sfx2.hxx"
#include "DeckLayouter.hxx"
#include "sfx2/sidebar/Theme.hxx"
#include "Panel.hxx"
#include "PanelTitleBar.hxx"
#include "Deck.hxx"
#include <vcl/window.hxx>
#include <vcl/scrbar.hxx>
using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;
namespace sfx2 { namespace sidebar {
namespace {
static const sal_Int32 MinimalPanelHeight (25);
}
#define IterateLayoutItems(iterator_name,container) \
for(::std::vector<LayoutItem>::iterator \
iterator_name(container.begin()), \
iEnd(container.end()); \
iterator_name!=iEnd; \
++iterator_name)
void DeckLayouter::LayoutDeck (
const Rectangle aContentArea,
SharedPanelContainer& rPanels,
Window& rDeckTitleBar,
Window& rScrollClipWindow,
Window& rScrollContainer,
Window& rFiller,
ScrollBar& rVerticalScrollBar)
{
if (aContentArea.GetWidth()<=0 || aContentArea.GetHeight()<=0)
return;
Rectangle aBox (PlaceDeckTitle(rDeckTitleBar, aContentArea));
if ( ! rPanels.empty())
{
// Prepare the layout item container.
::std::vector<LayoutItem> aLayoutItems;
aLayoutItems.resize(rPanels.size());
for (sal_Int32 nIndex(0),nCount(rPanels.size()); nIndex<nCount; ++nIndex)
{
aLayoutItems[nIndex].mpPanel = rPanels[nIndex];
aLayoutItems[nIndex].mnPanelIndex = nIndex;
}
aBox = LayoutPanels(
aBox,
aLayoutItems,
rScrollClipWindow,
rScrollContainer,
rVerticalScrollBar,
false);
}
UpdateFiller(rFiller, aBox);
}
Rectangle DeckLayouter::LayoutPanels (
const Rectangle aContentArea,
::std::vector<LayoutItem>& rLayoutItems,
Window& rScrollClipWindow,
Window& rScrollContainer,
ScrollBar& rVerticalScrollBar,
const bool bShowVerticalScrollBar)
{
Rectangle aBox (PlaceVerticalScrollBar(rVerticalScrollBar, aContentArea, bShowVerticalScrollBar));
const sal_Int32 nWidth (aBox.GetWidth());
// const sal_Int32 nPanelTitleBarHeight (Theme::GetInteger(Theme::Int_PanelTitleBarHeight));
// Prepare the separators, horizontal lines above and below the
// panel titels.
// const sal_Int32 nDeckSeparatorHeight (Theme::GetInteger(Theme::Int_DeckSeparatorHeight));
// Get the requested heights of the panels and the available
// height that is left when all panel titles and separators are
// taken into account.
sal_Int32 nAvailableHeight (aBox.GetHeight());
GetRequestedSizes(rLayoutItems, nAvailableHeight, aBox);
const sal_Int32 nTotalDecorationHeight (aBox.GetHeight() - nAvailableHeight);
// Analyze the requested heights.
// Determine the height that is available for panel content
// and count the different layouts.
sal_Int32 nTotalPreferredHeight (0);
sal_Int32 nTotalMinimumHeight (0);
IterateLayoutItems(iItem,rLayoutItems)
{
nTotalMinimumHeight += iItem->maLayoutSize.Minimum;
nTotalPreferredHeight += iItem->maLayoutSize.Preferred;
}
if (nTotalMinimumHeight > nAvailableHeight
&& ! bShowVerticalScrollBar)
{
// Not enough space, even when all panels are shrunk to their
// minimum height.
// Show a vertical scrollbar.
return LayoutPanels(
aContentArea,
rLayoutItems,
rScrollClipWindow,
rScrollContainer,
rVerticalScrollBar,
true);
}
// We are now in one of three modes.
// - The preferred height fits into the available size:
// Use the preferred size, distribute the remaining height bei
// enlarging panels.
// - The total minimum height fits into the available size:
// Use the minimum size, distribute the remaining height bei
// enlarging panels.
// - The total minimum height does not fit into the available
// size:
// Use the unmodified preferred height for all panels.
LayoutMode eMode (MinimumOrLarger);
if (bShowVerticalScrollBar)
eMode = Preferred;
else if (nTotalPreferredHeight <= nAvailableHeight)
eMode = PreferredOrLarger;
else
eMode = MinimumOrLarger;
if (eMode != Preferred)
{
const sal_Int32 nTotalHeight (eMode==MinimumOrLarger ? nTotalMinimumHeight : nTotalPreferredHeight);
DistributeHeights(
rLayoutItems,
nAvailableHeight-nTotalHeight,
aBox.GetHeight(),
eMode==MinimumOrLarger);
}
// Set position and size of the mpScrollClipWindow to the available
// size. Its child, the mpScrollContainer, may have a bigger
// height.
rScrollClipWindow.SetPosSizePixel(aBox.Left(), aBox.Top(), aBox.GetWidth(), aBox.GetHeight());
const sal_Int32 nContentHeight (
eMode==Preferred
? nTotalPreferredHeight + nTotalDecorationHeight
: aBox.GetHeight());
sal_Int32 nY = rVerticalScrollBar.GetThumbPos();
if (nContentHeight-nY < aBox.GetHeight())
nY = nContentHeight-aBox.GetHeight();
if (nY < 0)
nY = 0;
rScrollContainer.SetPosSizePixel(
0,
-nY,
nWidth,
nContentHeight);
if (bShowVerticalScrollBar)
SetupVerticalScrollBar(rVerticalScrollBar, nContentHeight, aBox.GetHeight());
const sal_Int32 nUsedHeight (PlacePanels(rLayoutItems, nWidth, eMode, rScrollContainer));
aBox.Top() += nUsedHeight;
return aBox;
}
sal_Int32 DeckLayouter::PlacePanels (
::std::vector<LayoutItem>& rLayoutItems,
const sal_Int32 nWidth,
const LayoutMode eMode,
Window& rScrollContainer)
{
::std::vector<sal_Int32> aSeparators;
const sal_Int32 nDeckSeparatorHeight (Theme::GetInteger(Theme::Int_DeckSeparatorHeight));
const sal_Int32 nPanelTitleBarHeight (Theme::GetInteger(Theme::Int_PanelTitleBarHeight));
sal_Int32 nY (0);
// Assign heights and places.
IterateLayoutItems(iItem,rLayoutItems)
{
if( !bool(iItem->mpPanel))
continue;
Panel& rPanel (*iItem->mpPanel);
// Separator above the panel title bar.
aSeparators.push_back(nY);
nY += nDeckSeparatorHeight;
// Place the title bar.
PanelTitleBar* pTitleBar = rPanel.GetTitleBar();
if (pTitleBar != NULL)
{
if (iItem->mbShowTitleBar)
{
pTitleBar->SetPosSizePixel(0, nY, nWidth, nPanelTitleBarHeight);
pTitleBar->Show();
nY += nPanelTitleBarHeight;
}
else
{
pTitleBar->Hide();
}
}
if (rPanel.IsExpanded())
{
rPanel.Show();
// Determine the height of the panel depending on layout
// mode and distributed heights.
sal_Int32 nPanelHeight (0);
switch(eMode)
{
case MinimumOrLarger:
nPanelHeight = iItem->maLayoutSize.Minimum + iItem->mnDistributedHeight;
break;
case PreferredOrLarger:
nPanelHeight = iItem->maLayoutSize.Preferred + iItem->mnDistributedHeight;
break;
case Preferred:
nPanelHeight = iItem->maLayoutSize.Preferred;
break;
default:
OSL_ASSERT(false);
break;
}
// Place the panel.
rPanel.SetPosSizePixel(0, nY, nWidth, nPanelHeight);
rPanel.Invalidate();
nY += nPanelHeight;
}
else
{
rPanel.Hide();
// Add a separator below the collapsed panel, if it is the
// last panel in the deck.
if (iItem == rLayoutItems.end()-1)
{
// Separator below the panel title bar.
aSeparators.push_back(nY);
nY += nDeckSeparatorHeight;
}
}
}
Deck::ScrollContainerWindow* pScrollContainerWindow
= dynamic_cast<Deck::ScrollContainerWindow*>(&rScrollContainer);
if (pScrollContainerWindow != NULL)
pScrollContainerWindow->SetSeparators(aSeparators);
return nY;
}
void DeckLayouter::GetRequestedSizes (
::std::vector<LayoutItem>& rLayoutItems,
sal_Int32& rAvailableHeight,
const Rectangle& rContentBox)
{
rAvailableHeight = rContentBox.GetHeight();
const sal_Int32 nPanelTitleBarHeight (Theme::GetInteger(Theme::Int_PanelTitleBarHeight));
const sal_Int32 nDeckSeparatorHeight (Theme::GetInteger(Theme::Int_DeckSeparatorHeight));
IterateLayoutItems(iItem,rLayoutItems)
{
ui::LayoutSize aLayoutSize (ui::LayoutSize(0,0,0));
if( bool(iItem->mpPanel))
{
if (rLayoutItems.size() == 1
&& iItem->mpPanel->IsTitleBarOptional())
{
// There is only one panel and its title bar is
// optional => hide it.
rAvailableHeight -= nDeckSeparatorHeight;
iItem->mbShowTitleBar = false;
}
else
{
// Show the title bar and a separator above and below
// the title bar.
rAvailableHeight -= nPanelTitleBarHeight;
rAvailableHeight -= nDeckSeparatorHeight;
}
if (iItem->mpPanel->IsExpanded())
{
Reference<ui::XSidebarPanel> xPanel (iItem->mpPanel->GetPanelComponent());
if (xPanel.is())
aLayoutSize = xPanel->getHeightForWidth(rContentBox.GetWidth());
else
aLayoutSize = ui::LayoutSize(MinimalPanelHeight, -1, 0);
}
}
iItem->maLayoutSize = aLayoutSize;
}
}
void DeckLayouter::DistributeHeights (
::std::vector<LayoutItem>& rLayoutItems,
const sal_Int32 nHeightToDistribute,
const sal_Int32 nContainerHeight,
const bool bMinimumHeightIsBase)
{
if (nHeightToDistribute <= 0)
return;
sal_Int32 nRemainingHeightToDistribute (nHeightToDistribute);
// Compute the weights as difference between panel base height
// (either its minimum or preferred height) and the container height.
sal_Int32 nTotalWeight (0);
sal_Int32 nNoMaximumCount (0);
IterateLayoutItems(iItem,rLayoutItems)
{
if (iItem->maLayoutSize.Maximum == 0)
continue;
if (iItem->maLayoutSize.Maximum < 0)
++nNoMaximumCount;
const sal_Int32 nBaseHeight (
bMinimumHeightIsBase
? iItem->maLayoutSize.Minimum
: iItem->maLayoutSize.Preferred);
if (nBaseHeight < nContainerHeight)
{
iItem->mnWeight = nContainerHeight - nBaseHeight;
nTotalWeight += iItem->mnWeight;
}
}
if (nTotalWeight == 0)
return;
// First pass of height distribution.
IterateLayoutItems(iItem,rLayoutItems)
{
const sal_Int32 nBaseHeight (
bMinimumHeightIsBase
? iItem->maLayoutSize.Minimum
: iItem->maLayoutSize.Preferred);
sal_Int32 nDistributedHeight (iItem->mnWeight * nHeightToDistribute / nTotalWeight);
if (nBaseHeight+nDistributedHeight > iItem->maLayoutSize.Maximum
&& iItem->maLayoutSize.Maximum >= 0)
{
nDistributedHeight = ::std::max<sal_Int32>(0,iItem->maLayoutSize.Maximum - nBaseHeight);
}
iItem->mnDistributedHeight = nDistributedHeight;
nRemainingHeightToDistribute -= nDistributedHeight;
}
if (nRemainingHeightToDistribute == 0)
return;
OSL_ASSERT(nRemainingHeightToDistribute > 0);
// It is possible that not all of the height could be distributed
// because of Maximum heights being smaller than expected.
// Distribute the remaining height between the panels that have no
// Maximum (ie Maximum==-1).
if (nNoMaximumCount == 0)
{
// There are no panels with unrestricted height.
return;
}
const sal_Int32 nAdditionalHeightPerPanel (nRemainingHeightToDistribute / nNoMaximumCount);
// Handle rounding error.
sal_Int32 nAdditionalHeightForFirstPanel (nRemainingHeightToDistribute
- nNoMaximumCount*nAdditionalHeightPerPanel);
IterateLayoutItems(iItem,rLayoutItems)
{
if (iItem->maLayoutSize.Maximum < 0)
{
iItem->mnDistributedHeight += nAdditionalHeightPerPanel + nAdditionalHeightForFirstPanel;
nRemainingHeightToDistribute -= nAdditionalHeightPerPanel + nAdditionalHeightForFirstPanel;
}
}
OSL_ASSERT(nRemainingHeightToDistribute==0);
}
Rectangle DeckLayouter::PlaceDeckTitle (
Window& rDeckTitleBar,
const Rectangle& rAvailableSpace)
{
if (static_cast<DockingWindow*>(rDeckTitleBar.GetParent()->GetParent())->IsFloatingMode())
{
// When the side bar is undocked then the outer system window displays the deck title.
rDeckTitleBar.Hide();
return rAvailableSpace;
}
else
{
const sal_Int32 nDeckTitleBarHeight (Theme::GetInteger(Theme::Int_DeckTitleBarHeight));
rDeckTitleBar.SetPosSizePixel(
rAvailableSpace.Left(),
rAvailableSpace.Top(),
rAvailableSpace.GetWidth(),
nDeckTitleBarHeight);
rDeckTitleBar.Show();
return Rectangle(
rAvailableSpace.Left(),
rAvailableSpace.Top() + nDeckTitleBarHeight,
rAvailableSpace.Right(),
rAvailableSpace.Bottom());
}
}
Rectangle DeckLayouter::PlaceVerticalScrollBar (
ScrollBar& rVerticalScrollBar,
const Rectangle& rAvailableSpace,
const bool bShowVerticalScrollBar)
{
if (bShowVerticalScrollBar)
{
const sal_Int32 nScrollBarWidth (rVerticalScrollBar.GetSizePixel().Width());
rVerticalScrollBar.SetPosSizePixel(
rAvailableSpace.Right() - nScrollBarWidth + 1,
rAvailableSpace.Top(),
nScrollBarWidth,
rAvailableSpace.GetHeight());
rVerticalScrollBar.Show();
return Rectangle(
rAvailableSpace.Left(),
rAvailableSpace.Top(),
rAvailableSpace.Right() - nScrollBarWidth,
rAvailableSpace.Bottom());
}
else
{
rVerticalScrollBar.Hide();
return rAvailableSpace;
}
}
void DeckLayouter::SetupVerticalScrollBar(
ScrollBar& rVerticalScrollBar,
const sal_Int32 nContentHeight,
const sal_Int32 nVisibleHeight)
{
OSL_ASSERT(nContentHeight > nVisibleHeight);
rVerticalScrollBar.SetRangeMin(0);
rVerticalScrollBar.SetRangeMax(nContentHeight-1);
rVerticalScrollBar.SetVisibleSize(nVisibleHeight);
}
void DeckLayouter::UpdateFiller (
Window& rFiller,
const Rectangle& rBox)
{
if (rBox.GetHeight() > 0)
{
// Show the filler.
rFiller.SetBackground(Theme::GetPaint(Theme::Paint_PanelBackground).GetWallpaper());
rFiller.SetPosSizePixel(rBox.TopLeft(), rBox.GetSize());
rFiller.Show();
}
else
{
// Hide the filler.
rFiller.Hide();
}
}
} } // end of namespace sfx2::sidebar