Files
libreoffice/sd/source/ui/slidesorter/view/SlsLayouter.cxx

762 lines
19 KiB
C++

/*************************************************************************
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright 2008 by Sun Microsystems, Inc.
*
* OpenOffice.org - a multi-platform office productivity suite
*
* $RCSfile: SlsLayouter.cxx,v $
* $Revision: 1.11 $
*
* 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 "Window.hxx"
#include <basegfx/numeric/ftools.hxx>
namespace sd { namespace slidesorter { namespace view {
Layouter::Layouter (const SharedSdWindow& rpWindow)
: mpWindow(rpWindow),
mnRequestedLeftBorder(35),
mnRequestedRightBorder(35),
mnRequestedTopBorder(10),
mnRequestedBottomBorder(10),
mnLeftBorder(30),
mnRightBorder(30),
mnTopBorder(10),
mnBottomBorder(10),
mnVerticalGap (20),
mnHorizontalGap (20),
mnMinimalWidth (100),
mnPreferredWidth (200),
mnMaximalWidth (300),
mnMinimalColumnCount (1),
mnMaximalColumnCount (5),
mnPageCount(0),
mnColumnCount(1),
mnRowCount(0),
maPageObjectSize(1,1),
mbIsVertical(true)
{
}
Layouter::~Layouter (void)
{
}
::boost::shared_ptr<PageObjectLayouter> Layouter::GetPageObjectLayouter (void) const
{
return mpPageObjectLayouter;
}
void Layouter::SetObjectWidth (
sal_Int32 nMinimalWidth,
sal_Int32 nMaximalWidth,
sal_Int32 nPreferredWidth)
{
if (nMinimalWidth <= nPreferredWidth && nPreferredWidth <= nMaximalWidth)
{
mnMinimalWidth = nMinimalWidth;
mnPreferredWidth = nMaximalWidth;
mnMaximalWidth = nPreferredWidth;
}
}
void Layouter::SetBorders (
sal_Int32 nLeftBorder,
sal_Int32 nRightBorder,
sal_Int32 nTopBorder,
sal_Int32 nBottomBorder)
{
if (nLeftBorder >= 0)
mnRequestedLeftBorder = nLeftBorder;
if (nRightBorder >= 0)
mnRequestedRightBorder = nRightBorder;
if (nTopBorder >= 0)
mnRequestedTopBorder = nTopBorder;
if (nBottomBorder >= 0)
mnRequestedBottomBorder = nBottomBorder;
}
void Layouter::SetGaps (
sal_Int32 nHorizontalGap,
sal_Int32 nVerticalGap)
{
if (nHorizontalGap >= 0)
mnHorizontalGap = nHorizontalGap;
if (nVerticalGap >= 0)
mnVerticalGap = nVerticalGap;
}
void Layouter::SetColumnCount (
sal_Int32 nMinimalColumnCount,
sal_Int32 nMaximalColumnCount)
{
if (nMinimalColumnCount <= nMaximalColumnCount)
{
mnMinimalColumnCount = nMinimalColumnCount;
mnMaximalColumnCount = nMaximalColumnCount;
}
}
bool Layouter::RearrangeHorizontal (
const Size& rWindowSize,
const Size& rPageSize,
const sal_uInt32 nPageCount)
{
OSL_ASSERT(mpWindow);
mbIsVertical = false;
mnPageCount = nPageCount;
if (rWindowSize.Width() > 0
&& rWindowSize.Height() > 0
&& rPageSize.Width() > 0
&& rPageSize.Height() > 0)
{
// Calculate the column count.
mnColumnCount = nPageCount;
mnRowCount = 1;
// 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;
}
// Calculate the width of each page object.
sal_uInt32 nTargetHeight = 0;
sal_uInt32 nRowCount = 1;
if (mnColumnCount > 0)
nTargetHeight = (rWindowSize.Height()
- mnTopBorder
- mnBottomBorder
- (nRowCount-1) * mnVerticalGap
)
/ nRowCount;
sal_uInt32 nMinimalHeight (mnMinimalWidth * rPageSize.Height() / rPageSize.Width());
sal_uInt32 nMaximalHeight (mnMaximalWidth * rPageSize.Height() / rPageSize.Width());
if (nTargetHeight < nMinimalHeight)
nTargetHeight = nMinimalHeight;
if (nTargetHeight > nMaximalHeight)
nTargetHeight = nMaximalHeight;
// Setup the page object layouter and ask it for the page object size.
mpPageObjectLayouter.reset(
new PageObjectLayouter(
Size(0, nTargetHeight),
rPageSize,
mpWindow,
nPageCount));
maPageObjectSize = mpPageObjectLayouter->GetPageObjectSize();
return true;
}
else
return false;
}
bool Layouter::RearrangeVertical (
const Size& rWindowSize,
const Size& rPreviewModelSize,
const sal_uInt32 nPageCount)
{
OSL_ASSERT(mpWindow);
mbIsVertical = true;
mnPageCount = nPageCount;
if (rWindowSize.Width() > 0
&& rWindowSize.Height() > 0
&& rPreviewModelSize.Width() > 0
&& rPreviewModelSize.Height() > 0)
{
// Calculate the column count.
mnColumnCount = (rWindowSize.Width() - mnRequestedLeftBorder - mnRequestedRightBorder)
/ (mnPreferredWidth + mnHorizontalGap);
if (mnColumnCount < mnMinimalColumnCount)
mnColumnCount = mnMinimalColumnCount;
if (mnColumnCount > mnMaximalColumnCount)
mnColumnCount = mnMaximalColumnCount;
mnRowCount = (nPageCount + mnColumnCount-1)/mnColumnCount;
// 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;
}
// Calculate the width of each page object.
sal_Int32 nTargetWidth = 0;
if (mnColumnCount > 0)
nTargetWidth = (rWindowSize.Width()
- mnLeftBorder
- mnRightBorder
- (mnColumnCount-1) * mnHorizontalGap
)
/ mnColumnCount;
if (nTargetWidth < mnMinimalWidth)
nTargetWidth = mnMinimalWidth;
if (nTargetWidth > mnMaximalWidth)
nTargetWidth = mnMaximalWidth;
// Setup the page object layouter and ask it for the page object size.
mpPageObjectLayouter.reset(
new PageObjectLayouter(
Size(nTargetWidth, 0),
rPreviewModelSize,
mpWindow,
nPageCount));
maPageObjectSize = mpPageObjectLayouter->GetPageObjectSize();
return true;
}
else
return false;
}
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);
// maPageObjectPixelSize = mpWindow->LogicToPixel (maPageObjectModelSize);
mpWindow->SetMapMode (aMapMode);
}
sal_Int32 Layouter::GetColumnCount (void) const
{
return mnColumnCount;
}
sal_Int32 Layouter::GetRowCount (void) const
{
return mnRowCount;
}
sal_Int32 Layouter::GetRow (const sal_Int32 nIndex) const
{
return nIndex / mnColumnCount;
}
sal_Int32 Layouter::GetColumn (const sal_Int32 nIndex) const
{
return nIndex % mnColumnCount;
}
sal_Int32 Layouter::GetIndex (const sal_Int32 nRow, const sal_Int32 nColumn) const
{
const sal_Int32 nIndex (nRow * mnColumnCount + nColumn);
OSL_ASSERT(nIndex>=0);
return ::std::min(nIndex, mnPageCount-1);
}
bool Layouter::IsColumnCountFixed (void) const
{
return mnMinimalColumnCount == mnMaximalColumnCount;
}
Size Layouter::GetPageObjectSize (void) const
{
return maPageObjectSize;
}
Rectangle Layouter::GetPageObjectBox (
const sal_Int32 nIndex,
const bool bIncludeBorderAndGap) const
{
const sal_Int32 nRow (GetRow(nIndex));
const sal_Int32 nColumn (GetColumn(nIndex));
Rectangle aBoundingBox(
Point (mnLeftBorder
+ nColumn * maPageObjectSize.Width()
+ (nColumn>0 ? nColumn : 0) * mnHorizontalGap,
mnTopBorder
+ nRow * maPageObjectSize.Height()
+ (nRow>0 ? nRow : 0) * mnVerticalGap),
maPageObjectSize);
if (bIncludeBorderAndGap)
{
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::GetPageBox (const sal_Int32 nObjectCount) const
{
sal_Int32 nCount (nObjectCount);
if (nCount < 0)
nCount = mnPageCount;
sal_Int32 nHorizontalSize = 0;
sal_Int32 nVerticalSize = 0;
if (mnColumnCount > 0)
{
sal_Int32 nRowCount = (nCount+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)
);
}
Point Layouter::GetInsertionMarkerLocation (
sal_Int32 nIndex,
bool bVertical,
bool bLeftOrTop) const
{
Rectangle aBox (GetPageObjectBox (nIndex));
Point aLocation = aBox.Center();
if (bVertical)
{
if (bLeftOrTop)
{
// Left.
aLocation.setX(aBox.Left() - (mnHorizontalGap+1)/2 - 1);
}
else
{
// Right.
aLocation.setX(aBox.Right() + mnHorizontalGap/2);
}
}
else
{
if (bLeftOrTop)
{
// Above.
aLocation.setY(aBox.Top() - mnVerticalGap/2);
}
else
{
// Below.
aLocation.setY(aBox.Bottom() + mnVerticalGap/2);
}
}
return aLocation;
}
Range Layouter::GetRangeOfVisiblePageObjects (const Rectangle& aVisibleArea) const
{
sal_Int32 nRow0 = GetRowAtPosition (aVisibleArea.Top(), true, GM_BOTH);
sal_Int32 nRow1 = GetRowAtPosition (aVisibleArea.Bottom(),
true, GM_BOTH);
return Range(
::std::min(nRow0 * mnColumnCount, mnPageCount-1),
::std::min((nRow1+1) * mnColumnCount - 1, mnPageCount-1));
}
Range Layouter::GetSelectionRange (
const Point& rAnchor,
const Point& rOther,
const Range& rCurrentSelectionRange) const
{
sal_Int32 nIndexA (GetInsertionIndex(rAnchor, true));
sal_Int32 nIndexB (GetInsertionIndex(rOther, true));
// When either of the locations does not lie over a page then we may
// have to adapt the start or end index.
Rectangle aBoxA (GetPageObjectBox(nIndexA));
Rectangle aBoxB (GetPageObjectBox(nIndexB));
if (nIndexA > nIndexB)
{
::std::swap(nIndexA,nIndexB);
::std::swap(aBoxA, aBoxB);
}
if (mnColumnCount == 1)
{
// Vertical arrangement of pages.
if (nIndexA <= nIndexB)
{
if (rOther.Y() < aBoxB.Top())
--nIndexB;
}
else
{
if (rOther.Y() > aBoxB.Bottom())
--nIndexA;
}
}
else
{
// Horizontal arrangement of pages.
if (nIndexA <= nIndexB)
{
if (rOther.X() < aBoxB.Left())
--nIndexB;
}
else
{
if (rOther.X() > aBoxB.Right())
--nIndexA;
}
}
return Range(
::std::min(nIndexA, nIndexB),
::std::min(mnPageCount-1, ::std::max(nIndexA, nIndexB)));
}
sal_Int32 Layouter::GetIndexAtPoint (
const Point& rPosition,
bool bIncludePageBorders) const
{
sal_Int32 nRow = GetRowAtPosition (rPosition.Y(),
bIncludePageBorders,
bIncludePageBorders ? GM_PAGE_BORDER : GM_NONE);
sal_Int32 nColumn = GetColumnAtPosition (rPosition.X(),
bIncludePageBorders,
bIncludePageBorders ? GM_PAGE_BORDER : GM_NONE);
if (nRow >= 0 && nColumn >= 0)
return nRow * mnColumnCount + nColumn;
else
return -1;
}
/** Calculation of the insertion index:
1. Determine the row. rPoint has to be in the row between upper and
lower border. If it is in a horizontal gap or border an invalid
insertion index (-1, which is a valid return value) will be returned.
2. Determine the column. Here both vertical borders and vertical gaps
will yield valid return values. The horizontal positions between the
center of page objects in column i and the center of page objects in
column i+1 will return column i+1 as insertion index.
When there is only one column and bAllowVerticalPosition is true than
take the vertical areas between rows into account as well.
*/
sal_Int32 Layouter::GetInsertionIndex (
const Point& rPosition,
bool bAllowVerticalPosition) const
{
sal_Int32 nIndex = -1;
sal_Int32 nRow = GetRowAtPosition (rPosition.Y(), true,
(mnColumnCount==1 && bAllowVerticalPosition) ? GM_BOTH : GM_BOTH);
sal_Int32 nColumn = GetColumnAtPosition (rPosition.X(), true, GM_BOTH);
if (nRow >= 0 && nColumn >= 0)
nIndex = nRow * mnColumnCount + nColumn;
return nIndex;
}
bool Layouter::IsVertical (void) const
{
return mbIsVertical;
}
sal_Int32 Layouter::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::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::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;
}
} } } // end of namespace ::sd::slidesorter::namespace