/************************************************************** * * 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 "TitleBar.hxx" #include "Deck.hxx" #include #include 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::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 aLayoutItems; aLayoutItems.resize(rPanels.size()); for (sal_Int32 nIndex(0),nCount(rPanels.size()); nIndex& 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()); rScrollContainer.SetPosSizePixel( 0, 0, nWidth, nContentHeight); if (bShowVerticalScrollBar) SetupVerticalScrollBar(rVerticalScrollBar, nContentHeight, aBox.GetHeight()); aBox.Top() += PlacePanels(rLayoutItems, nWidth, eMode, rScrollContainer); return aBox; } sal_Int32 DeckLayouter::PlacePanels ( ::std::vector& rLayoutItems, const sal_Int32 nWidth, const LayoutMode eMode, Window& rScrollContainer) { ::std::vector 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 (iItem->mpPanel == NULL) continue; Panel& rPanel (*iItem->mpPanel); // Separator above the panel title bar. aSeparators.push_back(nY); nY += nDeckSeparatorHeight; // Place the title bar. TitleBar* 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. OSL_TRACE("panel %d: placing @%d +%d", iItem->mnPanelIndex, nY, nPanelHeight); rPanel.SetPosSizePixel(0, nY, nWidth, nPanelHeight); 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(&rScrollContainer); if (pScrollContainerWindow != NULL) pScrollContainerWindow->SetSeparators(aSeparators); return nY; } void DeckLayouter::GetRequestedSizes ( ::std::vector& 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 (iItem->mpPanel != NULL) { 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 -= 2*nDeckSeparatorHeight; } if (iItem->mpPanel->IsExpanded()) { Reference xPanel (iItem->mpPanel->GetPanelComponent()); if (xPanel.is()) aLayoutSize = xPanel->getHeightForWidth(rContentBox.GetWidth()); else aLayoutSize = ui::LayoutSize(MinimalPanelHeight, 0, -1); } } iItem->maLayoutSize = aLayoutSize; } } void DeckLayouter::DistributeHeights ( ::std::vector& 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); sal_Int32 nIndex (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; } OSL_TRACE("panel %d: base height is %d, weight is %d, min/max/pref are %d/%d/%d", nIndex, nBaseHeight, iItem->mnWeight, iItem->maLayoutSize.Minimum, iItem->maLayoutSize.Maximum, iItem->maLayoutSize.Preferred); } if (nTotalWeight == 0) return; // First pass of height distribution. nIndex = 0; 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(0,iItem->maLayoutSize.Maximum - nBaseHeight); } iItem->mnDistributedHeight = nDistributedHeight; OSL_TRACE("panel %d: distributed height is %d", nIndex, 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); nIndex = 0; IterateLayoutItems(iItem,rLayoutItems) { if (iItem->maLayoutSize.Maximum < 0) { iItem->mnDistributedHeight += nAdditionalHeightPerPanel + nAdditionalHeightForFirstPanel; OSL_TRACE("panel %d: additionl height is %d", iItem->mnPanelIndex, nAdditionalHeightPerPanel + nAdditionalHeightForFirstPanel); nRemainingHeightToDistribute -= nAdditionalHeightPerPanel + nAdditionalHeightForFirstPanel; } } OSL_ASSERT(nRemainingHeightToDistribute==0); } Rectangle DeckLayouter::PlaceDeckTitle ( Window& rDeckTitleBar, const Rectangle& rAvailableSpace) { if (static_cast(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