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 Size
Layouter::AddGap(const Size
& rObjectSize
)
354 Size newSize
= rObjectSize
;
355 newSize
.AdjustWidth(Implementation::gnHorizontalGap
);
356 newSize
.AdjustHeight(Implementation::gnVerticalGap
);
360 ::tools::Rectangle
Layouter::GetPageObjectBox (
361 const sal_Int32 nIndex
,
362 const bool bIncludeBorderAndGap
) const
364 return mpImplementation
->GetPageObjectBox(nIndex
, bIncludeBorderAndGap
);
367 ::tools::Rectangle
Layouter::GetTotalBoundingBox() const
369 return mpImplementation
->GetTotalBoundingBox();
372 InsertPosition
Layouter::GetInsertPosition (
373 const Point
& rModelPosition
,
374 const Size
& rIndicatorSize
,
375 model::SlideSorterModel
const & rModel
) const
377 InsertPosition aPosition
;
378 mpImplementation
->CalculateLogicalInsertPosition(
381 mpImplementation
->CalculateGeometricPosition(
389 Range
Layouter::GetValidHorizontalSizeRange() const
391 return mpImplementation
->GetValidHorizontalSizeRange();
394 Range
Layouter::GetValidVerticalSizeRange() const
396 return mpImplementation
->GetValidVerticalSizeRange();
399 Range
Layouter::GetRangeOfVisiblePageObjects (const ::tools::Rectangle
& aVisibleArea
) const
401 return mpImplementation
->GetRangeOfVisiblePageObjects(aVisibleArea
);
404 sal_Int32
Layouter::GetIndexAtPoint (
405 const Point
& rPosition
,
406 const bool bIncludePageBorders
,
407 const bool bClampToValidRange
) const
409 const sal_Int32
nRow (
410 mpImplementation
->GetRowAtPosition (
413 bIncludePageBorders
? Implementation::GM_PAGE_BORDER
: Implementation::GM_NONE
));
414 const sal_Int32
nColumn (
415 mpImplementation
->GetColumnAtPosition (
418 bIncludePageBorders
? Implementation::GM_PAGE_BORDER
: Implementation::GM_NONE
));
420 return mpImplementation
->GetIndex(nRow
,nColumn
,bClampToValidRange
);
423 //===== Layouter::Implementation ==============================================
425 Layouter::Implementation
* Layouter::Implementation::Create (
426 const Implementation
& rImplementation
,
427 const Layouter::Orientation eOrientation
)
429 switch (eOrientation
)
431 case HORIZONTAL
: return new HorizontalImplementation(rImplementation
);
432 case VERTICAL
: return new VerticalImplementation(rImplementation
);
434 default: return new GridImplementation(rImplementation
);
438 Layouter::Implementation::Implementation (
440 std::shared_ptr
<view::Theme
> pTheme
)
446 maMinimalSize(132,98),
447 maPreferredSize(200,150),
448 maMaximalSize(600,400),
449 mnMinimalColumnCount(1),
450 mnMaximalColumnCount(15),
456 maPageObjectSize(1,1),
457 mpTheme(std::move(pTheme
))
461 Layouter::Implementation::Implementation (const Implementation
& rImplementation
)
462 : mpWindow(rImplementation
.mpWindow
),
463 mnLeftBorder(rImplementation
.mnLeftBorder
),
464 mnRightBorder(rImplementation
.mnRightBorder
),
465 mnTopBorder(rImplementation
.mnTopBorder
),
466 mnBottomBorder(rImplementation
.mnBottomBorder
),
467 maMinimalSize(rImplementation
.maMinimalSize
),
468 maPreferredSize(rImplementation
.maPreferredSize
),
469 maMaximalSize(rImplementation
.maMaximalSize
),
470 mnMinimalColumnCount(rImplementation
.mnMinimalColumnCount
),
471 mnMaximalColumnCount(rImplementation
.mnMaximalColumnCount
),
472 mnPageCount(rImplementation
.mnPageCount
),
473 mnColumnCount(rImplementation
.mnColumnCount
),
474 mnRowCount(rImplementation
.mnRowCount
),
475 mnMaxColumnCount(rImplementation
.mnMaxColumnCount
),
476 mnMaxRowCount(rImplementation
.mnMaxRowCount
),
477 maPageObjectSize(rImplementation
.maPageObjectSize
),
478 mpTheme(rImplementation
.mpTheme
)
482 Layouter::Implementation::~Implementation()
486 bool Layouter::Implementation::Rearrange (
487 const Size
& rWindowSize
,
488 const Size
& rPreviewModelSize
,
489 const sal_uInt32 nPageCount
)
491 mnPageCount
= nPageCount
;
493 // Return early when the window or the model have not yet been initialized.
494 if (rWindowSize
.IsEmpty())
496 if (rPreviewModelSize
.IsEmpty())
499 CalculateRowAndColumnCount(rWindowSize
);
501 // Update the border values.
502 mnLeftBorder
= mnRequestedLeftBorder
;
503 mnTopBorder
= mnRequestedTopBorder
;
504 mnRightBorder
= mnRequestedRightBorder
;
505 mnBottomBorder
= mnRequestedBottomBorder
;
506 if (mnColumnCount
> 1)
508 int nMinimumBorderWidth
= gnHorizontalGap
/2;
509 if (mnLeftBorder
< nMinimumBorderWidth
)
510 mnLeftBorder
= nMinimumBorderWidth
;
511 if (mnRightBorder
< nMinimumBorderWidth
)
512 mnRightBorder
= nMinimumBorderWidth
;
516 int nMinimumBorderHeight
= gnVerticalGap
/2;
517 if (mnTopBorder
< nMinimumBorderHeight
)
518 mnTopBorder
= nMinimumBorderHeight
;
519 if (mnBottomBorder
< nMinimumBorderHeight
)
520 mnBottomBorder
= nMinimumBorderHeight
;
523 mpPageObjectLayouter
=
524 std::make_shared
<PageObjectLayouter
>(
525 CalculateTargetSize(rWindowSize
),
530 maPageObjectSize
= mpPageObjectLayouter
->GetGridMaxSize();
532 CalculateMaxRowAndColumnCount(rWindowSize
);
537 sal_Int32
Layouter::Implementation::GetRowAtPosition (
538 sal_Int32 nYPosition
,
539 bool bIncludeBordersAndGaps
,
540 GapMembership eGapMembership
) const
544 const sal_Int32 nY
= nYPosition
- mnTopBorder
;
547 // Vertical distance from one row to the next.
548 const sal_Int32
nRowOffset (maPageObjectSize
.Height() + gnVerticalGap
);
550 // Calculate row consisting of page objects and gap below.
551 nRow
= nY
/ nRowOffset
;
553 const sal_Int32
nDistanceIntoGap ((nY
- nRow
*nRowOffset
) - maPageObjectSize
.Height());
554 // When inside the gap below then nYPosition is not over a page
556 if (nDistanceIntoGap
> 0)
558 sal_Int32 nResolvedRow
= ResolvePositionInGap(
563 if (!bIncludeBordersAndGaps
|| nResolvedRow
!= -1)
567 else if (bIncludeBordersAndGaps
)
569 // We are in the top border area. Set nRow to the first row when
570 // the top border shall be considered to belong to the first row.
577 sal_Int32
Layouter::Implementation::GetColumnAtPosition (
578 sal_Int32 nXPosition
,
579 bool bIncludeBordersAndGaps
,
580 GapMembership eGapMembership
) const
582 sal_Int32 nColumn
= -1;
584 sal_Int32 nX
= nXPosition
- mnLeftBorder
;
587 // Horizontal distance from one column to the next.
588 const sal_Int32
nColumnOffset (maPageObjectSize
.Width() + gnHorizontalGap
);
590 // Calculate row consisting of page objects and gap below.
591 nColumn
= nX
/ nColumnOffset
;
594 else if (nColumn
>= mnColumnCount
)
595 nColumn
= mnColumnCount
-1;
597 const sal_Int32
nDistanceIntoGap ((nX
- nColumn
*nColumnOffset
) - maPageObjectSize
.Width());
598 // When inside the gap at the right then nXPosition is not over a
600 if (nDistanceIntoGap
> 0)
602 sal_Int32 nResolvedColumn
= ResolvePositionInGap(
607 if (!bIncludeBordersAndGaps
|| nResolvedColumn
!= -1)
608 nColumn
= nResolvedColumn
;
611 else if (bIncludeBordersAndGaps
)
613 // We are in the left border area. Set nColumn to the first column
614 // when the left border shall be considered to belong to the first
621 sal_Int32
Layouter::Implementation::ResolvePositionInGap (
622 sal_Int32 nDistanceIntoGap
,
623 GapMembership eGapMembership
,
627 switch (eGapMembership
)
630 // The gap is no man's land.
636 // The lower half of the gap belongs to the next row or column.
637 sal_Int32 nFirstHalfGapWidth
= nGap
/ 2;
638 if (nDistanceIntoGap
> nFirstHalfGapWidth
)
644 // Row or column already at correct value.
648 // The complete gap belongs to the next row or column.
653 if (nDistanceIntoGap
> 0)
655 if (nDistanceIntoGap
> nGap
)
657 // Inside the border of the next row or column.
662 // Inside the gap between the page borders.
675 void Layouter::Implementation::CalculateGeometricPosition (
676 InsertPosition
& rPosition
,
677 const Size
& rIndicatorSize
,
678 const bool bIsVertical
,
679 model::SlideSorterModel
const & rModel
) const
681 // 1. Determine right/bottom of the leading page and the left/top of the
682 // trailing page object and how to distribute the missing space.
683 sal_Int32
nLeadingLocation (0);
684 sal_Int32
nTrailingLocation (0);
685 bool bIsLeadingFixed (false);
686 bool bIsTrailingFixed (false);
687 sal_Int32
nSecondaryLocation (0);
688 const sal_Int32
nIndex (rPosition
.GetIndex());
690 if (rPosition
.IsAtRunStart())
692 // Place indicator at the top of the column.
693 const ::tools::Rectangle
aOuterBox (GetPageObjectBox(nIndex
));
694 const ::tools::Rectangle
aInnerBox (GetInnerBoundingBox(rModel
, nIndex
));
697 nLeadingLocation
= aOuterBox
.Top();
698 nTrailingLocation
= aInnerBox
.Top();
699 nSecondaryLocation
= aInnerBox
.Center().X();
703 nLeadingLocation
= aOuterBox
.Left();
704 nTrailingLocation
= aInnerBox
.Left();
705 nSecondaryLocation
= aInnerBox
.Center().Y();
707 bIsLeadingFixed
= true;
709 else if (rPosition
.IsAtRunEnd())
711 // Place indicator at the bottom/right of the column/row.
713 const ::tools::Rectangle
aOuterBox (GetPageObjectBox(nIndex
-1));
714 const ::tools::Rectangle
aInnerBox (GetInnerBoundingBox(rModel
, nIndex
-1));
717 nLeadingLocation
= aInnerBox
.Bottom();
718 nTrailingLocation
= aOuterBox
.Bottom();
719 nSecondaryLocation
= aInnerBox
.Center().X();
723 nLeadingLocation
= aInnerBox
.Right();
724 nTrailingLocation
= aOuterBox
.Right();
725 nSecondaryLocation
= aInnerBox
.Center().Y();
727 bIsTrailingFixed
= true;
728 if ( ! rPosition
.IsExtraSpaceNeeded())
729 bIsLeadingFixed
= true;
733 // Place indicator between two rows/columns.
734 const ::tools::Rectangle
aBox1 (GetInnerBoundingBox(rModel
, nIndex
-1));
735 const ::tools::Rectangle
aBox2 (GetInnerBoundingBox(rModel
, nIndex
));
738 nLeadingLocation
= aBox1
.Bottom();
739 nTrailingLocation
= aBox2
.Top();
740 nSecondaryLocation
= (aBox1
.Center().X() + aBox2
.Center().X()) / 2;
744 nLeadingLocation
= aBox1
.Right();
745 nTrailingLocation
= aBox2
.Left();
746 nSecondaryLocation
= (aBox1
.Center().Y() + aBox2
.Center().Y()) / 2;
750 // 2. Calculate the location of the insert indicator and the offsets of
751 // leading and trailing pages.
752 const sal_Int32
nAvailableSpace (nTrailingLocation
- nLeadingLocation
);
753 const sal_Int32
nRequiredSpace (bIsVertical
? rIndicatorSize
.Height():rIndicatorSize
.Width());
754 const sal_Int32
nMissingSpace (::std::max(sal_Int32(0), nRequiredSpace
- nAvailableSpace
));
755 sal_Int32
nPrimaryLocation (0);
756 sal_Int32
nLeadingOffset (0);
757 sal_Int32
nTrailingOffset (0);
760 nPrimaryLocation
= nLeadingLocation
+ nRequiredSpace
/2;
761 if ( ! bIsTrailingFixed
)
762 nTrailingOffset
= nMissingSpace
;
764 else if (bIsTrailingFixed
)
766 nPrimaryLocation
= nTrailingLocation
- nRequiredSpace
/2;
767 nLeadingOffset
= -nMissingSpace
;
771 nPrimaryLocation
= (nLeadingLocation
+ nTrailingLocation
) /2;
772 nLeadingOffset
= -nMissingSpace
/2;
773 nTrailingOffset
= nMissingSpace
+ nLeadingOffset
;
778 rPosition
.SetGeometricalPosition(
779 Point(nSecondaryLocation
, nPrimaryLocation
),
780 Point(0, nLeadingOffset
),
781 Point(0, nTrailingOffset
));
785 rPosition
.SetGeometricalPosition(
786 Point(nPrimaryLocation
, nSecondaryLocation
),
787 Point(nLeadingOffset
, 0),
788 Point(nTrailingOffset
, 0));
792 ::tools::Rectangle
Layouter::Implementation::GetInnerBoundingBox (
793 model::SlideSorterModel
const & rModel
,
794 const sal_Int32 nIndex
) const
796 model::SharedPageDescriptor
pDescriptor (rModel
.GetPageDescriptor(nIndex
));
798 return ::tools::Rectangle();
800 PageObjectLayouter::Part ePart
= PageObjectLayouter::Part::Preview
;
802 if (pDescriptor
->HasState(model::PageDescriptor::ST_Selected
))
803 ePart
= PageObjectLayouter::Part::PageObject
;
805 return mpPageObjectLayouter
->GetBoundingBox(
807 PageObjectLayouter::ModelCoordinateSystem
, true);
810 Range
Layouter::Implementation::GetValidHorizontalSizeRange() const
813 mnLeftBorder
+ maMinimalSize
.Width() + mnRightBorder
,
814 mnLeftBorder
+ maMaximalSize
.Width() + mnRightBorder
);
817 Range
Layouter::Implementation::GetValidVerticalSizeRange() const
820 mnTopBorder
+ maMinimalSize
.Height() + mnBottomBorder
,
821 mnTopBorder
+ maMaximalSize
.Height() + mnBottomBorder
);
824 Range
Layouter::Implementation::GetRangeOfVisiblePageObjects (const ::tools::Rectangle
& aVisibleArea
) const
826 // technically that's not empty, but it's the default, so...
827 if (aVisibleArea
.IsEmpty())
828 return Range(-1, -1);
830 const sal_Int32
nRow0 (GetRowAtPosition(aVisibleArea
.Top(), true, GM_NEXT
));
831 const sal_Int32
nCol0 (GetColumnAtPosition(aVisibleArea
.Left(),true, GM_NEXT
));
832 const sal_Int32
nRow1 (GetRowAtPosition(aVisibleArea
.Bottom(), true, GM_PREVIOUS
));
833 const sal_Int32
nCol1 (GetColumnAtPosition(aVisibleArea
.Right(), true, GM_PREVIOUS
));
835 // When start and end lie in different rows then the range may include
836 // slides outside (left or right of) the given area.
837 return Range(GetIndex(nRow0
,nCol0
,true), GetIndex(nRow1
,nCol1
,true));
840 Size
Layouter::Implementation::GetTargetSize (
841 const Size
& rWindowSize
,
842 const bool bCalculateWidth
,
843 const bool bCalculateHeight
) const
845 if (mnColumnCount
<=0 || mnRowCount
<=0)
846 return maPreferredSize
;
847 if ( ! (bCalculateWidth
|| bCalculateHeight
))
849 OSL_ASSERT(bCalculateWidth
|| bCalculateHeight
);
850 return maPreferredSize
;
853 // Calculate the width of each page object.
854 Size
aTargetSize (0,0);
856 aTargetSize
.setWidth(
857 (rWindowSize
.Width() - mnLeftBorder
- mnRightBorder
858 - (mnColumnCount
-1) * gnHorizontalGap
)
860 else if (bCalculateHeight
)
861 aTargetSize
.setHeight(
862 (rWindowSize
.Height() - mnTopBorder
- mnBottomBorder
863 - (mnRowCount
-1) * gnVerticalGap
)
868 if (aTargetSize
.Width() < maMinimalSize
.Width())
869 aTargetSize
.setWidth(maMinimalSize
.Width());
870 else if (aTargetSize
.Width() > maMaximalSize
.Width())
871 aTargetSize
.setWidth(maMaximalSize
.Width());
873 else if (bCalculateHeight
)
875 if (aTargetSize
.Height() < maMinimalSize
.Height())
876 aTargetSize
.setHeight(maMinimalSize
.Height());
877 else if (aTargetSize
.Height() > maMaximalSize
.Height())
878 aTargetSize
.setHeight(maMaximalSize
.Height());
884 sal_Int32
Layouter::Implementation::GetIndex (
885 const sal_Int32 nRow
,
886 const sal_Int32 nColumn
,
887 const bool bClampToValidRange
) const
889 if (nRow
>= 0 && nColumn
>= 0)
891 const sal_Int32
nIndex (nRow
* mnColumnCount
+ nColumn
);
892 if (nIndex
>= mnPageCount
)
893 if (bClampToValidRange
)
894 return mnPageCount
-1;
900 else if (bClampToValidRange
)
906 ::tools::Rectangle
Layouter::Implementation::GetPageObjectBox (
907 const sal_Int32 nIndex
,
908 const bool bIncludeBorderAndGap
) const
910 const sal_Int32
nRow (nIndex
/ mnColumnCount
);
911 const sal_Int32
nColumn (nIndex
% mnColumnCount
);
913 const ::tools::Rectangle
aBoundingBox (GetPageObjectBox(nRow
,nColumn
));
914 if (bIncludeBorderAndGap
)
915 return AddBorderAndGap(aBoundingBox
, nRow
, nColumn
);
920 ::tools::Rectangle
Layouter::Implementation::GetPageObjectBox (
921 const sal_Int32 nRow
,
922 const sal_Int32 nColumn
) const
924 return ::tools::Rectangle(
926 + nColumn
* maPageObjectSize
.Width()
927 + std::max
<sal_Int32
>(nColumn
,0) * gnHorizontalGap
,
929 + nRow
* maPageObjectSize
.Height()
930 + std::max
<sal_Int32
>(nRow
,0) * gnVerticalGap
),
934 ::tools::Rectangle
Layouter::Implementation::AddBorderAndGap (
935 const ::tools::Rectangle
& rBoundingBox
,
936 const sal_Int32 nRow
,
937 const sal_Int32 nColumn
) const
939 ::tools::Rectangle
aBoundingBox (rBoundingBox
);
942 aBoundingBox
.SetLeft( 0 );
944 aBoundingBox
.AdjustLeft( -(gnHorizontalGap
/2) );
945 if (nColumn
== mnColumnCount
-1)
946 aBoundingBox
.AdjustRight(mnRightBorder
);
948 aBoundingBox
.AdjustRight(gnHorizontalGap
/2 );
950 aBoundingBox
.SetTop( 0 );
952 aBoundingBox
.AdjustTop( -(gnVerticalGap
/2) );
953 if (nRow
== mnRowCount
-1)
954 aBoundingBox
.AdjustBottom(mnBottomBorder
);
956 aBoundingBox
.AdjustBottom(gnVerticalGap
/2 );
960 ::tools::Rectangle
Layouter::Implementation::GetTotalBoundingBox() const
962 sal_Int32 nHorizontalSize
= 0;
963 sal_Int32 nVerticalSize
= 0;
964 if (mnColumnCount
> 0)
966 sal_Int32 nRowCount
= (mnPageCount
+mnColumnCount
-1) / mnColumnCount
;
970 + mnColumnCount
* maPageObjectSize
.Width();
971 if (mnColumnCount
> 1)
972 nHorizontalSize
+= (mnColumnCount
-1) * gnHorizontalGap
;
976 + nRowCount
* maPageObjectSize
.Height();
978 nVerticalSize
+= (nRowCount
-1) * gnVerticalGap
;
981 return ::tools::Rectangle (
983 Size (nHorizontalSize
, nVerticalSize
)
987 void Layouter::Implementation::CalculateVerticalLogicalInsertPosition (
988 const Point
& rModelPosition
,
989 InsertPosition
& rPosition
) const
991 const sal_Int32 nY
= rModelPosition
.Y() - mnTopBorder
+ maPageObjectSize
.Height()/2;
992 const sal_Int32
nRowHeight (maPageObjectSize
.Height() + gnVerticalGap
);
993 const sal_Int32
nRow (::std::min(mnPageCount
, nY
/ nRowHeight
));
994 rPosition
.SetLogicalPosition (
999 (nRow
== mnRowCount
),
1000 (nRow
>= mnMaxRowCount
));
1003 //===== HorizontalImplementation ================================================
1005 HorizontalImplementation::HorizontalImplementation (const Implementation
& rImplementation
)
1006 : Implementation(rImplementation
)
1010 Layouter::Orientation
HorizontalImplementation::GetOrientation() const
1012 return Layouter::HORIZONTAL
;
1015 void HorizontalImplementation::CalculateRowAndColumnCount (const Size
&)
1017 // Row and column count are fixed (for a given page count.)
1018 mnColumnCount
= mnPageCount
;
1022 void HorizontalImplementation::CalculateMaxRowAndColumnCount (const Size
& rWindowSize
)
1024 mnMaxColumnCount
= (rWindowSize
.Width() - mnLeftBorder
- mnRightBorder
)
1025 / (maPageObjectSize
.Width() + gnHorizontalGap
);
1029 Size
HorizontalImplementation::CalculateTargetSize (
1030 const Size
& rWindowSize
) const
1032 return Implementation::GetTargetSize(rWindowSize
, false, true);
1035 void HorizontalImplementation::CalculateLogicalInsertPosition (
1036 const Point
& rModelPosition
,
1037 InsertPosition
& rPosition
) const
1039 const sal_Int32 nX
= rModelPosition
.X() - mnLeftBorder
+ maPageObjectSize
.Width()/2;
1040 const sal_Int32
nColumnWidth (maPageObjectSize
.Width() + gnHorizontalGap
);
1041 const sal_Int32
nColumn (::std::min(mnPageCount
, nX
/ nColumnWidth
));
1042 rPosition
.SetLogicalPosition (
1047 (nColumn
== mnColumnCount
),
1048 (nColumn
>= mnMaxColumnCount
));
1051 //===== VerticalImplementation ================================================
1053 VerticalImplementation::VerticalImplementation (const Implementation
& rImplementation
)
1054 : Implementation(rImplementation
)
1058 Layouter::Orientation
VerticalImplementation::GetOrientation() const
1060 return Layouter::VERTICAL
;
1063 void VerticalImplementation::CalculateRowAndColumnCount (const Size
&)
1065 // Row and column count are fixed (for a given page count.)
1066 mnRowCount
= mnPageCount
;
1071 void VerticalImplementation::CalculateMaxRowAndColumnCount (const Size
& rWindowSize
)
1073 mnMaxRowCount
= (rWindowSize
.Height() - mnTopBorder
- mnBottomBorder
)
1074 / (maPageObjectSize
.Height() + gnVerticalGap
);
1075 mnMaxColumnCount
= 1;
1078 Size
VerticalImplementation::CalculateTargetSize (
1079 const Size
& rWindowSize
) const
1081 return Implementation::GetTargetSize(rWindowSize
, true, false);
1084 void VerticalImplementation::CalculateLogicalInsertPosition (
1085 const Point
& rModelPosition
,
1086 InsertPosition
& rPosition
) const
1088 return CalculateVerticalLogicalInsertPosition(rModelPosition
, rPosition
);
1091 //===== GridImplementation ================================================
1093 GridImplementation::GridImplementation (
1094 sd::Window
*pWindow
,
1095 const std::shared_ptr
<view::Theme
>& rpTheme
)
1096 : Implementation(pWindow
, rpTheme
)
1100 GridImplementation::GridImplementation (const Implementation
& rImplementation
)
1101 : Implementation(rImplementation
)
1105 Layouter::Orientation
GridImplementation::GetOrientation() const
1107 return Layouter::GRID
;
1110 void GridImplementation::CalculateRowAndColumnCount (const Size
& rWindowSize
)
1112 // Calculate the column count.
1114 = (rWindowSize
.Width() - mnRequestedLeftBorder
- mnRequestedRightBorder
)
1115 / (maPreferredSize
.Width() + gnHorizontalGap
);
1116 if (mnColumnCount
< mnMinimalColumnCount
)
1117 mnColumnCount
= mnMinimalColumnCount
;
1118 if (mnColumnCount
> mnMaximalColumnCount
)
1119 mnColumnCount
= mnMaximalColumnCount
;
1120 mnRowCount
= (mnPageCount
+ mnColumnCount
-1)/mnColumnCount
;
1123 void GridImplementation::CalculateMaxRowAndColumnCount (const Size
& rWindowSize
)
1125 mnMaxColumnCount
= (rWindowSize
.Width() - mnLeftBorder
- mnRightBorder
)
1126 / (maPageObjectSize
.Width() + gnHorizontalGap
);
1127 mnMaxRowCount
= (rWindowSize
.Height() - mnTopBorder
- mnBottomBorder
)
1128 / (maPageObjectSize
.Height() + gnVerticalGap
);
1131 Size
GridImplementation::CalculateTargetSize (
1132 const Size
& rWindowSize
) const
1134 return Implementation::GetTargetSize(rWindowSize
, true, true);
1137 void GridImplementation::CalculateLogicalInsertPosition (
1138 const Point
& rModelPosition
,
1139 InsertPosition
& rPosition
) const
1141 if (mnColumnCount
== 1)
1143 CalculateVerticalLogicalInsertPosition(rModelPosition
, rPosition
);
1147 // Handle the general case of more than one column.
1148 sal_Int32
nRow (::std::min(
1150 GetRowAtPosition (rModelPosition
.Y(), true, GM_BOTH
)));
1151 const sal_Int32 nX
= rModelPosition
.X() - mnLeftBorder
+ maPageObjectSize
.Width()/2;
1152 const sal_Int32
nColumnWidth (maPageObjectSize
.Width() + gnHorizontalGap
);
1153 sal_Int32
nColumn (::std::min(mnColumnCount
, nX
/ nColumnWidth
));
1154 sal_Int32
nIndex (nRow
* mnColumnCount
+ nColumn
);
1155 bool bIsAtRunEnd (nColumn
== mnColumnCount
);
1157 if (nIndex
>= mnPageCount
)
1159 nIndex
= mnPageCount
;
1160 nRow
= mnRowCount
-1;
1161 nColumn
= ::std::min(::std::min(mnPageCount
, mnColumnCount
), nColumn
);
1165 rPosition
.SetLogicalPosition (
1171 (nColumn
>= mnMaxColumnCount
));
1175 //===== InsertPosition ========================================================
1177 InsertPosition::InsertPosition()
1181 mbIsAtRunStart(false),
1182 mbIsAtRunEnd(false),
1183 mbIsExtraSpaceNeeded(false),
1185 maLeadingOffset(0,0),
1186 maTrailingOffset(0,0)
1190 bool InsertPosition::operator== (const InsertPosition
& rInsertPosition
) const
1192 // Do not compare the geometrical information (maLocation).
1193 return mnRow
==rInsertPosition
.mnRow
1194 && mnColumn
==rInsertPosition
.mnColumn
1195 && mnIndex
==rInsertPosition
.mnIndex
1196 && mbIsAtRunStart
==rInsertPosition
.mbIsAtRunStart
1197 && mbIsAtRunEnd
==rInsertPosition
.mbIsAtRunEnd
1198 && mbIsExtraSpaceNeeded
==rInsertPosition
.mbIsExtraSpaceNeeded
;
1201 bool InsertPosition::operator!= (const InsertPosition
& rInsertPosition
) const
1203 return !operator==(rInsertPosition
);
1206 void InsertPosition::SetLogicalPosition (
1207 const sal_Int32 nRow
,
1208 const sal_Int32 nColumn
,
1209 const sal_Int32 nIndex
,
1210 const bool bIsAtRunStart
,
1211 const bool bIsAtRunEnd
,
1212 const bool bIsExtraSpaceNeeded
)
1217 mbIsAtRunStart
= bIsAtRunStart
;
1218 mbIsAtRunEnd
= bIsAtRunEnd
;
1219 mbIsExtraSpaceNeeded
= bIsExtraSpaceNeeded
;
1222 void InsertPosition::SetGeometricalPosition(
1223 const Point
& rLocation
,
1224 const Point
& rLeadingOffset
,
1225 const Point
& rTrailingOffset
)
1227 maLocation
= rLocation
;
1228 maLeadingOffset
= rLeadingOffset
;
1229 maTrailingOffset
= rTrailingOffset
;
1232 } // end of namespace ::sd::slidesorter::namespace
1234 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */