1734 lines
63 KiB
C++
1734 lines
63 KiB
C++
/* -*- 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 <ndtxt.hxx>
|
|
#include <pam.hxx>
|
|
#include <frmatr.hxx>
|
|
#include <frmtool.hxx>
|
|
#include <viewopt.hxx>
|
|
#include <paratr.hxx>
|
|
#include <rootfrm.hxx>
|
|
#include <pagefrm.hxx>
|
|
#include <colfrm.hxx>
|
|
#include <swtypes.hxx>
|
|
#include <editeng/lrspitem.hxx>
|
|
#include <editeng/tstpitem.hxx>
|
|
#include <editeng/ulspitem.hxx>
|
|
#include <editeng/lspcitem.hxx>
|
|
#include "pormulti.hxx"
|
|
#include <doc.hxx>
|
|
#include <IDocumentDeviceAccess.hxx>
|
|
#include <sortedobjs.hxx>
|
|
|
|
#include <unicode/ubidi.h>
|
|
|
|
#include <txtfrm.hxx>
|
|
#include "inftxt.hxx"
|
|
#include "itrtxt.hxx"
|
|
#include <crstate.hxx>
|
|
#include <viewsh.hxx>
|
|
#include <swfntcch.hxx>
|
|
#include <flyfrm.hxx>
|
|
|
|
#define MIN_OFFSET_STEP 10
|
|
|
|
using namespace ::com::sun::star;
|
|
|
|
/*
|
|
* - SurvivalKit: For how long do we get past the last char of the line.
|
|
* - RightMargin abstains from adjusting position with -1
|
|
* - GetCharRect returns a GetEndCharRect for CursorMoveState::RightMargin
|
|
* - GetEndCharRect sets bRightMargin to true
|
|
* - SwTextCursor::bRightMargin is set to false by CharCursorToLine
|
|
*/
|
|
|
|
namespace
|
|
{
|
|
|
|
SwTextFrame *GetAdjFrameAtPos( SwTextFrame *pFrame, const SwPosition &rPos,
|
|
const bool bRightMargin, const bool bNoScroll = true )
|
|
{
|
|
// RightMargin in the last master line
|
|
TextFrameIndex const nOffset = pFrame->MapModelToViewPos(rPos);
|
|
SwTextFrame *pFrameAtPos = pFrame;
|
|
if( !bNoScroll || pFrame->GetFollow() )
|
|
{
|
|
pFrameAtPos = pFrame->GetFrameAtPos( rPos );
|
|
if (nOffset < pFrameAtPos->GetOffset() &&
|
|
!pFrameAtPos->IsFollow() )
|
|
{
|
|
assert(pFrameAtPos->MapModelToViewPos(rPos) == nOffset);
|
|
TextFrameIndex nNew(nOffset);
|
|
if (nNew < TextFrameIndex(MIN_OFFSET_STEP))
|
|
nNew = TextFrameIndex(0);
|
|
else
|
|
nNew -= TextFrameIndex(MIN_OFFSET_STEP);
|
|
sw_ChangeOffset( pFrameAtPos, nNew );
|
|
}
|
|
}
|
|
while( pFrame != pFrameAtPos )
|
|
{
|
|
pFrame = pFrameAtPos;
|
|
pFrame->GetFormatted();
|
|
pFrameAtPos = pFrame->GetFrameAtPos( rPos );
|
|
}
|
|
|
|
if( nOffset && bRightMargin )
|
|
{
|
|
while (pFrameAtPos &&
|
|
pFrameAtPos->MapViewToModelPos(pFrameAtPos->GetOffset()) == rPos &&
|
|
pFrameAtPos->IsFollow() )
|
|
{
|
|
pFrameAtPos->GetFormatted();
|
|
pFrameAtPos = pFrameAtPos->FindMaster();
|
|
}
|
|
OSL_ENSURE( pFrameAtPos, "+GetCharRect: no frame with my rightmargin" );
|
|
}
|
|
return pFrameAtPos ? pFrameAtPos : pFrame;
|
|
}
|
|
|
|
}
|
|
|
|
bool sw_ChangeOffset(SwTextFrame* pFrame, TextFrameIndex nNew)
|
|
{
|
|
// Do not scroll in areas and outside of flies
|
|
OSL_ENSURE( !pFrame->IsFollow(), "Illegal Scrolling by Follow!" );
|
|
if( pFrame->GetOffset() != nNew && !pFrame->IsInSct() )
|
|
{
|
|
SwFlyFrame *pFly = pFrame->FindFlyFrame();
|
|
// Attention: if e.g. in a column frame the size is still invalid
|
|
// we must not scroll around just like that
|
|
if ( ( pFly && pFly->isFrameAreaDefinitionValid() &&
|
|
!pFly->GetNextLink() && !pFly->GetPrevLink() ) ||
|
|
( !pFly && pFrame->IsInTab() ) )
|
|
{
|
|
SwViewShell* pVsh = pFrame->getRootFrame()->GetCurrShell();
|
|
if( pVsh )
|
|
{
|
|
if( pVsh->GetRingContainer().size() > 1 ||
|
|
( pFrame->GetDrawObjs() && pFrame->GetDrawObjs()->size() ) )
|
|
{
|
|
if( !pFrame->GetOffset() )
|
|
return false;
|
|
nNew = TextFrameIndex(0);
|
|
}
|
|
pFrame->SetOffset( nNew );
|
|
pFrame->SetPara( nullptr );
|
|
pFrame->GetFormatted();
|
|
if( pFrame->getFrameArea().HasArea() )
|
|
pFrame->getRootFrame()->GetCurrShell()->InvalidateWindows( pFrame->getFrameArea() );
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
SwTextFrame& SwTextFrame::GetFrameAtOfst(TextFrameIndex const nWhere)
|
|
{
|
|
SwTextFrame* pRet = this;
|
|
while( pRet->HasFollow() && nWhere >= pRet->GetFollow()->GetOffset() )
|
|
pRet = pRet->GetFollow();
|
|
return *pRet;
|
|
}
|
|
|
|
SwTextFrame *SwTextFrame::GetFrameAtPos( const SwPosition &rPos )
|
|
{
|
|
TextFrameIndex const nPos(MapModelToViewPos(rPos));
|
|
SwTextFrame *pFoll = this;
|
|
while( pFoll->GetFollow() )
|
|
{
|
|
if (nPos > pFoll->GetFollow()->GetOffset())
|
|
pFoll = pFoll->GetFollow();
|
|
else
|
|
{
|
|
if (nPos == pFoll->GetFollow()->GetOffset()
|
|
&& !SwTextCursor::IsRightMargin() )
|
|
pFoll = pFoll->GetFollow();
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
return pFoll;
|
|
}
|
|
|
|
/*
|
|
* GetCharRect() returns the char's char line described by aPos.
|
|
* GetModelPositionForViewPoint() does the reverse: It goes from a document coordinate to
|
|
* a Pam.
|
|
* Both are virtual in the frame base class and thus are redefined here.
|
|
*/
|
|
|
|
bool SwTextFrame::GetCharRect( SwRect& rOrig, const SwPosition &rPos,
|
|
SwCursorMoveState *pCMS, bool bAllowFarAway ) const
|
|
{
|
|
OSL_ENSURE( ! IsVertical() || ! IsSwapped(),"SwTextFrame::GetCharRect with swapped frame" );
|
|
|
|
if (IsLocked())
|
|
return false;
|
|
|
|
// Find the right frame first. We need to keep in mind that:
|
|
// - the cached information could be invalid (GetPara() == 0)
|
|
// - we could have a Follow
|
|
// - the Follow chain grows dynamically; the one we end up in
|
|
// needs to be formatted
|
|
|
|
// Optimisation: reading ahead saves us a GetAdjFrameAtPos
|
|
const bool bRightMargin = pCMS && ( CursorMoveState::RightMargin == pCMS->m_eState );
|
|
const bool bNoScroll = pCMS && pCMS->m_bNoScroll;
|
|
SwTextFrame *pFrame = GetAdjFrameAtPos( const_cast<SwTextFrame*>(this), rPos, bRightMargin,
|
|
bNoScroll );
|
|
pFrame->GetFormatted();
|
|
|
|
const SwFrame* pTmpFrame = pFrame->GetUpper();
|
|
if (pTmpFrame->getFrameArea().Top() == FAR_AWAY && !bAllowFarAway)
|
|
return false;
|
|
|
|
SwRectFnSet aRectFnSet(pFrame);
|
|
const SwTwips nUpperMaxY = aRectFnSet.GetPrtBottom(*pTmpFrame);
|
|
const SwTwips nFrameMaxY = aRectFnSet.GetPrtBottom(*pFrame);
|
|
|
|
// nMaxY is an absolute value
|
|
SwTwips nMaxY = aRectFnSet.IsVert() ?
|
|
( aRectFnSet.IsVertL2R() ? std::min( nFrameMaxY, nUpperMaxY ) : std::max( nFrameMaxY, nUpperMaxY ) ) :
|
|
std::min( nFrameMaxY, nUpperMaxY );
|
|
|
|
bool bRet = false;
|
|
|
|
if ( pFrame->IsEmpty() || ! aRectFnSet.GetHeight(pFrame->getFramePrintArea()) )
|
|
{
|
|
Point aPnt1 = pFrame->getFrameArea().Pos() + pFrame->getFramePrintArea().Pos();
|
|
SwTextNode const*const pTextNd(GetTextNodeForParaProps());
|
|
short nFirstOffset;
|
|
pTextNd->GetFirstLineOfsWithNum(nFirstOffset, {});
|
|
|
|
Point aPnt2;
|
|
if ( aRectFnSet.IsVert() )
|
|
{
|
|
if( nFirstOffset > 0 )
|
|
aPnt1.AdjustY(nFirstOffset );
|
|
if ( aPnt1.X() < nMaxY && !aRectFnSet.IsVertL2R() )
|
|
aPnt1.setX( nMaxY );
|
|
aPnt2.setX( aPnt1.X() + pFrame->getFramePrintArea().Width() );
|
|
aPnt2.setY( aPnt1.Y() );
|
|
if( aPnt2.X() < nMaxY )
|
|
aPnt2.setX( nMaxY );
|
|
}
|
|
else
|
|
{
|
|
if( nFirstOffset > 0 )
|
|
aPnt1.AdjustX(nFirstOffset );
|
|
|
|
if( aPnt1.Y() > nMaxY )
|
|
aPnt1.setY( nMaxY );
|
|
aPnt2.setX( aPnt1.X() );
|
|
aPnt2.setY( aPnt1.Y() + pFrame->getFramePrintArea().Height() );
|
|
if( aPnt2.Y() > nMaxY )
|
|
aPnt2.setY( nMaxY );
|
|
}
|
|
|
|
rOrig = SwRect( aPnt1, aPnt2 );
|
|
|
|
if ( pCMS )
|
|
{
|
|
pCMS->m_aRealHeight.setX( 0 );
|
|
pCMS->m_aRealHeight.setY( aRectFnSet.IsVert() ? -rOrig.Width() : rOrig.Height() );
|
|
}
|
|
|
|
if ( pFrame->IsRightToLeft() )
|
|
pFrame->SwitchLTRtoRTL( rOrig );
|
|
|
|
bRet = true;
|
|
}
|
|
else
|
|
{
|
|
if( !pFrame->HasPara() )
|
|
return false;
|
|
|
|
SwFrameSwapper aSwapper( pFrame, true );
|
|
if ( aRectFnSet.IsVert() )
|
|
nMaxY = pFrame->SwitchVerticalToHorizontal( nMaxY );
|
|
|
|
bool bGoOn = true;
|
|
TextFrameIndex const nOffset = MapModelToViewPos(rPos);
|
|
assert(nOffset != TextFrameIndex(COMPLETE_STRING)); // not going to end well
|
|
TextFrameIndex nNextOfst;
|
|
|
|
do
|
|
{
|
|
{
|
|
SwTextSizeInfo aInf( pFrame );
|
|
SwTextCursor aLine( pFrame, &aInf );
|
|
nNextOfst = aLine.GetEnd();
|
|
// See comment in AdjustFrame
|
|
// Include the line's last char?
|
|
if (bRightMargin)
|
|
aLine.GetEndCharRect( &rOrig, nOffset, pCMS, nMaxY );
|
|
else
|
|
aLine.GetCharRect( &rOrig, nOffset, pCMS, nMaxY );
|
|
bRet = true;
|
|
}
|
|
|
|
if ( pFrame->IsRightToLeft() )
|
|
pFrame->SwitchLTRtoRTL( rOrig );
|
|
|
|
if ( aRectFnSet.IsVert() )
|
|
pFrame->SwitchHorizontalToVertical( rOrig );
|
|
|
|
if( pFrame->IsUndersized() && pCMS && !pFrame->GetNext() &&
|
|
aRectFnSet.GetBottom(rOrig) == nUpperMaxY &&
|
|
pFrame->GetOffset() < nOffset &&
|
|
!pFrame->IsFollow() && !bNoScroll &&
|
|
TextFrameIndex(pFrame->GetText().getLength()) != nNextOfst)
|
|
{
|
|
bGoOn = sw_ChangeOffset( pFrame, nNextOfst );
|
|
}
|
|
else
|
|
bGoOn = false;
|
|
} while ( bGoOn );
|
|
|
|
if ( pCMS )
|
|
{
|
|
if ( pFrame->IsRightToLeft() )
|
|
{
|
|
if( pCMS->m_b2Lines && pCMS->m_p2Lines)
|
|
{
|
|
pFrame->SwitchLTRtoRTL( pCMS->m_p2Lines->aLine );
|
|
pFrame->SwitchLTRtoRTL( pCMS->m_p2Lines->aPortion );
|
|
}
|
|
}
|
|
|
|
if ( aRectFnSet.IsVert() )
|
|
{
|
|
if ( pCMS->m_bRealHeight )
|
|
{
|
|
pCMS->m_aRealHeight.setY( -pCMS->m_aRealHeight.Y() );
|
|
if ( pCMS->m_aRealHeight.Y() < 0 )
|
|
{
|
|
// writing direction is from top to bottom
|
|
pCMS->m_aRealHeight.setX( rOrig.Width() -
|
|
pCMS->m_aRealHeight.X() +
|
|
pCMS->m_aRealHeight.Y() );
|
|
}
|
|
}
|
|
if( pCMS->m_b2Lines && pCMS->m_p2Lines)
|
|
{
|
|
pFrame->SwitchHorizontalToVertical( pCMS->m_p2Lines->aLine );
|
|
pFrame->SwitchHorizontalToVertical( pCMS->m_p2Lines->aPortion );
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
if( bRet )
|
|
{
|
|
SwPageFrame *pPage = pFrame->FindPageFrame();
|
|
assert(pPage && "Text escaped from page?");
|
|
const SwTwips nOrigTop = aRectFnSet.GetTop(rOrig);
|
|
const SwTwips nPageTop = aRectFnSet.GetTop(pPage->getFrameArea());
|
|
const SwTwips nPageBott = aRectFnSet.GetBottom(pPage->getFrameArea());
|
|
|
|
// We have the following situation: if the frame is in an invalid
|
|
// sectionframe, it's possible that the frame is outside the page.
|
|
// If we restrict the cursor position to the page area, we enforce
|
|
// the formatting of the page, of the section frame and the frame itself.
|
|
if( aRectFnSet.YDiff( nPageTop, nOrigTop ) > 0 )
|
|
aRectFnSet.SetTop( rOrig, nPageTop );
|
|
|
|
if ( aRectFnSet.YDiff( nOrigTop, nPageBott ) > 0 )
|
|
aRectFnSet.SetTop( rOrig, nPageBott );
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
/*
|
|
* GetAutoPos() looks up the char's char line which is described by rPos
|
|
* and is used by the auto-positioned frame.
|
|
*/
|
|
|
|
bool SwTextFrame::GetAutoPos( SwRect& rOrig, const SwPosition &rPos ) const
|
|
{
|
|
if( IsHiddenNow() )
|
|
return false;
|
|
|
|
TextFrameIndex const nOffset = MapModelToViewPos(rPos);
|
|
SwTextFrame* pFrame = &(const_cast<SwTextFrame*>(this)->GetFrameAtOfst( nOffset ));
|
|
|
|
pFrame->GetFormatted();
|
|
const SwFrame* pTmpFrame = pFrame->GetUpper();
|
|
|
|
SwRectFnSet aRectFnSet(pTmpFrame);
|
|
SwTwips nUpperMaxY = aRectFnSet.GetPrtBottom(*pTmpFrame);
|
|
|
|
// nMaxY is in absolute value
|
|
SwTwips nMaxY;
|
|
if ( aRectFnSet.IsVert() )
|
|
{
|
|
if ( aRectFnSet.IsVertL2R() )
|
|
nMaxY = std::min( SwTwips(aRectFnSet.GetPrtBottom(*pFrame)), nUpperMaxY );
|
|
else
|
|
nMaxY = std::max( SwTwips(aRectFnSet.GetPrtBottom(*pFrame)), nUpperMaxY );
|
|
}
|
|
else
|
|
nMaxY = std::min( SwTwips(aRectFnSet.GetPrtBottom(*pFrame)), nUpperMaxY );
|
|
if ( pFrame->IsEmpty() || ! aRectFnSet.GetHeight(pFrame->getFramePrintArea()) )
|
|
{
|
|
Point aPnt1 = pFrame->getFrameArea().Pos() + pFrame->getFramePrintArea().Pos();
|
|
Point aPnt2;
|
|
if ( aRectFnSet.IsVert() )
|
|
{
|
|
if ( aPnt1.X() < nMaxY && !aRectFnSet.IsVertL2R() )
|
|
aPnt1.setX( nMaxY );
|
|
|
|
aPnt2.setX( aPnt1.X() + pFrame->getFramePrintArea().Width() );
|
|
aPnt2.setY( aPnt1.Y() );
|
|
if( aPnt2.X() < nMaxY )
|
|
aPnt2.setX( nMaxY );
|
|
}
|
|
else
|
|
{
|
|
if( aPnt1.Y() > nMaxY )
|
|
aPnt1.setY( nMaxY );
|
|
aPnt2.setX( aPnt1.X() );
|
|
aPnt2.setY( aPnt1.Y() + pFrame->getFramePrintArea().Height() );
|
|
if( aPnt2.Y() > nMaxY )
|
|
aPnt2.setY( nMaxY );
|
|
}
|
|
rOrig = SwRect( aPnt1, aPnt2 );
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
if( !pFrame->HasPara() )
|
|
return false;
|
|
|
|
SwFrameSwapper aSwapper( pFrame, true );
|
|
if ( aRectFnSet.IsVert() )
|
|
nMaxY = pFrame->SwitchVerticalToHorizontal( nMaxY );
|
|
|
|
SwTextSizeInfo aInf( pFrame );
|
|
SwTextCursor aLine( pFrame, &aInf );
|
|
SwCursorMoveState aTmpState( CursorMoveState::SetOnlyText );
|
|
aTmpState.m_bRealHeight = true;
|
|
aLine.GetCharRect( &rOrig, nOffset, &aTmpState, nMaxY );
|
|
if( aTmpState.m_aRealHeight.X() >= 0 )
|
|
{
|
|
rOrig.Pos().AdjustY(aTmpState.m_aRealHeight.X() );
|
|
rOrig.Height( aTmpState.m_aRealHeight.Y() );
|
|
}
|
|
|
|
if ( pFrame->IsRightToLeft() )
|
|
pFrame->SwitchLTRtoRTL( rOrig );
|
|
|
|
if ( aRectFnSet.IsVert() )
|
|
pFrame->SwitchHorizontalToVertical( rOrig );
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/** determine top of line for given position in the text frame
|
|
|
|
- Top of first paragraph line is the top of the printing area of the text frame
|
|
- If a proportional line spacing is applied use top of anchor character as
|
|
top of the line.
|
|
*/
|
|
bool SwTextFrame::GetTopOfLine( SwTwips& _onTopOfLine,
|
|
const SwPosition& _rPos ) const
|
|
{
|
|
bool bRet = true;
|
|
|
|
// get position offset
|
|
TextFrameIndex const nOffset = MapModelToViewPos(_rPos);
|
|
|
|
if (TextFrameIndex(GetText().getLength()) < nOffset)
|
|
{
|
|
bRet = false;
|
|
}
|
|
else
|
|
{
|
|
SwRectFnSet aRectFnSet(this);
|
|
if ( IsEmpty() || !aRectFnSet.GetHeight(getFramePrintArea()) )
|
|
{
|
|
// consider upper space amount considered
|
|
// for previous frame and the page grid.
|
|
_onTopOfLine = aRectFnSet.GetPrtTop(*this);
|
|
}
|
|
else
|
|
{
|
|
// determine formatted text frame that contains the requested position
|
|
SwTextFrame* pFrame = &(const_cast<SwTextFrame*>(this)->GetFrameAtOfst( nOffset ));
|
|
pFrame->GetFormatted();
|
|
aRectFnSet.Refresh(pFrame);
|
|
// If proportional line spacing is applied
|
|
// to the text frame, the top of the anchor character is also the
|
|
// top of the line.
|
|
// Otherwise the line layout determines the top of the line
|
|
const SvxLineSpacingItem& rSpace = GetAttrSet()->GetLineSpacing();
|
|
if ( rSpace.GetInterLineSpaceRule() == SvxInterLineSpaceRule::Prop )
|
|
{
|
|
SwRect aCharRect;
|
|
if ( GetAutoPos( aCharRect, _rPos ) )
|
|
{
|
|
_onTopOfLine = aRectFnSet.GetTop(aCharRect);
|
|
}
|
|
else
|
|
{
|
|
bRet = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// assure that text frame is in a horizontal layout
|
|
SwFrameSwapper aSwapper( pFrame, true );
|
|
// determine text line that contains the requested position
|
|
SwTextSizeInfo aInf( pFrame );
|
|
SwTextCursor aLine( pFrame, &aInf );
|
|
aLine.CharCursorToLine( nOffset );
|
|
// determine top of line
|
|
_onTopOfLine = aLine.Y();
|
|
if ( aRectFnSet.IsVert() )
|
|
{
|
|
_onTopOfLine = pFrame->SwitchHorizontalToVertical( _onTopOfLine );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
// Minimum distance of non-empty lines is a little less than 2 cm
|
|
#define FILL_MIN_DIST 1100
|
|
|
|
struct SwFillData
|
|
{
|
|
SwRect aFrame;
|
|
const SwCursorMoveState *pCMS;
|
|
SwPosition* pPos;
|
|
const Point& rPoint;
|
|
SwTwips nLineWidth;
|
|
bool bFirstLine : 1;
|
|
bool bInner : 1;
|
|
bool bColumn : 1;
|
|
bool bEmpty : 1;
|
|
SwFillData( const SwCursorMoveState *pC, SwPosition* pP, const SwRect& rR,
|
|
const Point& rPt ) : aFrame( rR ), pCMS( pC ), pPos( pP ), rPoint( rPt ),
|
|
nLineWidth( 0 ), bFirstLine( true ), bInner( false ), bColumn( false ),
|
|
bEmpty( true ){}
|
|
SwFillMode Mode() const { return pCMS->m_pFill->eMode; }
|
|
tools::Long X() const { return rPoint.X(); }
|
|
tools::Long Y() const { return rPoint.Y(); }
|
|
tools::Long Left() const { return aFrame.Left(); }
|
|
tools::Long Right() const { return aFrame.Right(); }
|
|
tools::Long Bottom() const { return aFrame.Bottom(); }
|
|
SwFillCursorPos &Fill() const { return *pCMS->m_pFill; }
|
|
void SetTab( sal_uInt16 nNew ) { pCMS->m_pFill->nTabCnt = nNew; }
|
|
void SetSpace( sal_uInt16 nNew ) { pCMS->m_pFill->nSpaceCnt = nNew; }
|
|
void SetSpaceOnly( sal_uInt16 nNew ) { pCMS->m_pFill->nSpaceOnlyCnt = nNew; }
|
|
void SetOrient( const sal_Int16 eNew ){ pCMS->m_pFill->eOrient = eNew; }
|
|
};
|
|
|
|
bool SwTextFrame::GetModelPositionForViewPoint_(SwPosition* pPos, const Point& rPoint,
|
|
const bool bChgFrame, SwCursorMoveState* pCMS ) const
|
|
{
|
|
// GetModelPositionForViewPoint_ is called by GetModelPositionForViewPoint and GetKeyCursorOfst.
|
|
// Never just a return false.
|
|
|
|
if( IsLocked() || IsHiddenNow() )
|
|
return false;
|
|
|
|
const_cast<SwTextFrame*>(this)->GetFormatted();
|
|
|
|
Point aOldPoint( rPoint );
|
|
|
|
if ( IsVertical() )
|
|
{
|
|
SwitchVerticalToHorizontal( const_cast<Point&>(rPoint) );
|
|
const_cast<SwTextFrame*>(this)->SwapWidthAndHeight();
|
|
}
|
|
|
|
if ( IsRightToLeft() )
|
|
SwitchRTLtoLTR( const_cast<Point&>(rPoint) );
|
|
|
|
std::unique_ptr<SwFillData> pFillData;
|
|
if ( pCMS && pCMS->m_pFill )
|
|
pFillData.reset(new SwFillData( pCMS, pPos, getFrameArea(), rPoint ));
|
|
|
|
if ( IsEmpty() )
|
|
{
|
|
*pPos = MapViewToModelPos(TextFrameIndex(0));
|
|
if( pCMS && pCMS->m_bFieldInfo )
|
|
{
|
|
SwTwips nDiff = rPoint.X() - getFrameArea().Left() - getFramePrintArea().Left();
|
|
if( nDiff > 50 || nDiff < 0 )
|
|
pCMS->m_bPosCorr = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SwTextSizeInfo aInf( const_cast<SwTextFrame*>(this) );
|
|
SwTextCursor aLine( const_cast<SwTextFrame*>(this), &aInf );
|
|
|
|
// See comment in AdjustFrame()
|
|
SwTwips nMaxY = getFrameArea().Top() + getFramePrintArea().Top() + getFramePrintArea().Height();
|
|
aLine.TwipsToLine( rPoint.Y() );
|
|
while( aLine.Y() + aLine.GetLineHeight() > nMaxY )
|
|
{
|
|
if( !aLine.Prev() )
|
|
break;
|
|
}
|
|
|
|
if( aLine.GetDropLines() >= aLine.GetLineNr() && 1 != aLine.GetLineNr()
|
|
&& rPoint.X() < aLine.FirstLeft() + aLine.GetDropLeft() )
|
|
while( aLine.GetLineNr() > 1 )
|
|
aLine.Prev();
|
|
|
|
TextFrameIndex nOffset = aLine.GetModelPositionForViewPoint(pPos, rPoint, bChgFrame, pCMS);
|
|
|
|
if( pCMS && pCMS->m_eState == CursorMoveState::NONE && aLine.GetEnd() == nOffset )
|
|
pCMS->m_eState = CursorMoveState::RightMargin;
|
|
|
|
// pPos is a pure IN parameter and must not be evaluated.
|
|
// pIter->GetModelPositionForViewPoint returns from a nesting with COMPLETE_STRING.
|
|
// If SwTextIter::GetModelPositionForViewPoint calls GetModelPositionForViewPoint further by itself
|
|
// nNode changes the position.
|
|
// In such cases, pPos must not be calculated.
|
|
if (TextFrameIndex(COMPLETE_STRING) != nOffset)
|
|
{
|
|
*pPos = MapViewToModelPos(nOffset);
|
|
if( pFillData )
|
|
{
|
|
if (TextFrameIndex(GetText().getLength()) > nOffset ||
|
|
rPoint.Y() < getFrameArea().Top() )
|
|
pFillData->bInner = true;
|
|
pFillData->bFirstLine = aLine.GetLineNr() < 2;
|
|
if (GetText().getLength())
|
|
{
|
|
pFillData->bEmpty = false;
|
|
pFillData->nLineWidth = aLine.GetCurr()->Width();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
bool bChgFillData = false;
|
|
if( pFillData && FindPageFrame()->getFrameArea().Contains( aOldPoint ) )
|
|
{
|
|
FillCursorPos( *pFillData );
|
|
bChgFillData = true;
|
|
}
|
|
|
|
if ( IsVertical() )
|
|
{
|
|
if ( bChgFillData )
|
|
SwitchHorizontalToVertical( pFillData->Fill().aCursor.Pos() );
|
|
const_cast<SwTextFrame*>(this)->SwapWidthAndHeight();
|
|
}
|
|
|
|
if ( IsRightToLeft() && bChgFillData )
|
|
{
|
|
SwitchLTRtoRTL( pFillData->Fill().aCursor.Pos() );
|
|
const sal_Int16 eOrient = pFillData->pCMS->m_pFill->eOrient;
|
|
|
|
if ( text::HoriOrientation::LEFT == eOrient )
|
|
pFillData->SetOrient( text::HoriOrientation::RIGHT );
|
|
else if ( text::HoriOrientation::RIGHT == eOrient )
|
|
pFillData->SetOrient( text::HoriOrientation::LEFT );
|
|
}
|
|
|
|
const_cast<Point&>(rPoint) = aOldPoint;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SwTextFrame::GetModelPositionForViewPoint(SwPosition* pPos, Point& rPoint,
|
|
SwCursorMoveState* pCMS, bool ) const
|
|
{
|
|
const bool bChgFrame = !(pCMS && CursorMoveState::UpDown == pCMS->m_eState);
|
|
return GetModelPositionForViewPoint_( pPos, rPoint, bChgFrame, pCMS );
|
|
}
|
|
|
|
/*
|
|
* Layout-oriented cursor movement to the line start.
|
|
*/
|
|
|
|
bool SwTextFrame::LeftMargin(SwPaM *pPam) const
|
|
{
|
|
assert(GetMergedPara() || &pPam->GetPointNode() == static_cast<SwContentNode const*>(GetDep()));
|
|
|
|
SwTextFrame *pFrame = GetAdjFrameAtPos( const_cast<SwTextFrame*>(this), *pPam->GetPoint(),
|
|
SwTextCursor::IsRightMargin() );
|
|
pFrame->GetFormatted();
|
|
TextFrameIndex nIndx;
|
|
if ( pFrame->IsEmpty() )
|
|
nIndx = TextFrameIndex(0);
|
|
else
|
|
{
|
|
SwTextSizeInfo aInf( pFrame );
|
|
SwTextCursor aLine( pFrame, &aInf );
|
|
|
|
aLine.CharCursorToLine(pFrame->MapModelToViewPos(*pPam->GetPoint()));
|
|
nIndx = aLine.GetStart();
|
|
if( pFrame->GetOffset() && !pFrame->IsFollow() && !aLine.GetPrev() )
|
|
{
|
|
sw_ChangeOffset(pFrame, TextFrameIndex(0));
|
|
nIndx = TextFrameIndex(0);
|
|
}
|
|
}
|
|
*pPam->GetPoint() = pFrame->MapViewToModelPos(nIndx);
|
|
SwTextCursor::SetRightMargin( false );
|
|
return true;
|
|
}
|
|
|
|
bool SwTextFrame::IsInHyphenatedWord(SwPaM *pPam, bool bSelection) const
|
|
{
|
|
assert(GetMergedPara() || &pPam->GetPointNode() == static_cast<SwContentNode const*>(GetDep()));
|
|
|
|
SwTextFrame *pFrame = GetAdjFrameAtPos( const_cast<SwTextFrame*>(this), *pPam->GetPoint(),
|
|
SwTextCursor::IsRightMargin() );
|
|
pFrame->GetFormatted();
|
|
if (!IsEmpty())
|
|
{
|
|
SwTextSizeInfo aInf( pFrame );
|
|
SwTextCursor aLine( pFrame, &aInf );
|
|
TextFrameIndex const nCursorPos(MapModelToViewPos(*pPam->GetPoint()));
|
|
aLine.CharCursorToLine(nCursorPos);
|
|
if ( aLine.GetCurr()->IsEndHyph() )
|
|
{
|
|
TextFrameIndex nPos(aLine.GetStart() + aLine.GetCurr()->GetLen());
|
|
while( nPos > nCursorPos && ' ' != aInf.GetText()[sal_Int32(nPos) - 1] )
|
|
--nPos;
|
|
if ( nPos == nCursorPos && ( bSelection ||
|
|
// without selection, the cursor must be inside the word, not before that
|
|
// to apply the character formatting, as usual
|
|
( nPos > aLine.GetStart() && ' ' != aInf.GetText()[sal_Int32(nPos) - 1] ) ) )
|
|
return true;
|
|
}
|
|
// the hyphenated word starts in the previous line
|
|
if ( aLine.GetStart() > TextFrameIndex(0) )
|
|
{
|
|
TextFrameIndex nPos(aLine.GetStart());
|
|
aLine.CharCursorToLine(nPos - TextFrameIndex(1));
|
|
if ( aLine.GetCurr()->IsEndHyph() )
|
|
{
|
|
while( nPos < nCursorPos && ' ' != aInf.GetText()[sal_Int32(nPos)] )
|
|
++nPos;
|
|
if ( nPos == nCursorPos &&
|
|
( bSelection || ' ' != aInf.GetText()[sal_Int32(nPos)] ) )
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* To the line end: That's the position before the last char of the line.
|
|
* Exception: In the last line, it should be able to place the cursor after
|
|
* the last char in order to append text.
|
|
*/
|
|
|
|
bool SwTextFrame::RightMargin(SwPaM *pPam, bool bAPI) const
|
|
{
|
|
assert(GetMergedPara() || &pPam->GetPointNode() == static_cast<SwContentNode const*>(GetDep()));
|
|
|
|
SwTextFrame *pFrame = GetAdjFrameAtPos( const_cast<SwTextFrame*>(this), *pPam->GetPoint(),
|
|
SwTextCursor::IsRightMargin() );
|
|
pFrame->GetFormatted();
|
|
TextFrameIndex nRightMargin(0);
|
|
if (!IsEmpty())
|
|
{
|
|
SwTextSizeInfo aInf( pFrame );
|
|
SwTextCursor aLine( pFrame, &aInf );
|
|
|
|
aLine.CharCursorToLine(MapModelToViewPos(*pPam->GetPoint()));
|
|
nRightMargin = aLine.GetStart() + aLine.GetCurr()->GetLen();
|
|
|
|
// We skip hard line breaks
|
|
if( aLine.GetCurr()->GetLen() &&
|
|
CH_BREAK == aInf.GetText()[sal_Int32(nRightMargin) - 1])
|
|
--nRightMargin;
|
|
else if( !bAPI && (aLine.GetNext() || pFrame->GetFollow()) )
|
|
{
|
|
while( nRightMargin > aLine.GetStart() &&
|
|
' ' == aInf.GetText()[sal_Int32(nRightMargin) - 1])
|
|
--nRightMargin;
|
|
}
|
|
}
|
|
*pPam->GetPoint() = pFrame->MapViewToModelPos(nRightMargin);
|
|
SwTextCursor::SetRightMargin( !bAPI );
|
|
return true;
|
|
}
|
|
|
|
// The following two methods try to put the Cursor into the next/successive
|
|
// line. If we do not have a preceding/successive line we forward the call
|
|
// to the base class.
|
|
// The Cursor's horizontal justification is done afterwards by the CursorShell.
|
|
|
|
namespace {
|
|
|
|
class SwSetToRightMargin
|
|
{
|
|
bool m_bRight;
|
|
|
|
public:
|
|
SwSetToRightMargin()
|
|
: m_bRight(false)
|
|
{
|
|
}
|
|
~SwSetToRightMargin() { SwTextCursor::SetRightMargin(m_bRight); }
|
|
void SetRight(const bool bNew) { m_bRight = bNew; }
|
|
};
|
|
|
|
}
|
|
|
|
bool SwTextFrame::UnitUp_( SwPaM *pPam, const SwTwips nOffset,
|
|
bool bSetInReadOnly ) const
|
|
{
|
|
// Set the RightMargin if needed
|
|
SwSetToRightMargin aSet;
|
|
|
|
if( IsInTab() &&
|
|
pPam->GetPointNode().StartOfSectionNode() !=
|
|
pPam->GetMarkNode().StartOfSectionNode() )
|
|
{
|
|
// If the PaM is located within different boxes, we have a table selection,
|
|
// which is handled by the base class.
|
|
return SwContentFrame::UnitUp( pPam, nOffset, bSetInReadOnly );
|
|
}
|
|
|
|
const_cast<SwTextFrame*>(this)->GetFormatted();
|
|
const TextFrameIndex nPos = MapModelToViewPos(*pPam->GetPoint());
|
|
SwRect aCharBox;
|
|
|
|
if( !IsEmpty() && !IsHiddenNow() )
|
|
{
|
|
TextFrameIndex nFormat(COMPLETE_STRING);
|
|
do
|
|
{
|
|
if (nFormat != TextFrameIndex(COMPLETE_STRING) && !IsFollow())
|
|
sw_ChangeOffset( const_cast<SwTextFrame*>(this), nFormat );
|
|
|
|
SwTextSizeInfo aInf( const_cast<SwTextFrame*>(this) );
|
|
SwTextCursor aLine( const_cast<SwTextFrame*>(this), &aInf );
|
|
|
|
// Optimize away flys with no flow and IsDummy()
|
|
if( nPos )
|
|
aLine.CharCursorToLine( nPos );
|
|
else
|
|
aLine.Top();
|
|
|
|
const SwLineLayout *pPrevLine = aLine.GetPrevLine();
|
|
const TextFrameIndex nStart = aLine.GetStart();
|
|
aLine.GetCharRect( &aCharBox, nPos );
|
|
|
|
bool bSecondOfDouble = ( aInf.IsMulti() && ! aInf.IsFirstMulti() );
|
|
bool bPrevLine = ( pPrevLine && pPrevLine != aLine.GetCurr() );
|
|
|
|
if( !pPrevLine && !bSecondOfDouble && GetOffset() && !IsFollow() )
|
|
{
|
|
nFormat = GetOffset();
|
|
TextFrameIndex nDiff = aLine.GetLength();
|
|
if( !nDiff )
|
|
nDiff = TextFrameIndex(MIN_OFFSET_STEP);
|
|
if( nFormat > nDiff )
|
|
nFormat = nFormat - nDiff;
|
|
else
|
|
nFormat = TextFrameIndex(0);
|
|
continue;
|
|
}
|
|
|
|
// We select the target line for the cursor, in case we are in a
|
|
// double line portion, prev line = curr line
|
|
if( bPrevLine && !bSecondOfDouble )
|
|
{
|
|
aLine.PrevLine();
|
|
while ( aLine.GetStart() == nStart &&
|
|
nullptr != ( pPrevLine = aLine.GetPrevLine() ) &&
|
|
pPrevLine != aLine.GetCurr() )
|
|
aLine.PrevLine();
|
|
}
|
|
|
|
if ( bPrevLine || bSecondOfDouble )
|
|
{
|
|
aCharBox.Width( aCharBox.SSize().Width() / 2 );
|
|
aCharBox.Pos().setX( aCharBox.Pos().X() - 150 );
|
|
|
|
// See comment in SwTextFrame::GetModelPositionForViewPoint()
|
|
#if OSL_DEBUG_LEVEL > 0
|
|
const SwNodeOffset nOldNode = pPam->GetPoint()->GetNodeIndex();
|
|
#endif
|
|
// The node should not be changed
|
|
TextFrameIndex nTmpOfst = aLine.GetModelPositionForViewPoint(pPam->GetPoint(),
|
|
aCharBox.Pos(), false );
|
|
#if OSL_DEBUG_LEVEL > 0
|
|
OSL_ENSURE( nOldNode == pPam->GetPoint()->GetNodeIndex(),
|
|
"SwTextFrame::UnitUp: illegal node change" );
|
|
#endif
|
|
|
|
// We make sure that we move up.
|
|
if( nTmpOfst >= nStart && nStart && !bSecondOfDouble )
|
|
{
|
|
nTmpOfst = nStart;
|
|
aSet.SetRight( true );
|
|
}
|
|
*pPam->GetPoint() = MapViewToModelPos(nTmpOfst);
|
|
return true;
|
|
}
|
|
|
|
if ( IsFollow() )
|
|
{
|
|
aLine.GetCharRect( &aCharBox, nPos );
|
|
aCharBox.Width( aCharBox.SSize().Width() / 2 );
|
|
}
|
|
break;
|
|
} while ( true );
|
|
}
|
|
/* If 'this' is a follow and a prev failed, we need to go to the
|
|
* last line of the master, which is us.
|
|
* Or: If we are a follow with follow, we need to get the master.
|
|
*/
|
|
if ( IsFollow() )
|
|
{
|
|
const SwTextFrame *pTmpPrev = FindMaster();
|
|
TextFrameIndex nOffs = GetOffset();
|
|
if( pTmpPrev )
|
|
{
|
|
SwViewShell *pSh = getRootFrame()->GetCurrShell();
|
|
const bool bProtectedAllowed = pSh && pSh->GetViewOptions()->IsCursorInProtectedArea();
|
|
const SwTextFrame *pPrevPrev = pTmpPrev;
|
|
// We skip protected frames and frames without content here
|
|
while( pPrevPrev && ( pPrevPrev->GetOffset() == nOffs ||
|
|
( !bProtectedAllowed && pPrevPrev->IsProtected() ) ) )
|
|
{
|
|
pTmpPrev = pPrevPrev;
|
|
nOffs = pTmpPrev->GetOffset();
|
|
if ( pPrevPrev->IsFollow() )
|
|
pPrevPrev = pTmpPrev->FindMaster();
|
|
else
|
|
pPrevPrev = nullptr;
|
|
}
|
|
if ( !pPrevPrev )
|
|
return pTmpPrev->SwContentFrame::UnitUp( pPam, nOffset, bSetInReadOnly );
|
|
aCharBox.Pos().setY( pPrevPrev->getFrameArea().Bottom() - 1 );
|
|
return pPrevPrev->GetKeyCursorOfst( pPam->GetPoint(), aCharBox.Pos() );
|
|
}
|
|
}
|
|
return SwContentFrame::UnitUp( pPam, nOffset, bSetInReadOnly );
|
|
}
|
|
|
|
// Used for Bidi. nPos is the logical position in the string, bLeft indicates
|
|
// if left arrow or right arrow was pressed. The return values are:
|
|
// nPos: the new visual position
|
|
// bLeft: whether the break iterator has to add or subtract from the
|
|
// current position
|
|
static void lcl_VisualMoveRecursion(const SwLineLayout& rCurrLine, TextFrameIndex nIdx,
|
|
TextFrameIndex & nPos, bool& bRight,
|
|
sal_uInt8& nCursorLevel, sal_uInt8 nDefaultDir )
|
|
{
|
|
const SwLinePortion* pPor = rCurrLine.GetFirstPortion();
|
|
const SwLinePortion* pLast = nullptr;
|
|
|
|
// What's the current portion?
|
|
while ( pPor && nIdx + pPor->GetLen() <= nPos )
|
|
{
|
|
nIdx = nIdx + pPor->GetLen();
|
|
pLast = pPor;
|
|
pPor = pPor->GetNextPortion();
|
|
}
|
|
|
|
if ( bRight )
|
|
{
|
|
bool bRecurse = pPor && pPor->IsMultiPortion() &&
|
|
static_cast<const SwMultiPortion*>(pPor)->IsBidi();
|
|
|
|
// 1. special case: at beginning of bidi portion
|
|
if ( bRecurse && nIdx == nPos )
|
|
{
|
|
nPos = nPos + pPor->GetLen();
|
|
|
|
// leave bidi portion
|
|
if ( nCursorLevel != nDefaultDir )
|
|
{
|
|
bRecurse = false;
|
|
}
|
|
else
|
|
// special case:
|
|
// buffer: abcXYZ123 in LTR paragraph
|
|
// view: abc123ZYX
|
|
// cursor is between c and X in the buffer and cursor level = 0
|
|
nCursorLevel++;
|
|
}
|
|
|
|
// 2. special case: at beginning of portion after bidi portion
|
|
else if ( pLast && pLast->IsMultiPortion() &&
|
|
static_cast<const SwMultiPortion*>(pLast)->IsBidi() && nIdx == nPos )
|
|
{
|
|
// enter bidi portion
|
|
if ( nCursorLevel != nDefaultDir )
|
|
{
|
|
bRecurse = true;
|
|
nIdx = nIdx - pLast->GetLen();
|
|
pPor = pLast;
|
|
}
|
|
}
|
|
|
|
// Recursion
|
|
if ( bRecurse )
|
|
{
|
|
const SwLineLayout& rLine = static_cast<const SwMultiPortion*>(pPor)->GetRoot();
|
|
TextFrameIndex nTmpPos = nPos - nIdx;
|
|
bool bTmpForward = ! bRight;
|
|
sal_uInt8 nTmpCursorLevel = nCursorLevel;
|
|
lcl_VisualMoveRecursion(rLine, TextFrameIndex(0), nTmpPos, bTmpForward,
|
|
nTmpCursorLevel, nDefaultDir + 1 );
|
|
|
|
nPos = nTmpPos + nIdx;
|
|
bRight = bTmpForward;
|
|
nCursorLevel = nTmpCursorLevel;
|
|
}
|
|
|
|
// go forward
|
|
else
|
|
{
|
|
bRight = true;
|
|
nCursorLevel = nDefaultDir;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
bool bRecurse = pPor && pPor->IsMultiPortion() && static_cast<const SwMultiPortion*>(pPor)->IsBidi();
|
|
|
|
// 1. special case: at beginning of bidi portion
|
|
if ( bRecurse && nIdx == nPos )
|
|
{
|
|
// leave bidi portion
|
|
if ( nCursorLevel == nDefaultDir )
|
|
{
|
|
bRecurse = false;
|
|
}
|
|
}
|
|
|
|
// 2. special case: at beginning of portion after bidi portion
|
|
else if ( pLast && pLast->IsMultiPortion() &&
|
|
static_cast<const SwMultiPortion*>(pLast)->IsBidi() && nIdx == nPos )
|
|
{
|
|
nPos = nPos - pLast->GetLen();
|
|
|
|
// enter bidi portion
|
|
if ( nCursorLevel % 2 == nDefaultDir % 2 )
|
|
{
|
|
bRecurse = true;
|
|
nIdx = nIdx - pLast->GetLen();
|
|
pPor = pLast;
|
|
|
|
// special case:
|
|
// buffer: abcXYZ123 in LTR paragraph
|
|
// view: abc123ZYX
|
|
// cursor is behind 3 in the buffer and cursor level = 2
|
|
if ( nDefaultDir + 2 == nCursorLevel )
|
|
nPos = nPos + pLast->GetLen();
|
|
}
|
|
}
|
|
|
|
// go forward
|
|
if ( bRecurse )
|
|
{
|
|
const SwLineLayout& rLine = static_cast<const SwMultiPortion*>(pPor)->GetRoot();
|
|
TextFrameIndex nTmpPos = nPos - nIdx;
|
|
bool bTmpForward = ! bRight;
|
|
sal_uInt8 nTmpCursorLevel = nCursorLevel;
|
|
lcl_VisualMoveRecursion(rLine, TextFrameIndex(0), nTmpPos, bTmpForward,
|
|
nTmpCursorLevel, nDefaultDir + 1 );
|
|
|
|
// special case:
|
|
// buffer: abcXYZ123 in LTR paragraph
|
|
// view: abc123ZYX
|
|
// cursor is between Z and 1 in the buffer and cursor level = 2
|
|
if ( nTmpPos == pPor->GetLen() && nTmpCursorLevel == nDefaultDir + 1 )
|
|
{
|
|
nTmpPos = nTmpPos - pPor->GetLen();
|
|
nTmpCursorLevel = nDefaultDir;
|
|
bTmpForward = ! bTmpForward;
|
|
}
|
|
|
|
nPos = nTmpPos + nIdx;
|
|
bRight = bTmpForward;
|
|
nCursorLevel = nTmpCursorLevel;
|
|
}
|
|
|
|
// go backward
|
|
else
|
|
{
|
|
bRight = false;
|
|
nCursorLevel = nDefaultDir;
|
|
}
|
|
}
|
|
}
|
|
|
|
void SwTextFrame::PrepareVisualMove(TextFrameIndex & nPos, sal_uInt8& nCursorLevel,
|
|
bool& bForward, bool bInsertCursor )
|
|
{
|
|
if( IsEmpty() || IsHiddenNow() )
|
|
return;
|
|
|
|
GetFormatted();
|
|
|
|
SwTextSizeInfo aInf(this);
|
|
SwTextCursor aLine(this, &aInf);
|
|
|
|
if( nPos )
|
|
aLine.CharCursorToLine( nPos );
|
|
else
|
|
aLine.Top();
|
|
|
|
const SwLineLayout* pLine = aLine.GetCurr();
|
|
const TextFrameIndex nStt = aLine.GetStart();
|
|
const TextFrameIndex nLen = pLine->GetLen();
|
|
|
|
// We have to distinguish between an insert and overwrite cursor:
|
|
// The insert cursor position depends on the cursor level:
|
|
// buffer: abcXYZdef in LTR paragraph
|
|
// display: abcZYXdef
|
|
// If cursor is between c and X in the buffer and cursor level is 0,
|
|
// the cursor blinks between c and Z and -> sets the cursor between Z and Y.
|
|
// If the cursor level is 1, the cursor blinks between X and d and
|
|
// -> sets the cursor between d and e.
|
|
// The overwrite cursor simply travels to the next visual character.
|
|
if ( bInsertCursor )
|
|
{
|
|
lcl_VisualMoveRecursion( *pLine, nStt, nPos, bForward,
|
|
nCursorLevel, IsRightToLeft() ? 1 : 0 );
|
|
return;
|
|
}
|
|
|
|
const sal_uInt8 nDefaultDir = static_cast<sal_uInt8>(IsRightToLeft() ? UBIDI_RTL : UBIDI_LTR);
|
|
const bool bVisualRight = ( nDefaultDir == UBIDI_LTR && bForward ) ||
|
|
( nDefaultDir == UBIDI_RTL && ! bForward );
|
|
|
|
// Bidi functions from icu 2.0
|
|
|
|
const sal_Unicode* pLineString = GetText().getStr();
|
|
|
|
UErrorCode nError = U_ZERO_ERROR;
|
|
UBiDi* pBidi = ubidi_openSized( sal_Int32(nLen), 0, &nError );
|
|
ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(pLineString),
|
|
sal_Int32(nLen), nDefaultDir, nullptr, &nError );
|
|
|
|
TextFrameIndex nTmpPos(0);
|
|
bool bOutOfBounds = false;
|
|
|
|
if ( nPos < nStt + nLen )
|
|
{
|
|
nTmpPos = TextFrameIndex(ubidi_getVisualIndex( pBidi, sal_Int32(nPos), &nError ));
|
|
|
|
// visual indices are always LTR aligned
|
|
if ( bVisualRight )
|
|
{
|
|
if (nTmpPos + TextFrameIndex(1) < nStt + nLen)
|
|
++nTmpPos;
|
|
else
|
|
{
|
|
nPos = nDefaultDir == UBIDI_RTL ? TextFrameIndex(0) : nStt + nLen;
|
|
bOutOfBounds = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( nTmpPos )
|
|
--nTmpPos;
|
|
else
|
|
{
|
|
nPos = nDefaultDir == UBIDI_RTL ? nStt + nLen : TextFrameIndex(0);
|
|
bOutOfBounds = true;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
nTmpPos = nDefaultDir == UBIDI_LTR ? nPos - TextFrameIndex(1) : TextFrameIndex(0);
|
|
}
|
|
|
|
if ( ! bOutOfBounds )
|
|
{
|
|
nPos = TextFrameIndex(ubidi_getLogicalIndex( pBidi, sal_Int32(nTmpPos), &nError ));
|
|
|
|
if ( bForward )
|
|
{
|
|
if ( nPos )
|
|
--nPos;
|
|
else
|
|
{
|
|
++nPos;
|
|
bForward = ! bForward;
|
|
}
|
|
}
|
|
else
|
|
++nPos;
|
|
}
|
|
|
|
ubidi_close( pBidi );
|
|
}
|
|
|
|
bool SwTextFrame::UnitDown_(SwPaM *pPam, const SwTwips nOffset,
|
|
bool bSetInReadOnly ) const
|
|
{
|
|
|
|
if ( IsInTab() &&
|
|
pPam->GetPointNode().StartOfSectionNode() !=
|
|
pPam->GetMarkNode().StartOfSectionNode() )
|
|
{
|
|
// If the PaM is located within different boxes, we have a table selection,
|
|
// which is handled by the base class.
|
|
return SwContentFrame::UnitDown( pPam, nOffset, bSetInReadOnly );
|
|
}
|
|
const_cast<SwTextFrame*>(this)->GetFormatted();
|
|
const TextFrameIndex nPos = MapModelToViewPos(*pPam->GetPoint());
|
|
SwRect aCharBox;
|
|
const SwContentFrame *pTmpFollow = nullptr;
|
|
|
|
if ( IsVertical() )
|
|
const_cast<SwTextFrame*>(this)->SwapWidthAndHeight();
|
|
|
|
if ( !IsEmpty() && !IsHiddenNow() )
|
|
{
|
|
TextFrameIndex nFormat(COMPLETE_STRING);
|
|
do
|
|
{
|
|
if (nFormat != TextFrameIndex(COMPLETE_STRING) && !IsFollow() &&
|
|
!sw_ChangeOffset( const_cast<SwTextFrame*>(this), nFormat ) )
|
|
break;
|
|
|
|
SwTextSizeInfo aInf( const_cast<SwTextFrame*>(this) );
|
|
SwTextCursor aLine( const_cast<SwTextFrame*>(this), &aInf );
|
|
nFormat = aLine.GetEnd();
|
|
|
|
aLine.CharCursorToLine( nPos );
|
|
|
|
const SwLineLayout* pNextLine = aLine.GetNextLine();
|
|
const TextFrameIndex nStart = aLine.GetStart();
|
|
aLine.GetCharRect( &aCharBox, nPos );
|
|
|
|
bool bFirstOfDouble = ( aInf.IsMulti() && aInf.IsFirstMulti() );
|
|
|
|
if( pNextLine || bFirstOfDouble )
|
|
{
|
|
aCharBox.Width( aCharBox.SSize().Width() / 2 );
|
|
#if OSL_DEBUG_LEVEL > 0
|
|
// See comment in SwTextFrame::GetModelPositionForViewPoint()
|
|
const SwNodeOffset nOldNode = pPam->GetPoint()->GetNodeIndex();
|
|
#endif
|
|
if ( pNextLine && ! bFirstOfDouble )
|
|
aLine.NextLine();
|
|
|
|
TextFrameIndex nTmpOfst = aLine.GetModelPositionForViewPoint( pPam->GetPoint(),
|
|
aCharBox.Pos(), false );
|
|
#if OSL_DEBUG_LEVEL > 0
|
|
OSL_ENSURE( nOldNode == pPam->GetPoint()->GetNodeIndex(),
|
|
"SwTextFrame::UnitDown: illegal node change" );
|
|
#endif
|
|
|
|
// We make sure that we move down.
|
|
if( nTmpOfst <= nStart && ! bFirstOfDouble )
|
|
nTmpOfst = nStart + TextFrameIndex(1);
|
|
*pPam->GetPoint() = MapViewToModelPos(nTmpOfst);
|
|
|
|
if ( IsVertical() )
|
|
const_cast<SwTextFrame*>(this)->SwapWidthAndHeight();
|
|
|
|
return true;
|
|
}
|
|
pTmpFollow = GetFollow();
|
|
if( nullptr != pTmpFollow )
|
|
{ // Skip protected follows
|
|
const SwContentFrame* pTmp = pTmpFollow;
|
|
SwViewShell *pSh = getRootFrame()->GetCurrShell();
|
|
if( !pSh || !pSh->GetViewOptions()->IsCursorInProtectedArea() )
|
|
{
|
|
while( pTmpFollow && pTmpFollow->IsProtected() )
|
|
{
|
|
pTmp = pTmpFollow;
|
|
pTmpFollow = pTmpFollow->GetFollow();
|
|
}
|
|
}
|
|
if( !pTmpFollow ) // Only protected ones left
|
|
{
|
|
if ( IsVertical() )
|
|
const_cast<SwTextFrame*>(this)->SwapWidthAndHeight();
|
|
return pTmp->SwContentFrame::UnitDown( pPam, nOffset, bSetInReadOnly );
|
|
}
|
|
|
|
aLine.GetCharRect( &aCharBox, nPos );
|
|
aCharBox.Width( aCharBox.SSize().Width() / 2 );
|
|
}
|
|
else if( !IsFollow() )
|
|
{
|
|
TextFrameIndex nTmpLen(aInf.GetText().getLength());
|
|
if( aLine.GetEnd() < nTmpLen )
|
|
{
|
|
if( nFormat <= GetOffset() )
|
|
{
|
|
nFormat = std::min(GetOffset() + TextFrameIndex(MIN_OFFSET_STEP),
|
|
nTmpLen );
|
|
if( nFormat <= GetOffset() )
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
break;
|
|
} while( true );
|
|
}
|
|
else
|
|
pTmpFollow = GetFollow();
|
|
|
|
if ( IsVertical() )
|
|
const_cast<SwTextFrame*>(this)->SwapWidthAndHeight();
|
|
|
|
// We take a shortcut for follows
|
|
if( pTmpFollow )
|
|
{
|
|
aCharBox.Pos().setY( pTmpFollow->getFrameArea().Top() + 1 );
|
|
return static_cast<const SwTextFrame*>(pTmpFollow)->GetKeyCursorOfst( pPam->GetPoint(),
|
|
aCharBox.Pos() );
|
|
}
|
|
return SwContentFrame::UnitDown( pPam, nOffset, bSetInReadOnly );
|
|
}
|
|
|
|
bool SwTextFrame::UnitUp(SwPaM *pPam, const SwTwips nOffset,
|
|
bool bSetInReadOnly ) const
|
|
{
|
|
/* We call ContentNode::GertFrame() in CursorSh::Up().
|
|
* This _always returns the master.
|
|
* In order to not mess with cursor travelling, we correct here
|
|
* in SwTextFrame.
|
|
* We calculate UnitUp for pFrame. pFrame is either a master (= this) or a
|
|
* follow (!= this).
|
|
*/
|
|
const SwTextFrame *pFrame = GetAdjFrameAtPos( const_cast<SwTextFrame*>(this), *(pPam->GetPoint()),
|
|
SwTextCursor::IsRightMargin() );
|
|
const bool bRet = pFrame->UnitUp_( pPam, nOffset, bSetInReadOnly );
|
|
|
|
// No SwTextCursor::SetRightMargin( false );
|
|
// Instead we have a SwSetToRightMargin in UnitUp_
|
|
return bRet;
|
|
}
|
|
|
|
bool SwTextFrame::UnitDown(SwPaM *pPam, const SwTwips nOffset,
|
|
bool bSetInReadOnly ) const
|
|
{
|
|
const SwTextFrame *pFrame = GetAdjFrameAtPos(const_cast<SwTextFrame*>(this), *(pPam->GetPoint()),
|
|
SwTextCursor::IsRightMargin() );
|
|
const bool bRet = pFrame->UnitDown_( pPam, nOffset, bSetInReadOnly );
|
|
SwTextCursor::SetRightMargin( false );
|
|
return bRet;
|
|
}
|
|
|
|
void SwTextFrame::FillCursorPos( SwFillData& rFill ) const
|
|
{
|
|
if( !rFill.bColumn && GetUpper()->IsColBodyFrame() ) // ColumnFrames now with BodyFrame
|
|
{
|
|
const SwColumnFrame* pTmp =
|
|
static_cast<const SwColumnFrame*>(GetUpper()->GetUpper()->GetUpper()->Lower()); // The 1st column
|
|
// The first SwFrame in BodyFrame of the first column
|
|
const SwFrame* pFrame = static_cast<const SwLayoutFrame*>(pTmp->Lower())->Lower();
|
|
sal_uInt16 nNextCol = 0;
|
|
// In which column do we end up in?
|
|
while( rFill.X() > pTmp->getFrameArea().Right() && pTmp->GetNext() )
|
|
{
|
|
pTmp = static_cast<const SwColumnFrame*>(pTmp->GetNext());
|
|
if( static_cast<const SwLayoutFrame*>(pTmp->Lower())->Lower() ) // ColumnFrames now with BodyFrame
|
|
{
|
|
pFrame = static_cast<const SwLayoutFrame*>(pTmp->Lower())->Lower();
|
|
nNextCol = 0;
|
|
}
|
|
else
|
|
++nNextCol; // Empty columns require column breaks
|
|
}
|
|
if( pTmp != GetUpper()->GetUpper() ) // Did we end up in another column?
|
|
{
|
|
if( !pFrame )
|
|
return;
|
|
if( nNextCol )
|
|
{
|
|
while( pFrame->GetNext() )
|
|
pFrame = pFrame->GetNext();
|
|
}
|
|
else
|
|
{
|
|
while( pFrame->GetNext() && pFrame->getFrameArea().Bottom() < rFill.Y() )
|
|
pFrame = pFrame->GetNext();
|
|
}
|
|
// No filling, if the last frame in the targeted column does
|
|
// not contain a paragraph, but e.g. a table
|
|
if( pFrame->IsTextFrame() )
|
|
{
|
|
rFill.Fill().nColumnCnt = nNextCol;
|
|
rFill.bColumn = true;
|
|
if( rFill.pPos )
|
|
{
|
|
SwTextFrame const*const pTextFrame(static_cast<const SwTextFrame*>(pFrame));
|
|
*rFill.pPos = pTextFrame->MapViewToModelPos(
|
|
TextFrameIndex(pTextFrame->GetText().getLength()));
|
|
}
|
|
if( nNextCol )
|
|
{
|
|
rFill.aFrame = pTmp->getFramePrintArea();
|
|
rFill.aFrame += pTmp->getFrameArea().Pos();
|
|
}
|
|
else
|
|
rFill.aFrame = pFrame->getFrameArea();
|
|
static_cast<const SwTextFrame*>(pFrame)->FillCursorPos( rFill );
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
std::unique_ptr<SwFont> pFnt;
|
|
SwTextFormatColl* pColl = GetTextNodeForParaProps()->GetTextColl();
|
|
SwTwips nFirst = GetTextNodeForParaProps()->GetSwAttrSet().GetULSpace().GetLower();
|
|
SwTwips nDiff = rFill.Y() - getFrameArea().Bottom();
|
|
if( nDiff < nFirst )
|
|
nDiff = -1;
|
|
else
|
|
pColl = &pColl->GetNextTextFormatColl();
|
|
SwAttrSet aSet(const_cast<SwDoc&>(GetDoc()).GetAttrPool(), aTextFormatCollSetRange );
|
|
const SwAttrSet* pSet = &pColl->GetAttrSet();
|
|
SwViewShell *pSh = getRootFrame()->GetCurrShell();
|
|
if (GetTextNodeForParaProps()->HasSwAttrSet())
|
|
{
|
|
// sw_redlinehide: pSet is mostly used for para props, but there are
|
|
// accesses to char props via pFnt - why does it use only the node's
|
|
// props for this, and not hints?
|
|
aSet.Put( *GetTextNodeForParaProps()->GetpSwAttrSet() );
|
|
aSet.SetParent( pSet );
|
|
pSet = &aSet;
|
|
pFnt.reset(new SwFont( pSet, &GetDoc().getIDocumentSettingAccess() ));
|
|
}
|
|
else
|
|
{
|
|
SwFontAccess aFontAccess( pColl, pSh );
|
|
pFnt.reset(new SwFont( aFontAccess.Get()->GetFont() ));
|
|
pFnt->CheckFontCacheId( pSh, pFnt->GetActual() );
|
|
}
|
|
OutputDevice* pOut = pSh->GetOut();
|
|
if( !pSh->GetViewOptions()->getBrowseMode() || pSh->GetViewOptions()->IsPrtFormat() )
|
|
pOut = GetDoc().getIDocumentDeviceAccess().getReferenceDevice( true );
|
|
|
|
pFnt->SetFntChg( true );
|
|
pFnt->ChgPhysFnt( pSh, *pOut );
|
|
|
|
SwTwips nLineHeight = pFnt->GetHeight( pSh, *pOut );
|
|
|
|
bool bFill = false;
|
|
if( nLineHeight )
|
|
{
|
|
bFill = true;
|
|
const SvxULSpaceItem &rUL = pSet->GetULSpace();
|
|
SwTwips nDist = std::max( rUL.GetLower(), rUL.GetUpper() );
|
|
if( rFill.Fill().nColumnCnt )
|
|
{
|
|
rFill.aFrame.Height( nLineHeight );
|
|
nDiff = rFill.Y() - rFill.Bottom();
|
|
nFirst = 0;
|
|
}
|
|
else if( nDist < nFirst )
|
|
nFirst = nFirst - nDist;
|
|
else
|
|
nFirst = 0;
|
|
nDist = std::max( nDist, SwTwips(GetLineSpace()) );
|
|
nDist += nLineHeight;
|
|
nDiff -= nFirst;
|
|
|
|
if( nDiff > 0 )
|
|
{
|
|
nDiff /= nDist;
|
|
rFill.Fill().nParaCnt = o3tl::narrowing<sal_uInt16>(nDiff + 1);
|
|
rFill.nLineWidth = 0;
|
|
rFill.bInner = false;
|
|
rFill.bEmpty = true;
|
|
rFill.SetOrient( text::HoriOrientation::LEFT );
|
|
}
|
|
else
|
|
nDiff = -1;
|
|
if( rFill.bInner )
|
|
bFill = false;
|
|
else
|
|
{
|
|
const SvxTabStopItem &rRuler = pSet->GetTabStops();
|
|
const SvxFirstLineIndentItem& rFirstLine(pSet->GetFirstLineIndent());
|
|
const SvxTextLeftMarginItem& rTextLeftMargin(pSet->GetTextLeftMargin());
|
|
const SvxRightMarginItem& rRightMargin(pSet->GetRightMargin());
|
|
|
|
SwRect &rRect = rFill.Fill().aCursor;
|
|
rRect.Top( rFill.Bottom() + (nDiff+1) * nDist - nLineHeight );
|
|
if( nFirst && nDiff > -1 )
|
|
rRect.Top( rRect.Top() + nFirst );
|
|
rRect.Height( nLineHeight );
|
|
|
|
SwTwips nLeft = rFill.Left() + rTextLeftMargin.ResolveLeft(rFirstLine, /*metrics*/ {})
|
|
+ GetTextNodeForParaProps()->GetLeftMarginWithNum();
|
|
SwTwips nRight = rFill.Right() - rRightMargin.ResolveRight({});
|
|
SwTwips nCenter = ( nLeft + nRight ) / 2;
|
|
rRect.Left( nLeft );
|
|
if( SwFillMode::Margin == rFill.Mode() )
|
|
{
|
|
if( rFill.bEmpty )
|
|
{
|
|
rFill.SetOrient( text::HoriOrientation::LEFT );
|
|
if( rFill.X() < nCenter )
|
|
{
|
|
if( rFill.X() > ( nLeft + 2 * nCenter ) / 3 )
|
|
{
|
|
rFill.SetOrient( text::HoriOrientation::CENTER );
|
|
rRect.Left( nCenter );
|
|
}
|
|
}
|
|
else if( rFill.X() > ( nRight + 2 * nCenter ) / 3 )
|
|
{
|
|
rFill.SetOrient( text::HoriOrientation::RIGHT );
|
|
rRect.Left( nRight );
|
|
}
|
|
else
|
|
{
|
|
rFill.SetOrient( text::HoriOrientation::CENTER );
|
|
rRect.Left( nCenter );
|
|
}
|
|
}
|
|
else
|
|
bFill = false;
|
|
}
|
|
else
|
|
{
|
|
SwTwips nSpace = 0;
|
|
if( SwFillMode::Tab != rFill.Mode() )
|
|
{
|
|
SwDrawTextInfo aDrawInf( pSh, *pOut, u" "_ustr, 0, 2 );
|
|
nSpace = pFnt->GetTextSize_( aDrawInf ).Width()/2;
|
|
}
|
|
if( rFill.X() >= nRight )
|
|
{
|
|
if( SwFillMode::Indent != rFill.Mode() && ( rFill.bEmpty ||
|
|
rFill.X() > rFill.nLineWidth + FILL_MIN_DIST ) )
|
|
{
|
|
rFill.SetOrient( text::HoriOrientation::RIGHT );
|
|
rRect.Left( nRight );
|
|
}
|
|
else
|
|
bFill = false;
|
|
}
|
|
else if( SwFillMode::Indent == rFill.Mode() )
|
|
{
|
|
SwTwips nIndent = rFill.X();
|
|
if( !rFill.bEmpty || nIndent > nRight )
|
|
bFill = false;
|
|
else
|
|
{
|
|
nIndent -= rFill.Left();
|
|
if( nIndent >= 0 && nSpace )
|
|
{
|
|
nIndent /= nSpace;
|
|
nIndent *= nSpace;
|
|
rFill.SetTab( sal_uInt16( nIndent ) );
|
|
rRect.Left( nIndent + rFill.Left() );
|
|
}
|
|
else
|
|
bFill = false;
|
|
}
|
|
}
|
|
else if( rFill.X() > nLeft )
|
|
{
|
|
SwTwips nTextLeft = rFill.Left() + rTextLeftMargin.ResolveTextLeft({})
|
|
+ GetTextNodeForParaProps()->GetLeftMarginWithNum(true);
|
|
rFill.nLineWidth += rFill.bFirstLine ? nLeft : nTextLeft;
|
|
SwTwips nLeftTab;
|
|
SwTwips nRightTab = nLeft;
|
|
sal_uInt16 nSpaceCnt = 0;
|
|
sal_uInt16 nSpaceOnlyCnt = 0;
|
|
sal_uInt16 nIdx = 0;
|
|
int nTabCnt = 0;
|
|
do
|
|
{
|
|
nLeftTab = nRightTab;
|
|
if( nIdx < rRuler.Count() )
|
|
{
|
|
const SvxTabStop &rTabStop = rRuler.operator[](nIdx);
|
|
nRightTab = nTextLeft + rTabStop.GetTabPos();
|
|
if( nLeftTab < nTextLeft && nRightTab > nTextLeft )
|
|
nRightTab = nTextLeft;
|
|
else
|
|
++nIdx;
|
|
if( nRightTab > rFill.nLineWidth )
|
|
++nTabCnt;
|
|
}
|
|
else
|
|
{
|
|
const SvxTabStopItem& rTab =
|
|
pSet->GetPool()->GetUserOrPoolDefaultItem( RES_PARATR_TABSTOP );
|
|
const SwTwips nDefTabDist = rTab[0].GetTabPos();
|
|
nRightTab = nLeftTab - nTextLeft;
|
|
nRightTab /= nDefTabDist;
|
|
nRightTab = nRightTab * nDefTabDist + nTextLeft;
|
|
while ( nRightTab <= nLeftTab )
|
|
nRightTab += nDefTabDist;
|
|
if( nRightTab > rFill.nLineWidth )
|
|
++nTabCnt;
|
|
while ( nRightTab < rFill.X() )
|
|
{
|
|
nRightTab += nDefTabDist;
|
|
if( nRightTab > rFill.nLineWidth )
|
|
++nTabCnt;
|
|
}
|
|
if( nLeftTab < nRightTab - nDefTabDist )
|
|
nLeftTab = nRightTab - nDefTabDist;
|
|
}
|
|
if( nRightTab > nRight )
|
|
nRightTab = nRight;
|
|
}
|
|
while( rFill.X() > nRightTab );
|
|
--nTabCnt;
|
|
if( SwFillMode::TabSpace == rFill.Mode() )
|
|
{
|
|
if( nSpace > 0 )
|
|
{
|
|
if( !nTabCnt )
|
|
nLeftTab = rFill.nLineWidth;
|
|
while( nLeftTab < rFill.X() )
|
|
{
|
|
nLeftTab += nSpace;
|
|
++nSpaceCnt;
|
|
}
|
|
if( nSpaceCnt )
|
|
{
|
|
nLeftTab -= nSpace;
|
|
--nSpaceCnt;
|
|
}
|
|
if( rFill.X() - nLeftTab > nRightTab - rFill.X() )
|
|
{
|
|
nSpaceCnt = 0;
|
|
++nTabCnt;
|
|
rRect.Left( nRightTab );
|
|
}
|
|
else
|
|
{
|
|
if( rFill.X() - nLeftTab > nSpace/2 )
|
|
{
|
|
++nSpaceCnt;
|
|
rRect.Left( nLeftTab + nSpace );
|
|
}
|
|
else
|
|
rRect.Left( nLeftTab );
|
|
}
|
|
}
|
|
else if( rFill.X() - nLeftTab < nRightTab - rFill.X() )
|
|
rRect.Left( nLeftTab );
|
|
else
|
|
{
|
|
if( nRightTab >= nRight )
|
|
{
|
|
rFill.SetOrient( text::HoriOrientation::RIGHT );
|
|
rRect.Left( nRight );
|
|
nTabCnt = 0;
|
|
nSpaceCnt = 0;
|
|
}
|
|
else
|
|
{
|
|
rRect.Left( nRightTab );
|
|
++nTabCnt;
|
|
}
|
|
}
|
|
}
|
|
else if( SwFillMode::Space == rFill.Mode() )
|
|
{
|
|
SwTwips nLeftSpace = nLeft;
|
|
while( nLeftSpace < rFill.X() )
|
|
{
|
|
nLeftSpace += nSpace;
|
|
++nSpaceOnlyCnt;
|
|
}
|
|
rRect.Left( nLeftSpace );
|
|
}
|
|
else
|
|
{
|
|
if( rFill.X() - nLeftTab < nRightTab - rFill.X() )
|
|
rRect.Left( nLeftTab );
|
|
else
|
|
{
|
|
if( nRightTab >= nRight )
|
|
{
|
|
rFill.SetOrient( text::HoriOrientation::RIGHT );
|
|
rRect.Left( nRight );
|
|
nTabCnt = 0;
|
|
nSpaceCnt = 0;
|
|
}
|
|
else
|
|
{
|
|
rRect.Left( nRightTab );
|
|
++nTabCnt;
|
|
}
|
|
}
|
|
}
|
|
rFill.SetTab( nTabCnt );
|
|
rFill.SetSpace( nSpaceCnt );
|
|
rFill.SetSpaceOnly( nSpaceOnlyCnt );
|
|
if( bFill )
|
|
{
|
|
if( std::abs( rFill.X() - nCenter ) <=
|
|
std::abs( rFill.X() - rRect.Left() ) )
|
|
{
|
|
rFill.SetOrient( text::HoriOrientation::CENTER );
|
|
rFill.SetTab( 0 );
|
|
rFill.SetSpace( 0 );
|
|
rFill.SetSpaceOnly( 0 );
|
|
rRect.Left( nCenter );
|
|
}
|
|
if( !rFill.bEmpty )
|
|
rFill.nLineWidth += FILL_MIN_DIST;
|
|
if( rRect.Left() < rFill.nLineWidth )
|
|
bFill = false;
|
|
}
|
|
}
|
|
}
|
|
// Do we extend over the page's/column's/etc. lower edge?
|
|
const SwFrame* pUp = GetUpper();
|
|
if( pUp->IsInSct() )
|
|
{
|
|
if( pUp->IsSctFrame() )
|
|
pUp = pUp->GetUpper();
|
|
else if( pUp->IsColBodyFrame() &&
|
|
pUp->GetUpper()->GetUpper()->IsSctFrame() )
|
|
pUp = pUp->GetUpper()->GetUpper()->GetUpper();
|
|
}
|
|
SwRectFnSet aRectFnSet(this);
|
|
SwTwips nLimit = aRectFnSet.GetPrtBottom(*pUp);
|
|
SwTwips nRectBottom = rRect.Bottom();
|
|
if ( aRectFnSet.IsVert() )
|
|
nRectBottom = SwitchHorizontalToVertical( nRectBottom );
|
|
|
|
if( aRectFnSet.YDiff( nLimit, nRectBottom ) < 0 )
|
|
bFill = false;
|
|
else
|
|
rRect.Width( 1 );
|
|
}
|
|
}
|
|
const_cast<SwCursorMoveState*>(rFill.pCMS)->m_bFillRet = bFill;
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|