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