bump product version to 5.0.4.1
[LibreOffice.git] / sd / source / ui / slidesorter / view / SlsLayouter.cxx
blobcb571c41dfcf07cace8046a99d2afa600cfebae9
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "view/SlsLayouter.hxx"
21 #include "model/SlideSorterModel.hxx"
22 #include "model/SlsPageDescriptor.hxx"
23 #include "Window.hxx"
24 #include <rtl/math.hxx>
25 #include <basegfx/numeric/ftools.hxx>
27 namespace sd { namespace slidesorter { namespace view {
29 class Layouter::Implementation
31 public:
32 VclPtr<sd::Window> mpWindow;
33 sal_Int32 mnRequestedLeftBorder;
34 sal_Int32 mnRequestedRightBorder;
35 sal_Int32 mnRequestedTopBorder;
36 sal_Int32 mnRequestedBottomBorder;
37 sal_Int32 mnLeftBorder;
38 sal_Int32 mnRightBorder;
39 sal_Int32 mnTopBorder;
40 sal_Int32 mnBottomBorder;
41 sal_Int32 mnVerticalGap;
42 sal_Int32 mnHorizontalGap;
43 Size maMinimalSize;
44 Size maPreferredSize;
45 Size maMaximalSize;
46 sal_Int32 mnMinimalColumnCount;
47 sal_Int32 mnMaximalColumnCount;
48 sal_Int32 mnPageCount;
49 sal_Int32 mnColumnCount;
50 sal_Int32 mnRowCount;
51 /// The maximum number of columns. Can only be larger than the current
52 /// number of columns when there are not enough pages to fill all
53 /// available columns.
54 sal_Int32 mnMaxColumnCount;
55 /// The maximum number of rows. Can only be larger than the current
56 /// number of rows when there are not enough pages to fill all available
57 /// rows.
58 sal_Int32 mnMaxRowCount;
59 Size maPageObjectSize;
60 ::boost::shared_ptr<PageObjectLayouter> mpPageObjectLayouter;
61 ::boost::shared_ptr<view::Theme> mpTheme;
63 /** Specify how the gap between two page objects is associated with the
64 page objects.
66 enum GapMembership {
67 GM_NONE, // Gap is not associated with any page object.
68 GM_PREVIOUS, // The whole gap is associated with the previous page
69 // object (left or above the gap.)
70 GM_BOTH, // Half of the gap is associated with previous, half
71 // with the next page object.
72 GM_NEXT, // The whole gap is associated with the next page
73 // object (right or below the gap.)
74 GM_PAGE_BORDER
77 static Implementation* Create (
78 const Implementation& rImplementation,
79 const Layouter::Orientation eOrientation);
81 virtual Layouter::Orientation GetOrientation() const = 0;
83 bool Rearrange (
84 const Size& rWindowSize,
85 const Size& rPreviewModelSize,
86 const sal_uInt32 nPageCount);
88 /** Calculate the row that the point with the given vertical coordinate
89 is over. The horizontal component is ignored.
90 @param nYPosition
91 Vertical position in model coordinates.
92 @param bIncludeBordersAndGaps
93 When this flag is <TRUE/> then the area of borders and gaps are
94 interpreted as belonging to one of the rows.
95 @param eGapMembership
96 Specifies to what row the gap areas belong. Here GM_NONE
97 corresponds to bIncludeBordersAndGaps being <FALSE/>. When
98 GM_BOTH is given then the upper half is associated to the row
99 above and the lower half to the row below. Values of
100 GM_PREVIOUS and GM_NEXT associate the whole gap area with the
101 row above or below respectively.
103 sal_Int32 GetRowAtPosition (
104 sal_Int32 nYPosition,
105 bool bIncludeBordersAndGaps,
106 GapMembership eGapMembership = GM_NONE) const;
108 /** Calculate the column that the point with the given horizontal
109 coordinate is over. The vertical component is ignored.
110 @param nXPosition
111 Horizontal position in model coordinates.
112 @param bIncludeBordersAndGaps
113 When this flag is <TRUE/> then the area of borders and gaps are
114 interpreted as belonging to one of the columns.
115 @param eGapMembership
116 Specifies to what column the gap areas belong.
118 sal_Int32 GetColumnAtPosition (
119 sal_Int32 nXPosition,
120 bool bIncludeBordersAndGaps,
121 GapMembership eGapMembership = GM_NONE) const;
123 /** This method is typically called from GetRowAtPosition() and
124 GetColumnAtPosition() to handle a position that lies inside the gap
125 between two adjacent rows or columns.
126 @param nDistanceIntoGap
127 Vertical distance from the bottom of the upper row down into the
128 gap or horizontal distance from the right edge right into the
129 gap.
130 @param eGapMemberhship
131 This value decides what areas in the gap belong to which (or no)
132 row or column.
133 @param nIndex
134 The row index of the upper row or the column index of the left
135 column.
136 @param nGap
137 Width or height of the gap in model coordiantes between the
138 page borders.
139 @return
140 Returns either the index of the upper row (as given as nRow), the
141 index of the lower row (nRow+1) or -1 to indicate that the
142 position belongs to no row.
144 static sal_Int32 ResolvePositionInGap (
145 sal_Int32 nDistanceIntoGap,
146 GapMembership eGapMembership,
147 sal_Int32 nIndex,
148 sal_Int32 nGap);
150 /** Calculate the logical part of the insert position, i.e. the page
151 after whicht to insert.
153 virtual void CalculateLogicalInsertPosition (
154 const Point& rModelPosition,
155 InsertPosition& rPosition) const = 0;
157 /** Calculate the geometrical part of the insert position, i.e. the
158 location of where to display the insertion indicator and the
159 distances about which the leading and trailing pages have to be
160 moved to make room for the indicator.
162 void CalculateGeometricPosition (
163 InsertPosition& rPosition,
164 const Size& rIndicatorSize,
165 const bool bIsVertical,
166 model::SlideSorterModel& rModel) const;
168 /** Return the bounding box of the preview or, when selected, of the page
169 object. Thus, it returns something like a visual bounding box.
171 Rectangle GetInnerBoundingBox (
172 model::SlideSorterModel& rModel,
173 const sal_Int32 nIndex) const;
175 Range GetValidHorizontalSizeRange() const;
176 Range GetValidVerticalSizeRange() const;
178 Range GetRangeOfVisiblePageObjects (const Rectangle& aVisibleArea) const;
179 sal_Int32 GetIndex (
180 const sal_Int32 nRow,
181 const sal_Int32 nColumn,
182 const bool bClampToValidRange) const;
184 Rectangle GetPageObjectBox (
185 const sal_Int32 nIndex,
186 const bool bIncludeBorderAndGap = false) const;
188 Rectangle GetPageObjectBox (
189 const sal_Int32 nRow,
190 const sal_Int32 nColumn) const;
192 Rectangle AddBorderAndGap (
193 const Rectangle& rBoundingBox,
194 const sal_Int32 nRow,
195 const sal_Int32 nColumn) const;
197 Rectangle GetTotalBoundingBox() const;
199 virtual ~Implementation();
201 protected:
202 Implementation (
203 sd::Window *pWindow,
204 const ::boost::shared_ptr<view::Theme>& rpTheme);
205 Implementation (const Implementation& rImplementation);
207 virtual void CalculateRowAndColumnCount (const Size& rWindowSize) = 0;
208 virtual void CalculateMaxRowAndColumnCount (const Size& rWindowSize) = 0;
209 virtual Size CalculateTargetSize (
210 const Size& rWindowSize,
211 const Size& rPreviewModelSize) const = 0;
212 Size GetTargetSize (
213 const Size& rWindowSize,
214 const Size& rPreviewModelSize,
215 const bool bCalculateWidth,
216 const bool bCalculateHeight) const;
217 void CalculateVerticalLogicalInsertPosition (
218 const Point& rModelPosition,
219 InsertPosition& rPosition) const;
222 /** The vertical layouter has one column and as many rows as there are
223 pages.
225 class VerticalImplementation : public Layouter::Implementation
227 public:
228 VerticalImplementation (const Implementation& rImplementation);
230 virtual Layouter::Orientation GetOrientation() const SAL_OVERRIDE;
232 void CalculateLogicalInsertPosition (
233 const Point& rModelPosition,
234 InsertPosition& rPosition) const SAL_OVERRIDE;
236 protected:
237 virtual void CalculateRowAndColumnCount (const Size& rWindowSize) SAL_OVERRIDE;
238 virtual void CalculateMaxRowAndColumnCount (const Size& rWindowSize) SAL_OVERRIDE;
239 virtual Size CalculateTargetSize (
240 const Size& rWindowSize,
241 const Size& rPreviewModelSize) const SAL_OVERRIDE;
244 /** The horizontal layouter has one row and as many columns as there are
245 pages.
247 class HorizontalImplementation : public Layouter::Implementation
249 public:
250 HorizontalImplementation (const Implementation& rImplementation);
252 virtual Layouter::Orientation GetOrientation() const SAL_OVERRIDE;
254 void CalculateLogicalInsertPosition (
255 const Point& rModelPosition,
256 InsertPosition& rPosition) const SAL_OVERRIDE;
258 protected:
259 virtual void CalculateRowAndColumnCount (const Size& rWindowSize) SAL_OVERRIDE;
260 virtual void CalculateMaxRowAndColumnCount (const Size& rWindowSize) SAL_OVERRIDE;
261 virtual Size CalculateTargetSize (
262 const Size& rWindowSize,
263 const Size& rPreviewModelSize) const SAL_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 ::boost::shared_ptr<view::Theme>& rpTheme);
276 GridImplementation (const Implementation& rImplementation);
278 virtual Layouter::Orientation GetOrientation() const SAL_OVERRIDE;
280 void CalculateLogicalInsertPosition (
281 const Point& rModelPosition,
282 InsertPosition& rPosition) const SAL_OVERRIDE;
284 protected:
285 virtual void CalculateRowAndColumnCount (const Size& rWindowSize) SAL_OVERRIDE;
286 virtual void CalculateMaxRowAndColumnCount (const Size& rWindowSize) SAL_OVERRIDE;
287 virtual Size CalculateTargetSize (
288 const Size& rWindowSize,
289 const Size& rPreviewModelSize) const SAL_OVERRIDE;
292 //===== Layouter ==============================================================
294 Layouter::Layouter (
295 sd::Window *pWindow,
296 const ::boost::shared_ptr<Theme>& rpTheme)
297 : mpImplementation(new GridImplementation(pWindow, rpTheme)),
298 mpWindow(pWindow)
302 Layouter::~Layouter()
306 ::boost::shared_ptr<PageObjectLayouter> Layouter::GetPageObjectLayouter() const
308 return mpImplementation->mpPageObjectLayouter;
311 void Layouter::SetColumnCount (
312 sal_Int32 nMinimalColumnCount,
313 sal_Int32 nMaximalColumnCount)
315 if (nMinimalColumnCount <= nMaximalColumnCount)
317 mpImplementation->mnMinimalColumnCount = nMinimalColumnCount;
318 mpImplementation->mnMaximalColumnCount = nMaximalColumnCount;
322 bool Layouter::Rearrange (
323 const Orientation eOrientation,
324 const Size& rWindowSize,
325 const Size& rPageSize,
326 const sal_uInt32 nPageCount)
328 OSL_ASSERT(mpWindow);
330 if (eOrientation != mpImplementation->GetOrientation())
331 mpImplementation.reset(Implementation::Create(*mpImplementation, eOrientation));
333 return mpImplementation->Rearrange(rWindowSize, rPageSize, nPageCount);
336 sal_Int32 Layouter::GetColumnCount() const
338 return mpImplementation->mnColumnCount;
341 sal_Int32 Layouter::GetIndex (const sal_Int32 nRow, const sal_Int32 nColumn) const
343 return mpImplementation->GetIndex(nRow,nColumn,true);
346 Size Layouter::GetPageObjectSize() const
348 return mpImplementation->maPageObjectSize;
351 Rectangle Layouter::GetPageObjectBox (
352 const sal_Int32 nIndex,
353 const bool bIncludeBorderAndGap) const
355 return mpImplementation->GetPageObjectBox(nIndex, bIncludeBorderAndGap);
358 Rectangle Layouter::GetTotalBoundingBox() const
360 return mpImplementation->GetTotalBoundingBox();
363 InsertPosition Layouter::GetInsertPosition (
364 const Point& rModelPosition,
365 const Size& rIndicatorSize,
366 model::SlideSorterModel& rModel) const
368 InsertPosition aPosition;
369 mpImplementation->CalculateLogicalInsertPosition(
370 rModelPosition,
371 aPosition);
372 mpImplementation->CalculateGeometricPosition(
373 aPosition,
374 rIndicatorSize,
375 GetColumnCount()==1,
376 rModel);
377 return aPosition;
380 Range Layouter::GetValidHorizontalSizeRange() const
382 return mpImplementation->GetValidHorizontalSizeRange();
385 Range Layouter::GetValidVerticalSizeRange() const
387 return mpImplementation->GetValidVerticalSizeRange();
390 Range Layouter::GetRangeOfVisiblePageObjects (const Rectangle& aVisibleArea) const
392 return mpImplementation->GetRangeOfVisiblePageObjects(aVisibleArea);
395 sal_Int32 Layouter::GetIndexAtPoint (
396 const Point& rPosition,
397 const bool bIncludePageBorders,
398 const bool bClampToValidRange) const
400 const sal_Int32 nRow (
401 mpImplementation->GetRowAtPosition (
402 rPosition.Y(),
403 bIncludePageBorders,
404 bIncludePageBorders ? Implementation::GM_PAGE_BORDER : Implementation::GM_NONE));
405 const sal_Int32 nColumn (
406 mpImplementation->GetColumnAtPosition (
407 rPosition.X(),
408 bIncludePageBorders,
409 bIncludePageBorders ? Implementation::GM_PAGE_BORDER : Implementation::GM_NONE));
411 return mpImplementation->GetIndex(nRow,nColumn,bClampToValidRange);
414 //===== Layouter::Implementation ==============================================
416 Layouter::Implementation* Layouter::Implementation::Create (
417 const Implementation& rImplementation,
418 const Layouter::Orientation eOrientation)
420 switch (eOrientation)
422 case HORIZONTAL: return new HorizontalImplementation(rImplementation);
423 case VERTICAL: return new VerticalImplementation(rImplementation);
424 case GRID:
425 default: return new GridImplementation(rImplementation);
429 Layouter::Implementation::Implementation (
430 sd::Window *pWindow,
431 const ::boost::shared_ptr<view::Theme>& rpTheme)
432 : mpWindow(pWindow),
433 mnRequestedLeftBorder(5),
434 mnRequestedRightBorder(5),
435 mnRequestedTopBorder(5),
436 mnRequestedBottomBorder(5),
437 mnLeftBorder(5),
438 mnRightBorder(5),
439 mnTopBorder(5),
440 mnBottomBorder(5),
441 mnVerticalGap (10 - 2*Theme_FocusIndicatorWidth),
442 mnHorizontalGap(10 - 2*Theme_FocusIndicatorWidth),
443 maMinimalSize(132,98),
444 maPreferredSize(200,150),
445 maMaximalSize(600,400),
446 mnMinimalColumnCount(1),
447 mnMaximalColumnCount(15),
448 mnPageCount(0),
449 mnColumnCount(1),
450 mnRowCount(0),
451 mnMaxColumnCount(0),
452 mnMaxRowCount(0),
453 maPageObjectSize(1,1),
454 mpPageObjectLayouter(),
455 mpTheme(rpTheme)
459 Layouter::Implementation::Implementation (const Implementation& rImplementation)
460 : mpWindow(rImplementation.mpWindow),
461 mnRequestedLeftBorder(rImplementation.mnRequestedLeftBorder),
462 mnRequestedRightBorder(rImplementation.mnRequestedRightBorder),
463 mnRequestedTopBorder(rImplementation.mnRequestedTopBorder),
464 mnRequestedBottomBorder(rImplementation.mnRequestedBottomBorder),
465 mnLeftBorder(rImplementation.mnLeftBorder),
466 mnRightBorder(rImplementation.mnRightBorder),
467 mnTopBorder(rImplementation.mnTopBorder),
468 mnBottomBorder(rImplementation.mnBottomBorder),
469 mnVerticalGap(rImplementation.mnVerticalGap),
470 mnHorizontalGap(rImplementation.mnHorizontalGap),
471 maMinimalSize(rImplementation.maMinimalSize),
472 maPreferredSize(rImplementation.maPreferredSize),
473 maMaximalSize(rImplementation.maMaximalSize),
474 mnMinimalColumnCount(rImplementation.mnMinimalColumnCount),
475 mnMaximalColumnCount(rImplementation.mnMaximalColumnCount),
476 mnPageCount(rImplementation.mnPageCount),
477 mnColumnCount(rImplementation.mnColumnCount),
478 mnRowCount(rImplementation.mnRowCount),
479 mnMaxColumnCount(rImplementation.mnMaxColumnCount),
480 mnMaxRowCount(rImplementation.mnMaxRowCount),
481 maPageObjectSize(rImplementation.maPageObjectSize),
482 mpPageObjectLayouter(),
483 mpTheme(rImplementation.mpTheme)
487 Layouter::Implementation::~Implementation()
491 bool Layouter::Implementation::Rearrange (
492 const Size& rWindowSize,
493 const Size& rPreviewModelSize,
494 const sal_uInt32 nPageCount)
496 mnPageCount = nPageCount;
498 // Return early when the window or the model have not yet been initialized.
499 if (rWindowSize.Width()<=0 || rWindowSize.Height()<=0)
500 return false;
501 if (rPreviewModelSize.Width()<=0 || rPreviewModelSize.Height()<=0)
502 return false;
504 CalculateRowAndColumnCount(rWindowSize);
506 // Update the border values.
507 mnLeftBorder = mnRequestedLeftBorder;
508 mnTopBorder = mnRequestedTopBorder;
509 mnRightBorder = mnRequestedRightBorder;
510 mnBottomBorder = mnRequestedBottomBorder;
511 if (mnColumnCount > 1)
513 int nMinimumBorderWidth = mnHorizontalGap/2;
514 if (mnLeftBorder < nMinimumBorderWidth)
515 mnLeftBorder = nMinimumBorderWidth;
516 if (mnRightBorder < nMinimumBorderWidth)
517 mnRightBorder = nMinimumBorderWidth;
519 else
521 int nMinimumBorderHeight = mnVerticalGap/2;
522 if (mnTopBorder < nMinimumBorderHeight)
523 mnTopBorder = nMinimumBorderHeight;
524 if (mnBottomBorder < nMinimumBorderHeight)
525 mnBottomBorder = nMinimumBorderHeight;
528 mpPageObjectLayouter.reset(
529 new PageObjectLayouter(
530 CalculateTargetSize(rWindowSize, rPreviewModelSize),
531 rPreviewModelSize,
532 mpWindow,
533 mnPageCount));
535 maPageObjectSize = mpPageObjectLayouter->GetGridMaxSize(
536 PageObjectLayouter::WindowCoordinateSystem);
538 CalculateMaxRowAndColumnCount(rWindowSize);
540 return true;
543 sal_Int32 Layouter::Implementation::GetRowAtPosition (
544 sal_Int32 nYPosition,
545 bool bIncludeBordersAndGaps,
546 GapMembership eGapMembership) const
548 sal_Int32 nRow = -1;
550 const sal_Int32 nY = nYPosition - mnTopBorder;
551 if (nY >= 0)
553 // Vertical distance from one row to the next.
554 const sal_Int32 nRowOffset (maPageObjectSize.Height() + mnVerticalGap);
556 // Calculate row consisting of page objects and gap below.
557 nRow = nY / nRowOffset;
559 const sal_Int32 nDistanceIntoGap ((nY - nRow*nRowOffset) - maPageObjectSize.Height());
560 // When inside the gap below then nYPosition is not over a page
561 // object.
562 if (nDistanceIntoGap > 0)
564 sal_Int32 nResolvedRow = ResolvePositionInGap(
565 nDistanceIntoGap,
566 eGapMembership,
567 nRow,
568 mnVerticalGap);
569 if (!bIncludeBordersAndGaps || nResolvedRow != -1)
570 nRow = nResolvedRow;
573 else if (bIncludeBordersAndGaps)
575 // We are in the top border area. Set nRow to the first row when
576 // the top border shall be considered to belong to the first row.
577 nRow = 0;
580 return nRow;
583 sal_Int32 Layouter::Implementation::GetColumnAtPosition (
584 sal_Int32 nXPosition,
585 bool bIncludeBordersAndGaps,
586 GapMembership eGapMembership) const
588 sal_Int32 nColumn = -1;
590 sal_Int32 nX = nXPosition - mnLeftBorder;
591 if (nX >= 0)
593 // Horizontal distance from one column to the next.
594 const sal_Int32 nColumnOffset (maPageObjectSize.Width() + mnHorizontalGap);
596 // Calculate row consisting of page objects and gap below.
597 nColumn = nX / nColumnOffset;
598 if (nColumn < 0)
599 nColumn = 0;
600 else if (nColumn >= mnColumnCount)
601 nColumn = mnColumnCount-1;
603 const sal_Int32 nDistanceIntoGap ((nX - nColumn*nColumnOffset) - maPageObjectSize.Width());
604 // When inside the gap at the right then nXPosition is not over a
605 // page object.
606 if (nDistanceIntoGap > 0)
608 sal_Int32 nResolvedColumn = ResolvePositionInGap(
609 nDistanceIntoGap,
610 eGapMembership,
611 nColumn,
612 mnHorizontalGap);
613 if (!bIncludeBordersAndGaps || nResolvedColumn != -1)
614 nColumn = nResolvedColumn;
617 else if (bIncludeBordersAndGaps)
619 // We are in the left border area. Set nColumn to the first column
620 // when the left border shall be considered to belong to the first
621 // column.
622 nColumn = 0;
624 return nColumn;
627 sal_Int32 Layouter::Implementation::ResolvePositionInGap (
628 sal_Int32 nDistanceIntoGap,
629 GapMembership eGapMembership,
630 sal_Int32 nIndex,
631 sal_Int32 nGap)
633 switch (eGapMembership)
635 case GM_NONE:
636 // The gap is no man's land.
637 nIndex = -1;
638 break;
640 case GM_BOTH:
642 // The lower half of the gap belongs to the next row or column.
643 sal_Int32 nFirstHalfGapWidth = nGap / 2;
644 if (nDistanceIntoGap > nFirstHalfGapWidth)
645 nIndex ++;
646 break;
649 case GM_PREVIOUS:
650 // Row or column already at correct value.
651 break;
653 case GM_NEXT:
654 // The complete gap belongs to the next row or column.
655 nIndex ++;
656 break;
658 case GM_PAGE_BORDER:
659 if (nDistanceIntoGap > 0)
661 if (nDistanceIntoGap > nGap)
663 // Inside the border of the next row or column.
664 nIndex ++;
666 else
668 // Inside the gap between the page borders.
669 nIndex = -1;
672 break;
674 default:
675 nIndex = -1;
678 return nIndex;
681 void Layouter::Implementation::CalculateGeometricPosition (
682 InsertPosition& rPosition,
683 const Size& rIndicatorSize,
684 const bool bIsVertical,
685 model::SlideSorterModel& rModel) const
687 // 1. Determine right/bottom of the leading page and the left/top of the
688 // trailing page object and how to distribute the missing space.
689 sal_Int32 nLeadingLocation (0);
690 sal_Int32 nTrailingLocation (0);
691 bool bIsLeadingFixed (false);
692 bool bIsTrailingFixed (false);
693 sal_Int32 nSecondaryLocation (0);
694 const sal_Int32 nIndex (rPosition.GetIndex());
696 if (rPosition.IsAtRunStart())
698 // Place indicator at the top of the column.
699 const Rectangle aOuterBox (GetPageObjectBox(nIndex));
700 const Rectangle aInnerBox (GetInnerBoundingBox(rModel, nIndex));
701 if (bIsVertical)
703 nLeadingLocation = aOuterBox.Top();
704 nTrailingLocation = aInnerBox.Top();
705 nSecondaryLocation = aInnerBox.Center().X();
707 else
709 nLeadingLocation = aOuterBox.Left();
710 nTrailingLocation = aInnerBox.Left();
711 nSecondaryLocation = aInnerBox.Center().Y();
713 bIsLeadingFixed = true;
715 else if (rPosition.IsAtRunEnd())
717 // Place indicator at the bottom/right of the column/row.
719 const Rectangle aOuterBox (GetPageObjectBox(nIndex-1));
720 const Rectangle aInnerBox (GetInnerBoundingBox(rModel, nIndex-1));
721 if (bIsVertical)
723 nLeadingLocation = aInnerBox.Bottom();
724 nTrailingLocation = aOuterBox.Bottom();
725 nSecondaryLocation = aInnerBox.Center().X();
727 else
729 nLeadingLocation = aInnerBox.Right();
730 nTrailingLocation = aOuterBox.Right();
731 nSecondaryLocation = aInnerBox.Center().Y();
733 bIsTrailingFixed = true;
734 if ( ! rPosition.IsExtraSpaceNeeded())
735 bIsLeadingFixed = true;
737 else
739 // Place indicator between two rows/columns.
740 const Rectangle aBox1 (GetInnerBoundingBox(rModel, nIndex-1));
741 const Rectangle aBox2 (GetInnerBoundingBox(rModel, nIndex));
742 if (bIsVertical)
744 nLeadingLocation = aBox1.Bottom();
745 nTrailingLocation = aBox2.Top();
746 nSecondaryLocation = (aBox1.Center().X() + aBox2.Center().X()) / 2;
748 else
750 nLeadingLocation = aBox1.Right();
751 nTrailingLocation = aBox2.Left();
752 nSecondaryLocation = (aBox1.Center().Y() + aBox2.Center().Y()) / 2;
756 // 2. Calculate the location of the insert indicator and the offsets of
757 // leading and trailing pages.
758 const sal_Int32 nAvailableSpace (nTrailingLocation - nLeadingLocation);
759 const sal_Int32 nRequiredSpace (bIsVertical ? rIndicatorSize.Height():rIndicatorSize.Width());
760 const sal_Int32 nMissingSpace (::std::max(sal_Int32(0), nRequiredSpace - nAvailableSpace));
761 sal_Int32 nPrimaryLocation (0);
762 sal_Int32 nLeadingOffset (0);
763 sal_Int32 nTrailingOffset (0);
764 if (bIsLeadingFixed)
766 nPrimaryLocation = nLeadingLocation + nRequiredSpace/2;
767 if ( ! bIsTrailingFixed)
768 nTrailingOffset = nMissingSpace;
770 else if (bIsTrailingFixed)
772 nPrimaryLocation = nTrailingLocation - nRequiredSpace/2;
773 nLeadingOffset = -nMissingSpace;
775 else
777 nPrimaryLocation = (nLeadingLocation + nTrailingLocation) /2;
778 nLeadingOffset = -nMissingSpace/2;
779 nTrailingOffset = nMissingSpace + nLeadingOffset;
782 if (bIsVertical)
784 rPosition.SetGeometricalPosition(
785 Point(nSecondaryLocation, nPrimaryLocation),
786 Point(0, nLeadingOffset),
787 Point(0, nTrailingOffset));
789 else
791 rPosition.SetGeometricalPosition(
792 Point(nPrimaryLocation, nSecondaryLocation),
793 Point(nLeadingOffset, 0),
794 Point(nTrailingOffset, 0));
798 Rectangle Layouter::Implementation::GetInnerBoundingBox (
799 model::SlideSorterModel& rModel,
800 const sal_Int32 nIndex) const
802 model::SharedPageDescriptor pDescriptor (rModel.GetPageDescriptor(nIndex));
803 if ( ! pDescriptor)
804 return Rectangle();
806 PageObjectLayouter::Part ePart = PageObjectLayouter::Preview;
808 if (pDescriptor->HasState(model::PageDescriptor::ST_Selected))
809 ePart = PageObjectLayouter::PageObject;
811 return mpPageObjectLayouter->GetBoundingBox(
812 pDescriptor, ePart,
813 PageObjectLayouter::ModelCoordinateSystem, true);
816 Range Layouter::Implementation::GetValidHorizontalSizeRange() const
818 return Range(
819 mnLeftBorder + maMinimalSize.Width() + mnRightBorder,
820 mnLeftBorder + maMaximalSize.Width() + mnRightBorder);
823 Range Layouter::Implementation::GetValidVerticalSizeRange() const
825 return Range(
826 mnTopBorder + maMinimalSize.Height() + mnBottomBorder,
827 mnTopBorder + maMaximalSize.Height() + mnBottomBorder);
830 Range Layouter::Implementation::GetRangeOfVisiblePageObjects (const Rectangle& aVisibleArea) const
832 const sal_Int32 nRow0 (GetRowAtPosition(aVisibleArea.Top(), true, GM_NEXT));
833 const sal_Int32 nCol0 (GetColumnAtPosition(aVisibleArea.Left(),true, GM_NEXT));
834 const sal_Int32 nRow1 (GetRowAtPosition(aVisibleArea.Bottom(), true, GM_PREVIOUS));
835 const sal_Int32 nCol1 (GetColumnAtPosition(aVisibleArea.Right(), true, GM_PREVIOUS));
837 // When start and end lie in different rows then the range may include
838 // slides outside (left or right of) the given area.
839 return Range(GetIndex(nRow0,nCol0,true), GetIndex(nRow1,nCol1,true));
842 Size Layouter::Implementation::GetTargetSize (
843 const Size& rWindowSize,
844 const Size& rPreviewModelSize,
845 const bool bCalculateWidth,
846 const bool bCalculateHeight) const
848 (void)rPreviewModelSize;
850 if (mnColumnCount<=0 || mnRowCount<=0)
851 return maPreferredSize;
852 if ( ! (bCalculateWidth || bCalculateHeight))
854 OSL_ASSERT(bCalculateWidth || bCalculateHeight);
855 return maPreferredSize;
858 // Calculate the width of each page object.
859 Size aTargetSize (0,0);
860 if (bCalculateWidth)
861 aTargetSize.setWidth(
862 (rWindowSize.Width() - mnLeftBorder - mnRightBorder
863 - (mnColumnCount-1) * mnHorizontalGap)
864 / mnColumnCount);
865 else if (bCalculateHeight)
866 aTargetSize.setHeight(
867 (rWindowSize.Height() - mnTopBorder - mnBottomBorder
868 - (mnRowCount-1) * mnVerticalGap)
869 / mnRowCount);
871 if (bCalculateWidth)
873 if (aTargetSize.Width() < maMinimalSize.Width())
874 aTargetSize.setWidth(maMinimalSize.Width());
875 else if (aTargetSize.Width() > maMaximalSize.Width())
876 aTargetSize.setWidth(maMaximalSize.Width());
878 else if (bCalculateHeight)
880 if (aTargetSize.Height() < maMinimalSize.Height())
881 aTargetSize.setHeight(maMinimalSize.Height());
882 else if (aTargetSize.Height() > maMaximalSize.Height())
883 aTargetSize.setHeight(maMaximalSize.Height());
886 return aTargetSize;
889 sal_Int32 Layouter::Implementation::GetIndex (
890 const sal_Int32 nRow,
891 const sal_Int32 nColumn,
892 const bool bClampToValidRange) const
894 if (nRow >= 0 && nColumn >= 0)
896 const sal_Int32 nIndex (nRow * mnColumnCount + nColumn);
897 if (nIndex >= mnPageCount)
898 if (bClampToValidRange)
899 return mnPageCount-1;
900 else
901 return -1;
902 else
903 return nIndex;
905 else if (bClampToValidRange)
906 return 0;
907 else
908 return -1;
911 Rectangle Layouter::Implementation::GetPageObjectBox (
912 const sal_Int32 nIndex,
913 const bool bIncludeBorderAndGap) const
915 const sal_Int32 nRow (nIndex / mnColumnCount);
916 const sal_Int32 nColumn (nIndex % mnColumnCount);
918 const Rectangle aBoundingBox (GetPageObjectBox(nRow,nColumn));
919 if (bIncludeBorderAndGap)
920 return AddBorderAndGap(aBoundingBox, nRow, nColumn);
921 else
922 return aBoundingBox;
925 Rectangle Layouter::Implementation::GetPageObjectBox (
926 const sal_Int32 nRow,
927 const sal_Int32 nColumn) const
929 return Rectangle(
930 Point (mnLeftBorder
931 + nColumn * maPageObjectSize.Width()
932 + (nColumn>0 ? nColumn : 0) * mnHorizontalGap,
933 mnTopBorder
934 + nRow * maPageObjectSize.Height()
935 + (nRow>0 ? nRow : 0) * mnVerticalGap),
936 maPageObjectSize);
939 Rectangle Layouter::Implementation::AddBorderAndGap (
940 const Rectangle& rBoundingBox,
941 const sal_Int32 nRow,
942 const sal_Int32 nColumn) const
944 Rectangle aBoundingBox (rBoundingBox);
946 if (nColumn == 0)
947 aBoundingBox.Left() = 0;
948 else
949 aBoundingBox.Left() -= mnHorizontalGap/2;
950 if (nColumn == mnColumnCount-1)
951 aBoundingBox.Right() += mnRightBorder;
952 else
953 aBoundingBox.Right() += mnHorizontalGap/2;
954 if (nRow == 0)
955 aBoundingBox.Top() = 0;
956 else
957 aBoundingBox.Top() -= mnVerticalGap/2;
958 if (nRow == mnRowCount-1)
959 aBoundingBox.Bottom() += mnBottomBorder;
960 else
961 aBoundingBox.Bottom() += mnVerticalGap/2;
962 return aBoundingBox;
965 Rectangle Layouter::Implementation::GetTotalBoundingBox() const
967 sal_Int32 nHorizontalSize = 0;
968 sal_Int32 nVerticalSize = 0;
969 if (mnColumnCount > 0)
971 sal_Int32 nRowCount = (mnPageCount+mnColumnCount-1) / mnColumnCount;
972 nHorizontalSize =
973 mnLeftBorder
974 + mnRightBorder
975 + mnColumnCount * maPageObjectSize.Width();
976 if (mnColumnCount > 1)
977 nHorizontalSize += (mnColumnCount-1) * mnHorizontalGap;
978 nVerticalSize =
979 mnTopBorder
980 + mnBottomBorder
981 + nRowCount * maPageObjectSize.Height();
982 if (nRowCount > 1)
983 nVerticalSize += (nRowCount-1) * mnVerticalGap;
986 return Rectangle (
987 Point(0,0),
988 Size (nHorizontalSize, nVerticalSize)
992 void Layouter::Implementation::CalculateVerticalLogicalInsertPosition (
993 const Point& rModelPosition,
994 InsertPosition& rPosition) const
996 const sal_Int32 nY = rModelPosition.Y() - mnTopBorder + maPageObjectSize.Height()/2;
997 const sal_Int32 nRowHeight (maPageObjectSize.Height() + mnVerticalGap);
998 const sal_Int32 nRow (::std::min(mnPageCount, nY / nRowHeight));
999 rPosition.SetLogicalPosition (
1000 nRow,
1002 nRow,
1003 (nRow == 0),
1004 (nRow == mnRowCount),
1005 (nRow >= mnMaxRowCount));
1008 //===== HorizontalImplementation ================================================
1010 HorizontalImplementation::HorizontalImplementation (const Implementation& rImplementation)
1011 : Implementation(rImplementation)
1015 Layouter::Orientation HorizontalImplementation::GetOrientation() const
1017 return Layouter::HORIZONTAL;
1020 void HorizontalImplementation::CalculateRowAndColumnCount (const Size& rWindowSize)
1022 (void)rWindowSize;
1024 // Row and column count are fixed (for a given page count.)
1025 mnColumnCount = mnPageCount;
1026 mnRowCount = 1;
1029 void HorizontalImplementation::CalculateMaxRowAndColumnCount (const Size& rWindowSize)
1031 mnMaxColumnCount = (rWindowSize.Width() - mnLeftBorder - mnRightBorder)
1032 / (maPageObjectSize.Width() + mnHorizontalGap);
1033 mnMaxRowCount = 1;
1036 Size HorizontalImplementation::CalculateTargetSize (
1037 const Size& rWindowSize,
1038 const Size& rPreviewModelSize) const
1040 return Implementation::GetTargetSize(rWindowSize, rPreviewModelSize, false, true);
1043 void HorizontalImplementation::CalculateLogicalInsertPosition (
1044 const Point& rModelPosition,
1045 InsertPosition& rPosition) const
1047 const sal_Int32 nX = rModelPosition.X() - mnLeftBorder + maPageObjectSize.Width()/2;
1048 const sal_Int32 nColumnWidth (maPageObjectSize.Width() + mnHorizontalGap);
1049 const sal_Int32 nColumn (::std::min(mnPageCount, nX / nColumnWidth));
1050 rPosition.SetLogicalPosition (
1052 nColumn,
1053 nColumn,
1054 (nColumn == 0),
1055 (nColumn == mnColumnCount),
1056 (nColumn >= mnMaxColumnCount));
1059 //===== VerticalImplementation ================================================
1061 VerticalImplementation::VerticalImplementation (const Implementation& rImplementation)
1062 : Implementation(rImplementation)
1066 Layouter::Orientation VerticalImplementation::GetOrientation() const
1068 return Layouter::VERTICAL;
1071 void VerticalImplementation::CalculateRowAndColumnCount (const Size& rWindowSize)
1073 (void)rWindowSize;
1075 // Row and column count are fixed (for a given page count.)
1076 mnRowCount = mnPageCount;
1077 mnColumnCount = 1;
1081 void VerticalImplementation::CalculateMaxRowAndColumnCount (const Size& rWindowSize)
1083 mnMaxRowCount = (rWindowSize.Height() - mnTopBorder - mnBottomBorder)
1084 / (maPageObjectSize.Height() + mnVerticalGap);
1085 mnMaxColumnCount = 1;
1088 Size VerticalImplementation::CalculateTargetSize (
1089 const Size& rWindowSize,
1090 const Size& rPreviewModelSize) const
1092 return Implementation::GetTargetSize(rWindowSize, rPreviewModelSize, true, false);
1095 void VerticalImplementation::CalculateLogicalInsertPosition (
1096 const Point& rModelPosition,
1097 InsertPosition& rPosition) const
1099 return CalculateVerticalLogicalInsertPosition(rModelPosition, rPosition);
1102 //===== GridImplementation ================================================
1104 GridImplementation::GridImplementation (
1105 sd::Window *pWindow,
1106 const ::boost::shared_ptr<view::Theme>& rpTheme)
1107 : Implementation(pWindow, rpTheme)
1111 GridImplementation::GridImplementation (const Implementation& rImplementation)
1112 : Implementation(rImplementation)
1116 Layouter::Orientation GridImplementation::GetOrientation() const
1118 return Layouter::GRID;
1121 void GridImplementation::CalculateRowAndColumnCount (const Size& rWindowSize)
1123 // Calculate the column count.
1124 mnColumnCount
1125 = (rWindowSize.Width() - mnRequestedLeftBorder - mnRequestedRightBorder)
1126 / (maPreferredSize.Width() + mnHorizontalGap);
1127 if (mnColumnCount < mnMinimalColumnCount)
1128 mnColumnCount = mnMinimalColumnCount;
1129 if (mnColumnCount > mnMaximalColumnCount)
1130 mnColumnCount = mnMaximalColumnCount;
1131 mnRowCount = (mnPageCount + mnColumnCount-1)/mnColumnCount;
1134 void GridImplementation::CalculateMaxRowAndColumnCount (const Size& rWindowSize)
1136 mnMaxColumnCount = (rWindowSize.Width() - mnLeftBorder - mnRightBorder)
1137 / (maPageObjectSize.Width() + mnHorizontalGap);
1138 mnMaxRowCount = (rWindowSize.Height() - mnTopBorder - mnBottomBorder)
1139 / (maPageObjectSize.Height() + mnVerticalGap);
1142 Size GridImplementation::CalculateTargetSize (
1143 const Size& rWindowSize,
1144 const Size& rPreviewModelSize) const
1146 return Implementation::GetTargetSize(rWindowSize, rPreviewModelSize, true, true);
1149 void GridImplementation::CalculateLogicalInsertPosition (
1150 const Point& rModelPosition,
1151 InsertPosition& rPosition) const
1153 if (mnColumnCount == 1)
1155 CalculateVerticalLogicalInsertPosition(rModelPosition, rPosition);
1157 else
1159 // Handle the general case of more than one column.
1160 sal_Int32 nRow (::std::min(
1161 mnRowCount-1,
1162 GetRowAtPosition (rModelPosition.Y(), true, GM_BOTH)));
1163 const sal_Int32 nX = rModelPosition.X() - mnLeftBorder + maPageObjectSize.Width()/2;
1164 const sal_Int32 nColumnWidth (maPageObjectSize.Width() + mnHorizontalGap);
1165 sal_Int32 nColumn (::std::min(mnColumnCount, nX / nColumnWidth));
1166 sal_Int32 nIndex (nRow * mnColumnCount + nColumn);
1167 bool bIsAtRunEnd (nColumn == mnColumnCount);
1169 if (nIndex >= mnPageCount)
1171 nIndex = mnPageCount;
1172 nRow = mnRowCount-1;
1173 nColumn = ::std::min(::std::min(mnPageCount, mnColumnCount), nColumn);
1174 bIsAtRunEnd = true;
1177 rPosition.SetLogicalPosition (
1178 nRow,
1179 nColumn,
1180 nIndex,
1181 (nColumn == 0),
1182 bIsAtRunEnd,
1183 (nColumn >= mnMaxColumnCount));
1187 //===== InsertPosition ========================================================
1189 InsertPosition::InsertPosition()
1190 : mnRow(-1),
1191 mnColumn(-1),
1192 mnIndex(-1),
1193 mbIsAtRunStart(false),
1194 mbIsAtRunEnd(false),
1195 mbIsExtraSpaceNeeded(false),
1196 maLocation(0,0),
1197 maLeadingOffset(0,0),
1198 maTrailingOffset(0,0)
1202 InsertPosition& InsertPosition::operator= (const InsertPosition& rInsertPosition)
1204 if (this != &rInsertPosition)
1206 mnRow = rInsertPosition.mnRow;
1207 mnColumn = rInsertPosition.mnColumn;
1208 mnIndex = rInsertPosition.mnIndex;
1209 mbIsAtRunStart = rInsertPosition.mbIsAtRunStart;
1210 mbIsAtRunEnd = rInsertPosition.mbIsAtRunEnd;
1211 mbIsExtraSpaceNeeded = rInsertPosition.mbIsExtraSpaceNeeded;
1212 maLocation = rInsertPosition.maLocation;
1213 maLeadingOffset = rInsertPosition.maLeadingOffset;
1214 maTrailingOffset = rInsertPosition.maTrailingOffset;
1216 return *this;
1219 bool InsertPosition::operator== (const InsertPosition& rInsertPosition) const
1221 // Do not compare the geometrical information (maLocation).
1222 return mnRow==rInsertPosition.mnRow
1223 && mnColumn==rInsertPosition.mnColumn
1224 && mnIndex==rInsertPosition.mnIndex
1225 && mbIsAtRunStart==rInsertPosition.mbIsAtRunStart
1226 && mbIsAtRunEnd==rInsertPosition.mbIsAtRunEnd
1227 && mbIsExtraSpaceNeeded==rInsertPosition.mbIsExtraSpaceNeeded;
1230 bool InsertPosition::operator!= (const InsertPosition& rInsertPosition) const
1232 return !operator==(rInsertPosition);
1235 void InsertPosition::SetLogicalPosition (
1236 const sal_Int32 nRow,
1237 const sal_Int32 nColumn,
1238 const sal_Int32 nIndex,
1239 const bool bIsAtRunStart,
1240 const bool bIsAtRunEnd,
1241 const bool bIsExtraSpaceNeeded)
1243 mnRow = nRow;
1244 mnColumn = nColumn;
1245 mnIndex = nIndex;
1246 mbIsAtRunStart = bIsAtRunStart;
1247 mbIsAtRunEnd = bIsAtRunEnd;
1248 mbIsExtraSpaceNeeded = bIsExtraSpaceNeeded;
1251 void InsertPosition::SetGeometricalPosition(
1252 const Point& rLocation,
1253 const Point& rLeadingOffset,
1254 const Point& rTrailingOffset)
1256 maLocation = rLocation;
1257 maLeadingOffset = rLeadingOffset;
1258 maTrailingOffset = rTrailingOffset;
1261 } } } // end of namespace ::sd::slidesorter::namespace
1263 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */