cid#1607171 Data race condition
[LibreOffice.git] / sd / source / ui / slidesorter / view / SlsLayouter.cxx
blob371e410d3fee7dbb54425e9e955ee7b796659461
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 Size Layouter::AddGap(const Size & rObjectSize)
354 Size newSize = rObjectSize;
355 newSize.AdjustWidth(Implementation::gnHorizontalGap);
356 newSize.AdjustHeight(Implementation::gnVerticalGap);
357 return newSize;
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(
379 rModelPosition,
380 aPosition);
381 mpImplementation->CalculateGeometricPosition(
382 aPosition,
383 rIndicatorSize,
384 GetColumnCount()==1,
385 rModel);
386 return aPosition;
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 (
411 rPosition.Y(),
412 bIncludePageBorders,
413 bIncludePageBorders ? Implementation::GM_PAGE_BORDER : Implementation::GM_NONE));
414 const sal_Int32 nColumn (
415 mpImplementation->GetColumnAtPosition (
416 rPosition.X(),
417 bIncludePageBorders,
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);
433 case GRID:
434 default: return new GridImplementation(rImplementation);
438 Layouter::Implementation::Implementation (
439 sd::Window *pWindow,
440 std::shared_ptr<view::Theme> pTheme)
441 : mpWindow(pWindow),
442 mnLeftBorder(5),
443 mnRightBorder(5),
444 mnTopBorder(5),
445 mnBottomBorder(5),
446 maMinimalSize(132,98),
447 maPreferredSize(200,150),
448 maMaximalSize(600,400),
449 mnMinimalColumnCount(1),
450 mnMaximalColumnCount(15),
451 mnPageCount(0),
452 mnColumnCount(1),
453 mnRowCount(0),
454 mnMaxColumnCount(0),
455 mnMaxRowCount(0),
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())
495 return false;
496 if (rPreviewModelSize.IsEmpty())
497 return false;
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;
514 else
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),
526 rPreviewModelSize,
527 mpWindow,
528 mnPageCount);
530 maPageObjectSize = mpPageObjectLayouter->GetGridMaxSize();
532 CalculateMaxRowAndColumnCount(rWindowSize);
534 return true;
537 sal_Int32 Layouter::Implementation::GetRowAtPosition (
538 sal_Int32 nYPosition,
539 bool bIncludeBordersAndGaps,
540 GapMembership eGapMembership) const
542 sal_Int32 nRow = -1;
544 const sal_Int32 nY = nYPosition - mnTopBorder;
545 if (nY >= 0)
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
555 // object.
556 if (nDistanceIntoGap > 0)
558 sal_Int32 nResolvedRow = ResolvePositionInGap(
559 nDistanceIntoGap,
560 eGapMembership,
561 nRow,
562 gnVerticalGap);
563 if (!bIncludeBordersAndGaps || nResolvedRow != -1)
564 nRow = nResolvedRow;
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.
571 nRow = 0;
574 return nRow;
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;
585 if (nX >= 0)
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;
592 if (nColumn < 0)
593 nColumn = 0;
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
599 // page object.
600 if (nDistanceIntoGap > 0)
602 sal_Int32 nResolvedColumn = ResolvePositionInGap(
603 nDistanceIntoGap,
604 eGapMembership,
605 nColumn,
606 gnHorizontalGap);
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
615 // column.
616 nColumn = 0;
618 return nColumn;
621 sal_Int32 Layouter::Implementation::ResolvePositionInGap (
622 sal_Int32 nDistanceIntoGap,
623 GapMembership eGapMembership,
624 sal_Int32 nIndex,
625 sal_Int32 nGap)
627 switch (eGapMembership)
629 case GM_NONE:
630 // The gap is no man's land.
631 nIndex = -1;
632 break;
634 case GM_BOTH:
636 // The lower half of the gap belongs to the next row or column.
637 sal_Int32 nFirstHalfGapWidth = nGap / 2;
638 if (nDistanceIntoGap > nFirstHalfGapWidth)
639 nIndex ++;
640 break;
643 case GM_PREVIOUS:
644 // Row or column already at correct value.
645 break;
647 case GM_NEXT:
648 // The complete gap belongs to the next row or column.
649 nIndex ++;
650 break;
652 case GM_PAGE_BORDER:
653 if (nDistanceIntoGap > 0)
655 if (nDistanceIntoGap > nGap)
657 // Inside the border of the next row or column.
658 nIndex ++;
660 else
662 // Inside the gap between the page borders.
663 nIndex = -1;
666 break;
668 default:
669 nIndex = -1;
672 return nIndex;
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));
695 if (bIsVertical)
697 nLeadingLocation = aOuterBox.Top();
698 nTrailingLocation = aInnerBox.Top();
699 nSecondaryLocation = aInnerBox.Center().X();
701 else
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));
715 if (bIsVertical)
717 nLeadingLocation = aInnerBox.Bottom();
718 nTrailingLocation = aOuterBox.Bottom();
719 nSecondaryLocation = aInnerBox.Center().X();
721 else
723 nLeadingLocation = aInnerBox.Right();
724 nTrailingLocation = aOuterBox.Right();
725 nSecondaryLocation = aInnerBox.Center().Y();
727 bIsTrailingFixed = true;
728 if ( ! rPosition.IsExtraSpaceNeeded())
729 bIsLeadingFixed = true;
731 else
733 // Place indicator between two rows/columns.
734 const ::tools::Rectangle aBox1 (GetInnerBoundingBox(rModel, nIndex-1));
735 const ::tools::Rectangle aBox2 (GetInnerBoundingBox(rModel, nIndex));
736 if (bIsVertical)
738 nLeadingLocation = aBox1.Bottom();
739 nTrailingLocation = aBox2.Top();
740 nSecondaryLocation = (aBox1.Center().X() + aBox2.Center().X()) / 2;
742 else
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);
758 if (bIsLeadingFixed)
760 nPrimaryLocation = nLeadingLocation + nRequiredSpace/2;
761 if ( ! bIsTrailingFixed)
762 nTrailingOffset = nMissingSpace;
764 else if (bIsTrailingFixed)
766 nPrimaryLocation = nTrailingLocation - nRequiredSpace/2;
767 nLeadingOffset = -nMissingSpace;
769 else
771 nPrimaryLocation = (nLeadingLocation + nTrailingLocation) /2;
772 nLeadingOffset = -nMissingSpace/2;
773 nTrailingOffset = nMissingSpace + nLeadingOffset;
776 if (bIsVertical)
778 rPosition.SetGeometricalPosition(
779 Point(nSecondaryLocation, nPrimaryLocation),
780 Point(0, nLeadingOffset),
781 Point(0, nTrailingOffset));
783 else
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));
797 if ( ! pDescriptor)
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(
806 pDescriptor, ePart,
807 PageObjectLayouter::ModelCoordinateSystem, true);
810 Range Layouter::Implementation::GetValidHorizontalSizeRange() const
812 return Range(
813 mnLeftBorder + maMinimalSize.Width() + mnRightBorder,
814 mnLeftBorder + maMaximalSize.Width() + mnRightBorder);
817 Range Layouter::Implementation::GetValidVerticalSizeRange() const
819 return Range(
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);
855 if (bCalculateWidth)
856 aTargetSize.setWidth(
857 (rWindowSize.Width() - mnLeftBorder - mnRightBorder
858 - (mnColumnCount-1) * gnHorizontalGap)
859 / mnColumnCount);
860 else if (bCalculateHeight)
861 aTargetSize.setHeight(
862 (rWindowSize.Height() - mnTopBorder - mnBottomBorder
863 - (mnRowCount-1) * gnVerticalGap)
864 / mnRowCount);
866 if (bCalculateWidth)
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());
881 return aTargetSize;
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;
895 else
896 return -1;
897 else
898 return nIndex;
900 else if (bClampToValidRange)
901 return 0;
902 else
903 return -1;
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);
916 else
917 return aBoundingBox;
920 ::tools::Rectangle Layouter::Implementation::GetPageObjectBox (
921 const sal_Int32 nRow,
922 const sal_Int32 nColumn) const
924 return ::tools::Rectangle(
925 Point (mnLeftBorder
926 + nColumn * maPageObjectSize.Width()
927 + std::max<sal_Int32>(nColumn,0) * gnHorizontalGap,
928 mnTopBorder
929 + nRow * maPageObjectSize.Height()
930 + std::max<sal_Int32>(nRow,0) * gnVerticalGap),
931 maPageObjectSize);
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);
941 if (nColumn == 0)
942 aBoundingBox.SetLeft( 0 );
943 else
944 aBoundingBox.AdjustLeft( -(gnHorizontalGap/2) );
945 if (nColumn == mnColumnCount-1)
946 aBoundingBox.AdjustRight(mnRightBorder );
947 else
948 aBoundingBox.AdjustRight(gnHorizontalGap/2 );
949 if (nRow == 0)
950 aBoundingBox.SetTop( 0 );
951 else
952 aBoundingBox.AdjustTop( -(gnVerticalGap/2) );
953 if (nRow == mnRowCount-1)
954 aBoundingBox.AdjustBottom(mnBottomBorder );
955 else
956 aBoundingBox.AdjustBottom(gnVerticalGap/2 );
957 return aBoundingBox;
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;
967 nHorizontalSize =
968 mnLeftBorder
969 + mnRightBorder
970 + mnColumnCount * maPageObjectSize.Width();
971 if (mnColumnCount > 1)
972 nHorizontalSize += (mnColumnCount-1) * gnHorizontalGap;
973 nVerticalSize =
974 mnTopBorder
975 + mnBottomBorder
976 + nRowCount * maPageObjectSize.Height();
977 if (nRowCount > 1)
978 nVerticalSize += (nRowCount-1) * gnVerticalGap;
981 return ::tools::Rectangle (
982 Point(0,0),
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 (
995 nRow,
997 nRow,
998 (nRow == 0),
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;
1019 mnRowCount = 1;
1022 void HorizontalImplementation::CalculateMaxRowAndColumnCount (const Size& rWindowSize)
1024 mnMaxColumnCount = (rWindowSize.Width() - mnLeftBorder - mnRightBorder)
1025 / (maPageObjectSize.Width() + gnHorizontalGap);
1026 mnMaxRowCount = 1;
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 (
1044 nColumn,
1045 nColumn,
1046 (nColumn == 0),
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;
1067 mnColumnCount = 1;
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.
1113 mnColumnCount
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);
1145 else
1147 // Handle the general case of more than one column.
1148 sal_Int32 nRow (::std::min(
1149 mnRowCount-1,
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);
1162 bIsAtRunEnd = true;
1165 rPosition.SetLogicalPosition (
1166 nRow,
1167 nColumn,
1168 nIndex,
1169 (nColumn == 0),
1170 bIsAtRunEnd,
1171 (nColumn >= mnMaxColumnCount));
1175 //===== InsertPosition ========================================================
1177 InsertPosition::InsertPosition()
1178 : mnRow(-1),
1179 mnColumn(-1),
1180 mnIndex(-1),
1181 mbIsAtRunStart(false),
1182 mbIsAtRunEnd(false),
1183 mbIsExtraSpaceNeeded(false),
1184 maLocation(0,0),
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)
1214 mnRow = nRow;
1215 mnColumn = nColumn;
1216 mnIndex = nIndex;
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: */