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 SharedPanelContainer& 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 if (pTitleBar != NULL) 221 { 222 if (iItem->mbShowTitleBar) 223 { 224 pTitleBar->SetPosSizePixel(0, nY, nWidth, nPanelTitleBarHeight); 225 pTitleBar->Show(); 226 nY += nPanelTitleBarHeight; 227 } 228 else 229 { 230 pTitleBar->Hide(); 231 } 232 } 233 234 if (rPanel.IsExpanded()) 235 { 236 rPanel.Show(); 237 238 // Determine the height of the panel depending on layout 239 // mode and distributed heights. 240 sal_Int32 nPanelHeight (0); 241 switch(eMode) 242 { 243 case MinimumOrLarger: 244 nPanelHeight = iItem->maLayoutSize.Minimum + iItem->mnDistributedHeight; 245 break; 246 case PreferredOrLarger: 247 nPanelHeight = iItem->maLayoutSize.Preferred + iItem->mnDistributedHeight; 248 break; 249 case Preferred: 250 nPanelHeight = iItem->maLayoutSize.Preferred; 251 break; 252 default: 253 OSL_ASSERT(false); 254 break; 255 } 256 257 // Place the panel. 258 OSL_TRACE("panel %d: placing @%d +%d", iItem->mnPanelIndex, nY, nPanelHeight); 259 rPanel.SetPosSizePixel(0, nY, nWidth, nPanelHeight); 260 261 nY += nPanelHeight; 262 } 263 else 264 { 265 rPanel.Hide(); 266 267 // Add a separator below the collapsed panel, if it is the 268 // last panel in the deck. 269 if (iItem == rLayoutItems.end()-1) 270 { 271 // Separator below the panel title bar. 272 aSeparators.push_back(nY); 273 nY += nDeckSeparatorHeight; 274 } 275 } 276 } 277 278 Deck::ScrollContainerWindow* pScrollContainerWindow 279 = dynamic_cast<Deck::ScrollContainerWindow*>(&rScrollContainer); 280 if (pScrollContainerWindow != NULL) 281 pScrollContainerWindow->SetSeparators(aSeparators); 282 283 return nY; 284 } 285 286 287 288 289 void DeckLayouter::GetRequestedSizes ( 290 ::std::vector<LayoutItem>& rLayoutItems, 291 sal_Int32& rAvailableHeight, 292 const Rectangle& rContentBox) 293 { 294 rAvailableHeight = rContentBox.GetHeight(); 295 296 const sal_Int32 nPanelTitleBarHeight (Theme::GetInteger(Theme::Int_PanelTitleBarHeight)); 297 const sal_Int32 nDeckSeparatorHeight (Theme::GetInteger(Theme::Int_DeckSeparatorHeight)); 298 299 IterateLayoutItems(iItem,rLayoutItems) 300 { 301 ui::LayoutSize aLayoutSize (ui::LayoutSize(0,0,0)); 302 if (iItem->mpPanel != NULL) 303 { 304 if (rLayoutItems.size() == 1 305 && iItem->mpPanel->IsTitleBarOptional()) 306 { 307 // There is only one panel and its title bar is 308 // optional => hide it. 309 rAvailableHeight -= nDeckSeparatorHeight; 310 iItem->mbShowTitleBar = false; 311 } 312 else 313 { 314 // Show the title bar and a separator above and below 315 // the title bar. 316 rAvailableHeight -= nPanelTitleBarHeight; 317 rAvailableHeight -= 2*nDeckSeparatorHeight; 318 } 319 320 if (iItem->mpPanel->IsExpanded()) 321 { 322 Reference<ui::XSidebarPanel> xPanel (iItem->mpPanel->GetPanelComponent()); 323 if (xPanel.is()) 324 aLayoutSize = xPanel->getHeightForWidth(rContentBox.GetWidth()); 325 else 326 aLayoutSize = ui::LayoutSize(MinimalPanelHeight, 0, -1); 327 } 328 } 329 iItem->maLayoutSize = aLayoutSize; 330 } 331 } 332 333 334 335 336 void DeckLayouter::DistributeHeights ( 337 ::std::vector<LayoutItem>& rLayoutItems, 338 const sal_Int32 nHeightToDistribute, 339 const sal_Int32 nContainerHeight, 340 const bool bMinimumHeightIsBase) 341 { 342 if (nHeightToDistribute <= 0) 343 return; 344 345 sal_Int32 nRemainingHeightToDistribute (nHeightToDistribute); 346 347 // Compute the weights as difference between panel base height 348 // (either its minimum or preferred height) and the container height. 349 sal_Int32 nTotalWeight (0); 350 sal_Int32 nNoMaximumCount (0); 351 sal_Int32 nIndex (0); 352 IterateLayoutItems(iItem,rLayoutItems) 353 { 354 if (iItem->maLayoutSize.Maximum == 0) 355 continue; 356 if (iItem->maLayoutSize.Maximum < 0) 357 ++nNoMaximumCount; 358 359 const sal_Int32 nBaseHeight ( 360 bMinimumHeightIsBase 361 ? iItem->maLayoutSize.Minimum 362 : iItem->maLayoutSize.Preferred); 363 if (nBaseHeight < nContainerHeight) 364 { 365 iItem->mnWeight = nContainerHeight - nBaseHeight; 366 nTotalWeight += iItem->mnWeight; 367 } 368 OSL_TRACE("panel %d: base height is %d, weight is %d, min/max/pref are %d/%d/%d", 369 nIndex, nBaseHeight, iItem->mnWeight, 370 iItem->maLayoutSize.Minimum, iItem->maLayoutSize.Maximum, iItem->maLayoutSize.Preferred); 371 } 372 373 if (nTotalWeight == 0) 374 return; 375 376 // First pass of height distribution. 377 nIndex = 0; 378 IterateLayoutItems(iItem,rLayoutItems) 379 { 380 const sal_Int32 nBaseHeight ( 381 bMinimumHeightIsBase 382 ? iItem->maLayoutSize.Minimum 383 : iItem->maLayoutSize.Preferred); 384 sal_Int32 nDistributedHeight (iItem->mnWeight * nHeightToDistribute / nTotalWeight); 385 if (nBaseHeight+nDistributedHeight > iItem->maLayoutSize.Maximum 386 && iItem->maLayoutSize.Maximum >= 0) 387 { 388 nDistributedHeight = ::std::max<sal_Int32>(0,iItem->maLayoutSize.Maximum - nBaseHeight); 389 } 390 iItem->mnDistributedHeight = nDistributedHeight; 391 OSL_TRACE("panel %d: distributed height is %d", nIndex, nDistributedHeight); 392 nRemainingHeightToDistribute -= nDistributedHeight; 393 } 394 395 if (nRemainingHeightToDistribute == 0) 396 return; 397 OSL_ASSERT(nRemainingHeightToDistribute > 0); 398 399 // It is possible that not all of the height could be distributed 400 // because of Maximum heights being smaller than expected. 401 // Distribute the remaining height between the panels that have no 402 // Maximum (ie Maximum==-1). 403 if (nNoMaximumCount == 0) 404 { 405 // There are no panels with unrestricted height. 406 return; 407 } 408 const sal_Int32 nAdditionalHeightPerPanel (nRemainingHeightToDistribute / nNoMaximumCount); 409 // Handle rounding error. 410 sal_Int32 nAdditionalHeightForFirstPanel (nRemainingHeightToDistribute 411 - nNoMaximumCount*nAdditionalHeightPerPanel); 412 nIndex = 0; 413 IterateLayoutItems(iItem,rLayoutItems) 414 { 415 if (iItem->maLayoutSize.Maximum < 0) 416 { 417 iItem->mnDistributedHeight += nAdditionalHeightPerPanel + nAdditionalHeightForFirstPanel; 418 OSL_TRACE("panel %d: additionl height is %d", 419 iItem->mnPanelIndex, 420 nAdditionalHeightPerPanel + nAdditionalHeightForFirstPanel); 421 nRemainingHeightToDistribute -= nAdditionalHeightPerPanel + nAdditionalHeightForFirstPanel; 422 } 423 } 424 425 OSL_ASSERT(nRemainingHeightToDistribute==0); 426 } 427 428 429 430 431 Rectangle DeckLayouter::PlaceDeckTitle ( 432 Window& rDeckTitleBar, 433 const Rectangle& rAvailableSpace) 434 { 435 if (static_cast<DockingWindow*>(rDeckTitleBar.GetParent()->GetParent())->IsFloatingMode()) 436 { 437 // When the side bar is undocked then the outer system window displays the deck title. 438 rDeckTitleBar.Hide(); 439 return rAvailableSpace; 440 } 441 else 442 { 443 const sal_Int32 nDeckTitleBarHeight (Theme::GetInteger(Theme::Int_DeckTitleBarHeight)); 444 rDeckTitleBar.SetPosSizePixel( 445 rAvailableSpace.Left(), 446 rAvailableSpace.Top(), 447 rAvailableSpace.GetWidth(), 448 nDeckTitleBarHeight); 449 rDeckTitleBar.Show(); 450 return Rectangle( 451 rAvailableSpace.Left(), 452 rAvailableSpace.Top() + nDeckTitleBarHeight, 453 rAvailableSpace.Right(), 454 rAvailableSpace.Bottom()); 455 } 456 } 457 458 459 460 461 Rectangle DeckLayouter::PlaceVerticalScrollBar ( 462 ScrollBar& rVerticalScrollBar, 463 const Rectangle& rAvailableSpace, 464 const bool bShowVerticalScrollBar) 465 { 466 if (bShowVerticalScrollBar) 467 { 468 const sal_Int32 nScrollBarWidth (rVerticalScrollBar.GetSizePixel().Width()); 469 rVerticalScrollBar.SetPosSizePixel( 470 rAvailableSpace.Right() - nScrollBarWidth + 1, 471 rAvailableSpace.Top(), 472 nScrollBarWidth, 473 rAvailableSpace.GetHeight()); 474 rVerticalScrollBar.Show(); 475 return Rectangle( 476 rAvailableSpace.Left(), 477 rAvailableSpace.Top(), 478 rAvailableSpace.Right() - nScrollBarWidth, 479 rAvailableSpace.Bottom()); 480 } 481 else 482 { 483 rVerticalScrollBar.Hide(); 484 return rAvailableSpace; 485 } 486 } 487 488 489 490 491 void DeckLayouter::SetupVerticalScrollBar( 492 ScrollBar& rVerticalScrollBar, 493 const sal_Int32 nContentHeight, 494 const sal_Int32 nVisibleHeight) 495 { 496 OSL_ASSERT(nContentHeight > nVisibleHeight); 497 498 rVerticalScrollBar.SetRangeMin(0); 499 rVerticalScrollBar.SetRangeMax(nContentHeight-1); 500 rVerticalScrollBar.SetVisibleSize(nVisibleHeight); 501 } 502 503 504 505 506 void DeckLayouter::UpdateFiller ( 507 Window& rFiller, 508 const Rectangle& rBox) 509 { 510 if (rBox.GetHeight() > 0) 511 { 512 // Show the filler. 513 rFiller.SetBackground(Theme::GetPaint(Theme::Paint_PanelBackground).GetWallpaper()); 514 rFiller.SetPosSizePixel(rBox.TopLeft(), rBox.GetSize()); 515 rFiller.Show(); 516 } 517 else 518 { 519 // Hide the filler. 520 rFiller.Hide(); 521 } 522 } 523 524 525 526 } } // end of namespace sfx2::sidebar 527