summaryrefslogtreecommitdiffstats
path: root/sw/source/core/text/frmcrsr.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sw/source/core/text/frmcrsr.cxx')
-rw-r--r--sw/source/core/text/frmcrsr.cxx1689
1 files changed, 1689 insertions, 0 deletions
diff --git a/sw/source/core/text/frmcrsr.cxx b/sw/source/core/text/frmcrsr.cxx
new file mode 100644
index 000000000..58792ecf8
--- /dev/null
+++ b/sw/source/core/text/frmcrsr.cxx
@@ -0,0 +1,1689 @@
+/* -*- 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() || IsHiddenNow() )
+ 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();
+ OSL_ENSURE( 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->GetNode() == 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;
+}
+
+/*
+ * 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->GetNode() == 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->GetNode().StartOfSectionNode() !=
+ pPam->GetNode( false ).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()->nNode.GetIndex();
+#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()->nNode.GetIndex(),
+ "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->GetNode().StartOfSectionNode() !=
+ pPam->GetNode( false ).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()->nNode.GetIndex();
+#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()->nNode.GetIndex(),
+ "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 SvxLRSpaceItem &rLRSpace = pSet->GetLRSpace();
+
+ 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() + rLRSpace.GetLeft() +
+ GetTextNodeForParaProps()->GetLeftMarginWithNum();
+ SwTwips nRight = rFill.Right() - rLRSpace.GetRight();
+ 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, " ", 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() + rLRSpace.GetTextLeft() +
+ GetTextNodeForParaProps()->GetLeftMarginWithNum(true);
+ rFill.nLineWidth += rFill.bFirstLine ? nLeft : nTextLeft;
+ SwTwips nLeftTab;
+ SwTwips nRightTab = nLeft;
+ sal_uInt16 nSpaceCnt = 0;
+ sal_uInt16 nSpaceOnlyCnt = 0;
+ sal_uInt16 nTabCnt = 0;
+ sal_uInt16 nIdx = 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()->GetDefaultItem( 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: */