Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  frmcrsr.cxx   Sprache: 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, true);
                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;
        } whiletrue );
    }
    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());
            ifstatic_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: */

Messung V0.5
C=95 H=96 G=95

¤ Dauer der Verarbeitung: 0.10 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge