1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
21 #include <view/SlsPageObjectLayouter.hxx>
22 #include <view/SlsTheme.hxx>
23 #include <view/SlsLayouter.hxx>
24 #include <model/SlideSorterModel.hxx>
25 #include <model/SlsPageDescriptor.hxx>
27 #include <osl/diagnose.h>
29 namespace sd::slidesorter::view
{
31 class Layouter::Implementation
34 VclPtr
<sd::Window
> mpWindow
;
35 static const sal_Int32 mnRequestedLeftBorder
= 5;
36 static const sal_Int32 mnRequestedRightBorder
= 5;
37 static const sal_Int32 mnRequestedTopBorder
= 5;
38 static const sal_Int32 mnRequestedBottomBorder
= 5;
39 sal_Int32 mnLeftBorder
;
40 sal_Int32 mnRightBorder
;
41 sal_Int32 mnTopBorder
;
42 sal_Int32 mnBottomBorder
;
43 static const sal_Int32 gnVerticalGap
= 10 - 2*Theme_FocusIndicatorWidth
;
44 static const sal_Int32 gnHorizontalGap
= 10 - 2*Theme_FocusIndicatorWidth
;
48 sal_Int32 mnMinimalColumnCount
;
49 sal_Int32 mnMaximalColumnCount
;
50 sal_Int32 mnPageCount
;
51 sal_Int32 mnColumnCount
;
53 /// The maximum number of columns. Can only be larger than the current
54 /// number of columns when there are not enough pages to fill all
55 /// available columns.
56 sal_Int32 mnMaxColumnCount
;
57 /// The maximum number of rows. Can only be larger than the current
58 /// number of rows when there are not enough pages to fill all available
60 sal_Int32 mnMaxRowCount
;
61 Size maPageObjectSize
;
62 std::shared_ptr
<PageObjectLayouter
> mpPageObjectLayouter
;
63 std::shared_ptr
<view::Theme
> mpTheme
;
65 /** Specify how the gap between two page objects is associated with the
69 GM_NONE
, // Gap is not associated with any page object.
70 GM_PREVIOUS
, // The whole gap is associated with the previous page
71 // object (left or above the gap.)
72 GM_BOTH
, // Half of the gap is associated with previous, half
73 // with the next page object.
74 GM_NEXT
, // The whole gap is associated with the next page
75 // object (right or below the gap.)
79 static Implementation
* Create (
80 const Implementation
& rImplementation
,
81 const Layouter::Orientation eOrientation
);
83 virtual Layouter::Orientation
GetOrientation() const = 0;
86 const Size
& rWindowSize
,
87 const Size
& rPreviewModelSize
,
88 const sal_uInt32 nPageCount
);
90 /** Calculate the row that the point with the given vertical coordinate
91 is over. The horizontal component is ignored.
93 Vertical position in model coordinates.
94 @param bIncludeBordersAndGaps
95 When this flag is <TRUE/> then the area of borders and gaps are
96 interpreted as belonging to one of the rows.
98 Specifies to what row the gap areas belong. Here GM_NONE
99 corresponds to bIncludeBordersAndGaps being <FALSE/>. When
100 GM_BOTH is given then the upper half is associated to the row
101 above and the lower half to the row below. Values of
102 GM_PREVIOUS and GM_NEXT associate the whole gap area with the
103 row above or below respectively.
105 sal_Int32
GetRowAtPosition (
106 sal_Int32 nYPosition
,
107 bool bIncludeBordersAndGaps
,
108 GapMembership eGapMembership
) const;
110 /** Calculate the column that the point with the given horizontal
111 coordinate is over. The vertical component is ignored.
113 Horizontal position in model coordinates.
114 @param bIncludeBordersAndGaps
115 When this flag is <TRUE/> then the area of borders and gaps are
116 interpreted as belonging to one of the columns.
117 @param eGapMembership
118 Specifies to what column the gap areas belong.
120 sal_Int32
GetColumnAtPosition (
121 sal_Int32 nXPosition
,
122 bool bIncludeBordersAndGaps
,
123 GapMembership eGapMembership
) const;
125 /** This method is typically called from GetRowAtPosition() and
126 GetColumnAtPosition() to handle a position that lies inside the gap
127 between two adjacent rows or columns.
128 @param nDistanceIntoGap
129 Vertical distance from the bottom of the upper row down into the
130 gap or horizontal distance from the right edge right into the
132 @param eGapMemberhship
133 This value decides what areas in the gap belong to which (or no)
136 The row index of the upper row or the column index of the left
139 Width or height of the gap in model coordinates between the
142 Returns either the index of the upper row (as given as nRow), the
143 index of the lower row (nRow+1) or -1 to indicate that the
144 position belongs to no row.
146 static sal_Int32
ResolvePositionInGap (
147 sal_Int32 nDistanceIntoGap
,
148 GapMembership eGapMembership
,
152 /** Calculate the logical part of the insert position, i.e. the page
153 after which to insert.
155 virtual void CalculateLogicalInsertPosition (
156 const Point
& rModelPosition
,
157 InsertPosition
& rPosition
) const = 0;
159 /** Calculate the geometrical part of the insert position, i.e. the
160 location of where to display the insertion indicator and the
161 distances about which the leading and trailing pages have to be
162 moved to make room for the indicator.
164 void CalculateGeometricPosition (
165 InsertPosition
& rPosition
,
166 const Size
& rIndicatorSize
,
167 const bool bIsVertical
,
168 model::SlideSorterModel
const & rModel
) const;
170 /** Return the bounding box of the preview or, when selected, of the page
171 object. Thus, it returns something like a visual bounding box.
173 ::tools::Rectangle
GetInnerBoundingBox (
174 model::SlideSorterModel
const & rModel
,
175 const sal_Int32 nIndex
) const;
177 Range
GetValidHorizontalSizeRange() const;
178 Range
GetValidVerticalSizeRange() const;
180 Range
GetRangeOfVisiblePageObjects (const ::tools::Rectangle
& aVisibleArea
) const;
182 const sal_Int32 nRow
,
183 const sal_Int32 nColumn
,
184 const bool bClampToValidRange
) const;
186 ::tools::Rectangle
GetPageObjectBox (
187 const sal_Int32 nIndex
,
188 const bool bIncludeBorderAndGap
= false) const;
190 ::tools::Rectangle
GetPageObjectBox (
191 const sal_Int32 nRow
,
192 const sal_Int32 nColumn
) const;
194 ::tools::Rectangle
AddBorderAndGap (
195 const ::tools::Rectangle
& rBoundingBox
,
196 const sal_Int32 nRow
,
197 const sal_Int32 nColumn
) const;
199 ::tools::Rectangle
GetTotalBoundingBox() const;
201 virtual ~Implementation();
206 std::shared_ptr
<view::Theme
> pTheme
);
207 explicit Implementation (const Implementation
& rImplementation
);
209 virtual void CalculateRowAndColumnCount (const Size
& rWindowSize
) = 0;
210 virtual void CalculateMaxRowAndColumnCount (const Size
& rWindowSize
) = 0;
211 virtual Size
CalculateTargetSize (
212 const Size
& rWindowSize
) const = 0;
214 const Size
& rWindowSize
,
215 const bool bCalculateWidth
,
216 const bool bCalculateHeight
) const;
217 void CalculateVerticalLogicalInsertPosition (
218 const Point
& rModelPosition
,
219 InsertPosition
& rPosition
) const;
224 /** The vertical layouter has one column and as many rows as there are
227 class VerticalImplementation
: public Layouter::Implementation
230 explicit VerticalImplementation (const Implementation
& rImplementation
);
232 virtual Layouter::Orientation
GetOrientation() const override
;
234 void CalculateLogicalInsertPosition (
235 const Point
& rModelPosition
,
236 InsertPosition
& rPosition
) const override
;
239 virtual void CalculateRowAndColumnCount (const Size
& rWindowSize
) override
;
240 virtual void CalculateMaxRowAndColumnCount (const Size
& rWindowSize
) override
;
241 virtual Size
CalculateTargetSize (
242 const Size
& rWindowSize
) const override
;
245 /** The horizontal layouter has one row and as many columns as there are
248 class HorizontalImplementation
: public Layouter::Implementation
251 explicit HorizontalImplementation(const Implementation
& rImplementation
);
253 virtual Layouter::Orientation
GetOrientation() const override
;
255 void CalculateLogicalInsertPosition (
256 const Point
& rModelPosition
,
257 InsertPosition
& rPosition
) const override
;
260 virtual void CalculateRowAndColumnCount (const Size
& rWindowSize
) override
;
261 virtual void CalculateMaxRowAndColumnCount (const Size
& rWindowSize
) override
;
262 virtual Size
CalculateTargetSize (
263 const Size
& rWindowSize
) const override
;
266 /** The number of columns of the grid layouter is defined via a control in
267 the slide sorter tool bar. The number of rows is calculated from the
268 number of columns and the number of pages.
270 class GridImplementation
: public Layouter::Implementation
275 const std::shared_ptr
<view::Theme
>& rpTheme
);
276 explicit GridImplementation(const Implementation
& rImplementation
);
278 virtual Layouter::Orientation
GetOrientation() const override
;
280 void CalculateLogicalInsertPosition (
281 const Point
& rModelPosition
,
282 InsertPosition
& rPosition
) const override
;
285 virtual void CalculateRowAndColumnCount (const Size
& rWindowSize
) override
;
286 virtual void CalculateMaxRowAndColumnCount (const Size
& rWindowSize
) override
;
287 virtual Size
CalculateTargetSize (
288 const Size
& rWindowSize
) const override
;
293 //===== Layouter ==============================================================
297 const std::shared_ptr
<Theme
>& rpTheme
)
298 : mpImplementation(new GridImplementation(pWindow
, rpTheme
)),
303 Layouter::~Layouter()
307 std::shared_ptr
<PageObjectLayouter
> const & Layouter::GetPageObjectLayouter() const
309 return mpImplementation
->mpPageObjectLayouter
;
312 void Layouter::SetColumnCount (
313 sal_Int32 nMinimalColumnCount
,
314 sal_Int32 nMaximalColumnCount
)
316 if (nMinimalColumnCount
<= nMaximalColumnCount
)
318 mpImplementation
->mnMinimalColumnCount
= nMinimalColumnCount
;
319 mpImplementation
->mnMaximalColumnCount
= nMaximalColumnCount
;
323 bool Layouter::Rearrange (
324 const Orientation eOrientation
,
325 const Size
& rWindowSize
,
326 const Size
& rPageSize
,
327 const sal_uInt32 nPageCount
)
329 OSL_ASSERT(mpWindow
);
331 if (eOrientation
!= mpImplementation
->GetOrientation())
332 mpImplementation
.reset(Implementation::Create(*mpImplementation
, eOrientation
));
334 return mpImplementation
->Rearrange(rWindowSize
, rPageSize
, nPageCount
);
337 sal_Int32
Layouter::GetColumnCount() const
339 return mpImplementation
->mnColumnCount
;
342 sal_Int32
Layouter::GetIndex (const sal_Int32 nRow
, const sal_Int32 nColumn
) const
344 return mpImplementation
->GetIndex(nRow
,nColumn
,true);
347 Size
const & Layouter::GetPageObjectSize() const
349 return mpImplementation
->maPageObjectSize
;
352 ::tools::Rectangle
Layouter::GetPageObjectBox (
353 const sal_Int32 nIndex
,
354 const bool bIncludeBorderAndGap
) const
356 return mpImplementation
->GetPageObjectBox(nIndex
, bIncludeBorderAndGap
);
359 ::tools::Rectangle
Layouter::GetTotalBoundingBox() const
361 return mpImplementation
->GetTotalBoundingBox();
364 InsertPosition
Layouter::GetInsertPosition (
365 const Point
& rModelPosition
,
366 const Size
& rIndicatorSize
,
367 model::SlideSorterModel
const & rModel
) const
369 InsertPosition aPosition
;
370 mpImplementation
->CalculateLogicalInsertPosition(
373 mpImplementation
->CalculateGeometricPosition(
381 Range
Layouter::GetValidHorizontalSizeRange() const
383 return mpImplementation
->GetValidHorizontalSizeRange();
386 Range
Layouter::GetValidVerticalSizeRange() const
388 return mpImplementation
->GetValidVerticalSizeRange();
391 Range
Layouter::GetRangeOfVisiblePageObjects (const ::tools::Rectangle
& aVisibleArea
) const
393 return mpImplementation
->GetRangeOfVisiblePageObjects(aVisibleArea
);
396 sal_Int32
Layouter::GetIndexAtPoint (
397 const Point
& rPosition
,
398 const bool bIncludePageBorders
,
399 const bool bClampToValidRange
) const
401 const sal_Int32
nRow (
402 mpImplementation
->GetRowAtPosition (
405 bIncludePageBorders
? Implementation::GM_PAGE_BORDER
: Implementation::GM_NONE
));
406 const sal_Int32
nColumn (
407 mpImplementation
->GetColumnAtPosition (
410 bIncludePageBorders
? Implementation::GM_PAGE_BORDER
: Implementation::GM_NONE
));
412 return mpImplementation
->GetIndex(nRow
,nColumn
,bClampToValidRange
);
415 //===== Layouter::Implementation ==============================================
417 Layouter::Implementation
* Layouter::Implementation::Create (
418 const Implementation
& rImplementation
,
419 const Layouter::Orientation eOrientation
)
421 switch (eOrientation
)
423 case HORIZONTAL
: return new HorizontalImplementation(rImplementation
);
424 case VERTICAL
: return new VerticalImplementation(rImplementation
);
426 default: return new GridImplementation(rImplementation
);
430 Layouter::Implementation::Implementation (
432 std::shared_ptr
<view::Theme
> pTheme
)
438 maMinimalSize(132,98),
439 maPreferredSize(200,150),
440 maMaximalSize(600,400),
441 mnMinimalColumnCount(1),
442 mnMaximalColumnCount(15),
448 maPageObjectSize(1,1),
449 mpTheme(std::move(pTheme
))
453 Layouter::Implementation::Implementation (const Implementation
& rImplementation
)
454 : mpWindow(rImplementation
.mpWindow
),
455 mnLeftBorder(rImplementation
.mnLeftBorder
),
456 mnRightBorder(rImplementation
.mnRightBorder
),
457 mnTopBorder(rImplementation
.mnTopBorder
),
458 mnBottomBorder(rImplementation
.mnBottomBorder
),
459 maMinimalSize(rImplementation
.maMinimalSize
),
460 maPreferredSize(rImplementation
.maPreferredSize
),
461 maMaximalSize(rImplementation
.maMaximalSize
),
462 mnMinimalColumnCount(rImplementation
.mnMinimalColumnCount
),
463 mnMaximalColumnCount(rImplementation
.mnMaximalColumnCount
),
464 mnPageCount(rImplementation
.mnPageCount
),
465 mnColumnCount(rImplementation
.mnColumnCount
),
466 mnRowCount(rImplementation
.mnRowCount
),
467 mnMaxColumnCount(rImplementation
.mnMaxColumnCount
),
468 mnMaxRowCount(rImplementation
.mnMaxRowCount
),
469 maPageObjectSize(rImplementation
.maPageObjectSize
),
470 mpTheme(rImplementation
.mpTheme
)
474 Layouter::Implementation::~Implementation()
478 bool Layouter::Implementation::Rearrange (
479 const Size
& rWindowSize
,
480 const Size
& rPreviewModelSize
,
481 const sal_uInt32 nPageCount
)
483 mnPageCount
= nPageCount
;
485 // Return early when the window or the model have not yet been initialized.
486 if (rWindowSize
.IsEmpty())
488 if (rPreviewModelSize
.IsEmpty())
491 CalculateRowAndColumnCount(rWindowSize
);
493 // Update the border values.
494 mnLeftBorder
= mnRequestedLeftBorder
;
495 mnTopBorder
= mnRequestedTopBorder
;
496 mnRightBorder
= mnRequestedRightBorder
;
497 mnBottomBorder
= mnRequestedBottomBorder
;
498 if (mnColumnCount
> 1)
500 int nMinimumBorderWidth
= gnHorizontalGap
/2;
501 if (mnLeftBorder
< nMinimumBorderWidth
)
502 mnLeftBorder
= nMinimumBorderWidth
;
503 if (mnRightBorder
< nMinimumBorderWidth
)
504 mnRightBorder
= nMinimumBorderWidth
;
508 int nMinimumBorderHeight
= gnVerticalGap
/2;
509 if (mnTopBorder
< nMinimumBorderHeight
)
510 mnTopBorder
= nMinimumBorderHeight
;
511 if (mnBottomBorder
< nMinimumBorderHeight
)
512 mnBottomBorder
= nMinimumBorderHeight
;
515 mpPageObjectLayouter
=
516 std::make_shared
<PageObjectLayouter
>(
517 CalculateTargetSize(rWindowSize
),
522 maPageObjectSize
= mpPageObjectLayouter
->GetGridMaxSize();
524 CalculateMaxRowAndColumnCount(rWindowSize
);
529 sal_Int32
Layouter::Implementation::GetRowAtPosition (
530 sal_Int32 nYPosition
,
531 bool bIncludeBordersAndGaps
,
532 GapMembership eGapMembership
) const
536 const sal_Int32 nY
= nYPosition
- mnTopBorder
;
539 // Vertical distance from one row to the next.
540 const sal_Int32
nRowOffset (maPageObjectSize
.Height() + gnVerticalGap
);
542 // Calculate row consisting of page objects and gap below.
543 nRow
= nY
/ nRowOffset
;
545 const sal_Int32
nDistanceIntoGap ((nY
- nRow
*nRowOffset
) - maPageObjectSize
.Height());
546 // When inside the gap below then nYPosition is not over a page
548 if (nDistanceIntoGap
> 0)
550 sal_Int32 nResolvedRow
= ResolvePositionInGap(
555 if (!bIncludeBordersAndGaps
|| nResolvedRow
!= -1)
559 else if (bIncludeBordersAndGaps
)
561 // We are in the top border area. Set nRow to the first row when
562 // the top border shall be considered to belong to the first row.
569 sal_Int32
Layouter::Implementation::GetColumnAtPosition (
570 sal_Int32 nXPosition
,
571 bool bIncludeBordersAndGaps
,
572 GapMembership eGapMembership
) const
574 sal_Int32 nColumn
= -1;
576 sal_Int32 nX
= nXPosition
- mnLeftBorder
;
579 // Horizontal distance from one column to the next.
580 const sal_Int32
nColumnOffset (maPageObjectSize
.Width() + gnHorizontalGap
);
582 // Calculate row consisting of page objects and gap below.
583 nColumn
= nX
/ nColumnOffset
;
586 else if (nColumn
>= mnColumnCount
)
587 nColumn
= mnColumnCount
-1;
589 const sal_Int32
nDistanceIntoGap ((nX
- nColumn
*nColumnOffset
) - maPageObjectSize
.Width());
590 // When inside the gap at the right then nXPosition is not over a
592 if (nDistanceIntoGap
> 0)
594 sal_Int32 nResolvedColumn
= ResolvePositionInGap(
599 if (!bIncludeBordersAndGaps
|| nResolvedColumn
!= -1)
600 nColumn
= nResolvedColumn
;
603 else if (bIncludeBordersAndGaps
)
605 // We are in the left border area. Set nColumn to the first column
606 // when the left border shall be considered to belong to the first
613 sal_Int32
Layouter::Implementation::ResolvePositionInGap (
614 sal_Int32 nDistanceIntoGap
,
615 GapMembership eGapMembership
,
619 switch (eGapMembership
)
622 // The gap is no man's land.
628 // The lower half of the gap belongs to the next row or column.
629 sal_Int32 nFirstHalfGapWidth
= nGap
/ 2;
630 if (nDistanceIntoGap
> nFirstHalfGapWidth
)
636 // Row or column already at correct value.
640 // The complete gap belongs to the next row or column.
645 if (nDistanceIntoGap
> 0)
647 if (nDistanceIntoGap
> nGap
)
649 // Inside the border of the next row or column.
654 // Inside the gap between the page borders.
667 void Layouter::Implementation::CalculateGeometricPosition (
668 InsertPosition
& rPosition
,
669 const Size
& rIndicatorSize
,
670 const bool bIsVertical
,
671 model::SlideSorterModel
const & rModel
) const
673 // 1. Determine right/bottom of the leading page and the left/top of the
674 // trailing page object and how to distribute the missing space.
675 sal_Int32
nLeadingLocation (0);
676 sal_Int32
nTrailingLocation (0);
677 bool bIsLeadingFixed (false);
678 bool bIsTrailingFixed (false);
679 sal_Int32
nSecondaryLocation (0);
680 const sal_Int32
nIndex (rPosition
.GetIndex());
682 if (rPosition
.IsAtRunStart())
684 // Place indicator at the top of the column.
685 const ::tools::Rectangle
aOuterBox (GetPageObjectBox(nIndex
));
686 const ::tools::Rectangle
aInnerBox (GetInnerBoundingBox(rModel
, nIndex
));
689 nLeadingLocation
= aOuterBox
.Top();
690 nTrailingLocation
= aInnerBox
.Top();
691 nSecondaryLocation
= aInnerBox
.Center().X();
695 nLeadingLocation
= aOuterBox
.Left();
696 nTrailingLocation
= aInnerBox
.Left();
697 nSecondaryLocation
= aInnerBox
.Center().Y();
699 bIsLeadingFixed
= true;
701 else if (rPosition
.IsAtRunEnd())
703 // Place indicator at the bottom/right of the column/row.
705 const ::tools::Rectangle
aOuterBox (GetPageObjectBox(nIndex
-1));
706 const ::tools::Rectangle
aInnerBox (GetInnerBoundingBox(rModel
, nIndex
-1));
709 nLeadingLocation
= aInnerBox
.Bottom();
710 nTrailingLocation
= aOuterBox
.Bottom();
711 nSecondaryLocation
= aInnerBox
.Center().X();
715 nLeadingLocation
= aInnerBox
.Right();
716 nTrailingLocation
= aOuterBox
.Right();
717 nSecondaryLocation
= aInnerBox
.Center().Y();
719 bIsTrailingFixed
= true;
720 if ( ! rPosition
.IsExtraSpaceNeeded())
721 bIsLeadingFixed
= true;
725 // Place indicator between two rows/columns.
726 const ::tools::Rectangle
aBox1 (GetInnerBoundingBox(rModel
, nIndex
-1));
727 const ::tools::Rectangle
aBox2 (GetInnerBoundingBox(rModel
, nIndex
));
730 nLeadingLocation
= aBox1
.Bottom();
731 nTrailingLocation
= aBox2
.Top();
732 nSecondaryLocation
= (aBox1
.Center().X() + aBox2
.Center().X()) / 2;
736 nLeadingLocation
= aBox1
.Right();
737 nTrailingLocation
= aBox2
.Left();
738 nSecondaryLocation
= (aBox1
.Center().Y() + aBox2
.Center().Y()) / 2;
742 // 2. Calculate the location of the insert indicator and the offsets of
743 // leading and trailing pages.
744 const sal_Int32
nAvailableSpace (nTrailingLocation
- nLeadingLocation
);
745 const sal_Int32
nRequiredSpace (bIsVertical
? rIndicatorSize
.Height():rIndicatorSize
.Width());
746 const sal_Int32
nMissingSpace (::std::max(sal_Int32(0), nRequiredSpace
- nAvailableSpace
));
747 sal_Int32
nPrimaryLocation (0);
748 sal_Int32
nLeadingOffset (0);
749 sal_Int32
nTrailingOffset (0);
752 nPrimaryLocation
= nLeadingLocation
+ nRequiredSpace
/2;
753 if ( ! bIsTrailingFixed
)
754 nTrailingOffset
= nMissingSpace
;
756 else if (bIsTrailingFixed
)
758 nPrimaryLocation
= nTrailingLocation
- nRequiredSpace
/2;
759 nLeadingOffset
= -nMissingSpace
;
763 nPrimaryLocation
= (nLeadingLocation
+ nTrailingLocation
) /2;
764 nLeadingOffset
= -nMissingSpace
/2;
765 nTrailingOffset
= nMissingSpace
+ nLeadingOffset
;
770 rPosition
.SetGeometricalPosition(
771 Point(nSecondaryLocation
, nPrimaryLocation
),
772 Point(0, nLeadingOffset
),
773 Point(0, nTrailingOffset
));
777 rPosition
.SetGeometricalPosition(
778 Point(nPrimaryLocation
, nSecondaryLocation
),
779 Point(nLeadingOffset
, 0),
780 Point(nTrailingOffset
, 0));
784 ::tools::Rectangle
Layouter::Implementation::GetInnerBoundingBox (
785 model::SlideSorterModel
const & rModel
,
786 const sal_Int32 nIndex
) const
788 model::SharedPageDescriptor
pDescriptor (rModel
.GetPageDescriptor(nIndex
));
790 return ::tools::Rectangle();
792 PageObjectLayouter::Part ePart
= PageObjectLayouter::Part::Preview
;
794 if (pDescriptor
->HasState(model::PageDescriptor::ST_Selected
))
795 ePart
= PageObjectLayouter::Part::PageObject
;
797 return mpPageObjectLayouter
->GetBoundingBox(
799 PageObjectLayouter::ModelCoordinateSystem
, true);
802 Range
Layouter::Implementation::GetValidHorizontalSizeRange() const
805 mnLeftBorder
+ maMinimalSize
.Width() + mnRightBorder
,
806 mnLeftBorder
+ maMaximalSize
.Width() + mnRightBorder
);
809 Range
Layouter::Implementation::GetValidVerticalSizeRange() const
812 mnTopBorder
+ maMinimalSize
.Height() + mnBottomBorder
,
813 mnTopBorder
+ maMaximalSize
.Height() + mnBottomBorder
);
816 Range
Layouter::Implementation::GetRangeOfVisiblePageObjects (const ::tools::Rectangle
& aVisibleArea
) const
818 // technically that's not empty, but it's the default, so...
819 if (aVisibleArea
.IsEmpty())
820 return Range(-1, -1);
822 const sal_Int32
nRow0 (GetRowAtPosition(aVisibleArea
.Top(), true, GM_NEXT
));
823 const sal_Int32
nCol0 (GetColumnAtPosition(aVisibleArea
.Left(),true, GM_NEXT
));
824 const sal_Int32
nRow1 (GetRowAtPosition(aVisibleArea
.Bottom(), true, GM_PREVIOUS
));
825 const sal_Int32
nCol1 (GetColumnAtPosition(aVisibleArea
.Right(), true, GM_PREVIOUS
));
827 // When start and end lie in different rows then the range may include
828 // slides outside (left or right of) the given area.
829 return Range(GetIndex(nRow0
,nCol0
,true), GetIndex(nRow1
,nCol1
,true));
832 Size
Layouter::Implementation::GetTargetSize (
833 const Size
& rWindowSize
,
834 const bool bCalculateWidth
,
835 const bool bCalculateHeight
) const
837 if (mnColumnCount
<=0 || mnRowCount
<=0)
838 return maPreferredSize
;
839 if ( ! (bCalculateWidth
|| bCalculateHeight
))
841 OSL_ASSERT(bCalculateWidth
|| bCalculateHeight
);
842 return maPreferredSize
;
845 // Calculate the width of each page object.
846 Size
aTargetSize (0,0);
848 aTargetSize
.setWidth(
849 (rWindowSize
.Width() - mnLeftBorder
- mnRightBorder
850 - (mnColumnCount
-1) * gnHorizontalGap
)
852 else if (bCalculateHeight
)
853 aTargetSize
.setHeight(
854 (rWindowSize
.Height() - mnTopBorder
- mnBottomBorder
855 - (mnRowCount
-1) * gnVerticalGap
)
860 if (aTargetSize
.Width() < maMinimalSize
.Width())
861 aTargetSize
.setWidth(maMinimalSize
.Width());
862 else if (aTargetSize
.Width() > maMaximalSize
.Width())
863 aTargetSize
.setWidth(maMaximalSize
.Width());
865 else if (bCalculateHeight
)
867 if (aTargetSize
.Height() < maMinimalSize
.Height())
868 aTargetSize
.setHeight(maMinimalSize
.Height());
869 else if (aTargetSize
.Height() > maMaximalSize
.Height())
870 aTargetSize
.setHeight(maMaximalSize
.Height());
876 sal_Int32
Layouter::Implementation::GetIndex (
877 const sal_Int32 nRow
,
878 const sal_Int32 nColumn
,
879 const bool bClampToValidRange
) const
881 if (nRow
>= 0 && nColumn
>= 0)
883 const sal_Int32
nIndex (nRow
* mnColumnCount
+ nColumn
);
884 if (nIndex
>= mnPageCount
)
885 if (bClampToValidRange
)
886 return mnPageCount
-1;
892 else if (bClampToValidRange
)
898 ::tools::Rectangle
Layouter::Implementation::GetPageObjectBox (
899 const sal_Int32 nIndex
,
900 const bool bIncludeBorderAndGap
) const
902 const sal_Int32
nRow (nIndex
/ mnColumnCount
);
903 const sal_Int32
nColumn (nIndex
% mnColumnCount
);
905 const ::tools::Rectangle
aBoundingBox (GetPageObjectBox(nRow
,nColumn
));
906 if (bIncludeBorderAndGap
)
907 return AddBorderAndGap(aBoundingBox
, nRow
, nColumn
);
912 ::tools::Rectangle
Layouter::Implementation::GetPageObjectBox (
913 const sal_Int32 nRow
,
914 const sal_Int32 nColumn
) const
916 return ::tools::Rectangle(
918 + nColumn
* maPageObjectSize
.Width()
919 + std::max
<sal_Int32
>(nColumn
,0) * gnHorizontalGap
,
921 + nRow
* maPageObjectSize
.Height()
922 + std::max
<sal_Int32
>(nRow
,0) * gnVerticalGap
),
926 ::tools::Rectangle
Layouter::Implementation::AddBorderAndGap (
927 const ::tools::Rectangle
& rBoundingBox
,
928 const sal_Int32 nRow
,
929 const sal_Int32 nColumn
) const
931 ::tools::Rectangle
aBoundingBox (rBoundingBox
);
934 aBoundingBox
.SetLeft( 0 );
936 aBoundingBox
.AdjustLeft( -(gnHorizontalGap
/2) );
937 if (nColumn
== mnColumnCount
-1)
938 aBoundingBox
.AdjustRight(mnRightBorder
);
940 aBoundingBox
.AdjustRight(gnHorizontalGap
/2 );
942 aBoundingBox
.SetTop( 0 );
944 aBoundingBox
.AdjustTop( -(gnVerticalGap
/2) );
945 if (nRow
== mnRowCount
-1)
946 aBoundingBox
.AdjustBottom(mnBottomBorder
);
948 aBoundingBox
.AdjustBottom(gnVerticalGap
/2 );
952 ::tools::Rectangle
Layouter::Implementation::GetTotalBoundingBox() const
954 sal_Int32 nHorizontalSize
= 0;
955 sal_Int32 nVerticalSize
= 0;
956 if (mnColumnCount
> 0)
958 sal_Int32 nRowCount
= (mnPageCount
+mnColumnCount
-1) / mnColumnCount
;
962 + mnColumnCount
* maPageObjectSize
.Width();
963 if (mnColumnCount
> 1)
964 nHorizontalSize
+= (mnColumnCount
-1) * gnHorizontalGap
;
968 + nRowCount
* maPageObjectSize
.Height();
970 nVerticalSize
+= (nRowCount
-1) * gnVerticalGap
;
973 return ::tools::Rectangle (
975 Size (nHorizontalSize
, nVerticalSize
)
979 void Layouter::Implementation::CalculateVerticalLogicalInsertPosition (
980 const Point
& rModelPosition
,
981 InsertPosition
& rPosition
) const
983 const sal_Int32 nY
= rModelPosition
.Y() - mnTopBorder
+ maPageObjectSize
.Height()/2;
984 const sal_Int32
nRowHeight (maPageObjectSize
.Height() + gnVerticalGap
);
985 const sal_Int32
nRow (::std::min(mnPageCount
, nY
/ nRowHeight
));
986 rPosition
.SetLogicalPosition (
991 (nRow
== mnRowCount
),
992 (nRow
>= mnMaxRowCount
));
995 //===== HorizontalImplementation ================================================
997 HorizontalImplementation::HorizontalImplementation (const Implementation
& rImplementation
)
998 : Implementation(rImplementation
)
1002 Layouter::Orientation
HorizontalImplementation::GetOrientation() const
1004 return Layouter::HORIZONTAL
;
1007 void HorizontalImplementation::CalculateRowAndColumnCount (const Size
&)
1009 // Row and column count are fixed (for a given page count.)
1010 mnColumnCount
= mnPageCount
;
1014 void HorizontalImplementation::CalculateMaxRowAndColumnCount (const Size
& rWindowSize
)
1016 mnMaxColumnCount
= (rWindowSize
.Width() - mnLeftBorder
- mnRightBorder
)
1017 / (maPageObjectSize
.Width() + gnHorizontalGap
);
1021 Size
HorizontalImplementation::CalculateTargetSize (
1022 const Size
& rWindowSize
) const
1024 return Implementation::GetTargetSize(rWindowSize
, false, true);
1027 void HorizontalImplementation::CalculateLogicalInsertPosition (
1028 const Point
& rModelPosition
,
1029 InsertPosition
& rPosition
) const
1031 const sal_Int32 nX
= rModelPosition
.X() - mnLeftBorder
+ maPageObjectSize
.Width()/2;
1032 const sal_Int32
nColumnWidth (maPageObjectSize
.Width() + gnHorizontalGap
);
1033 const sal_Int32
nColumn (::std::min(mnPageCount
, nX
/ nColumnWidth
));
1034 rPosition
.SetLogicalPosition (
1039 (nColumn
== mnColumnCount
),
1040 (nColumn
>= mnMaxColumnCount
));
1043 //===== VerticalImplementation ================================================
1045 VerticalImplementation::VerticalImplementation (const Implementation
& rImplementation
)
1046 : Implementation(rImplementation
)
1050 Layouter::Orientation
VerticalImplementation::GetOrientation() const
1052 return Layouter::VERTICAL
;
1055 void VerticalImplementation::CalculateRowAndColumnCount (const Size
&)
1057 // Row and column count are fixed (for a given page count.)
1058 mnRowCount
= mnPageCount
;
1063 void VerticalImplementation::CalculateMaxRowAndColumnCount (const Size
& rWindowSize
)
1065 mnMaxRowCount
= (rWindowSize
.Height() - mnTopBorder
- mnBottomBorder
)
1066 / (maPageObjectSize
.Height() + gnVerticalGap
);
1067 mnMaxColumnCount
= 1;
1070 Size
VerticalImplementation::CalculateTargetSize (
1071 const Size
& rWindowSize
) const
1073 return Implementation::GetTargetSize(rWindowSize
, true, false);
1076 void VerticalImplementation::CalculateLogicalInsertPosition (
1077 const Point
& rModelPosition
,
1078 InsertPosition
& rPosition
) const
1080 return CalculateVerticalLogicalInsertPosition(rModelPosition
, rPosition
);
1083 //===== GridImplementation ================================================
1085 GridImplementation::GridImplementation (
1086 sd::Window
*pWindow
,
1087 const std::shared_ptr
<view::Theme
>& rpTheme
)
1088 : Implementation(pWindow
, rpTheme
)
1092 GridImplementation::GridImplementation (const Implementation
& rImplementation
)
1093 : Implementation(rImplementation
)
1097 Layouter::Orientation
GridImplementation::GetOrientation() const
1099 return Layouter::GRID
;
1102 void GridImplementation::CalculateRowAndColumnCount (const Size
& rWindowSize
)
1104 // Calculate the column count.
1106 = (rWindowSize
.Width() - mnRequestedLeftBorder
- mnRequestedRightBorder
)
1107 / (maPreferredSize
.Width() + gnHorizontalGap
);
1108 if (mnColumnCount
< mnMinimalColumnCount
)
1109 mnColumnCount
= mnMinimalColumnCount
;
1110 if (mnColumnCount
> mnMaximalColumnCount
)
1111 mnColumnCount
= mnMaximalColumnCount
;
1112 mnRowCount
= (mnPageCount
+ mnColumnCount
-1)/mnColumnCount
;
1115 void GridImplementation::CalculateMaxRowAndColumnCount (const Size
& rWindowSize
)
1117 mnMaxColumnCount
= (rWindowSize
.Width() - mnLeftBorder
- mnRightBorder
)
1118 / (maPageObjectSize
.Width() + gnHorizontalGap
);
1119 mnMaxRowCount
= (rWindowSize
.Height() - mnTopBorder
- mnBottomBorder
)
1120 / (maPageObjectSize
.Height() + gnVerticalGap
);
1123 Size
GridImplementation::CalculateTargetSize (
1124 const Size
& rWindowSize
) const
1126 return Implementation::GetTargetSize(rWindowSize
, true, true);
1129 void GridImplementation::CalculateLogicalInsertPosition (
1130 const Point
& rModelPosition
,
1131 InsertPosition
& rPosition
) const
1133 if (mnColumnCount
== 1)
1135 CalculateVerticalLogicalInsertPosition(rModelPosition
, rPosition
);
1139 // Handle the general case of more than one column.
1140 sal_Int32
nRow (::std::min(
1142 GetRowAtPosition (rModelPosition
.Y(), true, GM_BOTH
)));
1143 const sal_Int32 nX
= rModelPosition
.X() - mnLeftBorder
+ maPageObjectSize
.Width()/2;
1144 const sal_Int32
nColumnWidth (maPageObjectSize
.Width() + gnHorizontalGap
);
1145 sal_Int32
nColumn (::std::min(mnColumnCount
, nX
/ nColumnWidth
));
1146 sal_Int32
nIndex (nRow
* mnColumnCount
+ nColumn
);
1147 bool bIsAtRunEnd (nColumn
== mnColumnCount
);
1149 if (nIndex
>= mnPageCount
)
1151 nIndex
= mnPageCount
;
1152 nRow
= mnRowCount
-1;
1153 nColumn
= ::std::min(::std::min(mnPageCount
, mnColumnCount
), nColumn
);
1157 rPosition
.SetLogicalPosition (
1163 (nColumn
>= mnMaxColumnCount
));
1167 //===== InsertPosition ========================================================
1169 InsertPosition::InsertPosition()
1173 mbIsAtRunStart(false),
1174 mbIsAtRunEnd(false),
1175 mbIsExtraSpaceNeeded(false),
1177 maLeadingOffset(0,0),
1178 maTrailingOffset(0,0)
1182 bool InsertPosition::operator== (const InsertPosition
& rInsertPosition
) const
1184 // Do not compare the geometrical information (maLocation).
1185 return mnRow
==rInsertPosition
.mnRow
1186 && mnColumn
==rInsertPosition
.mnColumn
1187 && mnIndex
==rInsertPosition
.mnIndex
1188 && mbIsAtRunStart
==rInsertPosition
.mbIsAtRunStart
1189 && mbIsAtRunEnd
==rInsertPosition
.mbIsAtRunEnd
1190 && mbIsExtraSpaceNeeded
==rInsertPosition
.mbIsExtraSpaceNeeded
;
1193 bool InsertPosition::operator!= (const InsertPosition
& rInsertPosition
) const
1195 return !operator==(rInsertPosition
);
1198 void InsertPosition::SetLogicalPosition (
1199 const sal_Int32 nRow
,
1200 const sal_Int32 nColumn
,
1201 const sal_Int32 nIndex
,
1202 const bool bIsAtRunStart
,
1203 const bool bIsAtRunEnd
,
1204 const bool bIsExtraSpaceNeeded
)
1209 mbIsAtRunStart
= bIsAtRunStart
;
1210 mbIsAtRunEnd
= bIsAtRunEnd
;
1211 mbIsExtraSpaceNeeded
= bIsExtraSpaceNeeded
;
1214 void InsertPosition::SetGeometricalPosition(
1215 const Point
& rLocation
,
1216 const Point
& rLeadingOffset
,
1217 const Point
& rTrailingOffset
)
1219 maLocation
= rLocation
;
1220 maLeadingOffset
= rLeadingOffset
;
1221 maTrailingOffset
= rTrailingOffset
;
1224 } // end of namespace ::sd::slidesorter::namespace
1226 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */