android: Update app-specific/MIME type icons
[LibreOffice.git] / sd / source / ui / slidesorter / view / SlsLayouter.cxx
blob8bc0daf3f5e9652c86959caf7b22241c469517c7
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 <utility>
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>
26 #include <Window.hxx>
27 #include <osl/diagnose.h>
29 namespace sd::slidesorter::view {
31 class Layouter::Implementation
33 public:
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;
45 Size maMinimalSize;
46 Size maPreferredSize;
47 Size maMaximalSize;
48 sal_Int32 mnMinimalColumnCount;
49 sal_Int32 mnMaximalColumnCount;
50 sal_Int32 mnPageCount;
51 sal_Int32 mnColumnCount;
52 sal_Int32 mnRowCount;
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
59 /// rows.
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
66 page objects.
68 enum GapMembership {
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.)
76 GM_PAGE_BORDER
79 static Implementation* Create (
80 const Implementation& rImplementation,
81 const Layouter::Orientation eOrientation);
83 virtual Layouter::Orientation GetOrientation() const = 0;
85 bool Rearrange (
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.
92 @param nYPosition
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.
97 @param eGapMembership
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.
112 @param nXPosition
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
131 gap.
132 @param eGapMemberhship
133 This value decides what areas in the gap belong to which (or no)
134 row or column.
135 @param nIndex
136 The row index of the upper row or the column index of the left
137 column.
138 @param nGap
139 Width or height of the gap in model coordinates between the
140 page borders.
141 @return
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,
149 sal_Int32 nIndex,
150 sal_Int32 nGap);
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;
181 sal_Int32 GetIndex (
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();
203 protected:
204 Implementation (
205 sd::Window *pWindow,
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;
213 Size GetTargetSize (
214 const Size& rWindowSize,
215 const bool bCalculateWidth,
216 const bool bCalculateHeight) const;
217 void CalculateVerticalLogicalInsertPosition (
218 const Point& rModelPosition,
219 InsertPosition& rPosition) const;
222 namespace {
224 /** The vertical layouter has one column and as many rows as there are
225 pages.
227 class VerticalImplementation : public Layouter::Implementation
229 public:
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;
238 protected:
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
246 pages.
248 class HorizontalImplementation : public Layouter::Implementation
250 public:
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;
259 protected:
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
272 public:
273 GridImplementation (
274 sd::Window *pWindow,
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;
284 protected:
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 ==============================================================
295 Layouter::Layouter (
296 sd::Window *pWindow,
297 const std::shared_ptr<Theme>& rpTheme)
298 : mpImplementation(new GridImplementation(pWindow, rpTheme)),
299 mpWindow(pWindow)
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(
371 rModelPosition,
372 aPosition);
373 mpImplementation->CalculateGeometricPosition(
374 aPosition,
375 rIndicatorSize,
376 GetColumnCount()==1,
377 rModel);
378 return aPosition;
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 (
403 rPosition.Y(),
404 bIncludePageBorders,
405 bIncludePageBorders ? Implementation::GM_PAGE_BORDER : Implementation::GM_NONE));
406 const sal_Int32 nColumn (
407 mpImplementation->GetColumnAtPosition (
408 rPosition.X(),
409 bIncludePageBorders,
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);
425 case GRID:
426 default: return new GridImplementation(rImplementation);
430 Layouter::Implementation::Implementation (
431 sd::Window *pWindow,
432 std::shared_ptr<view::Theme> pTheme)
433 : mpWindow(pWindow),
434 mnLeftBorder(5),
435 mnRightBorder(5),
436 mnTopBorder(5),
437 mnBottomBorder(5),
438 maMinimalSize(132,98),
439 maPreferredSize(200,150),
440 maMaximalSize(600,400),
441 mnMinimalColumnCount(1),
442 mnMaximalColumnCount(15),
443 mnPageCount(0),
444 mnColumnCount(1),
445 mnRowCount(0),
446 mnMaxColumnCount(0),
447 mnMaxRowCount(0),
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())
487 return false;
488 if (rPreviewModelSize.IsEmpty())
489 return false;
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;
506 else
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),
518 rPreviewModelSize,
519 mpWindow,
520 mnPageCount);
522 maPageObjectSize = mpPageObjectLayouter->GetGridMaxSize();
524 CalculateMaxRowAndColumnCount(rWindowSize);
526 return true;
529 sal_Int32 Layouter::Implementation::GetRowAtPosition (
530 sal_Int32 nYPosition,
531 bool bIncludeBordersAndGaps,
532 GapMembership eGapMembership) const
534 sal_Int32 nRow = -1;
536 const sal_Int32 nY = nYPosition - mnTopBorder;
537 if (nY >= 0)
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
547 // object.
548 if (nDistanceIntoGap > 0)
550 sal_Int32 nResolvedRow = ResolvePositionInGap(
551 nDistanceIntoGap,
552 eGapMembership,
553 nRow,
554 gnVerticalGap);
555 if (!bIncludeBordersAndGaps || nResolvedRow != -1)
556 nRow = nResolvedRow;
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.
563 nRow = 0;
566 return nRow;
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;
577 if (nX >= 0)
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;
584 if (nColumn < 0)
585 nColumn = 0;
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
591 // page object.
592 if (nDistanceIntoGap > 0)
594 sal_Int32 nResolvedColumn = ResolvePositionInGap(
595 nDistanceIntoGap,
596 eGapMembership,
597 nColumn,
598 gnHorizontalGap);
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
607 // column.
608 nColumn = 0;
610 return nColumn;
613 sal_Int32 Layouter::Implementation::ResolvePositionInGap (
614 sal_Int32 nDistanceIntoGap,
615 GapMembership eGapMembership,
616 sal_Int32 nIndex,
617 sal_Int32 nGap)
619 switch (eGapMembership)
621 case GM_NONE:
622 // The gap is no man's land.
623 nIndex = -1;
624 break;
626 case GM_BOTH:
628 // The lower half of the gap belongs to the next row or column.
629 sal_Int32 nFirstHalfGapWidth = nGap / 2;
630 if (nDistanceIntoGap > nFirstHalfGapWidth)
631 nIndex ++;
632 break;
635 case GM_PREVIOUS:
636 // Row or column already at correct value.
637 break;
639 case GM_NEXT:
640 // The complete gap belongs to the next row or column.
641 nIndex ++;
642 break;
644 case GM_PAGE_BORDER:
645 if (nDistanceIntoGap > 0)
647 if (nDistanceIntoGap > nGap)
649 // Inside the border of the next row or column.
650 nIndex ++;
652 else
654 // Inside the gap between the page borders.
655 nIndex = -1;
658 break;
660 default:
661 nIndex = -1;
664 return nIndex;
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));
687 if (bIsVertical)
689 nLeadingLocation = aOuterBox.Top();
690 nTrailingLocation = aInnerBox.Top();
691 nSecondaryLocation = aInnerBox.Center().X();
693 else
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));
707 if (bIsVertical)
709 nLeadingLocation = aInnerBox.Bottom();
710 nTrailingLocation = aOuterBox.Bottom();
711 nSecondaryLocation = aInnerBox.Center().X();
713 else
715 nLeadingLocation = aInnerBox.Right();
716 nTrailingLocation = aOuterBox.Right();
717 nSecondaryLocation = aInnerBox.Center().Y();
719 bIsTrailingFixed = true;
720 if ( ! rPosition.IsExtraSpaceNeeded())
721 bIsLeadingFixed = true;
723 else
725 // Place indicator between two rows/columns.
726 const ::tools::Rectangle aBox1 (GetInnerBoundingBox(rModel, nIndex-1));
727 const ::tools::Rectangle aBox2 (GetInnerBoundingBox(rModel, nIndex));
728 if (bIsVertical)
730 nLeadingLocation = aBox1.Bottom();
731 nTrailingLocation = aBox2.Top();
732 nSecondaryLocation = (aBox1.Center().X() + aBox2.Center().X()) / 2;
734 else
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);
750 if (bIsLeadingFixed)
752 nPrimaryLocation = nLeadingLocation + nRequiredSpace/2;
753 if ( ! bIsTrailingFixed)
754 nTrailingOffset = nMissingSpace;
756 else if (bIsTrailingFixed)
758 nPrimaryLocation = nTrailingLocation - nRequiredSpace/2;
759 nLeadingOffset = -nMissingSpace;
761 else
763 nPrimaryLocation = (nLeadingLocation + nTrailingLocation) /2;
764 nLeadingOffset = -nMissingSpace/2;
765 nTrailingOffset = nMissingSpace + nLeadingOffset;
768 if (bIsVertical)
770 rPosition.SetGeometricalPosition(
771 Point(nSecondaryLocation, nPrimaryLocation),
772 Point(0, nLeadingOffset),
773 Point(0, nTrailingOffset));
775 else
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));
789 if ( ! pDescriptor)
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(
798 pDescriptor, ePart,
799 PageObjectLayouter::ModelCoordinateSystem, true);
802 Range Layouter::Implementation::GetValidHorizontalSizeRange() const
804 return Range(
805 mnLeftBorder + maMinimalSize.Width() + mnRightBorder,
806 mnLeftBorder + maMaximalSize.Width() + mnRightBorder);
809 Range Layouter::Implementation::GetValidVerticalSizeRange() const
811 return Range(
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);
847 if (bCalculateWidth)
848 aTargetSize.setWidth(
849 (rWindowSize.Width() - mnLeftBorder - mnRightBorder
850 - (mnColumnCount-1) * gnHorizontalGap)
851 / mnColumnCount);
852 else if (bCalculateHeight)
853 aTargetSize.setHeight(
854 (rWindowSize.Height() - mnTopBorder - mnBottomBorder
855 - (mnRowCount-1) * gnVerticalGap)
856 / mnRowCount);
858 if (bCalculateWidth)
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());
873 return aTargetSize;
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;
887 else
888 return -1;
889 else
890 return nIndex;
892 else if (bClampToValidRange)
893 return 0;
894 else
895 return -1;
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);
908 else
909 return aBoundingBox;
912 ::tools::Rectangle Layouter::Implementation::GetPageObjectBox (
913 const sal_Int32 nRow,
914 const sal_Int32 nColumn) const
916 return ::tools::Rectangle(
917 Point (mnLeftBorder
918 + nColumn * maPageObjectSize.Width()
919 + std::max<sal_Int32>(nColumn,0) * gnHorizontalGap,
920 mnTopBorder
921 + nRow * maPageObjectSize.Height()
922 + std::max<sal_Int32>(nRow,0) * gnVerticalGap),
923 maPageObjectSize);
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);
933 if (nColumn == 0)
934 aBoundingBox.SetLeft( 0 );
935 else
936 aBoundingBox.AdjustLeft( -(gnHorizontalGap/2) );
937 if (nColumn == mnColumnCount-1)
938 aBoundingBox.AdjustRight(mnRightBorder );
939 else
940 aBoundingBox.AdjustRight(gnHorizontalGap/2 );
941 if (nRow == 0)
942 aBoundingBox.SetTop( 0 );
943 else
944 aBoundingBox.AdjustTop( -(gnVerticalGap/2) );
945 if (nRow == mnRowCount-1)
946 aBoundingBox.AdjustBottom(mnBottomBorder );
947 else
948 aBoundingBox.AdjustBottom(gnVerticalGap/2 );
949 return aBoundingBox;
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;
959 nHorizontalSize =
960 mnLeftBorder
961 + mnRightBorder
962 + mnColumnCount * maPageObjectSize.Width();
963 if (mnColumnCount > 1)
964 nHorizontalSize += (mnColumnCount-1) * gnHorizontalGap;
965 nVerticalSize =
966 mnTopBorder
967 + mnBottomBorder
968 + nRowCount * maPageObjectSize.Height();
969 if (nRowCount > 1)
970 nVerticalSize += (nRowCount-1) * gnVerticalGap;
973 return ::tools::Rectangle (
974 Point(0,0),
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 (
987 nRow,
989 nRow,
990 (nRow == 0),
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;
1011 mnRowCount = 1;
1014 void HorizontalImplementation::CalculateMaxRowAndColumnCount (const Size& rWindowSize)
1016 mnMaxColumnCount = (rWindowSize.Width() - mnLeftBorder - mnRightBorder)
1017 / (maPageObjectSize.Width() + gnHorizontalGap);
1018 mnMaxRowCount = 1;
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 (
1036 nColumn,
1037 nColumn,
1038 (nColumn == 0),
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;
1059 mnColumnCount = 1;
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.
1105 mnColumnCount
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);
1137 else
1139 // Handle the general case of more than one column.
1140 sal_Int32 nRow (::std::min(
1141 mnRowCount-1,
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);
1154 bIsAtRunEnd = true;
1157 rPosition.SetLogicalPosition (
1158 nRow,
1159 nColumn,
1160 nIndex,
1161 (nColumn == 0),
1162 bIsAtRunEnd,
1163 (nColumn >= mnMaxColumnCount));
1167 //===== InsertPosition ========================================================
1169 InsertPosition::InsertPosition()
1170 : mnRow(-1),
1171 mnColumn(-1),
1172 mnIndex(-1),
1173 mbIsAtRunStart(false),
1174 mbIsAtRunEnd(false),
1175 mbIsExtraSpaceNeeded(false),
1176 maLocation(0,0),
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)
1206 mnRow = nRow;
1207 mnColumn = nColumn;
1208 mnIndex = nIndex;
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: */