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
LayoutDeck(const Rectangle aContentArea,SharedPanelContainer & rPanels,Window & rDeckTitleBar,Window & rScrollClipWindow,Window & rScrollContainer,Window & rFiller,ScrollBar & rVerticalScrollBar)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
LayoutPanels(const Rectangle aContentArea,::std::vector<LayoutItem> & rLayoutItems,Window & rScrollClipWindow,Window & rScrollContainer,ScrollBar & rVerticalScrollBar,const bool bShowVerticalScrollBar)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
PlacePanels(::std::vector<LayoutItem> & rLayoutItems,const sal_Int32 nWidth,const LayoutMode eMode,Window & rScrollContainer)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
GetRequestedSizes(::std::vector<LayoutItem> & rLayoutItems,sal_Int32 & rAvailableHeight,const Rectangle & rContentBox)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
DistributeHeights(::std::vector<LayoutItem> & rLayoutItems,const sal_Int32 nHeightToDistribute,const sal_Int32 nContainerHeight,const bool bMinimumHeightIsBase)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
PlaceDeckTitle(Window & rDeckTitleBar,const Rectangle & rAvailableSpace)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
PlaceVerticalScrollBar(ScrollBar & rVerticalScrollBar,const Rectangle & rAvailableSpace,const bool bShowVerticalScrollBar)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
SetupVerticalScrollBar(ScrollBar & rVerticalScrollBar,const sal_Int32 nContentHeight,const sal_Int32 nVisibleHeight)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
UpdateFiller(Window & rFiller,const Rectangle & rBox)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