Files
libreoffice/sd/source/ui/slidesorter/view/SlsLayouter.cxx
Oliver Bolte e682a3ebd9 INTEGRATION: CWS pchfix02 (1.8.248); FILE MERGED
2006/09/01 17:37:20 kaib 1.8.248.1: #i68856# Added header markers and pch files
2006-09-16 18:10:25 +00:00

742 lines
21 KiB
C++

/*************************************************************************
*
* OpenOffice.org - a multi-platform office productivity suite
*
* $RCSfile: SlsLayouter.cxx,v $
*
* $Revision: 1.9 $
*
* last change: $Author: obo $ $Date: 2006-09-16 19:10:25 $
*
* The Contents of this file are made available subject to
* the terms of GNU Lesser General Public License Version 2.1.
*
*
* GNU Lesser General Public License Version 2.1
* =============================================
* Copyright 2005 by Sun Microsystems, Inc.
* 901 San Antonio Road, Palo Alto, CA 94303, USA
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
************************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sd.hxx"
#include "view/SlsLayouter.hxx"
#include <vcl/outdev.hxx>
#include <rtl/math.hxx>
namespace sd { namespace slidesorter { namespace view {
Layouter::Layouter (void)
: mnRequestedLeftBorder(10),
mnRequestedRightBorder(10),
mnRequestedTopBorder(10),
mnRequestedBottomBorder(10),
mnLeftBorder(10),
mnRightBorder(10),
mnTopBorder(10),
mnBottomBorder(10),
mnLeftPageBorder(0),
mnRightPageBorder(0),
mnTopPageBorder(0),
mnBottomPageBorder(0),
mnVerticalGap (20),
mnHorizontalGap (20),
mnInsertionMarkerThickness (4),
mnTotalVerticalGap(0),
mnTotalHorizontalGap(0),
mnMinimalWidth (100),
mnDesiredWidth (200),
mnMaximalWidth (300),
mnMinimalColumnCount (1),
mnMaximalColumnCount (5),
mnColumnCount (1),
maPageObjectModelSize (1,1),
maPageObjectPixelSize (1,1)
{
}
Layouter::~Layouter (void)
{
}
void Layouter::SetObjectWidth (
sal_Int32 nMinimalWidth,
sal_Int32 nMaximalWidth,
sal_Int32 nDesiredWidth)
{
if (nMinimalWidth <= nDesiredWidth && nDesiredWidth <= nMaximalWidth)
{
mnMinimalWidth = nMinimalWidth;
mnDesiredWidth = nMaximalWidth;
mnMaximalWidth = nDesiredWidth;
}
}
void Layouter::SetBorders (
sal_Int32 nLeftBorder,
sal_Int32 nRightBorder,
sal_Int32 nTopBorder,
sal_Int32 nBottomBorder)
{
if (nLeftBorder >= 0)
mnRequestedLeftBorder.mnScreen = nLeftBorder;
if (nRightBorder >= 0)
mnRequestedRightBorder.mnScreen = nRightBorder;
if (nTopBorder >= 0)
mnRequestedTopBorder.mnScreen = nTopBorder;
if (nBottomBorder >= 0)
mnRequestedBottomBorder.mnScreen = nBottomBorder;
}
void Layouter::SetPageBorders (
sal_Int32 nLeftBorder,
sal_Int32 nRightBorder,
sal_Int32 nTopBorder,
sal_Int32 nBottomBorder)
{
if (nLeftBorder >= 0)
mnLeftPageBorder.mnScreen = nLeftBorder;
if (nRightBorder >= 0)
mnRightPageBorder.mnScreen = nRightBorder;
if (nTopBorder >= 0)
mnTopPageBorder.mnScreen = nTopBorder;
if (nBottomBorder >= 0)
mnBottomPageBorder.mnScreen = nBottomBorder;
}
void Layouter::SetGaps (
sal_Int32 nHorizontalGap,
sal_Int32 nVerticalGap)
{
if (nHorizontalGap >= 0)
mnHorizontalGap.mnScreen = nHorizontalGap;
if (nVerticalGap >= 0)
mnVerticalGap.mnScreen = nVerticalGap;
}
void Layouter::SetColumnCount (
sal_Int32 nMinimalColumnCount,
sal_Int32 nMaximalColumnCount)
{
if (nMinimalColumnCount <= nMaximalColumnCount)
{
mnMinimalColumnCount = nMinimalColumnCount;
mnMaximalColumnCount = nMaximalColumnCount;
}
}
bool Layouter::Rearrange (
const Size& rWindowSize,
const Size& rPageObjectSize,
OutputDevice* pDevice)
{
if (rWindowSize.Width() > 0
&& rWindowSize.Height() > 0
&& rPageObjectSize.Width() > 0
&& rPageObjectSize.Height() > 0)
{
mnTotalHorizontalGap.mnScreen = mnHorizontalGap.mnScreen
+ mnRightPageBorder.mnScreen + mnLeftPageBorder.mnScreen;
mnTotalVerticalGap.mnScreen = mnVerticalGap.mnScreen
+ mnTopPageBorder.mnScreen + mnBottomPageBorder.mnScreen;
// Calculate the column count.
mnColumnCount = (rWindowSize.Width()
- mnRequestedLeftBorder.mnScreen - mnRequestedRightBorder.mnScreen)
/ (mnDesiredWidth + mnTotalHorizontalGap.mnScreen);
if (mnColumnCount < mnMinimalColumnCount)
mnColumnCount = mnMinimalColumnCount;
if (mnColumnCount > mnMaximalColumnCount)
mnColumnCount = mnMaximalColumnCount;
// Update the border values. The insertion marker has to have space.
mnLeftBorder.mnScreen = mnRequestedLeftBorder.mnScreen;
mnTopBorder.mnScreen = mnRequestedTopBorder.mnScreen;
mnRightBorder.mnScreen = mnRequestedRightBorder.mnScreen;
mnBottomBorder.mnScreen = mnRequestedBottomBorder.mnScreen;
if (mnColumnCount > 1)
{
int nMinimumBorderWidth = mnInsertionMarkerThickness.mnScreen
+ mnHorizontalGap.mnScreen/2;
if (mnLeftBorder.mnScreen < nMinimumBorderWidth)
mnLeftBorder.mnScreen = nMinimumBorderWidth;
if (mnRightBorder.mnScreen < nMinimumBorderWidth)
mnRightBorder.mnScreen = nMinimumBorderWidth;
}
else
{
int nMinimumBorderHeight = mnInsertionMarkerThickness.mnScreen
+ mnVerticalGap.mnScreen/2;
if (mnTopBorder.mnScreen < nMinimumBorderHeight)
mnTopBorder.mnScreen = nMinimumBorderHeight;
if (mnBottomBorder.mnScreen < nMinimumBorderHeight)
mnBottomBorder.mnScreen = nMinimumBorderHeight;
}
// Calculate the width of each page object.
sal_Int32 nTargetWidth = 0;
if (mnColumnCount > 0)
nTargetWidth = (rWindowSize.Width()
- mnLeftBorder.mnScreen
- mnRightBorder.mnScreen
- mnColumnCount * (mnRightPageBorder.mnScreen
+ mnLeftPageBorder.mnScreen)
- (mnColumnCount-1) * mnTotalHorizontalGap.mnScreen
)
/ mnColumnCount;
if (nTargetWidth < mnMinimalWidth)
nTargetWidth = mnMinimalWidth;
if (nTargetWidth > mnMaximalWidth)
nTargetWidth = mnMaximalWidth;
// Initialize the device with some arbitrary zoom factor just in
// case that the current zoom factor is numerically instable when
// used in a multiplication.
MapMode aMapMode (pDevice->GetMapMode());
aMapMode.SetScaleX (Fraction(1,1));
aMapMode.SetScaleY (Fraction(1,1));
pDevice->SetMapMode (aMapMode);
// Calculate the resulting scale factor and the page object size in
// pixels.
maPageObjectModelSize = rPageObjectSize;
int nPagePixelWidth (pDevice->LogicToPixel (maPageObjectModelSize).Width());
// Adapt the layout of the given output device to the new layout of
// page objects. The zoom factor is set so that the page objects in
// one row fill the screen.
Fraction aScaleFactor (nTargetWidth, nPagePixelWidth);
SetZoom (aMapMode.GetScaleX() * aScaleFactor, pDevice);
return true;
}
else
return false;
}
void Layouter::SetZoom (double nZoomFactor, OutputDevice* pDevice)
{
SetZoom(Fraction(nZoomFactor), pDevice);
}
void Layouter::SetZoom (Fraction nZoomFactor, OutputDevice* pDevice)
{
MapMode aMapMode (pDevice->GetMapMode());
aMapMode.SetScaleX (nZoomFactor);
aMapMode.SetScaleY (nZoomFactor);
maPageObjectPixelSize = pDevice->LogicToPixel (maPageObjectModelSize);
pDevice->SetMapMode (aMapMode);
// Transform frequently used values from pixel to model coordinates.
Size aTotalGap (pDevice->PixelToLogic (Size (
mnTotalHorizontalGap.mnScreen,
mnTotalVerticalGap.mnScreen)));
mnTotalHorizontalGap.mnModel = aTotalGap.Width();
mnTotalVerticalGap.mnModel = aTotalGap.Height();
Size aGap (pDevice->PixelToLogic (Size (
mnHorizontalGap.mnScreen,
mnVerticalGap.mnScreen)));
mnHorizontalGap.mnModel = aGap.Width();
mnVerticalGap.mnModel = aGap.Height();
Size aTopLeftBorder (pDevice->PixelToLogic (Size (
mnLeftBorder.mnScreen,
mnTopBorder.mnScreen)));
mnLeftBorder.mnModel = aTopLeftBorder.Width();
mnTopBorder.mnModel = aTopLeftBorder.Height();
Size aBottomRightBorder (pDevice->PixelToLogic (Size (
mnLeftBorder.mnScreen,
mnTopBorder.mnScreen)));
mnRightBorder.mnModel = aBottomRightBorder.Width();
mnBottomBorder.mnModel = aBottomRightBorder.Height();
Size aTopLeftPageBorder (pDevice->PixelToLogic (Size (
mnLeftPageBorder.mnScreen,
mnTopPageBorder.mnScreen)));
mnLeftPageBorder.mnModel = aTopLeftPageBorder.Width();
mnTopPageBorder.mnModel = aTopLeftPageBorder.Height();
Size aBottomRightPageBorder (pDevice->PixelToLogic (Size (
mnRightPageBorder.mnScreen,
mnBottomPageBorder.mnScreen)));
mnRightPageBorder.mnModel = aBottomRightPageBorder.Width();
mnBottomPageBorder.mnModel = aBottomRightPageBorder.Height();
mnInsertionMarkerThickness.mnModel = pDevice->PixelToLogic (
Size(mnInsertionMarkerThickness.mnScreen,0)).Width();
}
sal_Int32 Layouter::GetColumnCount (void) const
{
return mnColumnCount;
}
bool Layouter::IsColumnCountFixed (void) const
{
return mnMinimalColumnCount == mnMaximalColumnCount;
}
Size Layouter::GetPageObjectSize (void) const
{
return maPageObjectModelSize;
}
Rectangle Layouter::GetPageObjectBox (sal_Int32 nIndex) const
{
int nColumn = nIndex % mnColumnCount;
int nRow = nIndex / mnColumnCount;
return Rectangle (
Point (mnLeftBorder.mnModel
+ nColumn * maPageObjectModelSize.Width()
+ mnLeftPageBorder.mnModel
+ (nColumn>0 ? nColumn : 0) * mnTotalHorizontalGap.mnModel,
mnTopBorder.mnModel
+ nRow * maPageObjectModelSize.Height()
+ mnTopPageBorder.mnModel
+ (nRow>0 ? nRow : 0) * mnTotalVerticalGap.mnModel),
maPageObjectModelSize);
}
Rectangle Layouter::GetPageBox (sal_Int32 nObjectCount) const
{
sal_Int32 nHorizontalSize = 0;
sal_Int32 nVerticalSize = 0;
if (mnColumnCount > 0)
{
sal_Int32 nRowCount = (nObjectCount+mnColumnCount-1) / mnColumnCount;
nHorizontalSize =
mnLeftBorder.mnModel
+ mnRightBorder.mnModel
+ mnColumnCount * maPageObjectModelSize.Width()
+ mnLeftPageBorder.mnModel + mnRightPageBorder.mnModel;
if (mnColumnCount > 1)
nHorizontalSize
+= (mnColumnCount-1) * mnTotalHorizontalGap.mnModel;
nVerticalSize =
mnTopBorder.mnModel
+ mnBottomBorder.mnModel
+ nRowCount * maPageObjectModelSize.Height()
+ mnTopPageBorder.mnModel + mnBottomPageBorder.mnModel;
if (nRowCount > 1)
nVerticalSize += (nRowCount-1) * mnTotalVerticalGap.mnModel;
}
return Rectangle (
Point(0,0),
Size (nHorizontalSize, nVerticalSize)
);
}
Rectangle Layouter::GetInsertionMarkerBox (
sal_Int32 nIndex,
bool bVertical,
bool bLeftOrTop) const
{
Rectangle aBox (GetPageObjectBox (nIndex));
if (bVertical)
{
sal_Int32 nHorizontalInsertionMarkerOffset
= (mnHorizontalGap.mnModel-mnInsertionMarkerThickness.mnModel) / 2;
if (bLeftOrTop)
{
// Left.
aBox.Left() -= mnLeftPageBorder.mnModel
+ mnHorizontalGap.mnModel
- nHorizontalInsertionMarkerOffset;
}
else
{
// Right.
aBox.Left() = aBox.Right()
+ mnRightPageBorder.mnModel
+ nHorizontalInsertionMarkerOffset;
}
aBox.Right() = aBox.Left() + mnInsertionMarkerThickness.mnModel;
}
else
{
sal_Int32 nVerticalInsertionMarkerOffset
= (mnVerticalGap.mnModel - mnInsertionMarkerThickness.mnModel) / 2;
if (bLeftOrTop)
{
// Above.
aBox.Top() -= mnTopPageBorder.mnModel
+ mnVerticalGap.mnModel
- nVerticalInsertionMarkerOffset;
}
else
{
// Below.
aBox.Top() = aBox.Bottom()
+ mnBottomPageBorder.mnModel
+ nVerticalInsertionMarkerOffset;
}
aBox.Bottom() = aBox.Top() + mnInsertionMarkerThickness.mnModel;
}
return aBox;
}
sal_Int32 Layouter::GetIndexOfFirstVisiblePageObject (
const Rectangle& aVisibleArea) const
{
sal_Int32 nRow = GetRowAtPosition (aVisibleArea.Top(), true, GM_BOTH);
return nRow * mnColumnCount;
}
sal_Int32 Layouter::GetIndexOfLastVisiblePageObject (
const Rectangle& aVisibleArea) const
{
sal_Int32 nRow = GetRowAtPosition (aVisibleArea.Bottom(),
true, GM_BOTH);
return (nRow+1) * mnColumnCount - 1;
}
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;
}
Layouter::DoublePoint
Layouter::ConvertModelToLayouterCoordinates (
const Point& rModelPoint) const
{
sal_Int32 nColumn = GetColumnAtPosition (rModelPoint.X(), true, GM_BOTH);
sal_Int32 nColumnWidth
= maPageObjectModelSize.Width() + mnTotalHorizontalGap.mnModel;
sal_Int32 nDistanceIntoColumn =
rModelPoint.X() - mnLeftBorder.mnModel - mnLeftPageBorder.mnModel
- nColumn * nColumnWidth;
sal_Int32 nRow = GetRowAtPosition (rModelPoint.Y(), true, GM_BOTH);
sal_Int32 nRowHeight
= maPageObjectModelSize.Height() + mnTotalVerticalGap.mnModel;
sal_Int32 nDistanceIntoRow =
rModelPoint.Y() - mnTopBorder.mnModel - mnTopPageBorder.mnModel
- nRow * nRowHeight;
return DoublePoint (
nColumn + double(nDistanceIntoColumn) / double(nColumnWidth),
nRow + double(nDistanceIntoRow) / double(nRowHeight));
}
Point Layouter::ConvertLayouterToModelCoordinates (
const DoublePoint & rLayouterPoint) const
{
sal_Int32 nColumn = (sal_Int32) ::rtl::math::round(rLayouterPoint.first,
0,rtl_math_RoundingMode_Floor);
sal_Int32 nColumnWidth
= maPageObjectModelSize.Width() + mnTotalHorizontalGap.mnModel;
sal_Int32 nDistanceIntoColumn
= (sal_Int32)((rLayouterPoint.first - nColumn) * nColumnWidth);
sal_Int32 nRow = (sal_Int32) ::rtl::math::round(rLayouterPoint.second,
0,rtl_math_RoundingMode_Floor);
sal_Int32 nRowHeight
= maPageObjectModelSize.Height() + mnTotalVerticalGap.mnModel;
sal_Int32 nDistanceIntoRow
= (sal_Int32)((rLayouterPoint.second - nRow) * nRowHeight);
return Point (
mnLeftBorder.mnModel + mnLeftPageBorder.mnModel
+ nColumn * nColumnWidth + nDistanceIntoColumn,
mnTopBorder.mnModel + mnTopPageBorder.mnModel
+ nRow * nRowHeight + nDistanceIntoRow);
}
sal_Int32 Layouter::GetRowAtPosition (
sal_Int32 nYPosition,
bool bIncludeBordersAndGaps,
GapMembership eGapMembership) const
{
sal_Int32 nRow = -1;
const sal_Int32 nY = nYPosition
- mnTopBorder.mnModel - mnTopPageBorder.mnModel;
if (nY >= 0)
{
// Vertical distance from one row to the next.
const sal_Int32 nRowOffset (
maPageObjectModelSize.Height() + mnTotalVerticalGap.mnModel);
// Calculate row consisting of page objects and gap below.
nRow = nY / nRowOffset;
const sal_Int32 nDistanceIntoGap (
(nY - nRow*nRowOffset) - maPageObjectModelSize.Height());
// When inside the gap below then nYPosition is not over a page
// object.
if (nDistanceIntoGap > 0)
nRow = ResolvePositionInGap (
nDistanceIntoGap,
eGapMembership,
nRow,
mnBottomPageBorder.mnModel,
mnVerticalGap.mnModel);
}
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.mnModel - mnLeftPageBorder.mnModel;
if (nX >= 0)
{
// Horizontal distance from one column to the next.
const sal_Int32 nColumnOffset (
maPageObjectModelSize.Width() + mnTotalHorizontalGap.mnModel);
// 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) - maPageObjectModelSize.Width());
// When inside the gap at the right then nXPosition is not over a
// page object.
if (nDistanceIntoGap > 0)
nColumn = ResolvePositionInGap (
nDistanceIntoGap,
eGapMembership,
nColumn,
mnRightPageBorder.mnModel,
mnHorizontalGap.mnModel);
}
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 nLeftOrTopPageBorder,
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 = nLeftOrTopPageBorder + 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 > nLeftOrTopPageBorder)
{
if (nDistanceIntoGap > nLeftOrTopPageBorder + 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;
}
const Layouter::BackgroundRectangleList&
Layouter::GetBackgroundRectangleList (void) const
{
return maBackgroundRectangleList;
}
void Layouter::BuildBackgroundRectangleList (void)
{
maBackgroundRectangleList.clear();
maBackgroundRectangleList.resize(mnColumnCount*5);
maBackgroundRectangleList.push_back (
GetPageObjectBox (10));
}
} } } // end of namespace ::sd::slidesorter::namespace