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 sal_Int32 nY = rVerticalScrollBar.GetThumbPos(); 180 if (nContentHeight-nY < aBox.GetHeight()) 181 nY = nContentHeight-aBox.GetHeight(); 182 if (nY < 0) 183 nY = 0; 184 rScrollContainer.SetPosSizePixel( 185 0, 186 -nY, 187 nWidth, 188 nContentHeight); 189 190 if (bShowVerticalScrollBar) 191 SetupVerticalScrollBar(rVerticalScrollBar, nContentHeight, aBox.GetHeight()); 192 193 const sal_Int32 nUsedHeight (PlacePanels(rLayoutItems, nWidth, eMode, rScrollContainer)); 194 aBox.Top() += nUsedHeight; 195 return aBox; 196 } 197 198 199 200 201 sal_Int32 DeckLayouter::PlacePanels ( 202 ::std::vector<LayoutItem>& rLayoutItems, 203 const sal_Int32 nWidth, 204 const LayoutMode eMode, 205 Window& rScrollContainer) 206 { 207 ::std::vector<sal_Int32> aSeparators; 208 const sal_Int32 nDeckSeparatorHeight (Theme::GetInteger(Theme::Int_DeckSeparatorHeight)); 209 const sal_Int32 nPanelTitleBarHeight (Theme::GetInteger(Theme::Int_PanelTitleBarHeight)); 210 sal_Int32 nY (0); 211 212 // Assign heights and places. 213 IterateLayoutItems(iItem,rLayoutItems) 214 { 215 if (iItem->mpPanel == NULL) 216 continue; 217 218 Panel& rPanel (*iItem->mpPanel); 219 220 // Separator above the panel title bar. 221 aSeparators.push_back(nY); 222 nY += nDeckSeparatorHeight; 223 224 // Place the title bar. 225 TitleBar* pTitleBar = rPanel.GetTitleBar(); 226 if (pTitleBar != NULL) 227 { 228 if (iItem->mbShowTitleBar) 229 { 230 pTitleBar->SetPosSizePixel(0, nY, nWidth, nPanelTitleBarHeight); 231 pTitleBar->Show(); 232 nY += nPanelTitleBarHeight; 233 } 234 else 235 { 236 pTitleBar->Hide(); 237 } 238 } 239 240 if (rPanel.IsExpanded()) 241 { 242 rPanel.Show(); 243 244 // Determine the height of the panel depending on layout 245 // mode and distributed heights. 246 sal_Int32 nPanelHeight (0); 247 switch(eMode) 248 { 249 case MinimumOrLarger: 250 nPanelHeight = iItem->maLayoutSize.Minimum + iItem->mnDistributedHeight; 251 break; 252 case PreferredOrLarger: 253 nPanelHeight = iItem->maLayoutSize.Preferred + iItem->mnDistributedHeight; 254 break; 255 case Preferred: 256 nPanelHeight = iItem->maLayoutSize.Preferred; 257 break; 258 default: 259 OSL_ASSERT(false); 260 break; 261 } 262 263 // Place the panel. 264 rPanel.SetPosSizePixel(0, nY, nWidth, nPanelHeight); 265 266 nY += nPanelHeight; 267 } 268 else 269 { 270 rPanel.Hide(); 271 272 // Add a separator below the collapsed panel, if it is the 273 // last panel in the deck. 274 if (iItem == rLayoutItems.end()-1) 275 { 276 // Separator below the panel title bar. 277 aSeparators.push_back(nY); 278 nY += nDeckSeparatorHeight; 279 } 280 } 281 } 282 283 Deck::ScrollContainerWindow* pScrollContainerWindow 284 = dynamic_cast<Deck::ScrollContainerWindow*>(&rScrollContainer); 285 if (pScrollContainerWindow != NULL) 286 pScrollContainerWindow->SetSeparators(aSeparators); 287 288 return nY; 289 } 290 291 292 293 294 void DeckLayouter::GetRequestedSizes ( 295 ::std::vector<LayoutItem>& rLayoutItems, 296 sal_Int32& rAvailableHeight, 297 const Rectangle& rContentBox) 298 { 299 rAvailableHeight = rContentBox.GetHeight(); 300 301 const sal_Int32 nPanelTitleBarHeight (Theme::GetInteger(Theme::Int_PanelTitleBarHeight)); 302 const sal_Int32 nDeckSeparatorHeight (Theme::GetInteger(Theme::Int_DeckSeparatorHeight)); 303 304 IterateLayoutItems(iItem,rLayoutItems) 305 { 306 ui::LayoutSize aLayoutSize (ui::LayoutSize(0,0,0)); 307 if (iItem->mpPanel != NULL) 308 { 309 if (rLayoutItems.size() == 1 310 && iItem->mpPanel->IsTitleBarOptional()) 311 { 312 // There is only one panel and its title bar is 313 // optional => hide it. 314 rAvailableHeight -= nDeckSeparatorHeight; 315 iItem->mbShowTitleBar = false; 316 } 317 else 318 { 319 // Show the title bar and a separator above and below 320 // the title bar. 321 rAvailableHeight -= nPanelTitleBarHeight; 322 rAvailableHeight -= nDeckSeparatorHeight; 323 } 324 325 if (iItem->mpPanel->IsExpanded()) 326 { 327 Reference<ui::XSidebarPanel> xPanel (iItem->mpPanel->GetPanelComponent()); 328 if (xPanel.is()) 329 aLayoutSize = xPanel->getHeightForWidth(rContentBox.GetWidth()); 330 else 331 aLayoutSize = ui::LayoutSize(MinimalPanelHeight, 0, -1); 332 } 333 } 334 iItem->maLayoutSize = aLayoutSize; 335 } 336 } 337 338 339 340 341 void DeckLayouter::DistributeHeights ( 342 ::std::vector<LayoutItem>& rLayoutItems, 343 const sal_Int32 nHeightToDistribute, 344 const sal_Int32 nContainerHeight, 345 const bool bMinimumHeightIsBase) 346 { 347 if (nHeightToDistribute <= 0) 348 return; 349 350 sal_Int32 nRemainingHeightToDistribute (nHeightToDistribute); 351 352 // Compute the weights as difference between panel base height 353 // (either its minimum or preferred height) and the container height. 354 sal_Int32 nTotalWeight (0); 355 sal_Int32 nNoMaximumCount (0); 356 IterateLayoutItems(iItem,rLayoutItems) 357 { 358 if (iItem->maLayoutSize.Maximum == 0) 359 continue; 360 if (iItem->maLayoutSize.Maximum < 0) 361 ++nNoMaximumCount; 362 363 const sal_Int32 nBaseHeight ( 364 bMinimumHeightIsBase 365 ? iItem->maLayoutSize.Minimum 366 : iItem->maLayoutSize.Preferred); 367 if (nBaseHeight < nContainerHeight) 368 { 369 iItem->mnWeight = nContainerHeight - nBaseHeight; 370 nTotalWeight += iItem->mnWeight; 371 } 372 } 373 374 if (nTotalWeight == 0) 375 return; 376 377 // First pass of height distribution. 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 nRemainingHeightToDistribute -= nDistributedHeight; 392 } 393 394 if (nRemainingHeightToDistribute == 0) 395 return; 396 OSL_ASSERT(nRemainingHeightToDistribute > 0); 397 398 // It is possible that not all of the height could be distributed 399 // because of Maximum heights being smaller than expected. 400 // Distribute the remaining height between the panels that have no 401 // Maximum (ie Maximum==-1). 402 if (nNoMaximumCount == 0) 403 { 404 // There are no panels with unrestricted height. 405 return; 406 } 407 const sal_Int32 nAdditionalHeightPerPanel (nRemainingHeightToDistribute / nNoMaximumCount); 408 // Handle rounding error. 409 sal_Int32 nAdditionalHeightForFirstPanel (nRemainingHeightToDistribute 410 - nNoMaximumCount*nAdditionalHeightPerPanel); 411 IterateLayoutItems(iItem,rLayoutItems) 412 { 413 if (iItem->maLayoutSize.Maximum < 0) 414 { 415 iItem->mnDistributedHeight += 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