bump product version to 6.3.0.0.beta1
[LibreOffice.git] / sd / source / ui / slidesorter / view / SlsLayouter.cxx
blobc2332a69c56edd57b1c7557929dcabb6e9442023
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <view/SlsPageObjectLayouter.hxx>
21 #include <view/SlsTheme.hxx>
22 #include <view/SlsLayouter.hxx>
23 #include <model/SlideSorterModel.hxx>
24 #include <model/SlsPageDescriptor.hxx>
25 #include <Window.hxx>
26 #include <rtl/math.hxx>
27 #include <basegfx/numeric/ftools.hxx>
28 #include <osl/diagnose.h>
30 namespace sd { namespace slidesorter { namespace view {
32 class Layouter::Implementation
34 public:
35 VclPtr<sd::Window> mpWindow;
36 static const sal_Int32 mnRequestedLeftBorder = 5;
37 static const sal_Int32 mnRequestedRightBorder = 5;
38 static const sal_Int32 mnRequestedTopBorder = 5;
39 static const sal_Int32 mnRequestedBottomBorder = 5;
40 sal_Int32 mnLeftBorder;
41 sal_Int32 mnRightBorder;
42 sal_Int32 mnTopBorder;
43 sal_Int32 mnBottomBorder;
44 static const sal_Int32 gnVerticalGap = 10 - 2*Theme_FocusIndicatorWidth;
45 static const sal_Int32 gnHorizontalGap = 10 - 2*Theme_FocusIndicatorWidth;
46 Size const maMinimalSize;
47 Size const maPreferredSize;
48 Size const maMaximalSize;
49 sal_Int32 mnMinimalColumnCount;
50 sal_Int32 mnMaximalColumnCount;
51 sal_Int32 mnPageCount;
52 sal_Int32 mnColumnCount;
53 sal_Int32 mnRowCount;
54 /// The maximum number of columns. Can only be larger than the current
55 /// number of columns when there are not enough pages to fill all
56 /// available columns.
57 sal_Int32 mnMaxColumnCount;
58 /// The maximum number of rows. Can only be larger than the current
59 /// number of rows when there are not enough pages to fill all available
60 /// rows.
61 sal_Int32 mnMaxRowCount;
62 Size maPageObjectSize;
63 std::shared_ptr<PageObjectLayouter> mpPageObjectLayouter;
64 std::shared_ptr<view::Theme> mpTheme;
66 /** Specify how the gap between two page objects is associated with the
67 page objects.
69 enum GapMembership {
70 GM_NONE, // Gap is not associated with any page object.
71 GM_PREVIOUS, // The whole gap is associated with the previous page
72 // object (left or above the gap.)
73 GM_BOTH, // Half of the gap is associated with previous, half
74 // with the next page object.
75 GM_NEXT, // The whole gap is associated with the next page
76 // object (right or below the gap.)
77 GM_PAGE_BORDER
80 static Implementation* Create (
81 const Implementation& rImplementation,
82 const Layouter::Orientation eOrientation);
84 virtual Layouter::Orientation GetOrientation() const = 0;
86 bool Rearrange (
87 const Size& rWindowSize,
88 const Size& rPreviewModelSize,
89 const sal_uInt32 nPageCount);
91 /** Calculate the row that the point with the given vertical coordinate
92 is over. The horizontal component is ignored.
93 @param nYPosition
94 Vertical position in model coordinates.
95 @param bIncludeBordersAndGaps
96 When this flag is <TRUE/> then the area of borders and gaps are
97 interpreted as belonging to one of the rows.
98 @param eGapMembership
99 Specifies to what row the gap areas belong. Here GM_NONE
100 corresponds to bIncludeBordersAndGaps being <FALSE/>. When
101 GM_BOTH is given then the upper half is associated to the row
102 above and the lower half to the row below. Values of
103 GM_PREVIOUS and GM_NEXT associate the whole gap area with the
104 row above or below respectively.
106 sal_Int32 GetRowAtPosition (
107 sal_Int32 nYPosition,
108 bool bIncludeBordersAndGaps,
109 GapMembership eGapMembership) const;
111 /** Calculate the column that the point with the given horizontal
112 coordinate is over. The vertical component is ignored.
113 @param nXPosition
114 Horizontal position in model coordinates.
115 @param bIncludeBordersAndGaps
116 When this flag is <TRUE/> then the area of borders and gaps are
117 interpreted as belonging to one of the columns.
118 @param eGapMembership
119 Specifies to what column the gap areas belong.
121 sal_Int32 GetColumnAtPosition (
122 sal_Int32 nXPosition,
123 bool bIncludeBordersAndGaps,
124 GapMembership eGapMembership) const;
126 /** This method is typically called from GetRowAtPosition() and
127 GetColumnAtPosition() to handle a position that lies inside the gap
128 between two adjacent rows or columns.
129 @param nDistanceIntoGap
130 Vertical distance from the bottom of the upper row down into the
131 gap or horizontal distance from the right edge right into the
132 gap.
133 @param eGapMemberhship
134 This value decides what areas in the gap belong to which (or no)
135 row or column.
136 @param nIndex
137 The row index of the upper row or the column index of the left
138 column.
139 @param nGap
140 Width or height of the gap in model coordinates between the
141 page borders.
142 @return
143 Returns either the index of the upper row (as given as nRow), the
144 index of the lower row (nRow+1) or -1 to indicate that the
145 position belongs to no row.
147 static sal_Int32 ResolvePositionInGap (
148 sal_Int32 nDistanceIntoGap,
149 GapMembership eGapMembership,
150 sal_Int32 nIndex,
151 sal_Int32 nGap);
153 /** Calculate the logical part of the insert position, i.e. the page
154 after which to insert.
156 virtual void CalculateLogicalInsertPosition (
157 const Point& rModelPosition,
158 InsertPosition& rPosition) const = 0;
160 /** Calculate the geometrical part of the insert position, i.e. the
161 location of where to display the insertion indicator and the
162 distances about which the leading and trailing pages have to be
163 moved to make room for the indicator.
165 void CalculateGeometricPosition (
166 InsertPosition& rPosition,
167 const Size& rIndicatorSize,
168 const bool bIsVertical,
169 model::SlideSorterModel const & rModel) const;
171 /** Return the bounding box of the preview or, when selected, of the page
172 object. Thus, it returns something like a visual bounding box.
174 ::tools::Rectangle GetInnerBoundingBox (
175 model::SlideSorterModel const & rModel,
176 const sal_Int32 nIndex) const;
178 Range GetValidHorizontalSizeRange() const;
179 Range GetValidVerticalSizeRange() const;
181 Range GetRangeOfVisiblePageObjects (const ::tools::Rectangle& aVisibleArea) const;
182 sal_Int32 GetIndex (
183 const sal_Int32 nRow,
184 const sal_Int32 nColumn,
185 const bool bClampToValidRange) const;
187 ::tools::Rectangle GetPageObjectBox (
188 const sal_Int32 nIndex,
189 const bool bIncludeBorderAndGap = false) const;
191 ::tools::Rectangle GetPageObjectBox (
192 const sal_Int32 nRow,
193 const sal_Int32 nColumn) const;
195 ::tools::Rectangle AddBorderAndGap (
196 const ::tools::Rectangle& rBoundingBox,
197 const sal_Int32 nRow,
198 const sal_Int32 nColumn) const;
200 ::tools::Rectangle GetTotalBoundingBox() const;
202 virtual ~Implementation();
204 protected:
205 Implementation (
206 sd::Window *pWindow,
207 const std::shared_ptr<view::Theme>& rpTheme);
208 explicit Implementation (const Implementation& rImplementation);
210 virtual void CalculateRowAndColumnCount (const Size& rWindowSize) = 0;
211 virtual void CalculateMaxRowAndColumnCount (const Size& rWindowSize) = 0;
212 virtual Size CalculateTargetSize (
213 const Size& rWindowSize) const = 0;
214 Size GetTargetSize (
215 const Size& rWindowSize,
216 const bool bCalculateWidth,
217 const bool bCalculateHeight) const;
218 void CalculateVerticalLogicalInsertPosition (
219 const Point& rModelPosition,
220 InsertPosition& rPosition) const;
223 /** The vertical layouter has one column and as many rows as there are
224 pages.
226 class VerticalImplementation : public Layouter::Implementation
228 public:
229 explicit VerticalImplementation (const Implementation& rImplementation);
231 virtual Layouter::Orientation GetOrientation() const override;
233 void CalculateLogicalInsertPosition (
234 const Point& rModelPosition,
235 InsertPosition& rPosition) const override;
237 protected:
238 virtual void CalculateRowAndColumnCount (const Size& rWindowSize) override;
239 virtual void CalculateMaxRowAndColumnCount (const Size& rWindowSize) override;
240 virtual Size CalculateTargetSize (
241 const Size& rWindowSize) const override;
244 /** The horizontal layouter has one row and as many columns as there are
245 pages.
247 class HorizontalImplementation : public Layouter::Implementation
249 public:
250 explicit HorizontalImplementation(const Implementation& rImplementation);
252 virtual Layouter::Orientation GetOrientation() const override;
254 void CalculateLogicalInsertPosition (
255 const Point& rModelPosition,
256 InsertPosition& rPosition) const override;
258 protected:
259 virtual void CalculateRowAndColumnCount (const Size& rWindowSize) override;
260 virtual void CalculateMaxRowAndColumnCount (const Size& rWindowSize) override;
261 virtual Size CalculateTargetSize (
262 const Size& rWindowSize) const override;
265 /** The number of columns of the grid layouter is defined via a control in
266 the slide sorter tool bar. The number of rows is calculated from the
267 number of columns and the number of pages.
269 class GridImplementation : public Layouter::Implementation
271 public:
272 GridImplementation (
273 sd::Window *pWindow,
274 const std::shared_ptr<view::Theme>& rpTheme);
275 explicit GridImplementation(const Implementation& rImplementation);
277 virtual Layouter::Orientation GetOrientation() const override;
279 void CalculateLogicalInsertPosition (
280 const Point& rModelPosition,
281 InsertPosition& rPosition) const override;
283 protected:
284 virtual void CalculateRowAndColumnCount (const Size& rWindowSize) override;
285 virtual void CalculateMaxRowAndColumnCount (const Size& rWindowSize) override;
286 virtual Size CalculateTargetSize (
287 const Size& rWindowSize) const override;
290 //===== Layouter ==============================================================
292 Layouter::Layouter (
293 sd::Window *pWindow,
294 const std::shared_ptr<Theme>& rpTheme)
295 : mpImplementation(new GridImplementation(pWindow, rpTheme)),
296 mpWindow(pWindow)
300 Layouter::~Layouter()
304 std::shared_ptr<PageObjectLayouter> const & Layouter::GetPageObjectLayouter() const
306 return mpImplementation->mpPageObjectLayouter;
309 void Layouter::SetColumnCount (
310 sal_Int32 nMinimalColumnCount,
311 sal_Int32 nMaximalColumnCount)
313 if (nMinimalColumnCount <= nMaximalColumnCount)
315 mpImplementation->mnMinimalColumnCount = nMinimalColumnCount;
316 mpImplementation->mnMaximalColumnCount = nMaximalColumnCount;
320 bool Layouter::Rearrange (
321 const Orientation eOrientation,
322 const Size& rWindowSize,
323 const Size& rPageSize,
324 const sal_uInt32 nPageCount)
326 OSL_ASSERT(mpWindow);
328 if (eOrientation != mpImplementation->GetOrientation())
329 mpImplementation.reset(Implementation::Create(*mpImplementation, eOrientation));
331 return mpImplementation->Rearrange(rWindowSize, rPageSize, nPageCount);
334 sal_Int32 Layouter::GetColumnCount() const
336 return mpImplementation->mnColumnCount;
339 sal_Int32 Layouter::GetIndex (const sal_Int32 nRow, const sal_Int32 nColumn) const
341 return mpImplementation->GetIndex(nRow,nColumn,true);
344 Size const & Layouter::GetPageObjectSize() const
346 return mpImplementation->maPageObjectSize;
349 ::tools::Rectangle Layouter::GetPageObjectBox (
350 const sal_Int32 nIndex,
351 const bool bIncludeBorderAndGap) const
353 return mpImplementation->GetPageObjectBox(nIndex, bIncludeBorderAndGap);
356 ::tools::Rectangle Layouter::GetTotalBoundingBox() const
358 return mpImplementation->GetTotalBoundingBox();
361 InsertPosition Layouter::GetInsertPosition (
362 const Point& rModelPosition,
363 const Size& rIndicatorSize,
364 model::SlideSorterModel const & rModel) const
366 InsertPosition aPosition;
367 mpImplementation->CalculateLogicalInsertPosition(
368 rModelPosition,
369 aPosition);
370 mpImplementation->CalculateGeometricPosition(
371 aPosition,
372 rIndicatorSize,
373 GetColumnCount()==1,
374 rModel);
375 return aPosition;
378 Range Layouter::GetValidHorizontalSizeRange() const
380 return mpImplementation->GetValidHorizontalSizeRange();
383 Range Layouter::GetValidVerticalSizeRange() const
385 return mpImplementation->GetValidVerticalSizeRange();
388 Range Layouter::GetRangeOfVisiblePageObjects (const ::tools::Rectangle& aVisibleArea) const
390 return mpImplementation->GetRangeOfVisiblePageObjects(aVisibleArea);
393 sal_Int32 Layouter::GetIndexAtPoint (
394 const Point& rPosition,
395 const bool bIncludePageBorders,
396 const bool bClampToValidRange) const
398 const sal_Int32 nRow (
399 mpImplementation->GetRowAtPosition (
400 rPosition.Y(),
401 bIncludePageBorders,
402 bIncludePageBorders ? Implementation::GM_PAGE_BORDER : Implementation::GM_NONE));
403 const sal_Int32 nColumn (
404 mpImplementation->GetColumnAtPosition (
405 rPosition.X(),
406 bIncludePageBorders,
407 bIncludePageBorders ? Implementation::GM_PAGE_BORDER : Implementation::GM_NONE));
409 return mpImplementation->GetIndex(nRow,nColumn,bClampToValidRange);
412 //===== Layouter::Implementation ==============================================
414 Layouter::Implementation* Layouter::Implementation::Create (
415 const Implementation& rImplementation,
416 const Layouter::Orientation eOrientation)
418 switch (eOrientation)
420 case HORIZONTAL: return new HorizontalImplementation(rImplementation);
421 case VERTICAL: return new VerticalImplementation(rImplementation);
422 case GRID:
423 default: return new GridImplementation(rImplementation);
427 Layouter::Implementation::Implementation (
428 sd::Window *pWindow,
429 const std::shared_ptr<view::Theme>& rpTheme)
430 : mpWindow(pWindow),
431 mnLeftBorder(5),
432 mnRightBorder(5),
433 mnTopBorder(5),
434 mnBottomBorder(5),
435 maMinimalSize(132,98),
436 maPreferredSize(200,150),
437 maMaximalSize(600,400),
438 mnMinimalColumnCount(1),
439 mnMaximalColumnCount(15),
440 mnPageCount(0),
441 mnColumnCount(1),
442 mnRowCount(0),
443 mnMaxColumnCount(0),
444 mnMaxRowCount(0),
445 maPageObjectSize(1,1),
446 mpPageObjectLayouter(),
447 mpTheme(rpTheme)
451 Layouter::Implementation::Implementation (const Implementation& rImplementation)
452 : mpWindow(rImplementation.mpWindow),
453 mnLeftBorder(rImplementation.mnLeftBorder),
454 mnRightBorder(rImplementation.mnRightBorder),
455 mnTopBorder(rImplementation.mnTopBorder),
456 mnBottomBorder(rImplementation.mnBottomBorder),
457 maMinimalSize(rImplementation.maMinimalSize),
458 maPreferredSize(rImplementation.maPreferredSize),
459 maMaximalSize(rImplementation.maMaximalSize),
460 mnMinimalColumnCount(rImplementation.mnMinimalColumnCount),
461 mnMaximalColumnCount(rImplementation.mnMaximalColumnCount),
462 mnPageCount(rImplementation.mnPageCount),
463 mnColumnCount(rImplementation.mnColumnCount),
464 mnRowCount(rImplementation.mnRowCount),
465 mnMaxColumnCount(rImplementation.mnMaxColumnCount),
466 mnMaxRowCount(rImplementation.mnMaxRowCount),
467 maPageObjectSize(rImplementation.maPageObjectSize),
468 mpPageObjectLayouter(),
469 mpTheme(rImplementation.mpTheme)
473 Layouter::Implementation::~Implementation()
477 bool Layouter::Implementation::Rearrange (
478 const Size& rWindowSize,
479 const Size& rPreviewModelSize,
480 const sal_uInt32 nPageCount)
482 mnPageCount = nPageCount;
484 // Return early when the window or the model have not yet been initialized.
485 if (rWindowSize.Width()<=0 || rWindowSize.Height()<=0)
486 return false;
487 if (rPreviewModelSize.Width()<=0 || rPreviewModelSize.Height()<=0)
488 return false;
490 CalculateRowAndColumnCount(rWindowSize);
492 // Update the border values.
493 mnLeftBorder = mnRequestedLeftBorder;
494 mnTopBorder = mnRequestedTopBorder;
495 mnRightBorder = mnRequestedRightBorder;
496 mnBottomBorder = mnRequestedBottomBorder;
497 if (mnColumnCount > 1)
499 int nMinimumBorderWidth = gnHorizontalGap/2;
500 if (mnLeftBorder < nMinimumBorderWidth)
501 mnLeftBorder = nMinimumBorderWidth;
502 if (mnRightBorder < nMinimumBorderWidth)
503 mnRightBorder = nMinimumBorderWidth;
505 else
507 int nMinimumBorderHeight = gnVerticalGap/2;
508 if (mnTopBorder < nMinimumBorderHeight)
509 mnTopBorder = nMinimumBorderHeight;
510 if (mnBottomBorder < nMinimumBorderHeight)
511 mnBottomBorder = nMinimumBorderHeight;
514 mpPageObjectLayouter.reset(
515 new PageObjectLayouter(
516 CalculateTargetSize(rWindowSize),
517 rPreviewModelSize,
518 mpWindow,
519 mnPageCount));
521 maPageObjectSize = mpPageObjectLayouter->GetGridMaxSize();
523 CalculateMaxRowAndColumnCount(rWindowSize);
525 return true;
528 sal_Int32 Layouter::Implementation::GetRowAtPosition (
529 sal_Int32 nYPosition,
530 bool bIncludeBordersAndGaps,
531 GapMembership eGapMembership) const
533 sal_Int32 nRow = -1;
535 const sal_Int32 nY = nYPosition - mnTopBorder;
536 if (nY >= 0)
538 // Vertical distance from one row to the next.
539 const sal_Int32 nRowOffset (maPageObjectSize.Height() + gnVerticalGap);
541 // Calculate row consisting of page objects and gap below.
542 nRow = nY / nRowOffset;
544 const sal_Int32 nDistanceIntoGap ((nY - nRow*nRowOffset) - maPageObjectSize.Height());
545 // When inside the gap below then nYPosition is not over a page
546 // object.
547 if (nDistanceIntoGap > 0)
549 sal_Int32 nResolvedRow = ResolvePositionInGap(
550 nDistanceIntoGap,
551 eGapMembership,
552 nRow,
553 gnVerticalGap);
554 if (!bIncludeBordersAndGaps || nResolvedRow != -1)
555 nRow = nResolvedRow;
558 else if (bIncludeBordersAndGaps)
560 // We are in the top border area. Set nRow to the first row when
561 // the top border shall be considered to belong to the first row.
562 nRow = 0;
565 return nRow;
568 sal_Int32 Layouter::Implementation::GetColumnAtPosition (
569 sal_Int32 nXPosition,
570 bool bIncludeBordersAndGaps,
571 GapMembership eGapMembership) const
573 sal_Int32 nColumn = -1;
575 sal_Int32 nX = nXPosition - mnLeftBorder;
576 if (nX >= 0)
578 // Horizontal distance from one column to the next.
579 const sal_Int32 nColumnOffset (maPageObjectSize.Width() + gnHorizontalGap);
581 // Calculate row consisting of page objects and gap below.
582 nColumn = nX / nColumnOffset;
583 if (nColumn < 0)
584 nColumn = 0;
585 else if (nColumn >= mnColumnCount)
586 nColumn = mnColumnCount-1;
588 const sal_Int32 nDistanceIntoGap ((nX - nColumn*nColumnOffset) - maPageObjectSize.Width());
589 // When inside the gap at the right then nXPosition is not over a
590 // page object.
591 if (nDistanceIntoGap > 0)
593 sal_Int32 nResolvedColumn = ResolvePositionInGap(
594 nDistanceIntoGap,
595 eGapMembership,
596 nColumn,
597 gnHorizontalGap);
598 if (!bIncludeBordersAndGaps || nResolvedColumn != -1)
599 nColumn = nResolvedColumn;
602 else if (bIncludeBordersAndGaps)
604 // We are in the left border area. Set nColumn to the first column
605 // when the left border shall be considered to belong to the first
606 // column.
607 nColumn = 0;
609 return nColumn;
612 sal_Int32 Layouter::Implementation::ResolvePositionInGap (
613 sal_Int32 nDistanceIntoGap,
614 GapMembership eGapMembership,
615 sal_Int32 nIndex,
616 sal_Int32 nGap)
618 switch (eGapMembership)
620 case GM_NONE:
621 // The gap is no man's land.
622 nIndex = -1;
623 break;
625 case GM_BOTH:
627 // The lower half of the gap belongs to the next row or column.
628 sal_Int32 nFirstHalfGapWidth = nGap / 2;
629 if (nDistanceIntoGap > nFirstHalfGapWidth)
630 nIndex ++;
631 break;
634 case GM_PREVIOUS:
635 // Row or column already at correct value.
636 break;
638 case GM_NEXT:
639 // The complete gap belongs to the next row or column.
640 nIndex ++;
641 break;
643 case GM_PAGE_BORDER:
644 if (nDistanceIntoGap > 0)
646 if (nDistanceIntoGap > nGap)
648 // Inside the border of the next row or column.
649 nIndex ++;
651 else
653 // Inside the gap between the page borders.
654 nIndex = -1;
657 break;
659 default:
660 nIndex = -1;
663 return nIndex;
666 void Layouter::Implementation::CalculateGeometricPosition (
667 InsertPosition& rPosition,
668 const Size& rIndicatorSize,
669 const bool bIsVertical,
670 model::SlideSorterModel const & rModel) const
672 // 1. Determine right/bottom of the leading page and the left/top of the
673 // trailing page object and how to distribute the missing space.
674 sal_Int32 nLeadingLocation (0);
675 sal_Int32 nTrailingLocation (0);
676 bool bIsLeadingFixed (false);
677 bool bIsTrailingFixed (false);
678 sal_Int32 nSecondaryLocation (0);
679 const sal_Int32 nIndex (rPosition.GetIndex());
681 if (rPosition.IsAtRunStart())
683 // Place indicator at the top of the column.
684 const ::tools::Rectangle aOuterBox (GetPageObjectBox(nIndex));
685 const ::tools::Rectangle aInnerBox (GetInnerBoundingBox(rModel, nIndex));
686 if (bIsVertical)
688 nLeadingLocation = aOuterBox.Top();
689 nTrailingLocation = aInnerBox.Top();
690 nSecondaryLocation = aInnerBox.Center().X();
692 else
694 nLeadingLocation = aOuterBox.Left();
695 nTrailingLocation = aInnerBox.Left();
696 nSecondaryLocation = aInnerBox.Center().Y();
698 bIsLeadingFixed = true;
700 else if (rPosition.IsAtRunEnd())
702 // Place indicator at the bottom/right of the column/row.
704 const ::tools::Rectangle aOuterBox (GetPageObjectBox(nIndex-1));
705 const ::tools::Rectangle aInnerBox (GetInnerBoundingBox(rModel, nIndex-1));
706 if (bIsVertical)
708 nLeadingLocation = aInnerBox.Bottom();
709 nTrailingLocation = aOuterBox.Bottom();
710 nSecondaryLocation = aInnerBox.Center().X();
712 else
714 nLeadingLocation = aInnerBox.Right();
715 nTrailingLocation = aOuterBox.Right();
716 nSecondaryLocation = aInnerBox.Center().Y();
718 bIsTrailingFixed = true;
719 if ( ! rPosition.IsExtraSpaceNeeded())
720 bIsLeadingFixed = true;
722 else
724 // Place indicator between two rows/columns.
725 const ::tools::Rectangle aBox1 (GetInnerBoundingBox(rModel, nIndex-1));
726 const ::tools::Rectangle aBox2 (GetInnerBoundingBox(rModel, nIndex));
727 if (bIsVertical)
729 nLeadingLocation = aBox1.Bottom();
730 nTrailingLocation = aBox2.Top();
731 nSecondaryLocation = (aBox1.Center().X() + aBox2.Center().X()) / 2;
733 else
735 nLeadingLocation = aBox1.Right();
736 nTrailingLocation = aBox2.Left();
737 nSecondaryLocation = (aBox1.Center().Y() + aBox2.Center().Y()) / 2;
741 // 2. Calculate the location of the insert indicator and the offsets of
742 // leading and trailing pages.
743 const sal_Int32 nAvailableSpace (nTrailingLocation - nLeadingLocation);
744 const sal_Int32 nRequiredSpace (bIsVertical ? rIndicatorSize.Height():rIndicatorSize.Width());
745 const sal_Int32 nMissingSpace (::std::max(sal_Int32(0), nRequiredSpace - nAvailableSpace));
746 sal_Int32 nPrimaryLocation (0);
747 sal_Int32 nLeadingOffset (0);
748 sal_Int32 nTrailingOffset (0);
749 if (bIsLeadingFixed)
751 nPrimaryLocation = nLeadingLocation + nRequiredSpace/2;
752 if ( ! bIsTrailingFixed)
753 nTrailingOffset = nMissingSpace;
755 else if (bIsTrailingFixed)
757 nPrimaryLocation = nTrailingLocation - nRequiredSpace/2;
758 nLeadingOffset = -nMissingSpace;
760 else
762 nPrimaryLocation = (nLeadingLocation + nTrailingLocation) /2;
763 nLeadingOffset = -nMissingSpace/2;
764 nTrailingOffset = nMissingSpace + nLeadingOffset;
767 if (bIsVertical)
769 rPosition.SetGeometricalPosition(
770 Point(nSecondaryLocation, nPrimaryLocation),
771 Point(0, nLeadingOffset),
772 Point(0, nTrailingOffset));
774 else
776 rPosition.SetGeometricalPosition(
777 Point(nPrimaryLocation, nSecondaryLocation),
778 Point(nLeadingOffset, 0),
779 Point(nTrailingOffset, 0));
783 ::tools::Rectangle Layouter::Implementation::GetInnerBoundingBox (
784 model::SlideSorterModel const & rModel,
785 const sal_Int32 nIndex) const
787 model::SharedPageDescriptor pDescriptor (rModel.GetPageDescriptor(nIndex));
788 if ( ! pDescriptor)
789 return ::tools::Rectangle();
791 PageObjectLayouter::Part ePart = PageObjectLayouter::Part::Preview;
793 if (pDescriptor->HasState(model::PageDescriptor::ST_Selected))
794 ePart = PageObjectLayouter::Part::PageObject;
796 return mpPageObjectLayouter->GetBoundingBox(
797 pDescriptor, ePart,
798 PageObjectLayouter::ModelCoordinateSystem, true);
801 Range Layouter::Implementation::GetValidHorizontalSizeRange() const
803 return Range(
804 mnLeftBorder + maMinimalSize.Width() + mnRightBorder,
805 mnLeftBorder + maMaximalSize.Width() + mnRightBorder);
808 Range Layouter::Implementation::GetValidVerticalSizeRange() const
810 return Range(
811 mnTopBorder + maMinimalSize.Height() + mnBottomBorder,
812 mnTopBorder + maMaximalSize.Height() + mnBottomBorder);
815 Range Layouter::Implementation::GetRangeOfVisiblePageObjects (const ::tools::Rectangle& aVisibleArea) const
817 const sal_Int32 nRow0 (GetRowAtPosition(aVisibleArea.Top(), true, GM_NEXT));
818 const sal_Int32 nCol0 (GetColumnAtPosition(aVisibleArea.Left(),true, GM_NEXT));
819 const sal_Int32 nRow1 (GetRowAtPosition(aVisibleArea.Bottom(), true, GM_PREVIOUS));
820 const sal_Int32 nCol1 (GetColumnAtPosition(aVisibleArea.Right(), true, GM_PREVIOUS));
822 // When start and end lie in different rows then the range may include
823 // slides outside (left or right of) the given area.
824 return Range(GetIndex(nRow0,nCol0,true), GetIndex(nRow1,nCol1,true));
827 Size Layouter::Implementation::GetTargetSize (
828 const Size& rWindowSize,
829 const bool bCalculateWidth,
830 const bool bCalculateHeight) const
832 if (mnColumnCount<=0 || mnRowCount<=0)
833 return maPreferredSize;
834 if ( ! (bCalculateWidth || bCalculateHeight))
836 OSL_ASSERT(bCalculateWidth || bCalculateHeight);
837 return maPreferredSize;
840 // Calculate the width of each page object.
841 Size aTargetSize (0,0);
842 if (bCalculateWidth)
843 aTargetSize.setWidth(
844 (rWindowSize.Width() - mnLeftBorder - mnRightBorder
845 - (mnColumnCount-1) * gnHorizontalGap)
846 / mnColumnCount);
847 else if (bCalculateHeight)
848 aTargetSize.setHeight(
849 (rWindowSize.Height() - mnTopBorder - mnBottomBorder
850 - (mnRowCount-1) * gnVerticalGap)
851 / mnRowCount);
853 if (bCalculateWidth)
855 if (aTargetSize.Width() < maMinimalSize.Width())
856 aTargetSize.setWidth(maMinimalSize.Width());
857 else if (aTargetSize.Width() > maMaximalSize.Width())
858 aTargetSize.setWidth(maMaximalSize.Width());
860 else if (bCalculateHeight)
862 if (aTargetSize.Height() < maMinimalSize.Height())
863 aTargetSize.setHeight(maMinimalSize.Height());
864 else if (aTargetSize.Height() > maMaximalSize.Height())
865 aTargetSize.setHeight(maMaximalSize.Height());
868 return aTargetSize;
871 sal_Int32 Layouter::Implementation::GetIndex (
872 const sal_Int32 nRow,
873 const sal_Int32 nColumn,
874 const bool bClampToValidRange) const
876 if (nRow >= 0 && nColumn >= 0)
878 const sal_Int32 nIndex (nRow * mnColumnCount + nColumn);
879 if (nIndex >= mnPageCount)
880 if (bClampToValidRange)
881 return mnPageCount-1;
882 else
883 return -1;
884 else
885 return nIndex;
887 else if (bClampToValidRange)
888 return 0;
889 else
890 return -1;
893 ::tools::Rectangle Layouter::Implementation::GetPageObjectBox (
894 const sal_Int32 nIndex,
895 const bool bIncludeBorderAndGap) const
897 const sal_Int32 nRow (nIndex / mnColumnCount);
898 const sal_Int32 nColumn (nIndex % mnColumnCount);
900 const ::tools::Rectangle aBoundingBox (GetPageObjectBox(nRow,nColumn));
901 if (bIncludeBorderAndGap)
902 return AddBorderAndGap(aBoundingBox, nRow, nColumn);
903 else
904 return aBoundingBox;
907 ::tools::Rectangle Layouter::Implementation::GetPageObjectBox (
908 const sal_Int32 nRow,
909 const sal_Int32 nColumn) const
911 return ::tools::Rectangle(
912 Point (mnLeftBorder
913 + nColumn * maPageObjectSize.Width()
914 + std::max<sal_Int32>(nColumn,0) * gnHorizontalGap,
915 mnTopBorder
916 + nRow * maPageObjectSize.Height()
917 + std::max<sal_Int32>(nRow,0) * gnVerticalGap),
918 maPageObjectSize);
921 ::tools::Rectangle Layouter::Implementation::AddBorderAndGap (
922 const ::tools::Rectangle& rBoundingBox,
923 const sal_Int32 nRow,
924 const sal_Int32 nColumn) const
926 ::tools::Rectangle aBoundingBox (rBoundingBox);
928 if (nColumn == 0)
929 aBoundingBox.SetLeft( 0 );
930 else
931 aBoundingBox.AdjustLeft( -(gnHorizontalGap/2) );
932 if (nColumn == mnColumnCount-1)
933 aBoundingBox.AdjustRight(mnRightBorder );
934 else
935 aBoundingBox.AdjustRight(gnHorizontalGap/2 );
936 if (nRow == 0)
937 aBoundingBox.SetTop( 0 );
938 else
939 aBoundingBox.AdjustTop( -(gnVerticalGap/2) );
940 if (nRow == mnRowCount-1)
941 aBoundingBox.AdjustBottom(mnBottomBorder );
942 else
943 aBoundingBox.AdjustBottom(gnVerticalGap/2 );
944 return aBoundingBox;
947 ::tools::Rectangle Layouter::Implementation::GetTotalBoundingBox() const
949 sal_Int32 nHorizontalSize = 0;
950 sal_Int32 nVerticalSize = 0;
951 if (mnColumnCount > 0)
953 sal_Int32 nRowCount = (mnPageCount+mnColumnCount-1) / mnColumnCount;
954 nHorizontalSize =
955 mnLeftBorder
956 + mnRightBorder
957 + mnColumnCount * maPageObjectSize.Width();
958 if (mnColumnCount > 1)
959 nHorizontalSize += (mnColumnCount-1) * gnHorizontalGap;
960 nVerticalSize =
961 mnTopBorder
962 + mnBottomBorder
963 + nRowCount * maPageObjectSize.Height();
964 if (nRowCount > 1)
965 nVerticalSize += (nRowCount-1) * gnVerticalGap;
968 return ::tools::Rectangle (
969 Point(0,0),
970 Size (nHorizontalSize, nVerticalSize)
974 void Layouter::Implementation::CalculateVerticalLogicalInsertPosition (
975 const Point& rModelPosition,
976 InsertPosition& rPosition) const
978 const sal_Int32 nY = rModelPosition.Y() - mnTopBorder + maPageObjectSize.Height()/2;
979 const sal_Int32 nRowHeight (maPageObjectSize.Height() + gnVerticalGap);
980 const sal_Int32 nRow (::std::min(mnPageCount, nY / nRowHeight));
981 rPosition.SetLogicalPosition (
982 nRow,
984 nRow,
985 (nRow == 0),
986 (nRow == mnRowCount),
987 (nRow >= mnMaxRowCount));
990 //===== HorizontalImplementation ================================================
992 HorizontalImplementation::HorizontalImplementation (const Implementation& rImplementation)
993 : Implementation(rImplementation)
997 Layouter::Orientation HorizontalImplementation::GetOrientation() const
999 return Layouter::HORIZONTAL;
1002 void HorizontalImplementation::CalculateRowAndColumnCount (const Size&)
1004 // Row and column count are fixed (for a given page count.)
1005 mnColumnCount = mnPageCount;
1006 mnRowCount = 1;
1009 void HorizontalImplementation::CalculateMaxRowAndColumnCount (const Size& rWindowSize)
1011 mnMaxColumnCount = (rWindowSize.Width() - mnLeftBorder - mnRightBorder)
1012 / (maPageObjectSize.Width() + gnHorizontalGap);
1013 mnMaxRowCount = 1;
1016 Size HorizontalImplementation::CalculateTargetSize (
1017 const Size& rWindowSize) const
1019 return Implementation::GetTargetSize(rWindowSize, false, true);
1022 void HorizontalImplementation::CalculateLogicalInsertPosition (
1023 const Point& rModelPosition,
1024 InsertPosition& rPosition) const
1026 const sal_Int32 nX = rModelPosition.X() - mnLeftBorder + maPageObjectSize.Width()/2;
1027 const sal_Int32 nColumnWidth (maPageObjectSize.Width() + gnHorizontalGap);
1028 const sal_Int32 nColumn (::std::min(mnPageCount, nX / nColumnWidth));
1029 rPosition.SetLogicalPosition (
1031 nColumn,
1032 nColumn,
1033 (nColumn == 0),
1034 (nColumn == mnColumnCount),
1035 (nColumn >= mnMaxColumnCount));
1038 //===== VerticalImplementation ================================================
1040 VerticalImplementation::VerticalImplementation (const Implementation& rImplementation)
1041 : Implementation(rImplementation)
1045 Layouter::Orientation VerticalImplementation::GetOrientation() const
1047 return Layouter::VERTICAL;
1050 void VerticalImplementation::CalculateRowAndColumnCount (const Size&)
1052 // Row and column count are fixed (for a given page count.)
1053 mnRowCount = mnPageCount;
1054 mnColumnCount = 1;
1058 void VerticalImplementation::CalculateMaxRowAndColumnCount (const Size& rWindowSize)
1060 mnMaxRowCount = (rWindowSize.Height() - mnTopBorder - mnBottomBorder)
1061 / (maPageObjectSize.Height() + gnVerticalGap);
1062 mnMaxColumnCount = 1;
1065 Size VerticalImplementation::CalculateTargetSize (
1066 const Size& rWindowSize) const
1068 return Implementation::GetTargetSize(rWindowSize, true, false);
1071 void VerticalImplementation::CalculateLogicalInsertPosition (
1072 const Point& rModelPosition,
1073 InsertPosition& rPosition) const
1075 return CalculateVerticalLogicalInsertPosition(rModelPosition, rPosition);
1078 //===== GridImplementation ================================================
1080 GridImplementation::GridImplementation (
1081 sd::Window *pWindow,
1082 const std::shared_ptr<view::Theme>& rpTheme)
1083 : Implementation(pWindow, rpTheme)
1087 GridImplementation::GridImplementation (const Implementation& rImplementation)
1088 : Implementation(rImplementation)
1092 Layouter::Orientation GridImplementation::GetOrientation() const
1094 return Layouter::GRID;
1097 void GridImplementation::CalculateRowAndColumnCount (const Size& rWindowSize)
1099 // Calculate the column count.
1100 mnColumnCount
1101 = (rWindowSize.Width() - mnRequestedLeftBorder - mnRequestedRightBorder)
1102 / (maPreferredSize.Width() + gnHorizontalGap);
1103 if (mnColumnCount < mnMinimalColumnCount)
1104 mnColumnCount = mnMinimalColumnCount;
1105 if (mnColumnCount > mnMaximalColumnCount)
1106 mnColumnCount = mnMaximalColumnCount;
1107 mnRowCount = (mnPageCount + mnColumnCount-1)/mnColumnCount;
1110 void GridImplementation::CalculateMaxRowAndColumnCount (const Size& rWindowSize)
1112 mnMaxColumnCount = (rWindowSize.Width() - mnLeftBorder - mnRightBorder)
1113 / (maPageObjectSize.Width() + gnHorizontalGap);
1114 mnMaxRowCount = (rWindowSize.Height() - mnTopBorder - mnBottomBorder)
1115 / (maPageObjectSize.Height() + gnVerticalGap);
1118 Size GridImplementation::CalculateTargetSize (
1119 const Size& rWindowSize) const
1121 return Implementation::GetTargetSize(rWindowSize, true, true);
1124 void GridImplementation::CalculateLogicalInsertPosition (
1125 const Point& rModelPosition,
1126 InsertPosition& rPosition) const
1128 if (mnColumnCount == 1)
1130 CalculateVerticalLogicalInsertPosition(rModelPosition, rPosition);
1132 else
1134 // Handle the general case of more than one column.
1135 sal_Int32 nRow (::std::min(
1136 mnRowCount-1,
1137 GetRowAtPosition (rModelPosition.Y(), true, GM_BOTH)));
1138 const sal_Int32 nX = rModelPosition.X() - mnLeftBorder + maPageObjectSize.Width()/2;
1139 const sal_Int32 nColumnWidth (maPageObjectSize.Width() + gnHorizontalGap);
1140 sal_Int32 nColumn (::std::min(mnColumnCount, nX / nColumnWidth));
1141 sal_Int32 nIndex (nRow * mnColumnCount + nColumn);
1142 bool bIsAtRunEnd (nColumn == mnColumnCount);
1144 if (nIndex >= mnPageCount)
1146 nIndex = mnPageCount;
1147 nRow = mnRowCount-1;
1148 nColumn = ::std::min(::std::min(mnPageCount, mnColumnCount), nColumn);
1149 bIsAtRunEnd = true;
1152 rPosition.SetLogicalPosition (
1153 nRow,
1154 nColumn,
1155 nIndex,
1156 (nColumn == 0),
1157 bIsAtRunEnd,
1158 (nColumn >= mnMaxColumnCount));
1162 //===== InsertPosition ========================================================
1164 InsertPosition::InsertPosition()
1165 : mnRow(-1),
1166 mnColumn(-1),
1167 mnIndex(-1),
1168 mbIsAtRunStart(false),
1169 mbIsAtRunEnd(false),
1170 mbIsExtraSpaceNeeded(false),
1171 maLocation(0,0),
1172 maLeadingOffset(0,0),
1173 maTrailingOffset(0,0)
1177 bool InsertPosition::operator== (const InsertPosition& rInsertPosition) const
1179 // Do not compare the geometrical information (maLocation).
1180 return mnRow==rInsertPosition.mnRow
1181 && mnColumn==rInsertPosition.mnColumn
1182 && mnIndex==rInsertPosition.mnIndex
1183 && mbIsAtRunStart==rInsertPosition.mbIsAtRunStart
1184 && mbIsAtRunEnd==rInsertPosition.mbIsAtRunEnd
1185 && mbIsExtraSpaceNeeded==rInsertPosition.mbIsExtraSpaceNeeded;
1188 bool InsertPosition::operator!= (const InsertPosition& rInsertPosition) const
1190 return !operator==(rInsertPosition);
1193 void InsertPosition::SetLogicalPosition (
1194 const sal_Int32 nRow,
1195 const sal_Int32 nColumn,
1196 const sal_Int32 nIndex,
1197 const bool bIsAtRunStart,
1198 const bool bIsAtRunEnd,
1199 const bool bIsExtraSpaceNeeded)
1201 mnRow = nRow;
1202 mnColumn = nColumn;
1203 mnIndex = nIndex;
1204 mbIsAtRunStart = bIsAtRunStart;
1205 mbIsAtRunEnd = bIsAtRunEnd;
1206 mbIsExtraSpaceNeeded = bIsExtraSpaceNeeded;
1209 void InsertPosition::SetGeometricalPosition(
1210 const Point& rLocation,
1211 const Point& rLeadingOffset,
1212 const Point& rTrailingOffset)
1214 maLocation = rLocation;
1215 maLeadingOffset = rLeadingOffset;
1216 maTrailingOffset = rTrailingOffset;
1219 } } } // end of namespace ::sd::slidesorter::namespace
1221 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */