/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <hints.hxx>
#include <comphelper/flagguard.hxx>
#include <tools/line.hxx>
#include <editeng/opaqitem.hxx>
#include <editeng/protitem.hxx>
#include <vcl/settings.hxx>
#include <fmtpdsc.hxx>
#include <fmtsrnd.hxx>
#include <pagedesc.hxx>
#include <pagefrm.hxx>
#include <rootfrm.hxx>
#include <ftnfrm.hxx>
#include <flyfrm.hxx>
#include <tabfrm.hxx>
#include <rowfrm.hxx>
#include <cellfrm.hxx>
#include <txtfrm.hxx>
#include <notxtfrm.hxx>
#include <viewopt.hxx>
#include <DocumentSettingManager.hxx>
#include <viscrs.hxx>
#include <dflyobj.hxx>
#include <crstate.hxx>
#include <dcontact.hxx>
#include <sortedobjs.hxx>
#include <txatbase.hxx>
#include <fmtfld.hxx>
#include <fldbas.hxx>
#include <frmatr.hxx>
#include <frmtool.hxx>
#include "../text/inftxt.hxx"
#include "../text/itrpaint.hxx"
#include <ndtxt.hxx>
#include <undobj.hxx>
#include <flyfrms.hxx>
#include <sectfrm.hxx>
#include <swselectionlist.hxx>
#include <comphelper/lok.hxx>
#include <osl/diagnose.h>
namespace {
bool lcl_GetModelPositionForViewPoint_Objects( const SwPageFrame* pPageFrame, bool bSearchBackground,
SwPosition *pPos, Point const & rPoint, SwCursorMoveState* pCMS )
{
bool bRet = false;
Point aPoint( rPoint );
SwOrderIter aIter( pPageFrame );
aIter.Top();
while ( aIter() )
{
const SwVirtFlyDrawObj* pObj =
static_cast<const SwVirtFlyDrawObj*>(aIter());
if (const SwContact* pContact = ::GetUserCall( aIter() ))
{
const SwAnchoredObject* pAnchoredObj = pContact->GetAnchoredObj( aIter() );
const SwFrameFormat* pObjFormat = pAnchoredObj->GetFrameFormat();
const SwFormatSurround& rSurround = pObjFormat->GetSurround();
const SvxOpaqueItem& rOpaque = pObjFormat->GetOpaque();
bool bInBackground = ( rSurround.GetSurround() == css::text::WrapTextMode_THROUGH ) && !rOpaque.GetValue();
bool bBackgroundMatches = bInBackground == bSearchBackground;
const SwFlyFrame* pFly = pObj ? pObj->GetFlyFrame() : nullptr;
if ( pFly && bBackgroundMatches &&
( ( pCMS && pCMS->m_bSetInReadOnly ) ||
!pFly->IsProtected() ) &&
pFly->GetModelPositionForViewPoint( pPos, aPoint, pCMS ) )
{
bRet = true;
break;
}
if ( pCMS && pCMS->m_bStop )
return false;
}
aIter.Prev();
}
return bRet;
}
double lcl_getDistance( const SwRect& rRect, const Point& rPoint )
{
double nDist = 0.0;
// If the point is inside the rectangle, then distance is 0
// Otherwise, compute the distance to the center of the rectangle.
if ( !rRect.Contains( rPoint ) )
{
tools::Line aLine( rPoint, rRect.Center( ) );
nDist = aLine.GetLength( );
}
return nDist;
}
}
namespace {
//For SwFlyFrame::GetModelPositionForViewPoint
class SwCursorOszControl
{
public:
// So the compiler can initialize the class already. No DTOR and member
// as public members
const SwFlyFrame* m_pEntry;
const SwFlyFrame* m_pStack1;
const SwFlyFrame* m_pStack2;
bool ChkOsz( const SwFlyFrame *pFly )
{
bool bRet = true;
if (pFly != m_pStack1 && pFly != m_pStack2)
{
m_pStack1 = m_pStack2;
m_pStack2 = pFly;
bRet = false;
}
return bRet;
}
void Entry( const SwFlyFrame *pFly )
{
if (!m_pEntry)
m_pEntry = m_pStack1 = pFly;
}
void Exit( const SwFlyFrame *pFly )
{
if (pFly == m_pEntry)
m_pEntry = m_pStack1 = m_pStack2 = nullptr;
}
};
}
static SwCursorOszControl g_OszCtrl = { nullptr, nullptr, nullptr };
/** Searches the ContentFrame owning the PrtArea containing the point. */
bool SwLayoutFrame::GetModelPositionForViewPoint( SwPosition *pPos, Point &rPoint,
SwCursorMoveState* pCMS, bool ) const
{
vcl::RenderContext* pRenderContext = getRootFrame()->GetCurrShell()->GetOut();
bool bRet = false;
const SwFrame *pFrame = Lower();
while ( !bRet && pFrame )
{
pFrame->Calc(pRenderContext);
// #i43742# New function
const bool bContentCheck = pFrame->IsTextFrame() && pCMS && pCMS->m_bContentCheck;
const SwRect aPaintRect( bContentCheck ?
pFrame->UnionFrame() :
pFrame->GetPaintArea() );
auto pTextFrame = pFrame->DynCastTextFrame();
bool bSplitFly = false;
if (pTextFrame && pTextFrame->HasNonLastSplitFlyDrawObj())
{
// Don't consider a non-last anchor of the split fly, so the view point can be corrected
// to go to the nearest fly, instead of the last anchor on a later page.
bSplitFly = true;
}
if ( aPaintRect.Contains( rPoint ) &&
( bContentCheck || pFrame->GetModelPositionForViewPoint( pPos, rPoint, pCMS ) ) && !bSplitFly )
bRet = true;
else
pFrame = pFrame->GetNext();
if ( pCMS && pCMS->m_bStop )
return false;
}
return bRet;
}
/** Searches for the page containing the searched point. */
bool SwPageFrame::GetModelPositionForViewPoint( SwPosition *pPos, Point &rPoint,
SwCursorMoveState* pCMS, bool bTestBackground ) const
{
Point aPoint(std::clamp(rPoint.X(), getFrameArea().Left(), getFrameArea().Right()),
std::clamp(rPoint.Y(), getFrameArea().Top(), getFrameArea().Bottom()));
bool bRet = false;
//Could it be a free flying one?
//If his content should be protected, we can't set the Cursor in it, thus
//all changes should be impossible.
if ( GetSortedObjs() )
{
bRet = lcl_GetModelPositionForViewPoint_Objects( this, false, pPos, rPoint, pCMS );
}
if ( !bRet )
{
SwPosition aBackPos( *pPos );
SwPosition aTextPos( *pPos );
//We fix the StartPoint if no Content below the page 'answers' and then
//start all over again one page before the current one.
//However we can't use Flys in such a case.
if (!SwLayoutFrame::GetModelPositionForViewPoint(&aTextPos, aPoint, pCMS))
{
if ( pCMS && (pCMS->m_bStop || pCMS->m_bExactOnly) )
{
pCMS->m_bStop = true;
return false;
}
const SwContentFrame *pCnt = GetContentPos( aPoint, false, false, pCMS, false );
auto pTextFrame = pCnt ? pCnt->DynCastTextFrame() : nullptr;
if (pTextFrame)
{
SwFlyAtContentFrame* pFly = pTextFrame->HasNonLastSplitFlyDrawObj();
if (pFly)
{
// No exact match, looking for a nearest doc model position. Consider our fly
// frame.
pCnt = pFly->GetContentPos( aPoint, false, false, pCMS, false );
}
}
SAL_WARN_IF(!pCnt, "sw.layout", "Cursor is gone to a Black hole");
if (!pCnt)
return false;
// GetContentPos may have modified pCMS
if ( pCMS && pCMS->m_bStop )
return false;
bool bTextRet = false;
if( pCMS && pCMS->m_pFill && pCnt->IsTextFrame() )
bTextRet = pCnt->GetModelPositionForViewPoint( &aTextPos, rPoint, pCMS );
else
bTextRet = pCnt->GetModelPositionForViewPoint( &aTextPos, aPoint, pCMS );
if ( !bTextRet )
{
// Set point to pCnt, delete mark
// this may happen, if pCnt is hidden
if (pCnt->IsTextFrame())
{
aTextPos = static_cast<SwTextFrame const*>(pCnt)->MapViewToModelPos(TextFrameIndex(0));
}
else
{
assert(pCnt->IsNoTextFrame());
aTextPos.Assign( *static_cast<SwNoTextFrame const*>(pCnt)->GetNode() );
}
}
}
SwContentNode* pContentNode = aTextPos.GetNode().GetContentNode();
bool bConsiderBackground = true;
// If the text position is a clickable field, then that should have priority.
if (pContentNode && pContentNode->IsTextNode())
{
SwTextNode* pTextNd = pContentNode->GetTextNode();
SwTextAttr* pTextAttr = pTextNd->GetTextAttrForCharAt(aTextPos.GetContentIndex(), RES_TXTATR_FIELD);
if (pTextAttr)
{
const SwField* pField = pTextAttr->GetFormatField().GetField();
if (pField->IsClickable())
bConsiderBackground = false;
}
}
bool bBackRet = false;
// Check objects in the background if nothing else matched
if ( GetSortedObjs() )
{
bBackRet = lcl_GetModelPositionForViewPoint_Objects( this, true, &aBackPos, rPoint, pCMS );
}
if (bConsiderBackground && bTestBackground && bBackRet)
{
(*pPos) = aBackPos;
}
else if (!bBackRet)
{
(*pPos) = aTextPos;
}
else // bBackRet && !(bConsiderBackground && bTestBackground)
{
/* In order to provide a selection as accurate as possible when we have both
* text and background object, then we compute the distance between both
* would-be positions and the click point. The shortest distance wins.
*/
double nTextDistance = 0;
bool bValidTextDistance = false;
if (pContentNode)
{
SwContentFrame* pTextFrame = pContentNode->getLayoutFrame( getRootFrame( ) );
// try this again but prefer the "previous" position
SwCursorMoveState aMoveState;
SwCursorMoveState *const pState(pCMS ? pCMS : &aMoveState);
comphelper::FlagRestorationGuard g(
pState->m_bPosMatchesBounds, true);
SwPosition prevTextPos(*pPos);
if (SwLayoutFrame::GetModelPositionForViewPoint(&prevTextPos, aPoint, pState))
{
SwRect aTextRect;
pTextFrame->GetCharRect(aTextRect, prevTextPos);
if (prevTextPos.GetContentIndex() < pContentNode->Len())
{
// aRextRect is just a line on the left edge of the
// previous character; to get a better measure from
// lcl_getDistance, extend that to a rectangle over
// the entire character.
SwPosition nextTextPos(prevTextPos);
nextTextPos.AdjustContent(+1);
SwRect nextTextRect;
pTextFrame->GetCharRect(nextTextRect, nextTextPos);
SwRectFnSet aRectFnSet(pTextFrame);
if (aRectFnSet.GetTop(aTextRect) ==
aRectFnSet.GetTop(nextTextRect)) // same line?
{
// need to handle mixed RTL/LTR portions somehow
if (aRectFnSet.GetLeft(aTextRect) <
aRectFnSet.GetLeft(nextTextRect))
{
aRectFnSet.SetRight( aTextRect,
aRectFnSet.GetLeft(nextTextRect));
}
else // RTL
{
aRectFnSet.SetLeft( aTextRect,
aRectFnSet.GetLeft(nextTextRect));
}
}
}
nTextDistance = lcl_getDistance(aTextRect, rPoint);
bValidTextDistance = true;
}
}
double nBackDistance = 0;
bool bValidBackDistance = false;
SwContentNode* pBackNd = aBackPos.GetNode( ).GetContentNode( );
if ( pBackNd && bConsiderBackground)
{
// FIXME There are still cases were we don't have the proper node here.
SwContentFrame* pBackFrame = pBackNd->getLayoutFrame( getRootFrame( ) );
if (pBackFrame)
{
SwRect rBackRect;
pBackFrame->GetCharRect( rBackRect, aBackPos );
nBackDistance = lcl_getDistance( rBackRect, rPoint );
bValidBackDistance = true;
}
}
if ( bValidTextDistance && bValidBackDistance && basegfx::fTools::more( nTextDistance, nBackDistance ) )
{
(*pPos) = aBackPos;
}
else
{
(*pPos) = aTextPos;
}
}
}
rPoint = aPoint;
return true;
}
bool SwLayoutFrame::FillSelection( SwSelectionList& rList, const SwRect& rRect ) const
{
if( rRect.Overlaps(GetPaintArea()) )
{
const SwFrame* pFrame = Lower();
while( pFrame )
{
pFrame->FillSelection( rList, rRect );
pFrame = pFrame->GetNext();
}
}
return false;
}
bool SwPageFrame::FillSelection( SwSelectionList& rList, const SwRect& rRect ) const
{
bool bRet = false;
if( rRect.Overlaps(GetPaintArea()) )
{
bRet = SwLayoutFrame::FillSelection( rList, rRect );
if( GetSortedObjs() )
{
const SwSortedObjs &rObjs = *GetSortedObjs();
for (SwAnchoredObject* pAnchoredObj : rObjs)
{
const SwFlyFrame* pFly = pAnchoredObj->DynCastFlyFrame();
if( !pFly )
continue;
if( pFly->FillSelection( rList, rRect ) )
bRet = true;
}
}
}
return bRet;
}
bool SwRootFrame::FillSelection( SwSelectionList& aSelList, const SwRect& rRect) const
{
const SwFrame *pPage = Lower();
const tools::Long nBottom = rRect.Bottom();
while( pPage )
{
if( pPage->getFrameArea().Top() < nBottom )
{
if( pPage->getFrameArea().Bottom() > rRect.Top() )
pPage->FillSelection( aSelList, rRect );
pPage = pPage->GetNext();
}
else
pPage = nullptr;
}
return !aSelList.isEmpty();
}
/** Primary passes the call to the first page.
*
* @return false, if the passed Point gets changed
*/
bool SwRootFrame::GetModelPositionForViewPoint( SwPosition *pPos, Point &rPoint,
SwCursorMoveState* pCMS, bool bTestBackground ) const
{
const bool bOldAction = IsCallbackActionEnabled();
const_cast<SwRootFrame*>(this)->SetCallbackActionEnabled( false );
OSL_ENSURE( (Lower() && Lower()->IsPageFrame()), "No PageFrame found." );
if( pCMS && pCMS->m_pFill )
pCMS->m_bFillRet = false;
Point aOldPoint = rPoint;
// search for page containing rPoint. The borders around the pages are considered
const SwPageFrame* pPage = GetPageAtPos( rPoint, nullptr, true );
// #i95626#
// special handling for <rPoint> beyond root frames area
if ( !pPage &&
rPoint.X() > getFrameArea().Right() &&
rPoint.Y() > getFrameArea().Bottom() )
{
pPage = dynamic_cast<const SwPageFrame*>(Lower());
while ( pPage && pPage->GetNext() )
{
pPage = dynamic_cast<const SwPageFrame*>(pPage->GetNext());
}
}
if ( pPage )
{
pPage->SwPageFrame::GetModelPositionForViewPoint( pPos, rPoint, pCMS, bTestBackground );
}
const_cast<SwRootFrame*>(this)->SetCallbackActionEnabled( bOldAction );
if( pCMS )
{
if( pCMS->m_bStop )
return false;
if( pCMS->m_pFill )
return pCMS->m_bFillRet;
}
return aOldPoint == rPoint;
}
/**
* If this is about a Content-carrying cell the Cursor will be force inserted into one of the ContentFrames
* if there are no other options.
*
* There is no entry for protected cells.
*/
bool SwCellFrame::GetModelPositionForViewPoint( SwPosition *pPos, Point &rPoint,
SwCursorMoveState* pCMS, bool ) const
{
vcl::RenderContext* pRenderContext = getRootFrame()->GetCurrShell()->GetOut();
// cell frame does not necessarily have a lower (split table cell)
if ( !Lower() )
return false;
if ( !(pCMS && pCMS->m_bSetInReadOnly) &&
GetFormat()->GetProtect().IsContentProtected() )
return false;
if ( pCMS && pCMS->m_eState == CursorMoveState::TableSel )
{
const SwTabFrame *pTab = FindTabFrame();
if ( pTab->IsFollow() && pTab->IsInHeadline( *this ) )
{
pCMS->m_bStop = true;
return false;
}
}
if (Lower()->IsLayoutFrame())
return SwLayoutFrame::GetModelPositionForViewPoint(pPos, rPoint, pCMS);
Calc(pRenderContext);
bool bRet = false;
const SwFrame *pFrame = Lower();
while (pFrame && !bRet)
{
pFrame->Calc(pRenderContext);
if (pFrame->getFrameArea().Contains(rPoint))
{
bRet = pFrame->GetModelPositionForViewPoint(pPos, rPoint, pCMS);
if (pCMS && pCMS->m_bStop)
return false;
}
pFrame = pFrame->GetNext();
}
if (!bRet)
{
const bool bFill = pCMS && pCMS->m_pFill;
Point aPoint(rPoint);
const SwContentFrame *pCnt = GetContentPos(rPoint, true);
if (bFill && pCnt->IsTextFrame())
{
rPoint = aPoint;
}
pCnt->GetModelPositionForViewPoint(pPos, rPoint, pCMS);
}
return true;
}
//Problem: If two Flys have the same size and share the same position then
//they end inside each other.
//Because we recursively check if a Point doesn't randomly lie inside another
//fly which lies completely inside the current Fly we could trigger an endless
//loop with the mentioned situation above.
//Using the helper class SwCursorOszControl we prevent the recursion. During
//a recursion GetModelPositionForViewPoint picks the one which lies on top.
bool SwFlyFrame::GetModelPositionForViewPoint( SwPosition *pPos, Point &rPoint,
SwCursorMoveState* pCMS, bool ) const
{
vcl::RenderContext* pRenderContext = getRootFrame()->GetCurrShell()->GetOut();
g_OszCtrl.Entry( this );
//If the Points lies inside the Fly, we try hard to set the Cursor inside it.
//However if the Point sits inside a Fly which is completely located inside
//the current one, we call GetModelPositionForViewPoint for it.
Calc(pRenderContext);
bool bInside = getFrameArea().Contains( rPoint ) && Lower();
bool bRet = false;
//If a Frame contains a graphic, but only text was requested, it basically
//won't accept the Cursor.
if ( bInside && pCMS && pCMS->m_eState == CursorMoveState::SetOnlyText &&
(!Lower() || Lower()->IsNoTextFrame()) )
bInside = false;
const SwPageFrame *pPage = FindPageFrame();
if ( bInside && pPage && pPage->GetSortedObjs() )
{
SwOrderIter aIter( pPage );
aIter.Top();
while ( aIter() && !bRet )
{
const SwVirtFlyDrawObj* pObj = static_cast<const SwVirtFlyDrawObj*>(aIter());
const SwFlyFrame* pFly = pObj ? pObj->GetFlyFrame() : nullptr;
if ( pFly && pFly->getFrameArea().Contains( rPoint ) &&
getFrameArea().Contains( pFly->getFrameArea() ) )
{
if (g_OszCtrl.ChkOsz(pFly))
break;
bRet = pFly->GetModelPositionForViewPoint( pPos, rPoint, pCMS );
if ( bRet )
break;
if ( pCMS && pCMS->m_bStop )
return false;
}
aIter.Next();
}
}
while ( bInside && !bRet )
{
const SwFrame *pFrame = Lower();
while ( pFrame && !bRet )
{
pFrame->Calc(pRenderContext);
if ( pFrame->getFrameArea().Contains( rPoint ) )
{
bRet = pFrame->GetModelPositionForViewPoint( pPos, rPoint, pCMS );
if ( pCMS && pCMS->m_bStop )
return false;
}
pFrame = pFrame->GetNext();
}
if ( !bRet )
{
const bool bFill = pCMS && pCMS->m_pFill;
Point aPoint( rPoint );
const SwContentFrame *pCnt = GetContentPos( rPoint, true, false, pCMS );
if ( pCMS && pCMS->m_bStop )
return false;
if( bFill && pCnt->IsTextFrame() )
{
rPoint = aPoint;
}
pCnt->GetModelPositionForViewPoint( pPos, rPoint, pCMS );
bRet = true;
}
}
g_OszCtrl.Exit( this );
return bRet;
}
/** Layout dependent cursor travelling */
bool SwNoTextFrame::LeftMargin(SwPaM *pPam) const
{
if( &pPam->GetPointNode() != GetNode() )
return false;
pPam->GetPoint()->AssignStartIndex(*GetNode());
return true;
}
bool SwNoTextFrame::RightMargin(SwPaM *pPam, bool) const
{
if( &pPam->GetPointNode() != GetNode() )
return false;
pPam->GetPoint()->AssignEndIndex(*GetNode());
return true;
}
static const SwContentFrame *lcl_GetNxtCnt( const SwContentFrame* pCnt )
{
return pCnt->GetNextContentFrame();
}
static const SwContentFrame *lcl_GetPrvCnt( const SwContentFrame* pCnt )
{
return pCnt->GetPrevContentFrame();
}
typedef const SwContentFrame *(*GetNxtPrvCnt)( const SwContentFrame* );
/// Frame in repeated headline?
static bool lcl_IsInRepeatedHeadline( const SwFrame *pFrame,
const SwTabFrame** ppTFrame = nullptr )
{
const SwTabFrame *pTab = pFrame->FindTabFrame();
if( ppTFrame )
*ppTFrame = pTab;
return pTab && pTab->IsFollow() && pTab->IsInHeadline( *pFrame );
}
/// Skip protected table cells. Optionally also skip repeated headlines.
//MA 1998-01-26: Chg also skip other protected areas
//FME: Skip follow flow cells
static const SwContentFrame * lcl_MissProtectedFrames( const SwContentFrame *pCnt,
GetNxtPrvCnt fnNxtPrv,
bool bMissHeadline,
bool bInReadOnly,
bool bMissFollowFlowLine )
{
if ( pCnt && pCnt->IsInTab() )
{
bool bProtect = true;
while ( pCnt && bProtect )
{
const SwLayoutFrame *pCell = pCnt->GetUpper();
while ( pCell && !pCell->IsCellFrame() )
pCell = pCell->GetUpper();
if ( !pCell ||
( ( bInReadOnly || !pCell->GetFormat()->GetProtect().IsContentProtected() ) &&
( !bMissHeadline || !lcl_IsInRepeatedHeadline( pCell ) ) &&
( !bMissFollowFlowLine || !pCell->IsInFollowFlowRow() ) &&
!pCell->IsCoveredCell() ) )
bProtect = false;
else
pCnt = (*fnNxtPrv)( pCnt );
}
}
else if ( !bInReadOnly )
while ( pCnt && pCnt->IsProtected() )
pCnt = (*fnNxtPrv)( pCnt );
return pCnt;
}
static bool lcl_UpDown( SwPaM *pPam, const SwContentFrame *pStart,
GetNxtPrvCnt fnNxtPrv, bool bInReadOnly )
{
OSL_ENSURE( FrameContainsNode(*pStart, pPam->GetPointNode().GetIndex()),
"lcl_UpDown doesn't work for others." );
const SwContentFrame *pCnt = nullptr;
//We have to cheat a little bit during a table selection: Go to the
//beginning of the cell while going up and go to the end of the cell while
//going down.
bool bTableSel = false;
if ( pStart->IsInTab() &&
pPam->GetPointNode().StartOfSectionNode() !=
pPam->GetMarkNode().StartOfSectionNode() )
{
bTableSel = true;
const SwLayoutFrame *pCell = pStart->GetUpper();
while ( !pCell->IsCellFrame() )
pCell = pCell->GetUpper();
// Check, if cell has a Prev/Follow cell:
const bool bFwd = ( fnNxtPrv == lcl_GetNxtCnt );
const SwLayoutFrame* pTmpCell = bFwd ?
static_cast<const SwCellFrame*>(pCell)->GetFollowCell() :
static_cast<const SwCellFrame*>(pCell)->GetPreviousCell();
const SwContentFrame* pTmpStart = pStart;
while ( pTmpCell && nullptr != ( pTmpStart = pTmpCell->ContainsContent() ) )
{
pCell = pTmpCell;
pTmpCell = bFwd ?
static_cast<const SwCellFrame*>(pCell)->GetFollowCell() :
static_cast<const SwCellFrame*>(pCell)->GetPreviousCell();
}
const SwContentFrame *pNxt = pCnt = pTmpStart;
while ( pCell->IsAnLower( pNxt ) )
{
pCnt = pNxt;
pNxt = (*fnNxtPrv)( pNxt );
}
}
pCnt = (*fnNxtPrv)( pCnt ? pCnt : pStart );
pCnt = ::lcl_MissProtectedFrames( pCnt, fnNxtPrv, true, bInReadOnly, bTableSel );
const SwTabFrame *pStTab = pStart->FindTabFrame();
const SwTabFrame *pTable = nullptr;
const bool bTab = pStTab || (pCnt && pCnt->IsInTab());
bool bEnd = !bTab;
const SwFrame* pVertRefFrame = pStart;
if ( bTableSel && pStTab )
pVertRefFrame = pStTab;
SwRectFnSet aRectFnSet(pVertRefFrame);
SwTwips nX = 0;
if ( bTab )
{
// pStart or pCnt is inside a table. nX will be used for travelling:
SwRect aRect( pStart->getFrameArea() );
pStart->GetCharRect( aRect, *pPam->GetPoint() );
Point aCenter = aRect.Center();
nX = aRectFnSet.IsVert() ? aCenter.Y() : aCenter.X();
pTable = pCnt ? pCnt->FindTabFrame() : nullptr;
if ( !pTable )
pTable = pStTab;
if ( pStTab &&
!pStTab->GetUpper()->IsInTab() &&
!pTable->GetUpper()->IsInTab() )
{
const SwFrame *pCell = pStart->GetUpper();
while ( pCell && !pCell->IsCellFrame() )
pCell = pCell->GetUpper();
OSL_ENSURE( pCell, "could not find the cell" );
nX = aRectFnSet.XInc(aRectFnSet.GetLeft(pCell->getFrameArea()),
aRectFnSet.GetWidth(pCell->getFrameArea()) / 2);
//The flow leads from one table to the next. The X-value needs to be
//corrected based on the middle of the starting cell by the amount
//of the offset of the tables.
if ( pStTab != pTable )
{
nX += aRectFnSet.GetLeft(pTable->getFrameArea()) -
aRectFnSet.GetLeft(pStTab->getFrameArea());
}
}
// Restrict nX to the left and right borders of pTab:
// (is this really necessary?)
if (pTable && !pTable->GetUpper()->IsInTab())
{
const bool bRTL = pTable->IsRightToLeft();
const tools::Long nPrtLeft = bRTL ?
aRectFnSet.GetPrtRight(*pTable) :
aRectFnSet.GetPrtLeft(*pTable);
if (bRTL != (aRectFnSet.XDiff(nPrtLeft, nX) > 0))
nX = nPrtLeft;
else
{
const tools::Long nPrtRight = bRTL ?
aRectFnSet.GetPrtLeft(*pTable) :
aRectFnSet.GetPrtRight(*pTable);
if (bRTL != (aRectFnSet.XDiff(nX, nPrtRight) > 0))
nX = nPrtRight;
}
}
}
do
{
//If I'm in the DocumentBody, I want to stay there.
if ( pStart->IsInDocBody() )
{
while (pCnt && (!pCnt->IsInDocBody() || pCnt->IsHiddenNow()))
{
pCnt = (*fnNxtPrv)( pCnt );
pCnt = ::lcl_MissProtectedFrames( pCnt, fnNxtPrv, true, bInReadOnly, bTableSel );
}
}
//If I'm in the FootNoteArea, I try to reach the next FootNoteArea in
//case of necessity.
else if ( pStart->IsInFootnote() )
{
while (pCnt && (!pCnt->IsInFootnote() || pCnt->IsHiddenNow()))
{
pCnt = (*fnNxtPrv)( pCnt );
pCnt = ::lcl_MissProtectedFrames( pCnt, fnNxtPrv, true, bInReadOnly, bTableSel );
}
}
//In Flys we can go ahead blindly as long as we find a Content.
else if ( pStart->IsInFly() )
{
if (pCnt && pCnt->IsHiddenNow())
{
pCnt = (*fnNxtPrv)( pCnt );
pCnt = ::lcl_MissProtectedFrames( pCnt, fnNxtPrv, true, bInReadOnly, bTableSel );
}
}
//Otherwise I'll just refuse to leave to current area.
else if ( pCnt )
{
const SwFrame *pUp = pStart->GetUpper();
while (pUp && pUp->GetUpper() && !(pUp->GetType() & FRM_HEADFOOT))
pUp = pUp->GetUpper();
bool bSame = false;
const SwFrame *pCntUp = pCnt->GetUpper();
while ( pCntUp && !bSame )
{
if ( pUp == pCntUp )
bSame = true;
else
pCntUp = pCntUp->GetUpper();
}
if ( !bSame )
pCnt = nullptr;
else if (pCnt->IsHiddenNow()) // i73332
{
pCnt = (*fnNxtPrv)( pCnt );
pCnt = ::lcl_MissProtectedFrames( pCnt, fnNxtPrv, true, bInReadOnly, bTableSel );
}
}
if ( bTab )
{
if ( !pCnt )
bEnd = true;
else
{
const SwTabFrame *pTab = pCnt->FindTabFrame();
if( !pTab )
bEnd = true;
else
{
if ( pTab != pTable )
{
//The flow leads from one table to the next. The X-value
//needs to be corrected by the amount of the offset of
//the tables
if ( pTable &&
!pTab->GetUpper()->IsInTab() &&
!pTable->GetUpper()->IsInTab() )
nX += pTab->getFrameArea().Left() - pTable->getFrameArea().Left();
pTable = pTab;
}
const SwLayoutFrame *pCell = pCnt->GetUpper();
while ( pCell && !pCell->IsCellFrame() )
pCell = pCell->GetUpper();
Point aInsideCell;
Point aInsideCnt;
if ( pCell )
{
tools::Long nTmpTop = aRectFnSet.GetTop(pCell->getFrameArea());
if ( aRectFnSet.IsVert() )
{
if ( nTmpTop )
nTmpTop = aRectFnSet.XInc(nTmpTop, -1);
aInsideCell = Point( nTmpTop, nX );
}
else
aInsideCell = Point( nX, nTmpTop );
}
tools::Long nTmpTop = aRectFnSet.GetTop(pCnt->getFrameArea());
if ( aRectFnSet.IsVert() )
{
if ( nTmpTop )
nTmpTop = aRectFnSet.XInc(nTmpTop, -1);
aInsideCnt = Point( nTmpTop, nX );
}
else
aInsideCnt = Point( nX, nTmpTop );
if ( pCell && pCell->getFrameArea().Contains( aInsideCell ) )
{
bEnd = true;
//Get the right Content out of the cell.
if ( !pCnt->getFrameArea().Contains( aInsideCnt ) )
{
pCnt = pCell->ContainsContent();
if ( fnNxtPrv == lcl_GetPrvCnt )
while ( pCell->IsAnLower(pCnt->GetNextContentFrame()) )
pCnt = pCnt->GetNextContentFrame();
}
}
else if ( pCnt->getFrameArea().Contains( aInsideCnt ) )
bEnd = true;
}
}
if ( !bEnd )
{
pCnt = (*fnNxtPrv)( pCnt );
pCnt = ::lcl_MissProtectedFrames( pCnt, fnNxtPrv, true, bInReadOnly, bTableSel );
}
}
} while (!bEnd || (pCnt && pCnt->IsHiddenNow()));
if (pCnt == nullptr)
{
return false;
}
if (pCnt->IsTextFrame())
{
SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(pCnt));
*pPam->GetPoint() = pFrame->MapViewToModelPos(TextFrameIndex(
fnNxtPrv == lcl_GetPrvCnt
? pFrame->GetText().getLength()
: 0));
}
else
{ // set the Point on the Content-Node
assert(pCnt->IsNoTextFrame());
const SwContentNode* const pCNd = static_cast<SwNoTextFrame const*>(pCnt)->GetNode();
if ( fnNxtPrv == lcl_GetPrvCnt )
pPam->GetPoint()->AssignEndIndex(*pCNd);
else
pPam->GetPoint()->AssignStartIndex(*pCNd);
}
return true;
}
bool SwContentFrame::UnitUp( SwPaM* pPam, const SwTwips, bool bInReadOnly ) const
{
return ::lcl_UpDown( pPam, this, lcl_GetPrvCnt, bInReadOnly );
}
bool SwContentFrame::UnitDown( SwPaM* pPam, const SwTwips, bool bInReadOnly ) const
{
return ::lcl_UpDown( pPam, this, lcl_GetNxtCnt, bInReadOnly );
}
/** Returns the number of the current page.
*
* If the method gets a PaM then the current page is the one in which the PaM sits. Otherwise the
* current page is the first one inside the VisibleArea. We only work on available pages!
*/
sal_uInt16 SwRootFrame::GetCurrPage( const SwPaM *pActualCursor ) const
{
assert(pActualCursor && "got no page cursor");
SwFrame const*const pActFrame = pActualCursor->GetPoint()->GetNode().
GetContentNode()->getLayoutFrame(this,
pActualCursor->GetPoint());
return pActFrame->FindPageFrame()->GetPhyPageNum();
}
/** Returns a PaM which sits at the beginning of the requested page.
*
* Formatting is done as far as necessary.
* The PaM sits on the last page, if the page number was chosen too big.
*
* @return Null, if the operation was not possible.
*/
sal_uInt16 SwRootFrame::SetCurrPage( SwCursor* pToSet, sal_uInt16 nPageNum )
{
vcl::RenderContext* pRenderContext = GetCurrShell() ? GetCurrShell()->GetOut() : nullptr;
OSL_ENSURE( Lower() && Lower()->IsPageFrame(), "No page available." );
SwPageFrame *pPage = static_cast<SwPageFrame*>(Lower());
bool bEnd =false;
while ( !bEnd && pPage->GetPhyPageNum() != nPageNum )
{ if ( pPage->GetNext() )
pPage = static_cast<SwPageFrame*>(pPage->GetNext());
else
{ //Search the first ContentFrame and format until a new page is started
//or until the ContentFrame are all done.
const SwContentFrame *pContent = pPage->ContainsContent();
while ( pContent && pPage->IsAnLower( pContent ) )
{
pContent->Calc(pRenderContext);
pContent = pContent->GetNextContentFrame();
}
//Either this is a new page or we found the last page.
if ( pPage->GetNext() )
pPage = static_cast<SwPageFrame*>(pPage->GetNext());
else
bEnd = true;
}
}
//pPage now points to the 'requested' page. Now we have to create the PaM
//on the beginning of the first ContentFrame in the body-text.
//If this is a footnote-page, the PaM will be set in the first footnote.
const SwContentFrame *pContent = pPage->ContainsContent();
if ( pPage->IsFootnotePage() )
while ( pContent && !pContent->IsInFootnote() )
pContent = pContent->GetNextContentFrame();
else
while ( pContent && !pContent->IsInDocBody() )
pContent = pContent->GetNextContentFrame();
if ( pContent )
{
assert(pContent->IsTextFrame());
SwTextFrame const*const pFrame(static_cast<const SwTextFrame*>(pContent));
*pToSet->GetPoint() = pFrame->MapViewToModelPos(pFrame->GetOffset());
SwShellCursor* pSCursor = dynamic_cast<SwShellCursor*>(pToSet);
if( pSCursor )
{
Point &rPt = pSCursor->GetPtPos();
rPt = pContent->getFrameArea().Pos();
rPt += pContent->getFramePrintArea().Pos();
}
return pPage->GetPhyPageNum();
}
return 0;
}
SwContentFrame *GetFirstSub( const SwLayoutFrame *pLayout )
{
return const_cast<SwPageFrame*>(static_cast<const SwPageFrame*>(pLayout))->FindFirstBodyContent();
}
SwContentFrame *GetLastSub( const SwLayoutFrame *pLayout )
{
return const_cast<SwPageFrame*>(static_cast<const SwPageFrame*>(pLayout))->FindLastBodyContent();
}
SwLayoutFrame *GetNextFrame( const SwLayoutFrame *pFrame )
{
SwLayoutFrame *pNext =
(pFrame->GetNext() && pFrame->GetNext()->IsLayoutFrame()) ?
const_cast<SwLayoutFrame*>(static_cast<const SwLayoutFrame*>(pFrame->GetNext())) : nullptr;
// #i39402# in case of an empty page
if(pNext && !pNext->ContainsContent())
pNext = (pNext->GetNext() && pNext->GetNext()->IsLayoutFrame()) ?
static_cast<SwLayoutFrame*>(pNext->GetNext()) : nullptr;
return pNext;
}
SwLayoutFrame *GetThisFrame( const SwLayoutFrame *pFrame )
{
return const_cast<SwLayoutFrame*>(pFrame);
}
SwLayoutFrame *GetPrevFrame( const SwLayoutFrame *pFrame )
{
SwLayoutFrame *pPrev =
(pFrame->GetPrev() && pFrame->GetPrev()->IsLayoutFrame()) ?
const_cast<SwLayoutFrame*>(static_cast<const SwLayoutFrame*>(pFrame->GetPrev())) : nullptr;
// #i39402# in case of an empty page
if(pPrev && !pPrev->ContainsContent())
pPrev = (pPrev->GetPrev() && pPrev->GetPrev()->IsLayoutFrame()) ?
static_cast<SwLayoutFrame*>(pPrev->GetPrev()) : nullptr;
return pPrev;
}
/**
* Returns the first/last Contentframe (controlled using the parameter fnPosPage)
* of the current/previous/next page (controlled using the parameter fnWhichPage).
*/
bool GetFrameInPage( const SwContentFrame *pCnt, SwWhichPage fnWhichPage,
SwPosPage fnPosPage, SwPaM *pPam )
{
//First find the requested page, at first the current, then the one which
//was requests through fnWichPage.
const SwLayoutFrame *pLayoutFrame = pCnt->FindPageFrame();
if ( !pLayoutFrame || (nullptr == (pLayoutFrame = (*fnWhichPage)(pLayoutFrame))) )
return false;
//Now the desired ContentFrame below the page
pCnt = (*fnPosPage)(pLayoutFrame);
if( nullptr == pCnt )
return false;
else
{
// repeated headlines in tables
if ( pCnt->IsInTab() && fnPosPage == GetFirstSub )
{
const SwTabFrame* pTab = pCnt->FindTabFrame();
if ( pTab->IsFollow() )
{
if ( pTab->IsInHeadline( *pCnt ) )
{
SwLayoutFrame* pRow = pTab->GetFirstNonHeadlineRow();
if ( pRow )
{
// We are in the first line of a follow table
// with repeated headings.
// To actually make a "real" move we take the first content
// of the next row
pCnt = pRow->ContainsContent();
if ( ! pCnt )
return false;
}
}
}
}
assert(pCnt->IsTextFrame());
SwTextFrame const*const pFrame(static_cast<const SwTextFrame*>(pCnt));
TextFrameIndex const nIdx((fnPosPage == GetFirstSub)
? pFrame->GetOffset()
: (pFrame->GetFollow())
? pFrame->GetFollow()->GetOffset() - TextFrameIndex(1)
: TextFrameIndex(pFrame->GetText().getLength()));
*pPam->GetPoint() = pFrame->MapViewToModelPos(nIdx);
return true;
}
}
static sal_uInt64 CalcDiff(const Point &rPt1, const Point &rPt2)
{
//Calculate the distance between the two points.
//'delta' X^2 + 'delta'Y^2 = 'distance'^2
sal_uInt64 dX = std::max( rPt1.X(), rPt2.X() ) -
std::min( rPt1.X(), rPt2.X() ),
dY = std::max( rPt1.Y(), rPt2.Y() ) -
std::min( rPt1.Y(), rPt2.Y() );
return (dX * dX) + (dY * dY);
}
/** Check if the point lies inside the page part in which also the ContentFrame lies.
*
* In this context header, page body, footer and footnote-container count as page part.
* This will suit the purpose that the ContentFrame which lies in the "right" page part will be
* accepted instead of one which doesn't lie there although his distance to the point is shorter.
*/
static const SwLayoutFrame* lcl_Inside( const SwContentFrame *pCnt, Point const & rPt )
{
const SwLayoutFrame* pUp = pCnt->GetUpper();
while( pUp )
{
if( pUp->IsPageBodyFrame() || pUp->IsFooterFrame() || pUp->IsHeaderFrame() )
{
if( rPt.Y() >= pUp->getFrameArea().Top() && rPt.Y() <= pUp->getFrameArea().Bottom() )
return pUp;
return nullptr;
}
if( pUp->IsFootnoteContFrame() )
return pUp->getFrameArea().Contains( rPt ) ? pUp : nullptr;
pUp = pUp->GetUpper();
}
return nullptr;
}
/** Search for the nearest Content to pass.
*
* Considers the previous, the current and the next page.
* If no content is found, the area gets expanded until one is found.
*
* @return The 'semantically correct' position inside the PrtArea of the found ContentFrame.
*/
const SwContentFrame *SwLayoutFrame::GetContentPos( Point& rPoint,
const bool bDontLeave,
const bool bBodyOnly,
SwCursorMoveState *pCMS,
const bool bDefaultExpand ) const
{
//Determine the first ContentFrame.
const SwLayoutFrame *pStart = (!bDontLeave && bDefaultExpand && GetPrev()) ?
static_cast<const SwLayoutFrame*>(GetPrev()) : this;
const SwContentFrame *pContent = pStart->ContainsContent();
if ( !pContent && (GetPrev() && !bDontLeave) )
pContent = ContainsContent();
if ( bBodyOnly )
while ( pContent && !pContent->IsInDocBody() )
pContent = pContent->GetNextContentFrame();
const SwContentFrame *pActual= pContent;
const SwLayoutFrame *pInside = nullptr;
sal_uInt16 nMaxPage = GetPhyPageNum() + (bDefaultExpand ? 1 : 0);
Point aPoint = rPoint;
sal_uInt64 nDistance = SAL_MAX_UINT64;
while ( true ) //A loop to be sure we always find one.
{
while ( pContent &&
((!bDontLeave || IsAnLower( pContent )) &&
(pContent->GetPhyPageNum() <= nMaxPage)) )
{
if ( pContent->getFrameArea().Width() &&
( !bBodyOnly || pContent->IsInDocBody() ) )
{
//If the Content lies in a protected area (cell, Footnote, section),
//we search the next Content which is not protected.
const SwContentFrame *pComp = pContent;
pContent = ::lcl_MissProtectedFrames( pContent, lcl_GetNxtCnt, false,
pCMS && pCMS->m_bSetInReadOnly, false );
if ( pComp != pContent )
continue;
if (!pContent->IsHiddenNow())
{
SwRect aContentFrame( pContent->UnionFrame() );
if ( aContentFrame.Contains( rPoint ) )
{
pActual = pContent;
aPoint = rPoint;
break;
}
//The distance from rPoint to the nearest Point of pContent
//will now be calculated.
Point aContentPoint( rPoint );
//First set the vertical position
if ( aContentFrame.Top() > aContentPoint.Y() )
aContentPoint.setY( aContentFrame.Top() );
else if ( aContentFrame.Bottom() < aContentPoint.Y() )
aContentPoint.setY( aContentFrame.Bottom() );
//Now the horizontal position
if ( aContentFrame.Left() > aContentPoint.X() )
aContentPoint.setX( aContentFrame.Left() );
else if ( aContentFrame.Right() < aContentPoint.X() )
aContentPoint.setX( aContentFrame.Right() );
// pInside is a page area in which the point lies. As soon
// as pInside != 0 only frames are accepted which are
// placed inside.
if( !pInside || ( pInside->IsAnLower( pContent ) &&
( !pContent->IsInFootnote() || pInside->IsFootnoteContFrame() ) ) )
{
const sal_uInt64 nDiff = ::CalcDiff(aContentPoint, rPoint);
bool bBetter = nDiff < nDistance; // This one is nearer
if( !pInside )
{
pInside = lcl_Inside( pContent, rPoint );
if( pInside ) // In the "right" page area
bBetter = true;
}
if( bBetter )
{
aPoint = aContentPoint;
nDistance = nDiff;
pActual = pContent;
}
}
}
}
pContent = pContent->GetNextContentFrame();
if ( bBodyOnly )
while ( pContent && !pContent->IsInDocBody() )
pContent = pContent->GetNextContentFrame();
}
if ( !pActual )
{ //If we not yet found one we have to expand the searched
//area, sometime we will find one!
//MA 1997-01-09: Opt for many empty pages - if we only search inside
//the body, we can expand the searched area sufficiently in one step.
if ( bBodyOnly )
{
while ( !pContent && pStart->GetPrev() )
{
++nMaxPage;
if( !pStart->GetPrev()->IsLayoutFrame() )
return nullptr;
pStart = static_cast<const SwLayoutFrame*>(pStart->GetPrev());
if( pStart->IsInDocBody() )
pContent = pStart->ContainsContent();
else
{
const SwPageFrame *pPage = pStart->FindPageFrame();
if( !pPage )
return nullptr;
pContent = pPage->FindFirstBodyContent();
}
}
if ( !pContent ) // Somewhere down the road we have to start with one!
{
const SwPageFrame *pPage = pStart->FindPageFrame();
if( !pPage )
return nullptr;
pContent = pPage->GetUpper()->ContainsContent();
while ( pContent && !pContent->IsInDocBody() )
pContent = pContent->GetNextContentFrame();
if ( !pContent )
return nullptr; // There is no document content yet!
}
}
else
{
++nMaxPage;
if ( pStart->GetPrev() )
{
if( !pStart->GetPrev()->IsLayoutFrame() )
return nullptr;
pStart = static_cast<const SwLayoutFrame*>(pStart->GetPrev());
pContent = pStart->ContainsContent();
}
else // Somewhere down the road we have to start with one!
{
const SwPageFrame *pPage = pStart->FindPageFrame();
if( !pPage )
return nullptr;
pContent = pPage->GetUpper()->ContainsContent();
}
}
pActual = pContent;
}
else
break;
}
OSL_ENSURE( pActual, "no Content found." );
OSL_ENSURE( !bBodyOnly || pActual->IsInDocBody(), "Content not in Body." );
//Special case for selecting tables not in repeated TableHeadlines.
if ( pActual->IsInTab() && pCMS && pCMS->m_eState == CursorMoveState::TableSel )
{
const SwTabFrame *pTab = pActual->FindTabFrame();
if ( pTab->IsFollow() && pTab->IsInHeadline( *pActual ) )
{
pCMS->m_bStop = true;
return nullptr;
}
}
//A small correction at the first/last
Size aActualSize( pActual->getFramePrintArea().SSize() );
if ( aActualSize.Height() > pActual->GetUpper()->getFramePrintArea().Height() )
aActualSize.setHeight( pActual->GetUpper()->getFramePrintArea().Height() );
SwRectFnSet aRectFnSet(pActual);
if ( !pActual->GetPrev() &&
aRectFnSet.YDiff( aRectFnSet.GetPrtTop(*pActual),
aRectFnSet.IsVert() ? rPoint.X() : rPoint.Y() ) > 0 )
{
aPoint.setY( pActual->getFrameArea().Top() + pActual->getFramePrintArea().Top() );
aPoint.setX( pActual->getFrameArea().Left() +
( pActual->IsRightToLeft() || aRectFnSet.IsVert() ?
pActual->getFramePrintArea().Right() :
pActual->getFramePrintArea().Left() ) );
}
else if ( !pActual->GetNext() &&
aRectFnSet.YDiff( aRectFnSet.GetPrtBottom(*pActual),
aRectFnSet.IsVert() ? rPoint.X() : rPoint.Y() ) < 0 )
{
aPoint.setY( pActual->getFrameArea().Top() + pActual->getFramePrintArea().Bottom() );
aPoint.setX( pActual->getFrameArea().Left() +
( pActual->IsRightToLeft() || aRectFnSet.IsVert() ?
pActual->getFramePrintArea().Left() :
pActual->getFramePrintArea().Right() ) );
}
//Bring the Point into the PrtArea
const SwRect aRect( pActual->getFrameArea().Pos() + pActual->getFramePrintArea().Pos(),
aActualSize );
if ( aPoint.Y() < aRect.Top() )
aPoint.setY( aRect.Top() );
else if ( aPoint.Y() > aRect.Bottom() )
aPoint.setY( aRect.Bottom() );
if ( aPoint.X() < aRect.Left() )
aPoint.setX( aRect.Left() );
else if ( aPoint.X() > aRect.Right() )
aPoint.setX( aRect.Right() );
rPoint = aPoint;
return pActual;
}
/** Same as SwLayoutFrame::GetContentPos(). Specialized for fields and border. */
void SwPageFrame::GetContentPosition( const Point &rPt, SwPosition &rPos ) const
{
//Determine the first ContentFrame.
const SwContentFrame *pContent = ContainsContent();
if ( pContent )
{
//Look back one more (if possible).
const SwContentFrame *pTmp = pContent->GetPrevContentFrame();
while ( pTmp && !pTmp->IsInDocBody() )
pTmp = pTmp->GetPrevContentFrame();
if ( pTmp )
pContent = pTmp;
}
else
pContent = GetUpper()->ContainsContent();
const SwContentFrame *pAct = pContent;
Point aAct = rPt;
sal_uInt64 nDist = SAL_MAX_UINT64;
while ( pContent )
{
SwRect aContentFrame( pContent->UnionFrame() );
if ( aContentFrame.Contains( rPt ) )
{
//This is the nearest one.
pAct = pContent;
break;
}
//Calculate the distance from rPt to the nearest point of pContent.
Point aPoint( rPt );
//Calculate the vertical position first
if ( aContentFrame.Top() > rPt.Y() )
aPoint.setY( aContentFrame.Top() );
else if ( aContentFrame.Bottom() < rPt.Y() )
aPoint.setY( aContentFrame.Bottom() );
//And now the horizontal position
if ( aContentFrame.Left() > rPt.X() )
aPoint.setX( aContentFrame.Left() );
else if ( aContentFrame.Right() < rPt.X() )
aPoint.setX( aContentFrame.Right() );
const sal_uInt64 nDiff = ::CalcDiff( aPoint, rPt );
if ( nDiff < nDist )
{
aAct = aPoint;
nDist = nDiff;
pAct = pContent;
}
else if ( aContentFrame.Top() > getFrameArea().Bottom() )
//In terms of fields, it's not possible to be closer any more!
break;
pContent = pContent->GetNextContentFrame();
while ( pContent && !pContent->IsInDocBody() )
pContent = pContent->GetNextContentFrame();
}
//Bring the point into the PrtArea.
assert(pAct);
const SwRect aRect( pAct->getFrameArea().Pos() + pAct->getFramePrintArea().Pos(), pAct->getFramePrintArea().SSize() );
if ( aAct.Y() < aRect.Top() )
aAct.setY( aRect.Top() );
else if ( aAct.Y() > aRect.Bottom() )
aAct.setY( aRect.Bottom() );
if ( aAct.X() < aRect.Left() )
aAct.setX( aRect.Left() );
else if ( aAct.X() > aRect.Right() )
aAct.setX( aRect.Right() );
if (!pAct->isFrameAreaDefinitionValid() ||
(pAct->IsTextFrame() && !static_cast<SwTextFrame const*>(pAct)->HasPara()))
{
// ContentFrame not formatted -> always on node-beginning
// tdf#100635 also if the SwTextFrame would require reformatting,
// which is unwanted in case this is called from text formatting code
rPos = static_cast<SwTextFrame const*>(pAct)->MapViewToModelPos(TextFrameIndex(0));
}
else
{
SwCursorMoveState aTmpState( CursorMoveState::SetOnlyText );
pAct->GetModelPositionForViewPoint( &rPos, aAct, &aTmpState );
}
}
/** Search the nearest Content to the passed point.
*
* Only search inside the BodyText.
* @note Only the nearest vertically one will be searched.
* @note JP 11.10.2001: only in tables we try to find the right column - Bug 72294
*/
Point SwRootFrame::GetNextPrevContentPos( const Point& rPoint, bool bNext ) const
{
vcl::RenderContext* pRenderContext = GetCurrShell() ? GetCurrShell()->GetOut() : nullptr;
// #123110# - disable creation of an action by a callback
// event during processing of this method. Needed because formatting is
// triggered by this method.
DisableCallbackAction aDisableCallbackAction(const_cast<SwRootFrame&>(*this));
//Search the first ContentFrame and his successor in the body area.
//To be efficient (and not formatting too much) we'll start at the correct
//page.
const SwLayoutFrame *pPage = static_cast<const SwLayoutFrame*>(Lower());
if( pPage )
while( pPage->GetNext() && pPage->getFrameArea().Bottom() < rPoint.Y() )
pPage = static_cast<const SwLayoutFrame*>(pPage->GetNext());
const SwContentFrame *pCnt = pPage ? pPage->ContainsContent() : ContainsContent();
while ( pCnt && !pCnt->IsInDocBody() )
pCnt = pCnt->GetNextContentFrame();
if ( !pCnt )
return Point( 0, 0 );
pCnt->Calc(pRenderContext);
if( !bNext )
{
// As long as the point lies before the first ContentFrame and there are
// still precedent pages I'll go to the next page.
assert(pPage);
while ( rPoint.Y() < pCnt->getFrameArea().Top() && pPage->GetPrev() )
{
pPage = static_cast<const SwLayoutFrame*>(pPage->GetPrev());
pCnt = pPage->ContainsContent();
while ( !pCnt )
{
pPage = static_cast<const SwLayoutFrame*>(pPage->GetPrev());
if ( pPage )
pCnt = pPage->ContainsContent();
else
{
pCnt = ContainsContent();
return pCnt ? pCnt->UnionFrame().Pos() : Point();
}
}
pCnt->Calc(pRenderContext);
}
}
//Does the point lie above the first ContentFrame?
if ( rPoint.Y() < pCnt->getFrameArea().Top() && !lcl_IsInRepeatedHeadline( pCnt ) )
return pCnt->UnionFrame().Pos();
Point aRet(0, 0);
do
{
//Does the point lie in the current ContentFrame?
SwRect aContentFrame( pCnt->UnionFrame() );
if ( aContentFrame.Contains( rPoint ) && !lcl_IsInRepeatedHeadline( pCnt ))
{
aRet = rPoint;
break;
}
//Is the current one the last ContentFrame?
//If the next ContentFrame lies behind the point, then the current on is the
//one we searched.
const SwContentFrame *pNxt = pCnt->GetNextContentFrame();
while ( pNxt && !pNxt->IsInDocBody() )
pNxt = pNxt->GetNextContentFrame();
//Does the point lie behind the last ContentFrame?
if ( !pNxt )
{
aRet = Point( aContentFrame.Right(), aContentFrame.Bottom() );
break;
}
//If the next ContentFrame lies behind the point then it is the one we
//searched.
const SwTabFrame* pTFrame;
pNxt->Calc(pRenderContext);
if( pNxt->getFrameArea().Top() > rPoint.Y() &&
!lcl_IsInRepeatedHeadline( pCnt, &pTFrame ) &&
( !pTFrame || pNxt->getFrameArea().Left() > rPoint.X() ))
{
if (bNext)
aRet = pNxt->getFrameArea().Pos();
else
aRet = Point( aContentFrame.Right(), aContentFrame.Bottom() );
break;
}
pCnt = pNxt;
}
while (pCnt);
return aRet;
}
/** Returns the absolute document position of the desired page.
*
* Formatting is done only as far as needed and only if bFormat=true.
* Pos is set to the one of the last page, if the page number was chosen too big.
*
* @return Null, if the operation failed.
*/
Point SwRootFrame::GetPagePos( sal_uInt16 nPageNum ) const
{
OSL_ENSURE( Lower() && Lower()->IsPageFrame(), "No page available." );
const SwPageFrame *pPage = static_cast<const SwPageFrame*>(Lower());
while ( true )
{
if ( pPage->GetPhyPageNum() >= nPageNum || !pPage->GetNext() )
break;
pPage = static_cast<const SwPageFrame*>(pPage->GetNext());
}
return pPage->getFrameArea().Pos();
}
/** get page frame by physical page number
*
* @return pointer to the page frame with the given physical page number
*/
SwPageFrame* SwRootFrame::GetPageByPageNum( sal_uInt16 _nPageNum ) const
{
const SwPageFrame* pPageFrame = static_cast<const SwPageFrame*>( Lower() );
while ( pPageFrame && pPageFrame->GetPhyPageNum() < _nPageNum )
{
pPageFrame = static_cast<const SwPageFrame*>( pPageFrame->GetNext() );
}
if ( pPageFrame && pPageFrame->GetPhyPageNum() == _nPageNum )
{
return const_cast<SwPageFrame*>( pPageFrame );
}
else
{
return nullptr;
}
}
/**
* @return true, when the given physical pagenumber doesn't exist or this page is an empty page.
*/
bool SwRootFrame::IsDummyPage( sal_uInt16 nPageNum ) const
{
if( !Lower() || !nPageNum || nPageNum > GetPageNum() )
return true;
const SwPageFrame *pPage = static_cast<const SwPageFrame*>(Lower());
while( pPage && nPageNum < pPage->GetPhyPageNum() )
pPage = static_cast<const SwPageFrame*>(pPage->GetNext());
return !pPage || pPage->IsEmptyPage();
}
/** Is the Frame or rather the Section in which it lies protected?
*
* Also Fly in Fly in ... and Footnotes
*/
bool SwFrame::IsProtected() const
{
if (IsTextFrame())
{
const SwDoc *pDoc = &static_cast<const SwTextFrame*>(this)->GetDoc();
bool isFormProtected=pDoc->GetDocumentSettingManager().get(DocumentSettingId::PROTECT_FORM );
if (isFormProtected)
{
return false; // TODO a hack for now, well deal with it later, I we return true here we have a "double" locking
}
}
//The Frame can be protected in borders, cells or sections.
//Also goes up FlyFrames recursive and from footnote to anchor.
const SwFrame *pFrame = this;
do
{
if (pFrame->IsTextFrame())
{ // sw_redlinehide: redlines can't overlap section nodes, so any node will do
if (static_cast<SwTextFrame const*>(pFrame)->GetTextNodeFirst()->IsInProtectSect())
{
return true;
}
}
else if ( pFrame->IsContentFrame() )
{
assert(pFrame->IsNoTextFrame());
if (static_cast<const SwNoTextFrame*>(pFrame)->GetNode() &&
static_cast<const SwNoTextFrame*>(pFrame)->GetNode()->IsInProtectSect())
{
return true;
}
}
else
{
if ( static_cast<const SwLayoutFrame*>(pFrame)->GetFormat() &&
static_cast<const SwLayoutFrame*>(pFrame)->GetFormat()->
GetProtect().IsContentProtected() )
return true;
if ( pFrame->IsCoveredCell() )
return true;
}
if ( pFrame->IsFlyFrame() )
{
//In a chain the protection of the content can be specified by the
//master of the chain.
if ( static_cast<const SwFlyFrame*>(pFrame)->GetPrevLink() )
{
const SwFlyFrame *pMaster = static_cast<const SwFlyFrame*>(pFrame);
do
{ pMaster = pMaster->GetPrevLink();
} while ( pMaster->GetPrevLink() );
if ( pMaster->IsProtected() )
return true;
}
pFrame = static_cast<const SwFlyFrame*>(pFrame)->GetAnchorFrame();
}
else if ( pFrame->IsFootnoteFrame() )
pFrame = static_cast<const SwFootnoteFrame*>(pFrame)->GetRef();
else
pFrame = pFrame->GetUpper();
} while ( pFrame );
return false;
}
// virtual
bool SwFrame::IsHiddenNow() const
{
if (const auto* pSectFrame = FindSctFrame())
return pSectFrame->IsHiddenNow();
return false;
}
void SwFrame::MakeValidZeroHeight()
{
SwRectFnSet aRectFnSet(this);
{
SwFrameAreaDefinition::FramePrintAreaWriteAccess area(*this);
aRectFnSet.SetHeight(area, 0);
}
ShrinkFrame(aRectFnSet.GetHeight(getFrameArea()));
if (IsLayoutFrame()) // ShrinkFrame might do nothing!
{
SwFrameAreaDefinition::FrameAreaWriteAccess area(*this);
aRectFnSet.SetHeight(area, 0);
}
setFrameAreaSizeValid(true);
setFramePrintAreaValid(true);
setFrameAreaPositionValid(false);
}
/** @return the physical page number */
sal_uInt16 SwFrame::GetPhyPageNum() const
{
const SwPageFrame *pPage = FindPageFrame();
return pPage ? pPage->GetPhyPageNum() : 0;
}
/** Decides if the page want to be a right page or not.
*
* If the first content of the page has a page descriptor, we take the follow
* of the page descriptor of the last not empty page. If this descriptor allows
* only right(left) pages and the page isn't an empty page then it wants to be
* such right(left) page. If the descriptor allows right and left pages, we
* look for a number offset in the first content. If there is one, odd number
* results right pages (or left pages if document starts with even number),
* even number results left pages (or right pages if document starts with even
* number).
* If there is no number offset, we take the physical page number instead,
* but a previous empty page doesn't count.
*/
bool SwFrame::WannaRightPage() const
{
const SwPageFrame *pPage = FindPageFrame();
if ( !pPage || !pPage->GetUpper() )
return true;
const SwFrame *pFlow = pPage->FindFirstBodyContent();
const SwPageDesc *pDesc = nullptr;
::std::optional<sal_uInt16> oPgNum;
if ( pFlow )
{
if ( pFlow->IsInTab() )
pFlow = pFlow->FindTabFrame();
const SwFlowFrame *pTmp = SwFlowFrame::CastFlowFrame( pFlow );
if ( !pTmp->IsFollow() )
{
const SwFormatPageDesc& rPgDesc = pFlow->GetPageDescItem();
pDesc = rPgDesc.GetPageDesc();
oPgNum = rPgDesc.GetNumOffset();
}
}
if ( !pDesc )
{
const SwPageFrame* pPrv = static_cast<const SwPageFrame*>(pPage->GetPrev());
if( pPrv && pPrv->IsEmptyPage() )
pPrv = static_cast<const SwPageFrame*>(pPrv->GetPrev());
if( pPrv )
pDesc = pPrv->GetPageDesc()->GetFollow();
else
{
const SwDoc* pDoc = pPage->GetFormat()->GetDoc();
pDesc = &pDoc->GetPageDesc( 0 );
}
}
OSL_ENSURE( pDesc, "No pagedescriptor" );
bool isRightPage;
if( oPgNum )
isRightPage = sw::IsRightPageByNumber(*mpRoot, *oPgNum);
else
{
isRightPage = pPage->OnRightPage();
if( pPage->GetPrev() && static_cast<const SwPageFrame*>(pPage->GetPrev())->IsEmptyPage() )
isRightPage = !isRightPage;
}
if( !pPage->IsEmptyPage() )
{
assert(pDesc && "No pagedescriptor");
if( !pDesc->GetRightFormat() )
isRightPage = false;
else if( !pDesc->GetLeftFormat() )
isRightPage = true;
}
return isRightPage;
}
bool SwFrame::OnFirstPage() const
{
bool bRet = false;
const SwPageFrame *pPage = FindPageFrame();
if (pPage)
{
const SwPageFrame* pPrevFrame = dynamic_cast<const SwPageFrame*>(pPage->GetPrev());
if (pPrevFrame)
{
// first page of layout may be empty page, but only if it starts with "Left Page" style
const SwPageDesc* pDesc = pPage->GetPageDesc();
bRet = pPrevFrame->GetPageDesc() != pDesc;
}
else
bRet = true;
}
return bRet;
}
void SwFrame::Calc(vcl::RenderContext* pRenderContext) const
{
if ( !isFrameAreaPositionValid() || !isFramePrintAreaValid() || !isFrameAreaSizeValid() )
{
const_cast<SwFrame*>(this)->PrepareMake(pRenderContext);
}
}
Point SwFrame::GetRelPos() const
{
Point aRet( getFrameArea().Pos() );
// here we cast since SwLayoutFrame is declared only as forwarded
aRet -= GetUpper()->getFramePrintArea().Pos();
aRet -= GetUpper()->getFrameArea().Pos();
return aRet;
}
/** @return the virtual page number with the offset. */
sal_uInt16 SwFrame::GetVirtPageNum() const
{
const SwPageFrame *pPage = FindPageFrame();
if ( !pPage || !pPage->GetUpper() )
return 0;
sal_uInt16 nPhyPage = pPage->GetPhyPageNum();
const SwRootFrame* pRootFrame = static_cast<const SwRootFrame*>(pPage->GetUpper());
if ( !pRootFrame->IsVirtPageNum() )
return nPhyPage;
//Search the nearest section using the virtual page number.
const SwFrame *pFoundFrame = nullptr;
const SwPageFrame* pPageFrameIter = pPage;
while (pPageFrameIter)
{
const SwContentFrame* pContentFrame = pPageFrameIter->FindFirstBodyContent();
if (pContentFrame)
{
const SwFormatPageDesc& rFormatPageDesc = pContentFrame->GetPageDescItem();
if ( rFormatPageDesc.GetNumOffset() && rFormatPageDesc.GetDefinedIn() )
{
const sw::BroadcastingModify* pMod = rFormatPageDesc.GetDefinedIn();
sw::VirtPageNumHint aHint(pPage);
pMod->CallSwClientNotify(aHint);
if(aHint.GetPage())
{
pFoundFrame = aHint.GetFrame();
break;
}
}
}
pPageFrameIter = static_cast<const SwPageFrame*>(pPageFrameIter->GetPrev());
}
if ( pFoundFrame )
{
::std::optional<sal_uInt16> oNumOffset = pFoundFrame->GetPageDescItem().GetNumOffset();
if (oNumOffset)
{
return nPhyPage - pFoundFrame->GetPhyPageNum() + *oNumOffset;
}
else
{
return nPhyPage - pFoundFrame->GetPhyPageNum();
}
}
return nPhyPage;
}
/** Determines and sets those cells which are enclosed by the selection. */
bool SwRootFrame::MakeTableCursors( SwTableCursor& rTableCursor )
{
//Find Union-Rects and tables (Follows) of the selection.
OSL_ENSURE( rTableCursor.GetPointContentNode() && rTableCursor.GetMarkContentNode(),
"Tabselection not on Cnt." );
bool bRet = false;
// For new table models there's no need to ask the layout...
if( rTableCursor.NewTableSelection() )
return true;
Point aPtPt, aMkPt;
{
SwShellCursor* pShCursor = dynamic_cast<SwShellCursor*>(&rTableCursor);
if( pShCursor )
{
aPtPt = pShCursor->GetPtPos();
aMkPt = pShCursor->GetMkPos();
}
}
// #151012# Made code robust here
const SwContentNode* pTmpStartNode = rTableCursor.GetPointContentNode();
const SwContentNode* pTmpEndNode = rTableCursor.GetMarkContentNode();
std::pair<Point, bool> tmp(aPtPt, false);
const SwFrame *const pTmpStartFrame = pTmpStartNode ? pTmpStartNode->getLayoutFrame(this, nullptr, &tmp) : nullptr;
tmp.first = aMkPt;
const SwFrame *const pTmpEndFrame = pTmpEndNode ? pTmpEndNode->getLayoutFrame(this, nullptr, &tmp) : nullptr;
const SwLayoutFrame* pStart = pTmpStartFrame ? pTmpStartFrame->GetUpper() : nullptr;
const SwLayoutFrame* pEnd = pTmpEndFrame ? pTmpEndFrame->GetUpper() : nullptr;
OSL_ENSURE( pStart && pEnd, "MakeTableCursors: Good to have the code robust here!" );
/* #109590# Only change table boxes if the frames are
valid. Needed because otherwise the table cursor after moving
table cells by dnd resulted in an empty tables cursor. */
if ( pStart && pEnd && pStart->isFrameAreaDefinitionValid() && pEnd->isFrameAreaDefinitionValid())
{
SwSelUnions aUnions;
::MakeSelUnions( aUnions, pStart, pEnd );
SwSelBoxes aNew;
const bool bReadOnlyAvailable = rTableCursor.IsReadOnlyAvailable();
for (SwSelUnion & rUnion : aUnions)
{
const SwTabFrame *pTable = rUnion.GetTable();
// Skip any repeated headlines in the follow:
const SwLayoutFrame* pRow = pTable->IsFollow() ?
pTable->GetFirstNonHeadlineRow() :
static_cast<const SwLayoutFrame*>(pTable->Lower());
while ( pRow )
{
if ( pRow->getFrameArea().Overlaps( rUnion.GetUnion() ) )
{
const SwLayoutFrame *pCell = pRow->FirstCell();
while ( pCell && pRow->IsAnLower( pCell ) )
{
OSL_ENSURE( pCell->IsCellFrame(), "Frame without cell" );
if( IsFrameInTableSel( rUnion.GetUnion(), pCell ) &&
(bReadOnlyAvailable ||
!pCell->GetFormat()->GetProtect().IsContentProtected()))
{
SwTableBox* pInsBox = const_cast<SwTableBox*>(
static_cast<const SwCellFrame*>(pCell)->GetTabBox());
aNew.insert( pInsBox );
}
if ( pCell->GetNext() )
{
pCell = static_cast<const SwLayoutFrame*>(pCell->GetNext());
if ( pCell->Lower() && pCell->Lower()->IsRowFrame() )
pCell = pCell->FirstCell();
}
else
{
const SwLayoutFrame* pLastCell = pCell;
do
{
pCell = pCell->GetNextLayoutLeaf();
} while ( pCell && pLastCell->IsAnLower( pCell ) );
// For sections with columns
if( pCell && pCell->IsInTab() )
{
while( !pCell->IsCellFrame() )
{
pCell = pCell->GetUpper();
assert(pCell && "Where's my cell?");
}
}
}
}
}
pRow = static_cast<const SwLayoutFrame*>(pRow->GetNext());
}
}
rTableCursor.ActualizeSelection( aNew );
bRet = true;
}
return bRet;
}
static void Sub( SwRegionRects& rRegion, const SwRect& rRect )
{
if( rRect.Width() > 1 && rRect.Height() > 1 &&
rRect.Overlaps( rRegion.GetOrigin() ))
rRegion -= rRect;
}
static void Add( SwRegionRects& rRegion, const SwRect& rRect )
{
if( rRect.Width() > 1 && rRect.Height() > 1 )
rRegion += rRect;
}
/*
* The following situations can happen (simplified version, before
* CJK/CTL features were added):
* 1. Start and end in same line of text and in the same frame
* -> one rectangle out of start and end; and we're okay
* 2. Start and end in same frame
* -> expand start to the right, end to the left and if more than two
* lines of text are involved - calculate the in-between area
* 3. Start and end in different frames
* -> expand start to the right until frame-end, calculate Rect
* expand end to the left until frame-start, calculate Rect
* and if more than two frames are involved add the PrtArea of all
* frames which lie in between
*
* Big reorganization because of FlyFrame - those need to be excluded.
* Exceptions: - The Fly in which the selection took place (if it took place
* in a Fly)
* - The Flys which are below the text (in z-order)
* - The Flys which are anchored to somewhere inside the selection.
* Functioning: First a SwRegion with a root gets initialized.
* Out of the region the selected areas are cut out. The
* section gets compressed and finally inverted and thereby the
* rectangles are available for highlighting.
* In the end the Flys are cut out of the region.
*/
void SwRootFrame::CalcFrameRects(SwShellCursor const& rCursor, SwRects & rRects, RectsMode const eMode)
{
auto [pStartPos, pEndPos] = rCursor.StartEnd(); // SwPosition*
SwViewShell *pSh = GetCurrShell();
bool bIgnoreVisArea = true;
if (pSh)
bIgnoreVisArea = pSh->GetViewOptions()->IsPDFExport() || comphelper::LibreOfficeKit::isActive();
SwRegionRects aRegion( !bIgnoreVisArea ?
pSh->VisArea() :
getFrameArea() );
if( !pStartPos->GetNode().IsContentNode() ||
!pStartPos->GetNode().GetContentNode()->getLayoutFrame(this) ||
( pStartPos->GetNode() != pEndPos->GetNode() &&
( !pEndPos->GetNode().IsContentNode() ||
!pEndPos->GetNode().GetContentNode()->getLayoutFrame(this) ) ) )
{
return;
}
DisableCallbackAction a(*this); // the GetCharRect below may format
//First obtain the ContentFrames for the start and the end - those are needed
//anyway.
std::pair<Point, bool> tmp(rCursor.GetSttPos(), true);
SwContentFrame* pStartFrame = pStartPos->GetNode().
GetContentNode()->getLayoutFrame(this, pStartPos, &tmp);
tmp.first = rCursor.GetEndPos();
SwContentFrame* pEndFrame = pEndPos->GetNode().
GetContentNode()->getLayoutFrame(this, pEndPos, &tmp);
assert(pStartFrame && pEndFrame && "No ContentFrames found.");
//tdf#119224 start and end are expected to exist for the scope of this function
SwFrameDeleteGuard aStartFrameGuard(pStartFrame), aEndFrameGuard(pEndFrame);
// Do not subtract FlyFrames that contain selected Frames.
SwSortedObjs aSortObjs;
if ( pStartFrame->IsInFly() )
{
SwAnchoredObject* pObj = pStartFrame->FindFlyFrame();
OSL_ENSURE( pObj, "No Start Object." );
if (pObj) aSortObjs.Insert( *pObj );
SwAnchoredObject* pObj2 = pEndFrame->FindFlyFrame();
OSL_ENSURE( pObj2, "SwRootFrame::CalcFrameRects(..) - FlyFrame missing - looks like an invalid selection" );
if ( pObj2 != nullptr && pObj2 != pObj )
{
aSortObjs.Insert(*pObj2);
}
}
// if a selection which is not allowed exists, we correct what is not
// allowed (header/footer/table-headline start/end on different pages).
do { // middle check loop
const SwLayoutFrame* pSttLFrame = pStartFrame->GetUpper();
const SwFrameType cHdFtTableHd = SwFrameType::Header | SwFrameType::Footer | SwFrameType::Tab;
while( pSttLFrame &&
! (cHdFtTableHd & pSttLFrame->GetType() ))
pSttLFrame = pSttLFrame->GetUpper();
if( !pSttLFrame )
break;
const SwLayoutFrame* pEndLFrame = pEndFrame->GetUpper();
while( pEndLFrame &&
! (cHdFtTableHd & pEndLFrame->GetType() ))
pEndLFrame = pEndLFrame->GetUpper();
if( !pEndLFrame )
break;
OSL_ENSURE( pEndLFrame->GetType() == pSttLFrame->GetType(),
"Selection over different content" );
switch( pSttLFrame->GetType() )
{
case SwFrameType::Header:
case SwFrameType::Footer:
// On different pages? Then always on the start-page
if( pEndLFrame->FindPageFrame() != pSttLFrame->FindPageFrame() )
{
// Set end- to the start-ContentFrame
if( pStartPos == rCursor.GetPoint() )
pEndFrame = pStartFrame;
else
pStartFrame = pEndFrame;
}
break;
case SwFrameType::Tab:
// On different pages? Then check for table-headline
{
const SwTabFrame* pTabFrame = static_cast<const SwTabFrame*>(pSttLFrame);
if( ( pTabFrame->GetFollow() ||
static_cast<const SwTabFrame*>(pEndLFrame)->GetFollow() ) &&
pTabFrame->GetTable()->GetRowsToRepeat() > 0 &&
pTabFrame->GetLower() != static_cast<const SwTabFrame*>(pEndLFrame)->GetLower() &&
( lcl_IsInRepeatedHeadline( pStartFrame ) ||
lcl_IsInRepeatedHeadline( pEndFrame ) ) )
{
// Set end- to the start-ContentFrame
if( pStartPos == rCursor.GetPoint() )
pEndFrame = pStartFrame;
else
pStartFrame = pEndFrame;
}
}
break;
default: break;
}
} while( false );
SwCursorMoveState aTmpState( CursorMoveState::NONE );
aTmpState.m_b2Lines = true;
aTmpState.m_bNoScroll = true;
aTmpState.m_nCursorBidiLevel = pStartFrame->IsRightToLeft() ? 1 : 0;
//ContentRects to Start- and EndFrames.
SwRect aStRect, aEndRect;
pStartFrame->GetCharRect( aStRect, *pStartPos, &aTmpState );
std::unique_ptr<Sw2LinesPos> pSt2Pos = std::move(aTmpState.m_p2Lines);
aTmpState.m_nCursorBidiLevel = pEndFrame->IsRightToLeft() ? 1 : 0;
pEndFrame->GetCharRect( aEndRect, *pEndPos, &aTmpState );
std::unique_ptr<Sw2LinesPos> pEnd2Pos = std::move(aTmpState.m_p2Lines);
SwRect aStFrame ( pStartFrame->UnionFrame( true ) );
aStFrame.Intersection( pStartFrame->GetPaintArea() );
SwRect aEndFrame( pStartFrame == pEndFrame ? aStFrame : pEndFrame->UnionFrame( true ) );
if( pStartFrame != pEndFrame )
{
aEndFrame.Intersection( pEndFrame->GetPaintArea() );
}
SwRectFnSet aRectFnSet(pStartFrame);
const bool bR2L = pStartFrame->IsRightToLeft();
const bool bEndR2L = pEndFrame->IsRightToLeft();
const bool bB2T = pStartFrame->IsVertLRBT();
// If there's no doubleline portion involved or start and end are both
// in the same doubleline portion, all works fine, but otherwise
// we need the following...
if( pSt2Pos != pEnd2Pos && ( !pSt2Pos || !pEnd2Pos ||
pSt2Pos->aPortion != pEnd2Pos->aPortion ) )
{
// If we have a start(end) position inside a doubleline portion
// the surrounded part of the doubleline portion is subtracted
// from the region and the aStRect(aEndRect) is set to the
// end(start) of the doubleline portion.
if( pSt2Pos )
{
SwRect aTmp( aStRect );
// BiDi-Portions are swimming against the current.
const bool bPorR2L = ( MultiPortionType::BIDI == pSt2Pos->nMultiType ) ?
! bR2L :
bR2L;
if( MultiPortionType::BIDI == pSt2Pos->nMultiType &&
aRectFnSet.GetWidth(pSt2Pos->aPortion2) )
{
// nested bidi portion
tools::Long nRightAbs = aRectFnSet.GetRight(pSt2Pos->aPortion);
nRightAbs -= aRectFnSet.GetLeft(pSt2Pos->aPortion2);
tools::Long nLeftAbs = nRightAbs - aRectFnSet.GetWidth(pSt2Pos->aPortion2);
aRectFnSet.SetRight( aTmp, nRightAbs );
if ( ! pEnd2Pos || pEnd2Pos->aPortion != pSt2Pos->aPortion )
{
SwRect aTmp2( pSt2Pos->aPortion );
aRectFnSet.SetRight( aTmp2, nLeftAbs );
aTmp2.Intersection( aEndFrame );
Sub( aRegion, aTmp2 );
}
}
else
{
if( bPorR2L )
aRectFnSet.SetLeft( aTmp, aRectFnSet.GetLeft(pSt2Pos->aPortion) );
else
aRectFnSet.SetRight( aTmp, aRectFnSet.GetRight(pSt2Pos->aPortion) );
}
if( MultiPortionType::ROT_90 == pSt2Pos->nMultiType ||
aRectFnSet.GetTop(pSt2Pos->aPortion) ==
aRectFnSet.GetTop(aTmp) )
{
aRectFnSet.SetTop( aTmp, aRectFnSet.GetTop(pSt2Pos->aLine) );
}
aTmp.Intersection( aStFrame );
Sub( aRegion, aTmp );
SwTwips nTmp = aRectFnSet.GetBottom(pSt2Pos->aLine);
if( MultiPortionType::ROT_90 != pSt2Pos->nMultiType &&
aRectFnSet.BottomDist( aStRect, nTmp ) > 0 )
{
aRectFnSet.SetTop( aTmp, aRectFnSet.GetBottom(aTmp) );
aRectFnSet.SetBottom( aTmp, nTmp );
if( aRectFnSet.BottomDist( aStRect, aRectFnSet.GetBottom(pSt2Pos->aPortion) ) > 0 )
{
if( bPorR2L )
aRectFnSet.SetRight( aTmp, aRectFnSet.GetRight(pSt2Pos->aPortion) );
else
aRectFnSet.SetLeft( aTmp, aRectFnSet.GetLeft(pSt2Pos->aPortion) );
}
aTmp.Intersection( aStFrame );
Sub( aRegion, aTmp );
}
aStRect = pSt2Pos->aLine;
aRectFnSet.SetLeft( aStRect, bR2L ?
aRectFnSet.GetLeft(pSt2Pos->aPortion) :
aRectFnSet.GetRight(pSt2Pos->aPortion) );
aRectFnSet.SetWidth( aStRect, 1 );
}
if( pEnd2Pos )
{
SwRectFnSet fnRectX(pEndFrame);
SwRect aTmp( aEndRect );
// BiDi-Portions are swimming against the current.
const bool bPorR2L = ( MultiPortionType::BIDI == pEnd2Pos->nMultiType ) ?
! bEndR2L :
bEndR2L;
if( MultiPortionType::BIDI == pEnd2Pos->nMultiType &&
fnRectX.GetWidth(pEnd2Pos->aPortion2) )
{
// nested bidi portion
tools::Long nRightAbs = fnRectX.GetRight(pEnd2Pos->aPortion);
nRightAbs = nRightAbs - fnRectX.GetLeft(pEnd2Pos->aPortion2);
tools::Long nLeftAbs = nRightAbs - fnRectX.GetWidth(pEnd2Pos->aPortion2);
fnRectX.SetLeft( aTmp, nLeftAbs );
if ( ! pSt2Pos || pSt2Pos->aPortion != pEnd2Pos->aPortion )
{
SwRect aTmp2( pEnd2Pos->aPortion );
fnRectX.SetLeft( aTmp2, nRightAbs );
aTmp2.Intersection( aEndFrame );
Sub( aRegion, aTmp2 );
}
}
else
{
if ( bPorR2L )
fnRectX.SetRight( aTmp, fnRectX.GetRight(pEnd2Pos->aPortion) );
else
fnRectX.SetLeft( aTmp, fnRectX.GetLeft(pEnd2Pos->aPortion) );
}
if( MultiPortionType::ROT_90 == pEnd2Pos->nMultiType ||
fnRectX.GetBottom(pEnd2Pos->aPortion) ==
fnRectX.GetBottom(aEndRect) )
{
fnRectX.SetBottom( aTmp, fnRectX.GetBottom(pEnd2Pos->aLine) );
}
aTmp.Intersection( aEndFrame );
Sub( aRegion, aTmp );
// The next statement means neither ruby nor rotate(90):
if( MultiPortionType::RUBY != pEnd2Pos->nMultiType && MultiPortionType::ROT_90 != pEnd2Pos->nMultiType )
{
SwTwips nTmp = fnRectX.GetTop(pEnd2Pos->aLine);
if( fnRectX.GetTop(aEndRect) != nTmp )
{
fnRectX.SetBottom( aTmp, fnRectX.GetTop(aTmp) );
fnRectX.SetTop( aTmp, nTmp );
if( fnRectX.GetTop(aEndRect) !=
fnRectX.GetTop(pEnd2Pos->aPortion) )
{
if( bPorR2L )
fnRectX.SetLeft( aTmp, fnRectX.GetLeft(pEnd2Pos->aPortion) );
else
fnRectX.SetRight( aTmp, fnRectX.GetRight(pEnd2Pos->aPortion) );
}
aTmp.Intersection( aEndFrame );
Sub( aRegion, aTmp );
}
}
aEndRect = pEnd2Pos->aLine;
fnRectX.SetLeft( aEndRect, bEndR2L ?
fnRectX.GetRight(pEnd2Pos->aPortion) :
fnRectX.GetLeft(pEnd2Pos->aPortion) );
fnRectX.SetWidth( aEndRect, 1 );
}
}
else if( pSt2Pos && pEnd2Pos &&
MultiPortionType::BIDI == pSt2Pos->nMultiType &&
MultiPortionType::BIDI == pEnd2Pos->nMultiType &&
pSt2Pos->aPortion == pEnd2Pos->aPortion &&
pSt2Pos->aPortion2 != pEnd2Pos->aPortion2 )
{
// This is the ugly special case, where the selection starts and
// ends in the same bidi portion but one start or end is inside a
// nested bidi portion.
if ( aRectFnSet.GetWidth(pSt2Pos->aPortion2) )
{
SwRect aTmp( aStRect );
tools::Long nRightAbs = aRectFnSet.GetRight(pSt2Pos->aPortion);
nRightAbs -= aRectFnSet.GetLeft(pSt2Pos->aPortion2);
tools::Long nLeftAbs = nRightAbs - aRectFnSet.GetWidth(pSt2Pos->aPortion2);
aRectFnSet.SetRight( aTmp, nRightAbs );
aTmp.Intersection( aStFrame );
Sub( aRegion, aTmp );
aStRect = pSt2Pos->aLine;
aRectFnSet.SetLeft( aStRect, bR2L ? nRightAbs : nLeftAbs );
aRectFnSet.SetWidth( aStRect, 1 );
}
SwRectFnSet fnRectX(pEndFrame);
if ( fnRectX.GetWidth(pEnd2Pos->aPortion2) )
{
SwRect aTmp( aEndRect );
tools::Long nRightAbs = fnRectX.GetRight(pEnd2Pos->aPortion);
nRightAbs -= fnRectX.GetLeft(pEnd2Pos->aPortion2);
tools::Long nLeftAbs = nRightAbs - fnRectX.GetWidth(pEnd2Pos->aPortion2);
fnRectX.SetLeft( aTmp, nLeftAbs );
aTmp.Intersection( aEndFrame );
Sub( aRegion, aTmp );
aEndRect = pEnd2Pos->aLine;
fnRectX.SetLeft( aEndRect, bEndR2L ? nLeftAbs : nRightAbs );
fnRectX.SetWidth( aEndRect, 1 );
}
}
// The charrect may be outside the paintarea (for cursortravelling)
// but the selection has to be restricted to the paintarea
if( aStRect.Left() < aStFrame.Left() )
aStRect.Left( aStFrame.Left() );
else if( aStRect.Left() > aStFrame.Right() )
aStRect.Left( aStFrame.Right() );
SwTwips nTmp = aStRect.Right();
if( nTmp < aStFrame.Left() )
aStRect.Right( aStFrame.Left() );
else if( nTmp > aStFrame.Right() )
aStRect.Right( aStFrame.Right() );
if( aEndRect.Left() < aEndFrame.Left() )
aEndRect.Left( aEndFrame.Left() );
else if( aEndRect.Left() > aEndFrame.Right() )
aEndRect.Left( aEndFrame.Right() );
nTmp = aEndRect.Right();
if( nTmp < aEndFrame.Left() )
aEndRect.Right( aEndFrame.Left() );
else if( nTmp > aEndFrame.Right() )
aEndRect.Right( aEndFrame.Right() );
if( pStartFrame == pEndFrame )
{
bool bSameRotatedOrBidi = pSt2Pos && pEnd2Pos &&
( MultiPortionType::BIDI == pSt2Pos->nMultiType ||
MultiPortionType::ROT_270 == pSt2Pos->nMultiType ||
MultiPortionType::ROT_90 == pSt2Pos->nMultiType ) &&
pSt2Pos->aPortion == pEnd2Pos->aPortion;
// case 1: (Same frame and same line)
if( bSameRotatedOrBidi ||
aRectFnSet.GetTop(aStRect) == aRectFnSet.GetTop(aEndRect) )
{
Point aTmpSt( aStRect.Pos() );
Point aTmpEnd( aEndRect.Right(), aEndRect.Bottom() );
if (bSameRotatedOrBidi || bR2L || bB2T)
{
if( aTmpSt.Y() > aTmpEnd.Y() )
{
tools::Long nTmpY = aTmpEnd.Y();
aTmpEnd.setY( aTmpSt.Y() );
aTmpSt.setY( nTmpY );
}
if( aTmpSt.X() > aTmpEnd.X() )
{
tools::Long nTmpX = aTmpEnd.X();
aTmpEnd.setX( aTmpSt.X() );
aTmpSt.setX( nTmpX );
}
}
SwRect aTmp( aTmpSt, aTmpEnd );
// If content is selected which doesn't take space (e.g. PostIts,
// RefMarks, TOXMarks), then at least set the width of the Cursor.
if( 1 == aRectFnSet.GetWidth(aTmp) &&
pStartPos->GetContentIndex() !=
pEndPos->GetContentIndex() )
{
OutputDevice* pOut = pSh->GetOut();
tools::Long nCursorWidth = pOut->GetSettings().GetStyleSettings().
GetCursorSize();
aRectFnSet.SetWidth( aTmp, pOut->PixelToLogic(
Size( nCursorWidth, 0 ) ).Width() );
}
aTmp.Intersection( aStFrame );
Sub( aRegion, aTmp );
}
//case 2: (Same frame, but not the same line)
else
{
SwTwips lLeft, lRight;
if( pSt2Pos && pEnd2Pos && pSt2Pos->aPortion == pEnd2Pos->aPortion )
{
lLeft = aRectFnSet.GetLeft(pSt2Pos->aPortion);
lRight = aRectFnSet.GetRight(pSt2Pos->aPortion);
}
else
{
lLeft = aRectFnSet.GetLeft(pStartFrame->getFrameArea()) +
aRectFnSet.GetLeft(pStartFrame->getFramePrintArea());
lRight = aRectFnSet.GetRight(aEndFrame);
}
if( lLeft < aRectFnSet.GetLeft(aStFrame) )
lLeft = aRectFnSet.GetLeft(aStFrame);
if( lRight > aRectFnSet.GetRight(aStFrame) )
lRight = aRectFnSet.GetRight(aStFrame);
SwRect aSubRect( aStRect );
//First line
if( bR2L )
aRectFnSet.SetLeft( aSubRect, lLeft );
else
aRectFnSet.SetRight( aSubRect, lRight );
Sub( aRegion, aSubRect );
//If there's at least a twips between start- and endline,
//the whole area between will be added.
SwTwips aTmpBottom = aRectFnSet.GetBottom(aStRect);
SwTwips aTmpTop = aRectFnSet.GetTop(aEndRect);
if( aTmpBottom != aTmpTop )
{
aRectFnSet.SetLeft( aSubRect, lLeft );
aRectFnSet.SetRight( aSubRect, lRight );
aRectFnSet.SetTop( aSubRect, aTmpBottom );
aRectFnSet.SetBottom( aSubRect, aTmpTop );
Sub( aRegion, aSubRect );
}
//and the last line
aSubRect = aEndRect;
if( bR2L )
aRectFnSet.SetRight( aSubRect, lRight );
else
aRectFnSet.SetLeft( aSubRect, lLeft );
Sub( aRegion, aSubRect );
}
}
//case 3: (Different frames, maybe with other frames between)
else
{
//The startframe first...
SwRect aSubRect( aStRect );
if( bR2L )
aRectFnSet.SetLeft( aSubRect, aRectFnSet.GetLeft(aStFrame));
else
aRectFnSet.SetRight( aSubRect, aRectFnSet.GetRight(aStFrame));
Sub( aRegion, aSubRect );
SwTwips nTmpTwips = aRectFnSet.GetBottom(aStRect);
if( aRectFnSet.GetBottom(aStFrame) != nTmpTwips )
{
aSubRect = aStFrame;
aRectFnSet.SetTop( aSubRect, nTmpTwips );
Sub( aRegion, aSubRect );
}
//Now the frames between, if there are any
bool const bBody = pStartFrame->IsInDocBody();
const SwTableBox* pCellBox = pStartFrame->GetUpper()->IsCellFrame() ?
static_cast<const SwCellFrame*>(pStartFrame->GetUpper())->GetTabBox() : nullptr;
assert(pSh);
if (pSh->IsSelectAll())
pCellBox = nullptr;
const SwContentFrame *pContent = pStartFrame->GetNextContentFrame();
SwRect aPrvRect;
OSL_ENSURE( pContent,
"<SwRootFrame::CalcFrameRects(..)> - no content frame. This is a serious defect" );
while ( pContent && pContent != pEndFrame )
{
if ( pContent->IsInFly() )
{
const SwAnchoredObject* pObj = pContent->FindFlyFrame();
if (!aSortObjs.Contains(*pObj))
{ // is this even possible, assuming valid cursor pos.?
aSortObjs.Insert( *const_cast<SwAnchoredObject*>(pObj) );
}
}
// Consider only frames which have the same IsInDocBody value like pStartFrame
// If pStartFrame is inside a SwCellFrame, consider only frames which are inside the
// same cell frame (or its follow cell)
const SwTableBox* pTmpCellBox = pContent->GetUpper()->IsCellFrame() ?
static_cast<const SwCellFrame*>(pContent->GetUpper())->GetTabBox() : nullptr;
if (pSh->IsSelectAll())
pTmpCellBox = nullptr;
if ( bBody == pContent->IsInDocBody() &&
( !pCellBox || pCellBox == pTmpCellBox ) )
{
SwRect aCRect( pContent->UnionFrame( true ) );
aCRect.Intersection( pContent->GetPaintArea() );
if( aCRect.Overlaps( aRegion.GetOrigin() ))
{
SwRect aTmp( aPrvRect );
aTmp.Union( aCRect );
if ( (aPrvRect.Height() * aPrvRect.Width() +
aCRect.Height() * aCRect.Width()) ==
(aTmp.Height() * aTmp.Width()) )
{
aPrvRect.Union( aCRect );
}
else
{
if ( aPrvRect.HasArea() )
Sub( aRegion, aPrvRect );
aPrvRect = aCRect;
}
}
}
pContent = pContent->GetNextContentFrame();
OSL_ENSURE( pContent,
"<SwRootFrame::CalcFrameRects(..)> - no content frame. This is a serious defect!" );
}
if ( aPrvRect.HasArea() )
Sub( aRegion, aPrvRect );
// At last the endframe...
aRectFnSet.Refresh(pEndFrame);
nTmpTwips = aRectFnSet.GetTop(aEndRect);
if( aRectFnSet.GetTop(aEndFrame) != nTmpTwips )
{
aSubRect = aEndFrame;
aRectFnSet.SetBottom( aSubRect, nTmpTwips );
Sub( aRegion, aSubRect );
}
aSubRect = aEndRect;
if( bEndR2L )
aRectFnSet.SetRight(aSubRect, aRectFnSet.GetRight(aEndFrame));
else
aRectFnSet.SetLeft( aSubRect, aRectFnSet.GetLeft(aEndFrame) );
Sub( aRegion, aSubRect );
}
aRegion.Invert();
pSt2Pos.reset();
pEnd2Pos.reset();
// Cut out Flys in the foreground. We don't cut out a Fly when:
// - it's a Lower of StartFrame/EndFrame (FLY_AS_CHAR and all other Flys
// which sit in it recursively)
// - it's lower in the Z-order than the fly that contains the StartFrame
// (i.e. the one with the StartFrame is painted on top of it)
// - it's anchored to inside the selection and thus part of it
const SwPageFrame *pPage = pStartFrame->FindPageFrame();
const SwPageFrame *pEndPage = pEndFrame->FindPageFrame();
// for link rectangles: just remove all the fly portions - this prevents
// splitting of portions vertically (causes spurious extra PDF annotations)
if (eMode == RectsMode::NoAnchoredFlys)
{
for (SwContentFrame * pFrame = pStartFrame; ; pFrame = pFrame->GetFollow())
{
assert(pFrame->IsTextFrame());
SwTextGridItem const*const pGrid(GetGridItem(pFrame->FindPageFrame()));
SwTextPaintInfo info(static_cast<SwTextFrame*>(pFrame), pFrame->FindPageFrame()->getFrameArea());
SwTextPainter painter(static_cast<SwTextFrame*>(pFrame), &info);
// because nothing outside the start/end has been added, it doesn't
// matter to match exactly the start/end, subtracting outside is no-op
if (pFrame == pStartFrame)
{
painter.CharToLine(static_cast<SwTextFrame*>(pFrame)->MapModelToViewPos(*pStartPos));
}
do
{
info.SetPos(painter.GetTopLeft());
bool const bAdjustBaseLine(
painter.GetLineInfo().HasSpecialAlign(pFrame->IsVertical())
|| nullptr != pGrid || painter.GetCurr()->GetHangingBaseline());
SwTwips nAscent, nHeight;
painter.CalcAscentAndHeight(nAscent, nHeight);
SwTwips const nOldY(info.Y());
for (SwLinePortion const* pLP = painter.GetCurr()->GetFirstPortion();
pLP; pLP = pLP->GetNextPortion())
{
if (pLP->IsFlyPortion())
{
info.Y(info.Y() + (bAdjustBaseLine
? painter.AdjustBaseLine(*painter.GetCurr(), pLP)
: nAscent));
SwRect flyPortion;
info.CalcRect(*pLP, &flyPortion);
Sub(aRegion, flyPortion);
info.Y(nOldY);
}
pLP->Move(info);
}
}
while (painter.Next());
if (pFrame == pEndFrame)
{
break;
}
}
}
else while (pPage)
{
if ( pPage->GetSortedObjs() )
{
const SwSortedObjs &rObjs = *pPage->GetSortedObjs();
for (SwAnchoredObject* pAnchoredObj : rObjs)
{
const SwFlyFrame* pFly = pAnchoredObj->DynCastFlyFrame();
SdrObject const*const pObj(pAnchoredObj->GetDrawObj());
SwFormatSurround const& rSur(pAnchoredObj->GetFrameFormat()->GetSurround());
SwFormatAnchor const& rAnchor(pAnchoredObj->GetFrameFormat()->GetAnchor());
const SwPosition* anchoredAt = rAnchor.GetContentAnchor();
bool inSelection = (
anchoredAt != nullptr
&& ( (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR
&& IsDestroyFrameAnchoredAtChar(*anchoredAt, *pStartPos, *pEndPos))
|| (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA
&& IsSelectFrameAnchoredAtPara(*anchoredAt, *pStartPos, *pEndPos))));
if (inSelection)
{
Add(aRegion, pAnchoredObj->GetObjRect());
}
else if (pFly && !pFly->IsAnLower(pStartFrame) &&
(rSur.GetSurround() != css::text::WrapTextMode_THROUGH &&
!rSur.IsContour()) )
{
if ( aSortObjs.Contains( *pAnchoredObj ) )
continue;
bool bSub = true;
const sal_uInt32 nPos = pObj->GetOrdNum();
for ( size_t k = 0; bSub && k < aSortObjs.size(); ++k )
{
assert( dynamic_cast< const SwFlyFrame *>( aSortObjs[k] ) &&
"<SwRootFrame::CalcFrameRects(..)> - object in <aSortObjs> of unexpected type" );
const SwFlyFrame* pTmp = static_cast<SwFlyFrame*>(aSortObjs[k]);
do
{
if ( nPos < pTmp->GetVirtDrawObj()->GetOrdNumDirect() )
{
bSub = false;
}
else
{
pTmp = pTmp->GetAnchorFrame()->FindFlyFrame();
}
} while ( bSub && pTmp );
}
if ( bSub )
Sub( aRegion, pFly->getFrameArea() );
}
}
}
if ( pPage == pEndPage )
break;
else
pPage = static_cast<const SwPageFrame*>(pPage->GetNext());
}
// Because it looks better, we cut out the DropCaps.
SwRect aDropRect;
if ( pStartFrame->IsTextFrame() )
{
if ( static_cast<const SwTextFrame*>(pStartFrame)->GetDropRect( aDropRect ) )
Sub( aRegion, aDropRect );
}
if ( pEndFrame != pStartFrame && pEndFrame->IsTextFrame() )
{
if ( static_cast<const SwTextFrame*>(pEndFrame)->GetDropRect( aDropRect ) )
Sub( aRegion, aDropRect );
}
rRects.assign( aRegion.begin(), aRegion.end() );
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V530 The return value of function 'Intersection' is required to be utilized.
↑ V530 The return value of function 'Intersection' is required to be utilized.
↑ V530 The return value of function 'Intersection' is required to be utilized.
↑ V530 The return value of function 'Intersection' is required to be utilized.
↑ V530 The return value of function 'Intersection' is required to be utilized.
↑ V530 The return value of function 'Intersection' is required to be utilized.
↑ V530 The return value of function 'Intersection' is required to be utilized.
↑ V530 The return value of function 'Intersection' is required to be utilized.
↑ V530 The return value of function 'Intersection' is required to be utilized.
↑ V530 The return value of function 'Intersection' is required to be utilized.
↑ V530 The return value of function 'Intersection' is required to be utilized.
↑ V530 The return value of function 'Intersection' is required to be utilized.
↑ V530 The return value of function 'Union' is required to be utilized.
↑ V530 The return value of function 'Union' is required to be utilized.
↑ V595 The 'pTable' pointer was utilized before it was verified against nullptr. Check lines: 792, 799.
↑ V654 The condition 'pCnt' of loop is always true.
↑ V1004 The 'pSh' pointer was used unsafely after it was verified against nullptr. Check lines: 2071, 2075.