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