diff options
Diffstat (limited to 'sd/source/ui/slidesorter/view/SlsLayouter.cxx')
-rw-r--r-- | sd/source/ui/slidesorter/view/SlsLayouter.cxx | 1555 |
1 files changed, 1555 insertions, 0 deletions
diff --git a/sd/source/ui/slidesorter/view/SlsLayouter.cxx b/sd/source/ui/slidesorter/view/SlsLayouter.cxx new file mode 100644 index 000000000000..66f22097993a --- /dev/null +++ b/sd/source/ui/slidesorter/view/SlsLayouter.cxx @@ -0,0 +1,1555 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#include "precompiled_sd.hxx" + +#include "view/SlsLayouter.hxx" +#include "model/SlideSorterModel.hxx" +#include "model/SlsPageDescriptor.hxx" +#include "Window.hxx" +#include <rtl/math.hxx> +#include <basegfx/numeric/ftools.hxx> + +namespace { + sal_Int32 RoundToInt (const double nValue) + { + return sal_Int32(::rtl::math::round(nValue)); + } +} + + +namespace sd { namespace slidesorter { namespace view { + +class Layouter::Implementation +{ +public: + SharedSdWindow mpWindow; + sal_Int32 mnRequestedLeftBorder; + sal_Int32 mnRequestedRightBorder; + sal_Int32 mnRequestedTopBorder; + sal_Int32 mnRequestedBottomBorder; + sal_Int32 mnLeftBorder; + sal_Int32 mnRightBorder; + sal_Int32 mnTopBorder; + sal_Int32 mnBottomBorder; + sal_Int32 mnVerticalGap; + sal_Int32 mnHorizontalGap; + Size maMinimalSize; + Size maPreferredSize; + Size maMaximalSize; + sal_Int32 mnMinimalColumnCount; + sal_Int32 mnMaximalColumnCount; + sal_Int32 mnPageCount; + sal_Int32 mnColumnCount; + sal_Int32 mnRowCount; + /// The maximum number of columns. Can only be larger than the current + /// number of columns when there are not enough pages to fill all + /// available columns. + sal_Int32 mnMaxColumnCount; + /// The maximum number of rows. Can only be larger than the current + /// number of rows when there are not enough pages to fill all available + /// rows. + sal_Int32 mnMaxRowCount; + Size maPageObjectSize; + ::boost::shared_ptr<PageObjectLayouter> mpPageObjectLayouter; + ::boost::shared_ptr<view::Theme> mpTheme; + + /** Specify how the gap between two page objects is associated with the + page objects. + */ + enum GapMembership { + GM_NONE, // Gap is not associated with any page object. + GM_PREVIOUS, // The whole gap is associated with the previous page + // object (left or above the gap.) + GM_BOTH, // Half of the gap is associated with previous, half + // with the next page object. + GM_NEXT, // The whole gap is associated with the next page + // object (right or below the gap.) + GM_PAGE_BORDER + }; + + static Implementation* Create ( + const Implementation& rImplementation, + const Layouter::Orientation eOrientation); + + virtual Layouter::Orientation GetOrientation (void) const = 0; + + bool Rearrange ( + const Size& rWindowSize, + const Size& rPreviewModelSize, + const sal_uInt32 nPageCount); + + /** Calculate the row that the point with the given vertical coordinate + is over. The horizontal component is ignored. + @param nYPosition + Vertical position in model coordinates. + @param bIncludeBordersAndGaps + When this flag is <TRUE/> then the area of borders and gaps are + interpreted as belonging to one of the rows. + @param eGapMembership + Specifies to what row the gap areas belong. Here GM_NONE + corresponds to bIncludeBordersAndGaps being <FALSE/>. When + GM_BOTH is given then the upper half is associated to the row + above and the lower half to the row below. Values of + GM_PREVIOUS and GM_NEXT associate the whole gap area with the + row above or below respectively. + */ + sal_Int32 GetRowAtPosition ( + sal_Int32 nYPosition, + bool bIncludeBordersAndGaps, + GapMembership eGapMembership = GM_NONE) const; + + /** Calculate the column that the point with the given horizontal + coordinate is over. The verical component is ignored. + @param nXPosition + Horizontal position in model coordinates. + @param bIncludeBordersAndGaps + When this flag is <TRUE/> then the area of borders and gaps are + interpreted as belonging to one of the columns. + @param eGapMembership + Specifies to what column the gap areas belong. + */ + sal_Int32 GetColumnAtPosition ( + sal_Int32 nXPosition, + bool bIncludeBordersAndGaps, + GapMembership eGapMembership = GM_NONE) const; + + /** This method is typically called from GetRowAtPosition() and + GetColumnAtPosition() to handle a position that lies inside the gap + between two adjacent rows or columns. + @param nDistanceIntoGap + Vertical distance from the bottom of the upper row down into the + gap or or horizontal distance from the right edge right into the + gap. + @param eGapMemberhship + This value decides what areas in the gap belong to which (or no) + row or column. + @param nIndex + The row index of the upper row or the column index of the left + column. + @param nGap + Width or height of the gap in model coordiantes between the + page borders. + @return + Returns either the index of the upper row (as given as nRow), the + index of the lower row (nRow+1) or -1 to indicate that the + position belongs to no row. + */ + sal_Int32 ResolvePositionInGap ( + sal_Int32 nDistanceIntoGap, + GapMembership eGapMembership, + sal_Int32 nIndex, + sal_Int32 nGap) const; + + /** Calculate the logical part of the insert position, i.e. the page + after whicht to insert. + */ + virtual void CalculateLogicalInsertPosition ( + const Point& rModelPosition, + InsertPosition& rPosition) const = 0; + + /** Calculate the geometrical part of the insert position, i.e. the + location of where to display the insertion indicator and the + distances about which the leading and trailing pages have to be + moved to make room for the indicator. + */ + void CalculateGeometricPosition ( + InsertPosition& rPosition, + const Size& rIndicatorSize, + const bool bIsVertical, + model::SlideSorterModel& rModel) const; + + /** Return the bounding box of the preview or, when selected, of the page + object. Thus, it returns something like a visual bounding box. + */ + Rectangle GetInnerBoundingBox ( + model::SlideSorterModel& rModel, + const sal_Int32 nIndex) const; + + Range GetValidHorizontalSizeRange (void) const; + Range GetValidVerticalSizeRange (void) const; + + Range GetRangeOfVisiblePageObjects (const Rectangle& aVisibleArea) const; + sal_Int32 GetIndex ( + const sal_Int32 nRow, + const sal_Int32 nColumn, + const bool bClampToValidRange) const; + + Rectangle GetPageObjectBox ( + const sal_Int32 nIndex, + const bool bIncludeBorderAndGap = false) const; + + Rectangle GetPageObjectBox ( + const sal_Int32 nRow, + const sal_Int32 nColumn) const; + + Rectangle AddBorderAndGap ( + const Rectangle& rBoundingBox, + const sal_Int32 nRow, + const sal_Int32 nColumn) const; + + Rectangle GetTotalBoundingBox (void) const; + + virtual ~Implementation (void); + +protected: + Implementation ( + const SharedSdWindow& rpWindow, + const ::boost::shared_ptr<view::Theme>& rpTheme); + Implementation (const Implementation& rImplementation); + + virtual void CalculateRowAndColumnCount (const Size& rWindowSize) = 0; + virtual void CalculateMaxRowAndColumnCount (const Size& rWindowSize) = 0; + virtual Size CalculateTargetSize ( + const Size& rWindowSize, + const Size& rPreviewModelSize) const = 0; + Size GetTargetSize ( + const Size& rWindowSize, + const Size& rPreviewModelSize, + const bool bCalculateWidth, + const bool bCalculateHeight) const; + void CalculateVerticalLogicalInsertPosition ( + const Point& rModelPosition, + InsertPosition& rPosition) const; +}; + + +/** The vertical layouter has one column and as many rows as there are + pages. +*/ +class VerticalImplementation : public Layouter::Implementation +{ +public: + VerticalImplementation ( + const SharedSdWindow& rpWindow, + const ::boost::shared_ptr<view::Theme>& rpTheme); + VerticalImplementation (const Implementation& rImplementation); + + virtual Layouter::Orientation GetOrientation (void) const; + + void CalculateLogicalInsertPosition ( + const Point& rModelPosition, + InsertPosition& rPosition) const; + +protected: + virtual void CalculateRowAndColumnCount (const Size& rWindowSize); + virtual void CalculateMaxRowAndColumnCount (const Size& rWindowSize); + virtual Size CalculateTargetSize ( + const Size& rWindowSize, + const Size& rPreviewModelSize) const; +}; + + +/** The horizontal layouter has one row and as many columns as there are + pages. +*/ +class HorizontalImplementation : public Layouter::Implementation +{ +public: + HorizontalImplementation ( + const SharedSdWindow& rpWindow, + const ::boost::shared_ptr<view::Theme>& rpTheme); + HorizontalImplementation (const Implementation& rImplementation); + + virtual Layouter::Orientation GetOrientation (void) const; + + void CalculateLogicalInsertPosition ( + const Point& rModelPosition, + InsertPosition& rPosition) const; + +protected: + virtual void CalculateRowAndColumnCount (const Size& rWindowSize); + virtual void CalculateMaxRowAndColumnCount (const Size& rWindowSize); + virtual Size CalculateTargetSize ( + const Size& rWindowSize, + const Size& rPreviewModelSize) const; +}; + + +/** The number of columns of the grid layouter is defined via a control in + the slide sorter tool bar. The number of rows is calculated from the + number of columns and the number of pages. +*/ +class GridImplementation : public Layouter::Implementation +{ +public: + GridImplementation ( + const SharedSdWindow& rpWindow, + const ::boost::shared_ptr<view::Theme>& rpTheme); + GridImplementation (const Implementation& rImplementation); + + virtual Layouter::Orientation GetOrientation (void) const; + + void CalculateLogicalInsertPosition ( + const Point& rModelPosition, + InsertPosition& rPosition) const; + +protected: + virtual void CalculateRowAndColumnCount (const Size& rWindowSize); + virtual void CalculateMaxRowAndColumnCount (const Size& rWindowSize); + virtual Size CalculateTargetSize ( + const Size& rWindowSize, + const Size& rPreviewModelSize) const; +}; + + + + +//===== Layouter ============================================================== + +Layouter::Layouter ( + const SharedSdWindow& rpWindow, + const ::boost::shared_ptr<Theme>& rpTheme) + : mpImplementation(new GridImplementation(rpWindow, rpTheme)), + mpWindow(rpWindow) +{ +} + + + + +Layouter::~Layouter (void) +{ +} + + + + +::boost::shared_ptr<PageObjectLayouter> Layouter::GetPageObjectLayouter (void) const +{ + return mpImplementation->mpPageObjectLayouter; +} + + + + +void Layouter::SetBorders ( + sal_Int32 nLeftBorder, + sal_Int32 nRightBorder, + sal_Int32 nTopBorder, + sal_Int32 nBottomBorder) +{ + if (nLeftBorder >= 0) + mpImplementation->mnRequestedLeftBorder = nLeftBorder; + if (nRightBorder >= 0) + mpImplementation->mnRequestedRightBorder = nRightBorder; + if (nTopBorder >= 0) + mpImplementation->mnRequestedTopBorder = nTopBorder; + if (nBottomBorder >= 0) + mpImplementation->mnRequestedBottomBorder = nBottomBorder; +} + + + + +void Layouter::SetColumnCount ( + sal_Int32 nMinimalColumnCount, + sal_Int32 nMaximalColumnCount) +{ + if (nMinimalColumnCount <= nMaximalColumnCount) + { + mpImplementation->mnMinimalColumnCount = nMinimalColumnCount; + mpImplementation->mnMaximalColumnCount = nMaximalColumnCount; + } +} + + + + +bool Layouter::Rearrange ( + const Orientation eOrientation, + const Size& rWindowSize, + const Size& rPageSize, + const sal_uInt32 nPageCount) +{ + OSL_ASSERT(mpWindow); + + if (eOrientation != mpImplementation->GetOrientation()) + mpImplementation.reset(Implementation::Create(*mpImplementation, eOrientation)); + + return mpImplementation->Rearrange(rWindowSize, rPageSize, nPageCount); +} + + + + +void Layouter::_SetZoom (double nZoomFactor) +{ + _SetZoom(Fraction(nZoomFactor)); +} + + + + +void Layouter::_SetZoom (Fraction nZoomFactor) +{ + OSL_ASSERT(mpWindow); + + MapMode aMapMode (mpWindow->GetMapMode()); + aMapMode.SetScaleX (nZoomFactor); + aMapMode.SetScaleY (nZoomFactor); + mpWindow->SetMapMode (aMapMode); +} + + + + +sal_Int32 Layouter::GetColumnCount (void) const +{ + return mpImplementation->mnColumnCount; +} + + + + +sal_Int32 Layouter::GetRowCount (void) const +{ + return mpImplementation->mnRowCount; +} + + + + +sal_Int32 Layouter::GetRow (const sal_Int32 nIndex) const +{ + return nIndex / mpImplementation->mnColumnCount; +} + + + + +sal_Int32 Layouter::GetColumn (const sal_Int32 nIndex) const +{ + return nIndex % mpImplementation->mnColumnCount; +} + + + + +sal_Int32 Layouter::GetIndex (const sal_Int32 nRow, const sal_Int32 nColumn) const +{ + return mpImplementation->GetIndex(nRow,nColumn,true); +} + + + + +Size Layouter::GetPageObjectSize (void) const +{ + return mpImplementation->maPageObjectSize; +} + + + + +Rectangle Layouter::GetPageObjectBox ( + const sal_Int32 nIndex, + const bool bIncludeBorderAndGap) const +{ + return mpImplementation->GetPageObjectBox(nIndex, bIncludeBorderAndGap); +} + + + + +Rectangle Layouter::GetTotalBoundingBox (void) const +{ + return mpImplementation->GetTotalBoundingBox(); +} + + + + +InsertPosition Layouter::GetInsertPosition ( + const Point& rModelPosition, + const Size& rIndicatorSize, + model::SlideSorterModel& rModel) const +{ + InsertPosition aPosition; + mpImplementation->CalculateLogicalInsertPosition( + rModelPosition, + aPosition); + mpImplementation->CalculateGeometricPosition( + aPosition, + rIndicatorSize, + GetColumnCount()==1, + rModel); + return aPosition; +} + + + + +Range Layouter::GetValidHorizontalSizeRange (void) const +{ + return mpImplementation->GetValidHorizontalSizeRange(); +} + + + + +Range Layouter::GetValidVerticalSizeRange (void) const +{ + return mpImplementation->GetValidVerticalSizeRange(); +} + + + + +Range Layouter::GetRangeOfVisiblePageObjects (const Rectangle& aVisibleArea) const +{ + return mpImplementation->GetRangeOfVisiblePageObjects(aVisibleArea); +} + + + + +sal_Int32 Layouter::GetIndexAtPoint ( + const Point& rPosition, + const bool bIncludePageBorders, + const bool bClampToValidRange) const +{ + const sal_Int32 nRow ( + mpImplementation->GetRowAtPosition ( + rPosition.Y(), + bIncludePageBorders, + bIncludePageBorders ? Implementation::GM_PAGE_BORDER : Implementation::GM_NONE)); + const sal_Int32 nColumn ( + mpImplementation->GetColumnAtPosition ( + rPosition.X(), + bIncludePageBorders, + bIncludePageBorders ? Implementation::GM_PAGE_BORDER : Implementation::GM_NONE)); + + return mpImplementation->GetIndex(nRow,nColumn,bClampToValidRange); +} + + + + +//===== Layouter::Implementation ============================================== + +Layouter::Implementation* Layouter::Implementation::Create ( + const Implementation& rImplementation, + const Layouter::Orientation eOrientation) +{ + switch (eOrientation) + { + case HORIZONTAL: return new HorizontalImplementation(rImplementation); + case VERTICAL: return new VerticalImplementation(rImplementation); + case GRID: + default: return new GridImplementation(rImplementation); + } +} + + + + +Layouter::Implementation::Implementation ( + const SharedSdWindow& rpWindow, + const ::boost::shared_ptr<view::Theme>& rpTheme) + : mpWindow(rpWindow), + mnRequestedLeftBorder(5), + mnRequestedRightBorder(5), + mnRequestedTopBorder(5), + mnRequestedBottomBorder(5), + mnLeftBorder(5), + mnRightBorder(5), + mnTopBorder(5), + mnBottomBorder(5), + mnVerticalGap (10 - 2*rpTheme->GetIntegerValue(Theme::Integer_FocusIndicatorWidth)), + mnHorizontalGap(10 - 2*rpTheme->GetIntegerValue(Theme::Integer_FocusIndicatorWidth)), + maMinimalSize(132,98), + maPreferredSize(200,150), + maMaximalSize(300,200), + mnMinimalColumnCount(1), + mnMaximalColumnCount(15), + mnPageCount(0), + mnColumnCount(1), + mnRowCount(0), + mnMaxColumnCount(0), + mnMaxRowCount(0), + maPageObjectSize(1,1), + mpPageObjectLayouter(), + mpTheme(rpTheme) +{ +} + + + + +Layouter::Implementation::Implementation (const Implementation& rImplementation) + : mpWindow(rImplementation.mpWindow), + mnRequestedLeftBorder(rImplementation.mnRequestedLeftBorder), + mnRequestedRightBorder(rImplementation.mnRequestedRightBorder), + mnRequestedTopBorder(rImplementation.mnRequestedTopBorder), + mnRequestedBottomBorder(rImplementation.mnRequestedBottomBorder), + mnLeftBorder(rImplementation.mnLeftBorder), + mnRightBorder(rImplementation.mnRightBorder), + mnTopBorder(rImplementation.mnTopBorder), + mnBottomBorder(rImplementation.mnBottomBorder), + mnVerticalGap(rImplementation.mnVerticalGap), + mnHorizontalGap(rImplementation.mnHorizontalGap), + maMinimalSize(rImplementation.maMinimalSize), + maPreferredSize(rImplementation.maPreferredSize), + maMaximalSize(rImplementation.maMaximalSize), + mnMinimalColumnCount(rImplementation.mnMinimalColumnCount), + mnMaximalColumnCount(rImplementation.mnMaximalColumnCount), + mnPageCount(rImplementation.mnPageCount), + mnColumnCount(rImplementation.mnColumnCount), + mnRowCount(rImplementation.mnRowCount), + mnMaxColumnCount(rImplementation.mnMaxColumnCount), + mnMaxRowCount(rImplementation.mnMaxRowCount), + maPageObjectSize(rImplementation.maPageObjectSize), + mpPageObjectLayouter(), + mpTheme(rImplementation.mpTheme) +{ +} + + + + +Layouter::Implementation::~Implementation (void) +{ +} + + + + +bool Layouter::Implementation::Rearrange ( + const Size& rWindowSize, + const Size& rPreviewModelSize, + const sal_uInt32 nPageCount) +{ + mnPageCount = nPageCount; + + // Return early when the window or the model have not yet been initialized. + if (rWindowSize.Width()<=0 || rWindowSize.Height()<=0) + return false; + if (rPreviewModelSize.Width()<=0 || rPreviewModelSize.Height()<=0) + return false; + + CalculateRowAndColumnCount(rWindowSize); + + // Update the border values. + mnLeftBorder = mnRequestedLeftBorder; + mnTopBorder = mnRequestedTopBorder; + mnRightBorder = mnRequestedRightBorder; + mnBottomBorder = mnRequestedBottomBorder; + if (mnColumnCount > 1) + { + int nMinimumBorderWidth = mnHorizontalGap/2; + if (mnLeftBorder < nMinimumBorderWidth) + mnLeftBorder = nMinimumBorderWidth; + if (mnRightBorder < nMinimumBorderWidth) + mnRightBorder = nMinimumBorderWidth; + } + else + { + int nMinimumBorderHeight = mnVerticalGap/2; + if (mnTopBorder < nMinimumBorderHeight) + mnTopBorder = nMinimumBorderHeight; + if (mnBottomBorder < nMinimumBorderHeight) + mnBottomBorder = nMinimumBorderHeight; + } + + mpPageObjectLayouter.reset( + new PageObjectLayouter( + mpTheme, + CalculateTargetSize(rWindowSize, rPreviewModelSize), + rPreviewModelSize, + mpWindow, + mnPageCount)); + maPageObjectSize = mpPageObjectLayouter->GetSize( + PageObjectLayouter::FocusIndicator, + PageObjectLayouter::WindowCoordinateSystem); + + CalculateMaxRowAndColumnCount(rWindowSize); + + return true; +} + + + + +sal_Int32 Layouter::Implementation::GetRowAtPosition ( + sal_Int32 nYPosition, + bool bIncludeBordersAndGaps, + GapMembership eGapMembership) const +{ + sal_Int32 nRow = -1; + + const sal_Int32 nY = nYPosition - mnTopBorder; + if (nY >= 0) + { + // Vertical distance from one row to the next. + const sal_Int32 nRowOffset (maPageObjectSize.Height() + mnVerticalGap); + + // Calculate row consisting of page objects and gap below. + nRow = nY / nRowOffset; + + const sal_Int32 nDistanceIntoGap ((nY - nRow*nRowOffset) - maPageObjectSize.Height()); + // When inside the gap below then nYPosition is not over a page + // object. + if (nDistanceIntoGap > 0) + nRow = ResolvePositionInGap ( + nDistanceIntoGap, + eGapMembership, + nRow, + mnVerticalGap); + } + else if (bIncludeBordersAndGaps) + { + // We are in the top border area. Set nRow to the first row when + // the top border shall be considered to belong to the first row. + nRow = 0; + } + + return nRow; +} + + + + +sal_Int32 Layouter::Implementation::GetColumnAtPosition ( + sal_Int32 nXPosition, + bool bIncludeBordersAndGaps, + GapMembership eGapMembership) const +{ + sal_Int32 nColumn = -1; + + sal_Int32 nX = nXPosition - mnLeftBorder; + if (nX >= 0) + { + // Horizontal distance from one column to the next. + const sal_Int32 nColumnOffset (maPageObjectSize.Width() + mnHorizontalGap); + + // Calculate row consisting of page objects and gap below. + nColumn = nX / nColumnOffset; + if (nColumn < 0) + nColumn = 0; + else if (nColumn >= mnColumnCount) + nColumn = mnColumnCount-1; + + const sal_Int32 nDistanceIntoGap ((nX - nColumn*nColumnOffset) - maPageObjectSize.Width()); + // When inside the gap at the right then nXPosition is not over a + // page object. + if (nDistanceIntoGap > 0) + nColumn = ResolvePositionInGap ( + nDistanceIntoGap, + eGapMembership, + nColumn, + mnHorizontalGap); + } + else if (bIncludeBordersAndGaps) + { + // We are in the left border area. Set nColumn to the first column + // when the left border shall be considered to belong to the first + // column. + nColumn = 0; + } + return nColumn; +} + + + + +sal_Int32 Layouter::Implementation::ResolvePositionInGap ( + sal_Int32 nDistanceIntoGap, + GapMembership eGapMembership, + sal_Int32 nIndex, + sal_Int32 nGap) const +{ + switch (eGapMembership) + { + case GM_NONE: + // The gap is no man's land. + nIndex = -1; + break; + + case GM_BOTH: + { + // The lower half of the gap belongs to the next row or column. + sal_Int32 nFirstHalfGapWidth = nGap / 2; + if (nDistanceIntoGap > nFirstHalfGapWidth) + nIndex ++; + break; + } + + case GM_PREVIOUS: + // Row or column already at correct value. + break; + + case GM_NEXT: + // The complete gap belongs to the next row or column. + nIndex ++; + break; + + case GM_PAGE_BORDER: + if (nDistanceIntoGap > 0) + { + if (nDistanceIntoGap > nGap) + { + // Inside the border of the next row or column. + nIndex ++; + } + else + { + // Inside the gap between the page borders. + nIndex = -1; + } + } + break; + + default: + nIndex = -1; + } + + return nIndex; +} + + + + +void Layouter::Implementation::CalculateGeometricPosition ( + InsertPosition& rPosition, + const Size& rIndicatorSize, + const bool bIsVertical, + model::SlideSorterModel& rModel) const +{ + // 1. Determine right/bottom of the leading page and the left/top of the + // trailing page object and how to distribute the missing space. + sal_Int32 nLeadingLocation (0); + sal_Int32 nTrailingLocation (0); + bool bIsLeadingFixed (false); + bool bIsTrailingFixed (false); + sal_Int32 nSecondaryLocation (0); + const sal_Int32 nIndex (rPosition.GetIndex()); + + if (rPosition.IsAtRunStart()) + { + // Place indicator at the top of the column. + const Rectangle aOuterBox (GetPageObjectBox(nIndex)); + const Rectangle aInnerBox (GetInnerBoundingBox(rModel, nIndex)); + if (bIsVertical) + { + nLeadingLocation = aOuterBox.Top(); + nTrailingLocation = aInnerBox.Top(); + nSecondaryLocation = aInnerBox.Center().X(); + } + else + { + nLeadingLocation = aOuterBox.Left(); + nTrailingLocation = aInnerBox.Left(); + nSecondaryLocation = aInnerBox.Center().Y(); + } + bIsLeadingFixed = true; + } + else if (rPosition.IsAtRunEnd()) + { + // Place indicator at the bottom/right of the column/row. + + const Rectangle aOuterBox (GetPageObjectBox(nIndex-1)); + const Rectangle aInnerBox (GetInnerBoundingBox(rModel, nIndex-1)); + if (bIsVertical) + { + nLeadingLocation = aInnerBox.Bottom(); + nTrailingLocation = aOuterBox.Bottom(); + nSecondaryLocation = aInnerBox.Center().X(); + } + else + { + nLeadingLocation = aInnerBox.Right(); + nTrailingLocation = aOuterBox.Right(); + nSecondaryLocation = aInnerBox.Center().Y(); + } + bIsTrailingFixed = true; + if ( ! rPosition.IsExtraSpaceNeeded()) + bIsLeadingFixed = true; + } + else + { + // Place indicator between two rows/columns. + const Rectangle aBox1 (GetInnerBoundingBox(rModel, nIndex-1)); + const Rectangle aBox2 (GetInnerBoundingBox(rModel, nIndex)); + if (bIsVertical) + { + nLeadingLocation = aBox1.Bottom(); + nTrailingLocation = aBox2.Top(); + nSecondaryLocation = (aBox1.Center().X() + aBox2.Center().X()) / 2; + } + else + { + nLeadingLocation = aBox1.Right(); + nTrailingLocation = aBox2.Left(); + nSecondaryLocation = (aBox1.Center().Y() + aBox2.Center().Y()) / 2; + } + } + + // 2. Calculate the location of the insert indicator and the offsets of + // leading and trailing pages. + const sal_Int32 nAvailableSpace (nTrailingLocation - nLeadingLocation); + const sal_Int32 nRequiredSpace (bIsVertical ? rIndicatorSize.Height():rIndicatorSize.Width()); + const sal_Int32 nMissingSpace (::std::max(sal_Int32(0), nRequiredSpace - nAvailableSpace)); + sal_Int32 nPrimaryLocation (0); + sal_Int32 nLeadingOffset (0); + sal_Int32 nTrailingOffset (0); + if (bIsLeadingFixed) + { + nPrimaryLocation = nLeadingLocation + nRequiredSpace/2; + if ( ! bIsTrailingFixed) + nTrailingOffset = nMissingSpace; + } + else if (bIsTrailingFixed) + { + nPrimaryLocation = nTrailingLocation - nRequiredSpace/2; + nLeadingOffset = -nMissingSpace; + } + else + { + nPrimaryLocation = (nLeadingLocation + nTrailingLocation) /2; + nLeadingOffset = -nMissingSpace/2; + nTrailingOffset = nMissingSpace + nLeadingOffset; + } + + if (bIsVertical) + { + rPosition.SetGeometricalPosition( + Point(nSecondaryLocation, nPrimaryLocation), + Point(0, nLeadingOffset), + Point(0, nTrailingOffset)); + } + else + { + rPosition.SetGeometricalPosition( + Point(nPrimaryLocation, nSecondaryLocation), + Point(nLeadingOffset, 0), + Point(nTrailingOffset, 0)); + } +} + + + + +Rectangle Layouter::Implementation::GetInnerBoundingBox ( + model::SlideSorterModel& rModel, + const sal_Int32 nIndex) const +{ + model::SharedPageDescriptor pDescriptor (rModel.GetPageDescriptor(nIndex)); + if ( ! pDescriptor) + return Rectangle(); + + const Point aLocation (pDescriptor->GetLocation(true)); + if (pDescriptor->HasState(model::PageDescriptor::ST_Selected)) + return mpPageObjectLayouter->GetBoundingBox( + aLocation, + PageObjectLayouter::PageObject, + PageObjectLayouter::ModelCoordinateSystem); + else + return mpPageObjectLayouter->GetBoundingBox( + aLocation, + PageObjectLayouter::Preview, + PageObjectLayouter::ModelCoordinateSystem); +} + + + + +Range Layouter::Implementation::GetValidHorizontalSizeRange (void) const +{ + return Range( + mnLeftBorder + maMinimalSize.Width() + mnRightBorder, + mnLeftBorder + maMaximalSize.Width() + mnRightBorder); +} + + + + +Range Layouter::Implementation::GetValidVerticalSizeRange (void) const +{ + return Range( + mnTopBorder + maMinimalSize.Height() + mnBottomBorder, + mnTopBorder + maMaximalSize.Height() + mnBottomBorder); +} + + + + +Range Layouter::Implementation::GetRangeOfVisiblePageObjects (const Rectangle& aVisibleArea) const +{ + const sal_Int32 nRow0 (GetRowAtPosition(aVisibleArea.Top(), true, GM_NEXT)); + const sal_Int32 nCol0 (GetColumnAtPosition(aVisibleArea.Left(),true, GM_NEXT)); + const sal_Int32 nRow1 (GetRowAtPosition(aVisibleArea.Bottom(), true, GM_PREVIOUS)); + const sal_Int32 nCol1 (GetColumnAtPosition(aVisibleArea.Right(), true, GM_PREVIOUS)); + + // When start and end lie in different rows then the range may include + // slides outside (left or right of) the given area. + return Range(GetIndex(nRow0,nCol0,true), GetIndex(nRow1,nCol1,true)); +} + + + + +Size Layouter::Implementation::GetTargetSize ( + const Size& rWindowSize, + const Size& rPreviewModelSize, + const bool bCalculateWidth, + const bool bCalculateHeight) const +{ + (void)rPreviewModelSize; + + if (mnColumnCount<=0 || mnRowCount<=0) + return maPreferredSize; + if ( ! (bCalculateWidth || bCalculateHeight)) + { + OSL_ASSERT(bCalculateWidth || bCalculateHeight); + return maPreferredSize; + } + + // Calculate the width of each page object. + Size aTargetSize (0,0); + if (bCalculateWidth) + aTargetSize.setWidth( + (rWindowSize.Width() - mnLeftBorder - mnRightBorder + - (mnColumnCount-1) * mnHorizontalGap) + / mnColumnCount); + else if (bCalculateHeight) + aTargetSize.setHeight( + (rWindowSize.Height() - mnTopBorder - mnBottomBorder + - (mnRowCount-1) * mnVerticalGap) + / mnRowCount); + + if (bCalculateWidth) + { + if (aTargetSize.Width() < maMinimalSize.Width()) + aTargetSize.setWidth(maMinimalSize.Width()); + else if (aTargetSize.Width() > maMaximalSize.Width()) + aTargetSize.setWidth(maMaximalSize.Width()); + } + else if (bCalculateHeight) + { + if (aTargetSize.Height() < maMinimalSize.Height()) + aTargetSize.setHeight(maMinimalSize.Height()); + else if (aTargetSize.Height() > maMaximalSize.Height()) + aTargetSize.setHeight(maMaximalSize.Height()); + } + + return aTargetSize; +} + + + + +sal_Int32 Layouter::Implementation::GetIndex ( + const sal_Int32 nRow, + const sal_Int32 nColumn, + const bool bClampToValidRange) const +{ + if (nRow >= 0 && nColumn >= 0) + { + const sal_Int32 nIndex (nRow * mnColumnCount + nColumn); + if (nIndex >= mnPageCount) + if (bClampToValidRange) + return mnPageCount-1; + else + return -1; + else + return nIndex; + } + else if (bClampToValidRange) + return 0; + else + return -1; +} + + + + +Rectangle Layouter::Implementation::GetPageObjectBox ( + const sal_Int32 nIndex, + const bool bIncludeBorderAndGap) const +{ + const sal_Int32 nRow (nIndex / mnColumnCount); + const sal_Int32 nColumn (nIndex % mnColumnCount); + + const Rectangle aBoundingBox (GetPageObjectBox(nRow,nColumn)); + if (bIncludeBorderAndGap) + return AddBorderAndGap(aBoundingBox, nRow, nColumn); + else + return aBoundingBox; +} + + + + +Rectangle Layouter::Implementation::GetPageObjectBox ( + const sal_Int32 nRow, + const sal_Int32 nColumn) const +{ + return Rectangle( + Point (mnLeftBorder + + nColumn * maPageObjectSize.Width() + + (nColumn>0 ? nColumn : 0) * mnHorizontalGap, + mnTopBorder + + nRow * maPageObjectSize.Height() + + (nRow>0 ? nRow : 0) * mnVerticalGap), + maPageObjectSize); +} + + + + + +Rectangle Layouter::Implementation::AddBorderAndGap ( + const Rectangle& rBoundingBox, + const sal_Int32 nRow, + const sal_Int32 nColumn) const +{ + Rectangle aBoundingBox (rBoundingBox); + + if (nColumn == 0) + aBoundingBox.Left() = 0; + else + aBoundingBox.Left() -= mnHorizontalGap/2; + if (nColumn == mnColumnCount-1) + aBoundingBox.Right() += mnRightBorder; + else + aBoundingBox.Right() += mnHorizontalGap/2; + if (nRow == 0) + aBoundingBox.Top() = 0; + else + aBoundingBox.Top() -= mnVerticalGap/2; + if (nRow == mnRowCount-1) + aBoundingBox.Bottom() += mnBottomBorder; + else + aBoundingBox.Bottom() += mnVerticalGap/2; + return aBoundingBox; +} + + + + +Rectangle Layouter::Implementation::GetTotalBoundingBox (void) const +{ + sal_Int32 nHorizontalSize = 0; + sal_Int32 nVerticalSize = 0; + if (mnColumnCount > 0) + { + sal_Int32 nRowCount = (mnPageCount+mnColumnCount-1) / mnColumnCount; + nHorizontalSize = + mnLeftBorder + + mnRightBorder + + mnColumnCount * maPageObjectSize.Width(); + if (mnColumnCount > 1) + nHorizontalSize += (mnColumnCount-1) * mnHorizontalGap; + nVerticalSize = + mnTopBorder + + mnBottomBorder + + nRowCount * maPageObjectSize.Height(); + if (nRowCount > 1) + nVerticalSize += (nRowCount-1) * mnVerticalGap; + } + + return Rectangle ( + Point(0,0), + Size (nHorizontalSize, nVerticalSize) + ); +} + + + + +void Layouter::Implementation::CalculateVerticalLogicalInsertPosition ( + const Point& rModelPosition, + InsertPosition& rPosition) const +{ + const sal_Int32 nY = rModelPosition.Y() - mnTopBorder + maPageObjectSize.Height()/2; + const sal_Int32 nRowHeight (maPageObjectSize.Height() + mnVerticalGap); + const sal_Int32 nRow (::std::min(mnPageCount, nY / nRowHeight)); + rPosition.SetLogicalPosition ( + nRow, + 0, + nRow, + (nRow == 0), + (nRow == mnRowCount), + (nRow >= mnMaxRowCount)); +} + + + + +//===== HorizontalImplementation ================================================ + +HorizontalImplementation::HorizontalImplementation ( + const SharedSdWindow& rpWindow, + const ::boost::shared_ptr<view::Theme>& rpTheme) + : Implementation(rpWindow, rpTheme) +{ +} + + + + +HorizontalImplementation::HorizontalImplementation (const Implementation& rImplementation) + : Implementation(rImplementation) +{ +} + + + + +Layouter::Orientation HorizontalImplementation::GetOrientation (void) const +{ + return Layouter::HORIZONTAL; +} + + + + +void HorizontalImplementation::CalculateRowAndColumnCount (const Size& rWindowSize) +{ + (void)rWindowSize; + + // Row and column count are fixed (for a given page count.) + mnColumnCount = mnPageCount; + mnRowCount = 1; +} + + + + +void HorizontalImplementation::CalculateMaxRowAndColumnCount (const Size& rWindowSize) +{ + mnMaxColumnCount = (rWindowSize.Width() - mnLeftBorder - mnRightBorder) + / (maPageObjectSize.Width() + mnHorizontalGap); + mnMaxRowCount = 1; +} + + + + +Size HorizontalImplementation::CalculateTargetSize ( + const Size& rWindowSize, + const Size& rPreviewModelSize) const +{ + return Implementation::GetTargetSize(rWindowSize, rPreviewModelSize, false, true); +} + + + + +void HorizontalImplementation::CalculateLogicalInsertPosition ( + const Point& rModelPosition, + InsertPosition& rPosition) const +{ + const sal_Int32 nX = rModelPosition.X() - mnLeftBorder + maPageObjectSize.Width()/2; + const sal_Int32 nColumnWidth (maPageObjectSize.Width() + mnHorizontalGap); + const sal_Int32 nColumn (::std::min(mnPageCount, nX / nColumnWidth)); + rPosition.SetLogicalPosition ( + 0, + nColumn, + nColumn, + (nColumn == 0), + (nColumn == mnColumnCount), + (nColumn >= mnMaxColumnCount)); +} + + + + +//===== VerticalImplementation ================================================ + +VerticalImplementation::VerticalImplementation ( + const SharedSdWindow& rpWindow, + const ::boost::shared_ptr<view::Theme>& rpTheme) + : Implementation(rpWindow, rpTheme) +{ +} + + + + +VerticalImplementation::VerticalImplementation (const Implementation& rImplementation) + : Implementation(rImplementation) +{ +} + + + + +Layouter::Orientation VerticalImplementation::GetOrientation (void) const +{ + return Layouter::VERTICAL; +} + + + + +void VerticalImplementation::CalculateRowAndColumnCount (const Size& rWindowSize) +{ + (void)rWindowSize; + + // Row and column count are fixed (for a given page count.) + mnRowCount = mnPageCount; + mnColumnCount = 1; + +} + + + + +void VerticalImplementation::CalculateMaxRowAndColumnCount (const Size& rWindowSize) +{ + mnMaxRowCount = (rWindowSize.Height() - mnTopBorder - mnBottomBorder) + / (maPageObjectSize.Height() + mnVerticalGap); + mnMaxColumnCount = 1; +} + + + + +Size VerticalImplementation::CalculateTargetSize ( + const Size& rWindowSize, + const Size& rPreviewModelSize) const +{ + return Implementation::GetTargetSize(rWindowSize, rPreviewModelSize, true, false); +} + + + + +void VerticalImplementation::CalculateLogicalInsertPosition ( + const Point& rModelPosition, + InsertPosition& rPosition) const +{ + return CalculateVerticalLogicalInsertPosition(rModelPosition, rPosition); +} + + + + +//===== GridImplementation ================================================ + +GridImplementation::GridImplementation ( + const SharedSdWindow& rpWindow, + const ::boost::shared_ptr<view::Theme>& rpTheme) + : Implementation(rpWindow, rpTheme) +{ +} + + + + +GridImplementation::GridImplementation (const Implementation& rImplementation) + : Implementation(rImplementation) +{ +} + + + + +Layouter::Orientation GridImplementation::GetOrientation (void) const +{ + return Layouter::GRID; +} + + + + +void GridImplementation::CalculateRowAndColumnCount (const Size& rWindowSize) +{ + // Calculate the column count. + mnColumnCount + = (rWindowSize.Width() - mnRequestedLeftBorder - mnRequestedRightBorder) + / (maPreferredSize.Width() + mnHorizontalGap); + if (mnColumnCount < mnMinimalColumnCount) + mnColumnCount = mnMinimalColumnCount; + if (mnColumnCount > mnMaximalColumnCount) + mnColumnCount = mnMaximalColumnCount; + mnRowCount = (mnPageCount + mnColumnCount-1)/mnColumnCount; +} + + + + +void GridImplementation::CalculateMaxRowAndColumnCount (const Size& rWindowSize) +{ + mnMaxColumnCount = (rWindowSize.Width() - mnLeftBorder - mnRightBorder) + / (maPageObjectSize.Width() + mnHorizontalGap); + mnMaxRowCount = (rWindowSize.Height() - mnTopBorder - mnBottomBorder) + / (maPageObjectSize.Height() + mnVerticalGap); +} + + + + + +Size GridImplementation::CalculateTargetSize ( + const Size& rWindowSize, + const Size& rPreviewModelSize) const +{ + return Implementation::GetTargetSize(rWindowSize, rPreviewModelSize, true, true); +} + + + + +void GridImplementation::CalculateLogicalInsertPosition ( + const Point& rModelPosition, + InsertPosition& rPosition) const +{ + if (mnColumnCount == 1) + { + CalculateVerticalLogicalInsertPosition(rModelPosition, rPosition); + } + else + { + // Handle the general case of more than one column. + sal_Int32 nRow (::std::min( + mnRowCount-1, + GetRowAtPosition (rModelPosition.Y(), true, GM_BOTH))); + const sal_Int32 nX = rModelPosition.X() - mnLeftBorder + maPageObjectSize.Width()/2; + const sal_Int32 nColumnWidth (maPageObjectSize.Width() + mnHorizontalGap); + sal_Int32 nColumn (::std::min(mnColumnCount, nX / nColumnWidth)); + sal_Int32 nIndex (nRow * mnColumnCount + nColumn); + bool bIsAtRunEnd (nColumn == mnColumnCount); + + if (nIndex >= mnPageCount) + { + nIndex = mnPageCount; + nRow = mnRowCount-1; + nColumn = ::std::min(::std::min(mnPageCount, mnColumnCount), nColumn); + bIsAtRunEnd = true; + } + + rPosition.SetLogicalPosition ( + nRow, + nColumn, + nIndex, + (nColumn == 0), + bIsAtRunEnd, + (nColumn >= mnMaxColumnCount)); + } +} + + + + +//===== InsertPosition ======================================================== + +InsertPosition::InsertPosition (void) + : mnRow(-1), + mnColumn(-1), + mnIndex(-1), + mbIsAtRunStart(false), + mbIsAtRunEnd(false), + mbIsExtraSpaceNeeded(false), + maLocation(0,0), + maLeadingOffset(0,0), + maTrailingOffset(0,0) +{ +} + + + + +InsertPosition& InsertPosition::operator= (const InsertPosition& rInsertPosition) +{ + if (this != &rInsertPosition) + { + mnRow = rInsertPosition.mnRow; + mnColumn = rInsertPosition.mnColumn; + mnIndex = rInsertPosition.mnIndex; + mbIsAtRunStart = rInsertPosition.mbIsAtRunStart; + mbIsAtRunEnd = rInsertPosition.mbIsAtRunEnd; + mbIsExtraSpaceNeeded = rInsertPosition.mbIsExtraSpaceNeeded; + maLocation = rInsertPosition.maLocation; + maLeadingOffset = rInsertPosition.maLeadingOffset; + maTrailingOffset = rInsertPosition.maTrailingOffset; + } + return *this; +} + + + + +bool InsertPosition::operator== (const InsertPosition& rInsertPosition) const +{ + // Do not compare the geometrical information (maLocation). + return mnRow==rInsertPosition.mnRow + && mnColumn==rInsertPosition.mnColumn + && mnIndex==rInsertPosition.mnIndex + && mbIsAtRunStart==rInsertPosition.mbIsAtRunStart + && mbIsAtRunEnd==rInsertPosition.mbIsAtRunEnd + && mbIsExtraSpaceNeeded==rInsertPosition.mbIsExtraSpaceNeeded; +} + + + + +bool InsertPosition::operator!= (const InsertPosition& rInsertPosition) const +{ + return !operator==(rInsertPosition); +} + + + + +void InsertPosition::SetLogicalPosition ( + const sal_Int32 nRow, + const sal_Int32 nColumn, + const sal_Int32 nIndex, + const bool bIsAtRunStart, + const bool bIsAtRunEnd, + const bool bIsExtraSpaceNeeded) +{ + mnRow = nRow; + mnColumn = nColumn; + mnIndex = nIndex; + mbIsAtRunStart = bIsAtRunStart; + mbIsAtRunEnd = bIsAtRunEnd; + mbIsExtraSpaceNeeded = bIsExtraSpaceNeeded; +} + + + + +void InsertPosition::SetGeometricalPosition( + const Point aLocation, + const Point aLeadingOffset, + const Point aTrailingOffset) +{ + maLocation = aLocation; + maLeadingOffset = aLeadingOffset; + maTrailingOffset = aTrailingOffset; +} + + + +} } } // end of namespace ::sd::slidesorter::namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |