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