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 "PanelTitleBar.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( !bool(iItem->mpPanel)) 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 PanelTitleBar* 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 rPanel.Invalidate(); 266 267 nY += nPanelHeight; 268 } 269 else 270 { 271 rPanel.Hide(); 272 273 // Add a separator below the collapsed panel, if it is the 274 // last panel in the deck. 275 if (iItem == rLayoutItems.end()-1) 276 { 277 // Separator below the panel title bar. 278 aSeparators.push_back(nY); 279 nY += nDeckSeparatorHeight; 280 } 281 } 282 } 283 284 Deck::ScrollContainerWindow* pScrollContainerWindow 285 = dynamic_cast<Deck::ScrollContainerWindow*>(&rScrollContainer); 286 if (pScrollContainerWindow != NULL) 287 pScrollContainerWindow->SetSeparators(aSeparators); 288 289 return nY; 290 } 291 292 293 294 295 void DeckLayouter::GetRequestedSizes ( 296 ::std::vector<LayoutItem>& rLayoutItems, 297 sal_Int32& rAvailableHeight, 298 const Rectangle& rContentBox) 299 { 300 rAvailableHeight = rContentBox.GetHeight(); 301 302 const sal_Int32 nPanelTitleBarHeight (Theme::GetInteger(Theme::Int_PanelTitleBarHeight)); 303 const sal_Int32 nDeckSeparatorHeight (Theme::GetInteger(Theme::Int_DeckSeparatorHeight)); 304 305 IterateLayoutItems(iItem,rLayoutItems) 306 { 307 ui::LayoutSize aLayoutSize (ui::LayoutSize(0,0,0)); 308 if( bool(iItem->mpPanel)) 309 { 310 if (rLayoutItems.size() == 1 311 && iItem->mpPanel->IsTitleBarOptional()) 312 { 313 // There is only one panel and its title bar is 314 // optional => hide it. 315 rAvailableHeight -= nDeckSeparatorHeight; 316 iItem->mbShowTitleBar = false; 317 } 318 else 319 { 320 // Show the title bar and a separator above and below 321 // the title bar. 322 rAvailableHeight -= nPanelTitleBarHeight; 323 rAvailableHeight -= nDeckSeparatorHeight; 324 } 325 326 if (iItem->mpPanel->IsExpanded()) 327 { 328 Reference<ui::XSidebarPanel> xPanel (iItem->mpPanel->GetPanelComponent()); 329 if (xPanel.is()) 330 aLayoutSize = xPanel->getHeightForWidth(rContentBox.GetWidth()); 331 else 332 aLayoutSize = ui::LayoutSize(MinimalPanelHeight, -1, 0); 333 } 334 } 335 iItem->maLayoutSize = aLayoutSize; 336 } 337 } 338 339 340 341 342 void DeckLayouter::DistributeHeights ( 343 ::std::vector<LayoutItem>& rLayoutItems, 344 const sal_Int32 nHeightToDistribute, 345 const sal_Int32 nContainerHeight, 346 const bool bMinimumHeightIsBase) 347 { 348 if (nHeightToDistribute <= 0) 349 return; 350 351 sal_Int32 nRemainingHeightToDistribute (nHeightToDistribute); 352 353 // Compute the weights as difference between panel base height 354 // (either its minimum or preferred height) and the container height. 355 sal_Int32 nTotalWeight (0); 356 sal_Int32 nNoMaximumCount (0); 357 IterateLayoutItems(iItem,rLayoutItems) 358 { 359 if (iItem->maLayoutSize.Maximum == 0) 360 continue; 361 if (iItem->maLayoutSize.Maximum < 0) 362 ++nNoMaximumCount; 363 364 const sal_Int32 nBaseHeight ( 365 bMinimumHeightIsBase 366 ? iItem->maLayoutSize.Minimum 367 : iItem->maLayoutSize.Preferred); 368 if (nBaseHeight < nContainerHeight) 369 { 370 iItem->mnWeight = nContainerHeight - nBaseHeight; 371 nTotalWeight += iItem->mnWeight; 372 } 373 } 374 375 if (nTotalWeight == 0) 376 return; 377 378 // First pass of height distribution. 379 IterateLayoutItems(iItem,rLayoutItems) 380 { 381 const sal_Int32 nBaseHeight ( 382 bMinimumHeightIsBase 383 ? iItem->maLayoutSize.Minimum 384 : iItem->maLayoutSize.Preferred); 385 sal_Int32 nDistributedHeight (iItem->mnWeight * nHeightToDistribute / nTotalWeight); 386 if (nBaseHeight+nDistributedHeight > iItem->maLayoutSize.Maximum 387 && iItem->maLayoutSize.Maximum >= 0) 388 { 389 nDistributedHeight = ::std::max<sal_Int32>(0,iItem->maLayoutSize.Maximum - nBaseHeight); 390 } 391 iItem->mnDistributedHeight = 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 IterateLayoutItems(iItem,rLayoutItems) 413 { 414 if (iItem->maLayoutSize.Maximum < 0) 415 { 416 iItem->mnDistributedHeight += nAdditionalHeightPerPanel + nAdditionalHeightForFirstPanel; 417 nRemainingHeightToDistribute -= nAdditionalHeightPerPanel + nAdditionalHeightForFirstPanel; 418 } 419 } 420 421 OSL_ASSERT(nRemainingHeightToDistribute==0); 422 } 423 424 425 426 427 Rectangle DeckLayouter::PlaceDeckTitle ( 428 Window& rDeckTitleBar, 429 const Rectangle& rAvailableSpace) 430 { 431 if (static_cast<DockingWindow*>(rDeckTitleBar.GetParent()->GetParent())->IsFloatingMode()) 432 { 433 // When the side bar is undocked then the outer system window displays the deck title. 434 rDeckTitleBar.Hide(); 435 return rAvailableSpace; 436 } 437 else 438 { 439 const sal_Int32 nDeckTitleBarHeight (Theme::GetInteger(Theme::Int_DeckTitleBarHeight)); 440 rDeckTitleBar.SetPosSizePixel( 441 rAvailableSpace.Left(), 442 rAvailableSpace.Top(), 443 rAvailableSpace.GetWidth(), 444 nDeckTitleBarHeight); 445 rDeckTitleBar.Show(); 446 return Rectangle( 447 rAvailableSpace.Left(), 448 rAvailableSpace.Top() + nDeckTitleBarHeight, 449 rAvailableSpace.Right(), 450 rAvailableSpace.Bottom()); 451 } 452 } 453 454 455 456 457 Rectangle DeckLayouter::PlaceVerticalScrollBar ( 458 ScrollBar& rVerticalScrollBar, 459 const Rectangle& rAvailableSpace, 460 const bool bShowVerticalScrollBar) 461 { 462 if (bShowVerticalScrollBar) 463 { 464 const sal_Int32 nScrollBarWidth (rVerticalScrollBar.GetSizePixel().Width()); 465 rVerticalScrollBar.SetPosSizePixel( 466 rAvailableSpace.Right() - nScrollBarWidth + 1, 467 rAvailableSpace.Top(), 468 nScrollBarWidth, 469 rAvailableSpace.GetHeight()); 470 rVerticalScrollBar.Show(); 471 return Rectangle( 472 rAvailableSpace.Left(), 473 rAvailableSpace.Top(), 474 rAvailableSpace.Right() - nScrollBarWidth, 475 rAvailableSpace.Bottom()); 476 } 477 else 478 { 479 rVerticalScrollBar.Hide(); 480 return rAvailableSpace; 481 } 482 } 483 484 485 486 487 void DeckLayouter::SetupVerticalScrollBar( 488 ScrollBar& rVerticalScrollBar, 489 const sal_Int32 nContentHeight, 490 const sal_Int32 nVisibleHeight) 491 { 492 OSL_ASSERT(nContentHeight > nVisibleHeight); 493 494 rVerticalScrollBar.SetRangeMin(0); 495 rVerticalScrollBar.SetRangeMax(nContentHeight-1); 496 rVerticalScrollBar.SetVisibleSize(nVisibleHeight); 497 } 498 499 500 501 502 void DeckLayouter::UpdateFiller ( 503 Window& rFiller, 504 const Rectangle& rBox) 505 { 506 if (rBox.GetHeight() > 0) 507 { 508 // Show the filler. 509 rFiller.SetBackground(Theme::GetPaint(Theme::Paint_PanelBackground).GetWallpaper()); 510 rFiller.SetPosSizePixel(rBox.TopLeft(), rBox.GetSize()); 511 rFiller.Show(); 512 } 513 else 514 { 515 // Hide the filler. 516 rFiller.Hide(); 517 } 518 } 519 520 521 522 } } // end of namespace sfx2::sidebar 523