1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 #include "precompiled_sfx2.hxx" 23 24 #include "DeckLayouter.hxx" 25 #include "sfx2/sidebar/Theme.hxx" 26 #include "Panel.hxx" 27 #include "TitleBar.hxx" 28 #include "Deck.hxx" 29 30 #include <vcl/window.hxx> 31 #include <vcl/scrbar.hxx> 32 33 using namespace ::com::sun::star; 34 using namespace ::com::sun::star::uno; 35 36 37 namespace sfx2 { namespace sidebar { 38 39 40 namespace { 41 static const sal_Int32 MinimalPanelHeight (25); 42 } 43 44 #define IterateLayoutItems(iterator_name,container) \ 45 for(::std::vector<LayoutItem>::iterator \ 46 iterator_name(container.begin()), \ 47 iEnd(container.end()); \ 48 iterator_name!=iEnd; \ 49 ++iterator_name) 50 51 52 53 void DeckLayouter::LayoutDeck ( 54 const Rectangle aContentArea, 55 ::std::vector<Panel*>& rPanels, 56 Window& rDeckTitleBar, 57 Window& rScrollClipWindow, 58 Window& rScrollContainer, 59 Window& rFiller, 60 ScrollBar& rVerticalScrollBar) 61 { 62 if (aContentArea.GetWidth()<=0 || aContentArea.GetHeight()<=0) 63 return; 64 Rectangle aBox (PlaceDeckTitle(rDeckTitleBar, aContentArea)); 65 66 if ( ! rPanels.empty()) 67 { 68 // Prepare the layout item container. 69 ::std::vector<LayoutItem> aLayoutItems; 70 aLayoutItems.resize(rPanels.size()); 71 for (sal_Int32 nIndex(0),nCount(rPanels.size()); nIndex<nCount; ++nIndex) 72 { 73 aLayoutItems[nIndex].mpPanel = rPanels[nIndex]; 74 aLayoutItems[nIndex].mnPanelIndex = nIndex; 75 } 76 aBox = LayoutPanels( 77 aBox, 78 aLayoutItems, 79 rScrollClipWindow, 80 rScrollContainer, 81 rVerticalScrollBar, 82 false); 83 } 84 UpdateFiller(rFiller, aBox); 85 } 86 87 88 89 90 Rectangle DeckLayouter::LayoutPanels ( 91 const Rectangle aContentArea, 92 ::std::vector<LayoutItem>& rLayoutItems, 93 Window& rScrollClipWindow, 94 Window& rScrollContainer, 95 ScrollBar& rVerticalScrollBar, 96 const bool bShowVerticalScrollBar) 97 { 98 Rectangle aBox (PlaceVerticalScrollBar(rVerticalScrollBar, aContentArea, bShowVerticalScrollBar)); 99 100 const sal_Int32 nWidth (aBox.GetWidth()); 101 const sal_Int32 nPanelTitleBarHeight (Theme::GetInteger(Theme::Int_PanelTitleBarHeight)); 102 103 // Prepare the separators, horizontal lines above and below the 104 // panel titels. 105 const sal_Int32 nDeckSeparatorHeight (Theme::GetInteger(Theme::Int_DeckSeparatorHeight)); 106 107 // Get the requested heights of the panels and the available 108 // height that is left when all panel titles and separators are 109 // taken into account. 110 sal_Int32 nAvailableHeight (aBox.GetHeight()); 111 GetRequestedSizes(rLayoutItems, nAvailableHeight, aBox); 112 const sal_Int32 nTotalDecorationHeight (aBox.GetHeight() - nAvailableHeight); 113 114 // Analyze the requested heights. 115 // Determine the height that is available for panel content 116 // and count the different layouts. 117 sal_Int32 nTotalPreferredHeight (0); 118 sal_Int32 nTotalMinimumHeight (0); 119 IterateLayoutItems(iItem,rLayoutItems) 120 { 121 nTotalMinimumHeight += iItem->maLayoutSize.Minimum; 122 nTotalPreferredHeight += iItem->maLayoutSize.Preferred; 123 } 124 125 if (nTotalMinimumHeight > nAvailableHeight 126 && ! bShowVerticalScrollBar) 127 { 128 // Not enough space, even when all panels are shrunk to their 129 // minimum height. 130 // Show a vertical scrollbar. 131 return LayoutPanels( 132 aContentArea, 133 rLayoutItems, 134 rScrollClipWindow, 135 rScrollContainer, 136 rVerticalScrollBar, 137 true); 138 } 139 140 // We are now in one of three modes. 141 // - The preferred height fits into the available size: 142 // Use the preferred size, distribute the remaining height bei 143 // enlarging panels. 144 // - The total minimum height fits into the available size: 145 // Use the minimum size, distribute the remaining height bei 146 // enlarging panels. 147 // - The total minimum height does not fit into the available 148 // size: 149 // Use the unmodified preferred height for all panels. 150 151 LayoutMode eMode (MinimumOrLarger); 152 if (bShowVerticalScrollBar) 153 eMode = Preferred; 154 else if (nTotalPreferredHeight <= nAvailableHeight) 155 eMode = PreferredOrLarger; 156 else 157 eMode = MinimumOrLarger; 158 159 if (eMode != Preferred) 160 { 161 const sal_Int32 nTotalHeight (eMode==MinimumOrLarger ? nTotalMinimumHeight : nTotalPreferredHeight); 162 163 DistributeHeights( 164 rLayoutItems, 165 nAvailableHeight-nTotalHeight, 166 aBox.GetHeight(), 167 eMode==MinimumOrLarger); 168 } 169 170 // Set position and size of the mpScrollClipWindow to the available 171 // size. Its child, the mpScrollContainer, may have a bigger 172 // height. 173 rScrollClipWindow.SetPosSizePixel(aBox.Left(), aBox.Top(), aBox.GetWidth(), aBox.GetHeight()); 174 175 const sal_Int32 nContentHeight ( 176 eMode==Preferred 177 ? nTotalPreferredHeight + nTotalDecorationHeight 178 : aBox.GetHeight()); 179 rScrollContainer.SetPosSizePixel( 180 0, 181 0, 182 nWidth, 183 nContentHeight); 184 185 if (bShowVerticalScrollBar) 186 SetupVerticalScrollBar(rVerticalScrollBar, nContentHeight, aBox.GetHeight()); 187 188 aBox.Top() += PlacePanels(rLayoutItems, nWidth, eMode, rScrollContainer); 189 return aBox; 190 } 191 192 193 194 195 sal_Int32 DeckLayouter::PlacePanels ( 196 ::std::vector<LayoutItem>& rLayoutItems, 197 const sal_Int32 nWidth, 198 const LayoutMode eMode, 199 Window& rScrollContainer) 200 { 201 ::std::vector<sal_Int32> aSeparators; 202 const sal_Int32 nDeckSeparatorHeight (Theme::GetInteger(Theme::Int_DeckSeparatorHeight)); 203 const sal_Int32 nPanelTitleBarHeight (Theme::GetInteger(Theme::Int_PanelTitleBarHeight)); 204 sal_Int32 nY (0); 205 206 // Assign heights and places. 207 IterateLayoutItems(iItem,rLayoutItems) 208 { 209 if (iItem->mpPanel == NULL) 210 continue; 211 212 Panel& rPanel (*iItem->mpPanel); 213 214 // Separator above the panel title bar. 215 aSeparators.push_back(nY); 216 nY += nDeckSeparatorHeight; 217 218 // Place the title bar. 219 TitleBar* pTitleBar = rPanel.GetTitleBar(); 220 pTitleBar->SetPosSizePixel(0, nY, nWidth, nPanelTitleBarHeight); 221 pTitleBar->Show(); 222 nY += nPanelTitleBarHeight; 223 224 // Separator below the panel title bar. 225 aSeparators.push_back(nY); 226 nY += nDeckSeparatorHeight; 227 228 if (rPanel.IsExpanded()) 229 { 230 rPanel.Show(); 231 232 // Determine the height of the panel depending on layout 233 // mode and distributed heights. 234 sal_Int32 nPanelHeight (0); 235 switch(eMode) 236 { 237 case MinimumOrLarger: 238 nPanelHeight = iItem->maLayoutSize.Minimum + iItem->mnDistributedHeight; 239 break; 240 case PreferredOrLarger: 241 nPanelHeight = iItem->maLayoutSize.Preferred + iItem->mnDistributedHeight; 242 break; 243 case Preferred: 244 nPanelHeight = iItem->maLayoutSize.Preferred; 245 break; 246 default: 247 OSL_ASSERT(false); 248 break; 249 } 250 251 // Place the panel. 252 OSL_TRACE("panel %d: placing @%d +%d", iItem->mnPanelIndex, nY, nPanelHeight); 253 rPanel.SetPosSizePixel(0, nY, nWidth, nPanelHeight); 254 255 nY += nPanelHeight; 256 } 257 else 258 { 259 rPanel.Hide(); 260 } 261 } 262 263 Deck::ScrollContainerWindow* pScrollContainerWindow 264 = dynamic_cast<Deck::ScrollContainerWindow*>(&rScrollContainer); 265 if (pScrollContainerWindow != NULL) 266 pScrollContainerWindow->SetSeparators(aSeparators); 267 268 return nY; 269 } 270 271 272 273 274 void DeckLayouter::GetRequestedSizes ( 275 ::std::vector<LayoutItem>& rLayoutItems, 276 sal_Int32& rAvailableHeight, 277 const Rectangle& rContentBox) 278 { 279 rAvailableHeight = rContentBox.GetHeight(); 280 281 const sal_Int32 nPanelTitleBarHeight (Theme::GetInteger(Theme::Int_PanelTitleBarHeight)); 282 const sal_Int32 nDeckSeparatorHeight (Theme::GetInteger(Theme::Int_DeckSeparatorHeight)); 283 284 IterateLayoutItems(iItem,rLayoutItems) 285 { 286 ui::LayoutSize aLayoutSize (ui::LayoutSize(0,0,0)); 287 if (iItem->mpPanel != NULL) 288 { 289 rAvailableHeight -= nPanelTitleBarHeight; 290 rAvailableHeight -= 2*nDeckSeparatorHeight; 291 292 if (iItem->mpPanel->IsExpanded()) 293 { 294 Reference<ui::XSidebarPanel> xPanel (iItem->mpPanel->GetPanelComponent()); 295 if (xPanel.is()) 296 aLayoutSize = xPanel->getHeightForWidth(rContentBox.GetWidth()); 297 else 298 aLayoutSize = ui::LayoutSize(MinimalPanelHeight, 0, -1); 299 } 300 } 301 iItem->maLayoutSize = aLayoutSize; 302 } 303 } 304 305 306 307 308 void DeckLayouter::DistributeHeights ( 309 ::std::vector<LayoutItem>& rLayoutItems, 310 const sal_Int32 nHeightToDistribute, 311 const sal_Int32 nContainerHeight, 312 const bool bMinimumHeightIsBase) 313 { 314 if (nHeightToDistribute <= 0) 315 return; 316 317 sal_Int32 nRemainingHeightToDistribute (nHeightToDistribute); 318 319 // Compute the weights as difference between panel base height 320 // (either its minimum or preferred height) and the container height. 321 sal_Int32 nTotalWeight (0); 322 sal_Int32 nNoMaximumCount (0); 323 sal_Int32 nIndex (0); 324 IterateLayoutItems(iItem,rLayoutItems) 325 { 326 if (iItem->maLayoutSize.Maximum == 0) 327 continue; 328 if (iItem->maLayoutSize.Maximum < 0) 329 ++nNoMaximumCount; 330 331 const sal_Int32 nBaseHeight ( 332 bMinimumHeightIsBase 333 ? iItem->maLayoutSize.Minimum 334 : iItem->maLayoutSize.Preferred); 335 if (nBaseHeight < nContainerHeight) 336 { 337 iItem->mnWeight = nContainerHeight - nBaseHeight; 338 nTotalWeight += iItem->mnWeight; 339 } 340 OSL_TRACE("panel %d: base height is %d, weight is %d, min/max/pref are %d/%d/%d", 341 nIndex, nBaseHeight, iItem->mnWeight, 342 iItem->maLayoutSize.Minimum, iItem->maLayoutSize.Maximum, iItem->maLayoutSize.Preferred); 343 } 344 345 if (nTotalWeight == 0) 346 return; 347 348 // First pass of height distribution. 349 nIndex = 0; 350 IterateLayoutItems(iItem,rLayoutItems) 351 { 352 const sal_Int32 nBaseHeight ( 353 bMinimumHeightIsBase 354 ? iItem->maLayoutSize.Minimum 355 : iItem->maLayoutSize.Preferred); 356 sal_Int32 nDistributedHeight (iItem->mnWeight * nHeightToDistribute / nTotalWeight); 357 if (nBaseHeight+nDistributedHeight > iItem->maLayoutSize.Maximum 358 && iItem->maLayoutSize.Maximum >= 0) 359 { 360 nDistributedHeight = ::std::max<sal_Int32>(0,iItem->maLayoutSize.Maximum - nBaseHeight); 361 } 362 iItem->mnDistributedHeight = nDistributedHeight; 363 OSL_TRACE("panel %d: distributed height is %d", nIndex, nDistributedHeight); 364 nRemainingHeightToDistribute -= nDistributedHeight; 365 } 366 367 if (nRemainingHeightToDistribute == 0) 368 return; 369 OSL_ASSERT(nRemainingHeightToDistribute > 0); 370 371 // It is possible that not all of the height could be distributed 372 // because of Maximum heights being smaller than expected. 373 // Distribute the remaining height between the panels that have no 374 // Maximum (ie Maximum==-1). 375 if (nNoMaximumCount == 0) 376 { 377 // There are no panels with unrestricted height. 378 return; 379 } 380 const sal_Int32 nAdditionalHeightPerPanel (nRemainingHeightToDistribute / nNoMaximumCount); 381 // Handle rounding error. 382 sal_Int32 nAdditionalHeightForFirstPanel (nRemainingHeightToDistribute 383 - nNoMaximumCount*nAdditionalHeightPerPanel); 384 nIndex = 0; 385 IterateLayoutItems(iItem,rLayoutItems) 386 { 387 if (iItem->maLayoutSize.Maximum < 0) 388 { 389 iItem->mnDistributedHeight += nAdditionalHeightPerPanel + nAdditionalHeightForFirstPanel; 390 OSL_TRACE("panel %d: additionl height is %d", 391 iItem->mnPanelIndex, 392 nAdditionalHeightPerPanel + nAdditionalHeightForFirstPanel); 393 nRemainingHeightToDistribute -= nAdditionalHeightPerPanel + nAdditionalHeightForFirstPanel; 394 } 395 } 396 397 OSL_ASSERT(nRemainingHeightToDistribute==0); 398 } 399 400 401 402 403 Rectangle DeckLayouter::PlaceDeckTitle ( 404 Window& rDeckTitleBar, 405 const Rectangle& rAvailableSpace) 406 { 407 if (static_cast<DockingWindow*>(rDeckTitleBar.GetParent()->GetParent())->IsFloatingMode()) 408 { 409 // When the side bar is undocked then the outer system window displays the deck title. 410 rDeckTitleBar.Hide(); 411 return rAvailableSpace; 412 } 413 else 414 { 415 const sal_Int32 nDeckTitleBarHeight (Theme::GetInteger(Theme::Int_DeckTitleBarHeight)); 416 rDeckTitleBar.SetPosSizePixel( 417 rAvailableSpace.Left(), 418 rAvailableSpace.Top(), 419 rAvailableSpace.GetWidth(), 420 nDeckTitleBarHeight); 421 rDeckTitleBar.Show(); 422 return Rectangle( 423 rAvailableSpace.Left(), 424 rAvailableSpace.Top() + nDeckTitleBarHeight, 425 rAvailableSpace.Right(), 426 rAvailableSpace.Bottom()); 427 } 428 } 429 430 431 432 433 Rectangle DeckLayouter::PlaceVerticalScrollBar ( 434 ScrollBar& rVerticalScrollBar, 435 const Rectangle& rAvailableSpace, 436 const bool bShowVerticalScrollBar) 437 { 438 if (bShowVerticalScrollBar) 439 { 440 const sal_Int32 nScrollBarWidth (rVerticalScrollBar.GetSizePixel().Width()); 441 rVerticalScrollBar.SetPosSizePixel( 442 rAvailableSpace.Right() - nScrollBarWidth + 1, 443 rAvailableSpace.Top(), 444 nScrollBarWidth, 445 rAvailableSpace.GetHeight()); 446 rVerticalScrollBar.Show(); 447 return Rectangle( 448 rAvailableSpace.Left(), 449 rAvailableSpace.Top(), 450 rAvailableSpace.Right() - nScrollBarWidth, 451 rAvailableSpace.Bottom()); 452 } 453 else 454 { 455 rVerticalScrollBar.Hide(); 456 return rAvailableSpace; 457 } 458 } 459 460 461 462 463 void DeckLayouter::SetupVerticalScrollBar( 464 ScrollBar& rVerticalScrollBar, 465 const sal_Int32 nContentHeight, 466 const sal_Int32 nVisibleHeight) 467 { 468 OSL_ASSERT(nContentHeight > nVisibleHeight); 469 470 rVerticalScrollBar.SetRangeMin(0); 471 rVerticalScrollBar.SetRangeMax(nContentHeight-1); 472 rVerticalScrollBar.SetVisibleSize(nVisibleHeight); 473 } 474 475 476 477 478 void DeckLayouter::UpdateFiller ( 479 Window& rFiller, 480 const Rectangle& rBox) 481 { 482 if (rBox.GetHeight() > 0) 483 { 484 // Show the filler. 485 rFiller.SetBackground(Theme::GetPaint(Theme::Paint_PanelBackground).GetWallpaper()); 486 rFiller.SetPosSizePixel(rBox.TopLeft(), rBox.GetSize()); 487 rFiller.Show(); 488 } 489 else 490 { 491 // Hide the filler. 492 rFiller.Hide(); 493 } 494 } 495 496 497 498 } } // end of namespace sfx2::sidebar 499