| /************************************************************** |
| * |
| * 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 |