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