diff options
Diffstat (limited to 'sw/source/core/text/frmcrsr.cxx')
-rw-r--r-- | sw/source/core/text/frmcrsr.cxx | 1752 |
1 files changed, 1752 insertions, 0 deletions
diff --git a/sw/source/core/text/frmcrsr.cxx b/sw/source/core/text/frmcrsr.cxx new file mode 100644 index 000000000000..2f35e82649b5 --- /dev/null +++ b/sw/source/core/text/frmcrsr.cxx @@ -0,0 +1,1752 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sw.hxx" + + +#include "ndtxt.hxx" // GetNode() +#include "pam.hxx" // SwPosition +#include "frmtool.hxx" +#include "viewopt.hxx" +#include "paratr.hxx" +#include "rootfrm.hxx" +#include "pagefrm.hxx" +#include "colfrm.hxx" +#include "txttypes.hxx" +#include <sfx2/printer.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/tstpitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/lspcitem.hxx> +#include <pormulti.hxx> // SwMultiPortion +#include <doc.hxx> +#include <sortedobjs.hxx> + +#include <unicode/ubidi.h> + +#include "txtfrm.hxx" // SwTxtFrm +#include "inftxt.hxx" // SwTxtSizeInfo +#include "itrtxt.hxx" // SwTxtCursor +#include "crstate.hxx" // SwTxtCursor +#include "viewsh.hxx" // InvalidateWindows +#include "swfntcch.hxx" // SwFontAccess +#include "flyfrm.hxx" + +#if OSL_DEBUG_LEVEL > 1 +#include "txtpaint.hxx" +#endif + +#define MIN_OFFSET_STEP 10 + +using namespace ::com::sun::star; + + +/* + * 1170-SurvivalKit: Wie gelangt man hinter das letzte Zeichen der Zeile. + * - RightMargin verzichtet auf den Positionsausgleich mit -1 + * - GetCharRect liefert bei MV_RIGHTMARGIN ein GetEndCharRect + * - GetEndCharRect setzt bRightMargin auf sal_True + * - SwTxtCursor::bRightMargin wird per CharCrsrToLine auf sal_False gesetzt + */ + +/************************************************************************* + * GetAdjFrmAtPos() + *************************************************************************/ + +SwTxtFrm *GetAdjFrmAtPos( SwTxtFrm *pFrm, const SwPosition &rPos, + const sal_Bool bRightMargin, const sal_Bool bNoScroll = sal_True ) +{ + // 8810: vgl. 1170, RightMargin in der letzten Masterzeile... + const xub_StrLen nOffset = rPos.nContent.GetIndex(); + SwTxtFrm *pFrmAtPos = pFrm; + if( !bNoScroll || pFrm->GetFollow() ) + { + pFrmAtPos = pFrm->GetFrmAtPos( rPos ); + if( nOffset < pFrmAtPos->GetOfst() && + !pFrmAtPos->IsFollow() ) + { + xub_StrLen nNew = nOffset; + if( nNew < MIN_OFFSET_STEP ) + nNew = 0; + else + nNew -= MIN_OFFSET_STEP; + lcl_ChangeOffset( pFrmAtPos, nNew ); + } + } + while( pFrm != pFrmAtPos ) + { + pFrm = pFrmAtPos; + pFrm->GetFormatted(); + pFrmAtPos = (SwTxtFrm*)pFrm->GetFrmAtPos( rPos ); + } + + if( nOffset && bRightMargin ) + { + while( pFrmAtPos && pFrmAtPos->GetOfst() == nOffset && + pFrmAtPos->IsFollow() ) + { + pFrmAtPos->GetFormatted(); + pFrmAtPos = pFrmAtPos->FindMaster(); + } + OSL_ENSURE( pFrmAtPos, "+GetCharRect: no frame with my rightmargin" ); + } + return pFrmAtPos ? pFrmAtPos : pFrm; +} + +sal_Bool lcl_ChangeOffset( SwTxtFrm* pFrm, xub_StrLen nNew ) +{ + // In Bereichen und ausserhalb von Flies wird nicht mehr gescrollt. + OSL_ENSURE( !pFrm->IsFollow(), "Illegal Scrolling by Follow!" ); + if( pFrm->GetOfst() != nNew && !pFrm->IsInSct() ) + { + SwFlyFrm *pFly = pFrm->FindFlyFrm(); + // Vorsicht, wenn z.B. bei einem spaltigen Rahmen die Groesse noch invalide ist, + // duerfen wir nicht mal eben herumscrollen + if ( ( pFly && pFly->IsValid() && + !pFly->GetNextLink() && !pFly->GetPrevLink() ) || + ( !pFly && pFrm->IsInTab() ) ) + { + ViewShell* pVsh = pFrm->getRootFrm()->GetCurrShell(); + if( pVsh ) + { + if( pVsh->GetNext() != pVsh || + ( pFrm->GetDrawObjs() && pFrm->GetDrawObjs()->Count() ) ) + { + if( !pFrm->GetOfst() ) + return sal_False; + nNew = 0; + } + pFrm->SetOfst( nNew ); + pFrm->SetPara( 0 ); + pFrm->GetFormatted(); + if( pFrm->Frm().HasArea() ) + pFrm->getRootFrm()->GetCurrShell()->InvalidateWindows( pFrm->Frm() ); + return sal_True; + } + } + } + return sal_False; +} + +/************************************************************************* + * GetFrmAtOfst(), GetFrmAtPos() + *************************************************************************/ + +// OD 07.10.2003 #110978# +SwTxtFrm& SwTxtFrm::GetFrmAtOfst( const xub_StrLen nWhere ) +{ + SwTxtFrm* pRet = this; + while( pRet->HasFollow() && nWhere >= pRet->GetFollow()->GetOfst() ) + pRet = pRet->GetFollow(); + return *pRet; +} + +SwTxtFrm *SwTxtFrm::GetFrmAtPos( const SwPosition &rPos ) +{ + SwTxtFrm *pFoll = (SwTxtFrm*)this; + while( pFoll->GetFollow() ) + { + if( rPos.nContent.GetIndex() > pFoll->GetFollow()->GetOfst() ) + pFoll = pFoll->GetFollow(); + else + { + if( rPos.nContent.GetIndex() == pFoll->GetFollow()->GetOfst() + && !SwTxtCursor::IsRightMargin() ) + pFoll = pFoll->GetFollow(); + else + break; + } + } + return pFoll; +} + +/************************************************************************* + * SwTxtFrm::GetCharRect() + *************************************************************************/ + +/* + * GetCharRect() findet die Characterzelle des Characters, dass + * durch aPos beschrieben wird. GetCrsrOfst() findet den + * umgekehrten Weg: Von einer Dokumentkoordinate zu einem Pam. + * Beide sind virtuell in der Framebasisklasse und werden deshalb + * immer angezogen. + */ + +sal_Bool SwTxtFrm::GetCharRect( SwRect& rOrig, const SwPosition &rPos, + SwCrsrMoveState *pCMS ) const +{ + OSL_ENSURE( ! IsVertical() || ! IsSwapped(),"SwTxtFrm::GetCharRect with swapped frame" ); + + if( IsLocked() || IsHiddenNow() ) + return sal_False; + + //Erstmal den richtigen Frm finden, dabei muss beachtet werden, dass: + //- die gecachten Informationen verworfen sein koennen (GetPara() == 0) + //- das ein Follow gemeint sein kann + //- das die Kette der Follows dynamisch waechst; der in den wir + // schliesslich gelangen muss aber Formatiert sein. + + // opt: reading ahead erspart uns ein GetAdjFrmAtPos + const sal_Bool bRightMargin = pCMS && ( MV_RIGHTMARGIN == pCMS->eState ); + const sal_Bool bNoScroll = pCMS && pCMS->bNoScroll; + SwTxtFrm *pFrm = GetAdjFrmAtPos( (SwTxtFrm*)this, rPos, bRightMargin, + bNoScroll ); + pFrm->GetFormatted(); + const SwFrm* pTmpFrm = (SwFrm*)pFrm->GetUpper(); + + SWRECTFN ( pFrm ) + const SwTwips nUpperMaxY = (pTmpFrm->*fnRect->fnGetPrtBottom)(); + const SwTwips nFrmMaxY = (pFrm->*fnRect->fnGetPrtBottom)(); + + // nMaxY is an absolute value + //Badaa: 2008-04-18 * Support for Classical Mongolian Script (SCMS) joint with Jiayanmin + SwTwips nMaxY = bVert ? + ( bVertL2R ? Min( nFrmMaxY, nUpperMaxY ) : Max( nFrmMaxY, nUpperMaxY ) ) : + Min( nFrmMaxY, nUpperMaxY ); + + sal_Bool bRet = sal_False; + + if ( pFrm->IsEmpty() || ! (pFrm->Prt().*fnRect->fnGetHeight)() ) + { + Point aPnt1 = pFrm->Frm().Pos() + pFrm->Prt().Pos(); + SwTxtNode* pTxtNd = ((SwTxtFrm*)this)->GetTxtNode(); + short nFirstOffset; + pTxtNd->GetFirstLineOfsWithNum( nFirstOffset ); + + Point aPnt2; + if ( bVert ) + { + if( nFirstOffset > 0 ) + aPnt1.Y() += nFirstOffset; + //Badaa: 2008-04-18 * Support for Classical Mongolian Script (SCMS) joint with Jiayanmin + if ( aPnt1.X() < nMaxY && !bVertL2R ) + aPnt1.X() = nMaxY; + aPnt2.X() = aPnt1.X() + pFrm->Prt().Width(); + aPnt2.Y() = aPnt1.Y(); + if( aPnt2.X() < nMaxY ) + aPnt2.X() = nMaxY; + } + else + { + if( nFirstOffset > 0 ) + aPnt1.X() += nFirstOffset; + + if( aPnt1.Y() > nMaxY ) + aPnt1.Y() = nMaxY; + aPnt2.X() = aPnt1.X(); + aPnt2.Y() = aPnt1.Y() + pFrm->Prt().Height(); + if( aPnt2.Y() > nMaxY ) + aPnt2.Y() = nMaxY; + } + + rOrig = SwRect( aPnt1, aPnt2 ); + + if ( pCMS ) + { + pCMS->aRealHeight.X() = 0; + pCMS->aRealHeight.Y() = bVert ? -rOrig.Width() : rOrig.Height(); + } + + if ( pFrm->IsRightToLeft() ) + pFrm->SwitchLTRtoRTL( rOrig ); + + bRet = sal_True; + } + else + { + if( !pFrm->HasPara() ) + return sal_False; + + SwFrmSwapper aSwapper( pFrm, sal_True ); + if ( bVert ) + nMaxY = pFrm->SwitchVerticalToHorizontal( nMaxY ); + + sal_Bool bGoOn = sal_True; + xub_StrLen nOffset = rPos.nContent.GetIndex(); + xub_StrLen nNextOfst; + + do + { + { + SwTxtSizeInfo aInf( pFrm ); + SwTxtCursor aLine( pFrm, &aInf ); + nNextOfst = aLine.GetEnd(); + // Siehe Kommentar in AdjustFrm + // 1170: das letzte Zeichen der Zeile mitnehmen? + bRet = bRightMargin ? aLine.GetEndCharRect( &rOrig, nOffset, pCMS, nMaxY ) + : aLine.GetCharRect( &rOrig, nOffset, pCMS, nMaxY ); + } + + if ( pFrm->IsRightToLeft() ) + pFrm->SwitchLTRtoRTL( rOrig ); + + if ( bVert ) + pFrm->SwitchHorizontalToVertical( rOrig ); + + if( pFrm->IsUndersized() && pCMS && !pFrm->GetNext() && + (rOrig.*fnRect->fnGetBottom)() == nUpperMaxY && + pFrm->GetOfst() < nOffset && + !pFrm->IsFollow() && !bNoScroll && + pFrm->GetTxtNode()->GetTxt().Len() != nNextOfst ) + bGoOn = lcl_ChangeOffset( pFrm, nNextOfst ); + else + bGoOn = sal_False; + } while ( bGoOn ); + + if ( pCMS ) + { + if ( pFrm->IsRightToLeft() ) + { + if( pCMS->b2Lines && pCMS->p2Lines) + { + pFrm->SwitchLTRtoRTL( pCMS->p2Lines->aLine ); + pFrm->SwitchLTRtoRTL( pCMS->p2Lines->aPortion ); + } + } + + if ( bVert ) + { + if ( pCMS->bRealHeight ) + { + pCMS->aRealHeight.Y() = -pCMS->aRealHeight.Y(); + if ( pCMS->aRealHeight.Y() < 0 ) + { + // writing direction is from top to bottom + pCMS->aRealHeight.X() = ( rOrig.Width() - + pCMS->aRealHeight.X() + + pCMS->aRealHeight.Y() ); + } + } + if( pCMS->b2Lines && pCMS->p2Lines) + { + pFrm->SwitchHorizontalToVertical( pCMS->p2Lines->aLine ); + pFrm->SwitchHorizontalToVertical( pCMS->p2Lines->aPortion ); + } + } + + } + } + if( bRet ) + { + SwPageFrm *pPage = pFrm->FindPageFrm(); + OSL_ENSURE( pPage, "Text esaped from page?" ); + const SwTwips nOrigTop = (rOrig.*fnRect->fnGetTop)(); + const SwTwips nPageTop = (pPage->Frm().*fnRect->fnGetTop)(); + const SwTwips nPageBott = (pPage->Frm().*fnRect->fnGetBottom)(); + + // 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 himself. + if( (*fnRect->fnYDiff)( nPageTop, nOrigTop ) > 0 ) + (rOrig.*fnRect->fnSetTop)( nPageTop ); + + if ( (*fnRect->fnYDiff)( nOrigTop, nPageBott ) > 0 ) + (rOrig.*fnRect->fnSetTop)( nPageBott ); + } + + return bRet; +} + +/************************************************************************* + * SwTxtFrm::GetAutoPos() + *************************************************************************/ + +/* + * GetAutoPos() findet die Characterzelle des Characters, dass + * durch aPos beschrieben wird und wird von autopositionierten Rahmen genutzt. + */ + +sal_Bool SwTxtFrm::GetAutoPos( SwRect& rOrig, const SwPosition &rPos ) const +{ + if( IsHiddenNow() ) + return sal_False; + + xub_StrLen nOffset = rPos.nContent.GetIndex(); + SwTxtFrm* pFrm = &(const_cast<SwTxtFrm*>(this)->GetFrmAtOfst( nOffset )); + + pFrm->GetFormatted(); + const SwFrm* pTmpFrm = (SwFrm*)pFrm->GetUpper(); + + SWRECTFN( pTmpFrm ) + SwTwips nUpperMaxY = (pTmpFrm->*fnRect->fnGetPrtBottom)(); + + // nMaxY is in absolute value + //Badaa: 2008-04-18 * Support for Classical Mongolian Script (SCMS) joint with Jiayanmin + SwTwips nMaxY = bVert ? + ( bVertL2R ? Min( (pFrm->*fnRect->fnGetPrtBottom)(), nUpperMaxY ) : Max( (pFrm->*fnRect->fnGetPrtBottom)(), nUpperMaxY ) ) : + Min( (pFrm->*fnRect->fnGetPrtBottom)(), nUpperMaxY ); + + if ( pFrm->IsEmpty() || ! (pFrm->Prt().*fnRect->fnGetHeight)() ) + { + Point aPnt1 = pFrm->Frm().Pos() + pFrm->Prt().Pos(); + Point aPnt2; + if ( bVert ) + { + if ( aPnt1.X() < nMaxY && !bVertL2R ) + aPnt1.X() = nMaxY; + + aPnt2.X() = aPnt1.X() + pFrm->Prt().Width(); + aPnt2.Y() = aPnt1.Y(); + if( aPnt2.X() < nMaxY ) + aPnt2.X() = nMaxY; + } + else + { + if( aPnt1.Y() > nMaxY ) + aPnt1.Y() = nMaxY; + aPnt2.X() = aPnt1.X(); + aPnt2.Y() = aPnt1.Y() + pFrm->Prt().Height(); + if( aPnt2.Y() > nMaxY ) + aPnt2.Y() = nMaxY; + } + rOrig = SwRect( aPnt1, aPnt2 ); + return sal_True; + } + else + { + if( !pFrm->HasPara() ) + return sal_False; + + SwFrmSwapper aSwapper( pFrm, sal_True ); + if ( bVert ) + nMaxY = pFrm->SwitchVerticalToHorizontal( nMaxY ); + + SwTxtSizeInfo aInf( pFrm ); + SwTxtCursor aLine( pFrm, &aInf ); + SwCrsrMoveState aTmpState( MV_SETONLYTEXT ); + aTmpState.bRealHeight = sal_True; + if( aLine.GetCharRect( &rOrig, nOffset, &aTmpState, nMaxY ) ) + { + if( aTmpState.aRealHeight.X() >= 0 ) + { + rOrig.Pos().Y() += aTmpState.aRealHeight.X(); + rOrig.Height( aTmpState.aRealHeight.Y() ); + } + + if ( pFrm->IsRightToLeft() ) + pFrm->SwitchLTRtoRTL( rOrig ); + + if ( bVert ) + pFrm->SwitchHorizontalToVertical( rOrig ); + + return sal_True; + } + return sal_False; + } +} + +/** determine top of line for given position in the text frame + + OD 11.11.2003 #i22341# + OD 2004-03-18 #114789# - corrections: + - 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. + + @author OD +*/ +bool SwTxtFrm::GetTopOfLine( SwTwips& _onTopOfLine, + const SwPosition& _rPos ) const +{ + bool bRet = true; + + // get position offset + xub_StrLen nOffset = _rPos.nContent.GetIndex(); + + if ( GetTxt().Len() < nOffset ) + { + bRet = false; + } + else + { + SWRECTFN( this ) + if ( IsEmpty() || !(Prt().*fnRect->fnGetHeight)() ) + { + // OD 2004-03-18 #i11860# - consider upper space amount considered + // for previous frame and the page grid. + _onTopOfLine = (this->*fnRect->fnGetPrtTop)(); + } + else + { + // determine formatted text frame that contains the requested position + SwTxtFrm* pFrm = &(const_cast<SwTxtFrm*>(this)->GetFrmAtOfst( nOffset )); + pFrm->GetFormatted(); + SWREFRESHFN( pFrm ) + // OD 2004-03-18 #114789# - 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() == SVX_INTER_LINE_SPACE_PROP ) + { + SwRect aCharRect; + if ( GetAutoPos( aCharRect, _rPos ) ) + { + _onTopOfLine = (aCharRect.*fnRect->fnGetTop)(); + } + else + { + bRet = false; + } + } + else + { + // assure that text frame is in a horizontal layout + SwFrmSwapper aSwapper( pFrm, sal_True ); + // determine text line that contains the requested position + SwTxtSizeInfo aInf( pFrm ); + SwTxtCursor aLine( pFrm, &aInf ); + aLine.CharCrsrToLine( nOffset ); + // determine top of line + _onTopOfLine = aLine.Y(); + if ( bVert ) + { + _onTopOfLine = pFrm->SwitchHorizontalToVertical( _onTopOfLine ); + } + } + } + } + + return bRet; +} + +/************************************************************************* + * SwTxtFrm::_GetCrsrOfst() + *************************************************************************/ + +// Minimaler Abstand von nichtleeren Zeilen etwas weniger als 2 cm +#define FILL_MIN_DIST 1100 + +struct SwFillData +{ + SwRect aFrm; + const SwCrsrMoveState *pCMS; + SwPosition* pPos; + const Point& rPoint; + SwTwips nLineWidth; + sal_Bool bFirstLine : 1; + sal_Bool bInner : 1; + sal_Bool bColumn : 1; + sal_Bool bEmpty : 1; + SwFillData( const SwCrsrMoveState *pC, SwPosition* pP, const SwRect& rR, + const Point& rPt ) : aFrm( rR ), pCMS( pC ), pPos( pP ), rPoint( rPt ), + nLineWidth( 0 ), bFirstLine( sal_True ), bInner( sal_False ), bColumn( sal_False ), + bEmpty( sal_True ){} + SwFillMode Mode() const { return pCMS->pFill->eMode; } + long X() const { return rPoint.X(); } + long Y() const { return rPoint.Y(); } + long Left() const { return aFrm.Left(); } + long Right() const { return aFrm.Right(); } + long Bottom() const { return aFrm.Bottom(); } + SwRect& Frm() { return aFrm; } + SwFillCrsrPos &Fill() const { return *pCMS->pFill; } + void SetTab( MSHORT nNew ) { pCMS->pFill->nTabCnt = nNew; } + void SetSpace( MSHORT nNew ) { pCMS->pFill->nSpaceCnt = nNew; } + void SetOrient( const sal_Int16 eNew ){ pCMS->pFill->eOrient = eNew; } +}; + +sal_Bool SwTxtFrm::_GetCrsrOfst(SwPosition* pPos, const Point& rPoint, + const sal_Bool bChgFrm, SwCrsrMoveState* pCMS ) const +{ + // 8804: _GetCrsrOfst wird vom GetCrsrOfst und GetKeyCrsrOfst gerufen. + // In keinem Fall nur ein return sal_False. + + if( IsLocked() || IsHiddenNow() ) + return sal_False; + + ((SwTxtFrm*)this)->GetFormatted(); + + Point aOldPoint( rPoint ); + + if ( IsVertical() ) + { + SwitchVerticalToHorizontal( (Point&)rPoint ); + ((SwTxtFrm*)this)->SwapWidthAndHeight(); + } + + if ( IsRightToLeft() ) + SwitchRTLtoLTR( (Point&)rPoint ); + + SwFillData *pFillData = ( pCMS && pCMS->pFill ) ? + new SwFillData( pCMS, pPos, Frm(), rPoint ) : NULL; + + if ( IsEmpty() ) + { + SwTxtNode* pTxtNd = ((SwTxtFrm*)this)->GetTxtNode(); + pPos->nNode = *pTxtNd; + pPos->nContent.Assign( pTxtNd, 0 ); + if( pCMS && pCMS->bFieldInfo ) + { + SwTwips nDiff = rPoint.X() - Frm().Left() - Prt().Left(); + if( nDiff > 50 || nDiff < 0 ) + ((SwCrsrMoveState*)pCMS)->bPosCorr = sal_True; + } + } + else + { + SwTxtSizeInfo aInf( (SwTxtFrm*)this ); + SwTxtCursor aLine( ((SwTxtFrm*)this), &aInf ); + + // Siehe Kommentar in AdjustFrm() + SwTwips nMaxY = Frm().Top() + Prt().Top() + Prt().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(); + + xub_StrLen nOffset = aLine.GetCrsrOfst( pPos, rPoint, bChgFrm, pCMS ); + + if( pCMS && pCMS->eState == MV_NONE && aLine.GetEnd() == nOffset ) + ((SwCrsrMoveState*)pCMS)->eState = MV_RIGHTMARGIN; + + // 6776: pPos ist ein reiner IN-Parameter, der nicht ausgewertet werden darf. + // Das pIter->GetCrsrOfst returnt aus einer Verschachtelung mit STRING_LEN. + // Wenn SwTxtIter::GetCrsrOfst von sich aus weitere GetCrsrOfst + // ruft, so aendert sich nNode der Position. In solchen Faellen + // darf pPos nicht berechnet werden. + if( STRING_LEN != nOffset ) + { + SwTxtNode* pTxtNd = ((SwTxtFrm*)this)->GetTxtNode(); + pPos->nNode = *pTxtNd; + pPos->nContent.Assign( pTxtNd, nOffset ); + if( pFillData ) + { + if( pTxtNd->GetTxt().Len() > nOffset || + rPoint.Y() < Frm().Top() ) + pFillData->bInner = sal_True; + pFillData->bFirstLine = aLine.GetLineNr() < 2; + if( pTxtNd->GetTxt().Len() ) + { + pFillData->bEmpty = sal_False; + pFillData->nLineWidth = aLine.GetCurr()->Width(); + } + } + } + } + sal_Bool bChgFillData = sal_False; + if( pFillData && FindPageFrm()->Frm().IsInside( aOldPoint ) ) + { + FillCrsrPos( *pFillData ); + bChgFillData = sal_True; + } + + if ( IsVertical() ) + { + if ( bChgFillData ) + SwitchHorizontalToVertical( pFillData->Fill().aCrsr.Pos() ); + ((SwTxtFrm*)this)->SwapWidthAndHeight(); + } + + if ( IsRightToLeft() && bChgFillData ) + { + SwitchLTRtoRTL( pFillData->Fill().aCrsr.Pos() ); + const sal_Int16 eOrient = pFillData->pCMS->pFill->eOrient; + + if ( text::HoriOrientation::LEFT == eOrient ) + pFillData->SetOrient( text::HoriOrientation::RIGHT ); + else if ( text::HoriOrientation::RIGHT == eOrient ) + pFillData->SetOrient( text::HoriOrientation::LEFT ); + } + + (Point&)rPoint = aOldPoint; + delete pFillData; + + return sal_True; +} + +/************************************************************************* + * virtual SwTxtFrm::GetCrsrOfst() + *************************************************************************/ + +sal_Bool SwTxtFrm::GetCrsrOfst(SwPosition* pPos, Point& rPoint, + SwCrsrMoveState* pCMS ) const +{ + MSHORT nChgFrm = 2; + if( pCMS ) + { + if( MV_UPDOWN == pCMS->eState ) + nChgFrm = 0; + else if( MV_SETONLYTEXT == pCMS->eState || + MV_TBLSEL == pCMS->eState ) + nChgFrm = 1; + } + return _GetCrsrOfst( pPos, rPoint, nChgFrm != 0, pCMS ); +} + +/************************************************************************* + * SwTxtFrm::LeftMargin() + *************************************************************************/ + +/* + * Layout-orientierte Cursorbewegungen + */ + +/* + * an den Zeilenanfang + */ + +sal_Bool SwTxtFrm::LeftMargin(SwPaM *pPam) const +{ + if( ((const SwNode*)pPam->GetNode()) != GetNode() ) + pPam->GetPoint()->nNode = *((SwTxtFrm*)this)->GetTxtNode(); + + SwTxtFrm *pFrm = GetAdjFrmAtPos( (SwTxtFrm*)this, *pPam->GetPoint(), + SwTxtCursor::IsRightMargin() ); + pFrm->GetFormatted(); + xub_StrLen nIndx; + if ( pFrm->IsEmpty() ) + nIndx = 0; + else + { + SwTxtSizeInfo aInf( pFrm ); + SwTxtCursor aLine( pFrm, &aInf ); + + aLine.CharCrsrToLine(pPam->GetPoint()->nContent.GetIndex()); + nIndx = aLine.GetStart(); + if( pFrm->GetOfst() && !pFrm->IsFollow() && !aLine.GetPrev() ) + { + lcl_ChangeOffset( pFrm, 0 ); + nIndx = 0; + } + } + pPam->GetPoint()->nContent = SwIndex( pFrm->GetTxtNode(), nIndx ); + SwTxtCursor::SetRightMargin( sal_False ); + return sal_True; +} + +/************************************************************************* + * SwTxtFrm::RightMargin() + *************************************************************************/ + +/* + * An das Zeilenende:Das ist die Position vor dem letzten + * Character in der Zeile. Ausnahme: In der letzten Zeile soll + * der Cursor auch hinter dem letzten Character stehen koennen, + * um Text anhaengen zu koennen. + * + */ + +sal_Bool SwTxtFrm::RightMargin(SwPaM *pPam, sal_Bool bAPI) const +{ + if( ((const SwNode*)pPam->GetNode()) != GetNode() ) + pPam->GetPoint()->nNode = *((SwTxtFrm*)this)->GetTxtNode(); + + SwTxtFrm *pFrm = GetAdjFrmAtPos( (SwTxtFrm*)this, *pPam->GetPoint(), + SwTxtCursor::IsRightMargin() ); + pFrm->GetFormatted(); + xub_StrLen nRightMargin; + if ( IsEmpty() ) + nRightMargin = 0; + else + { + SwTxtSizeInfo aInf( pFrm ); + SwTxtCursor aLine( pFrm, &aInf ); + + aLine.CharCrsrToLine(pPam->GetPoint()->nContent.GetIndex()); + nRightMargin = aLine.GetStart() + aLine.GetCurr()->GetLen(); + + // Harte Zeilenumbrueche lassen wir hinter uns. + if( aLine.GetCurr()->GetLen() && + CH_BREAK == aInf.GetTxt().GetChar( nRightMargin - 1 ) ) + --nRightMargin; + else if( !bAPI && (aLine.GetNext() || pFrm->GetFollow()) ) + { + while( nRightMargin > aLine.GetStart() && + ' ' == aInf.GetTxt().GetChar( nRightMargin - 1 ) ) + --nRightMargin; + } + } + pPam->GetPoint()->nContent = SwIndex( pFrm->GetTxtNode(), nRightMargin ); + SwTxtCursor::SetRightMargin( !bAPI ); + return sal_True; +} + +/************************************************************************* + * SwTxtFrm::_UnitUp() + *************************************************************************/ + +//Die beiden folgenden Methoden versuchen zunaechst den Crsr in die +//nachste/folgende Zeile zu setzen. Gibt es im Frame keine vorhergehende/ +//folgende Zeile, so wird der Aufruf an die Basisklasse weitergeleitet. +//Die Horizontale Ausrichtung des Crsr wird hinterher von der CrsrShell +//vorgenommen. + +class SwSetToRightMargin +{ + sal_Bool bRight; +public: + inline SwSetToRightMargin() : bRight( sal_False ) { } + inline ~SwSetToRightMargin() { SwTxtCursor::SetRightMargin( bRight ); } + inline void SetRight( const sal_Bool bNew ) { bRight = bNew; } +}; + +sal_Bool SwTxtFrm::_UnitUp( SwPaM *pPam, const SwTwips nOffset, + sal_Bool bSetInReadOnly ) const +{ + // 8626: Im Notfall den RightMargin setzen. + SwSetToRightMargin aSet; + + if( IsInTab() && + pPam->GetNode( sal_True )->StartOfSectionNode() != + pPam->GetNode( sal_False )->StartOfSectionNode() ) + { + //Wenn der PaM in unterschiedlichen Boxen sitzt, so handelt es sich um + //eine Tabellenselektion; diese wird von der Basisklasse abgearbeitet. + return SwCntntFrm::UnitUp( pPam, nOffset, bSetInReadOnly ); + } + + ((SwTxtFrm*)this)->GetFormatted(); + const xub_StrLen nPos = pPam->GetPoint()->nContent.GetIndex(); + SwRect aCharBox; + + if( !IsEmpty() && !IsHiddenNow() ) + { + xub_StrLen nFormat = STRING_LEN; + do + { + if( nFormat != STRING_LEN && !IsFollow() ) + lcl_ChangeOffset( ((SwTxtFrm*)this), nFormat ); + + SwTxtSizeInfo aInf( (SwTxtFrm*)this ); + SwTxtCursor aLine( ((SwTxtFrm*)this), &aInf ); + + // 8116: Flys ohne Umlauf und IsDummy(); hier wegoptimiert + if( nPos ) + aLine.CharCrsrToLine( nPos ); + else + aLine.Top(); + + const SwLineLayout *pPrevLine = aLine.GetPrevLine(); + const xub_StrLen nStart = aLine.GetStart(); + aLine.GetCharRect( &aCharBox, nPos ); + + sal_Bool bSecondOfDouble = ( aInf.IsMulti() && ! aInf.IsFirstMulti() ); + sal_Bool bPrevLine = ( pPrevLine && pPrevLine != aLine.GetCurr() ); + + if( !pPrevLine && !bSecondOfDouble && GetOfst() && !IsFollow() ) + { + nFormat = GetOfst(); + xub_StrLen nDiff = aLine.GetLength(); + if( !nDiff ) + nDiff = MIN_OFFSET_STEP; + if( nFormat > nDiff ) + nFormat = nFormat - nDiff; + else + nFormat = 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 && + 0 != ( pPrevLine = aLine.GetPrevLine() ) && + pPrevLine != aLine.GetCurr() ) + aLine.PrevLine(); + } + + if ( bPrevLine || bSecondOfDouble ) + { + aCharBox.SSize().Width() /= 2; + aCharBox.Pos().X() = aCharBox.Pos().X() - 150; + + // siehe Kommentar in SwTxtFrm::GetCrsrOfst() +#if OSL_DEBUG_LEVEL > 1 + const sal_uLong nOldNode = pPam->GetPoint()->nNode.GetIndex(); +#endif + // Der Node soll nicht gewechselt werden + xub_StrLen nTmpOfst = aLine.GetCrsrOfst( pPam->GetPoint(), + aCharBox.Pos(), sal_False ); +#if OSL_DEBUG_LEVEL > 1 + OSL_ENSURE( nOldNode == pPam->GetPoint()->nNode.GetIndex(), + "SwTxtFrm::UnitUp: illegal node change" ); +#endif + + // 7684: Wir stellen sicher, dass wir uns nach oben bewegen. + if( nTmpOfst >= nStart && nStart && !bSecondOfDouble ) + { + nTmpOfst = nStart; + aSet.SetRight( sal_True ); + } + pPam->GetPoint()->nContent = + SwIndex( ((SwTxtFrm*)this)->GetTxtNode(), nTmpOfst ); + return sal_True; + } + + if ( IsFollow() ) + { + aLine.GetCharRect( &aCharBox, nPos ); + aCharBox.SSize().Width() /= 2; + } + break; + } while ( sal_True ); + } + /* Wenn this ein Follow ist und ein Prev miszlang, so + * muessen wir in die letzte Zeile des Master ... und der sind wir. + * Oder wir sind ein Follow mit Follow, dann muessen wir uns den + * Master extra besorgen... + */ + if ( IsFollow() ) + { + const SwTxtFrm *pTmpPrev = FindMaster(); + xub_StrLen nOffs = GetOfst(); + if( pTmpPrev ) + { + ViewShell *pSh = getRootFrm()->GetCurrShell(); + sal_Bool bProtectedAllowed = pSh && pSh->GetViewOptions()->IsCursorInProtectedArea(); + const SwTxtFrm *pPrevPrev = pTmpPrev; + // Hier werden geschuetzte Frames und Frame ohne Inhalt ausgelassen + while( pPrevPrev && ( pPrevPrev->GetOfst() == nOffs || + ( !bProtectedAllowed && pPrevPrev->IsProtected() ) ) ) + { + pTmpPrev = pPrevPrev; + nOffs = pTmpPrev->GetOfst(); + if ( pPrevPrev->IsFollow() ) + pPrevPrev = pTmpPrev->FindMaster(); + else + pPrevPrev = NULL; + } + if ( !pPrevPrev ) + return pTmpPrev->SwCntntFrm::UnitUp( pPam, nOffset, bSetInReadOnly ); + aCharBox.Pos().Y() = pPrevPrev->Frm().Bottom() - 1; + return pPrevPrev->GetKeyCrsrOfst( pPam->GetPoint(), aCharBox.Pos() ); + } + } + return SwCntntFrm::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 +void lcl_VisualMoveRecursion( const SwLineLayout& rCurrLine, xub_StrLen nIdx, + xub_StrLen& nPos, sal_Bool& bRight, + sal_uInt8& nCrsrLevel, sal_uInt8 nDefaultDir ) +{ + const SwLinePortion* pPor = rCurrLine.GetFirstPortion(); + const SwLinePortion* pLast = 0; + + // what's the current portion + while ( pPor && nIdx + pPor->GetLen() <= nPos ) + { + nIdx = nIdx + pPor->GetLen(); + pLast = pPor; + pPor = pPor->GetPortion(); + } + + if ( bRight ) + { + sal_Bool bRecurse = pPor && pPor->IsMultiPortion() && + ((SwMultiPortion*)pPor)->IsBidi(); + + // 1. special case: at beginning of bidi portion + if ( bRecurse && nIdx == nPos ) + { + nPos = nPos + pPor->GetLen(); + + // leave bidi portion + if ( nCrsrLevel != nDefaultDir ) + { + bRecurse = sal_False; + } + else + // special case: + // buffer: abcXYZ123 in LTR paragraph + // view: abc123ZYX + // cursor is between c and X in the buffer and cursor level = 0 + nCrsrLevel++; + } + + // 2. special case: at beginning of portion after bidi portion + else if ( pLast && pLast->IsMultiPortion() && + ((SwMultiPortion*)pLast)->IsBidi() && nIdx == nPos ) + { + // enter bidi portion + if ( nCrsrLevel != nDefaultDir ) + { + bRecurse = sal_True; + nIdx = nIdx - pLast->GetLen(); + pPor = pLast; + } + } + + // Recursion + if ( bRecurse ) + { + const SwLineLayout& rLine = ((SwMultiPortion*)pPor)->GetRoot(); + xub_StrLen nTmpPos = nPos - nIdx; + sal_Bool bTmpForward = ! bRight; + sal_uInt8 nTmpCrsrLevel = nCrsrLevel; + lcl_VisualMoveRecursion( rLine, 0, nTmpPos, bTmpForward, + nTmpCrsrLevel, nDefaultDir + 1 ); + + nPos = nTmpPos + nIdx; + bRight = bTmpForward; + nCrsrLevel = nTmpCrsrLevel; + } + + // go forward + else + { + bRight = sal_True; + nCrsrLevel = nDefaultDir; + } + + } + else + { + sal_Bool bRecurse = pPor && pPor->IsMultiPortion() && ((SwMultiPortion*)pPor)->IsBidi(); + + // 1. special case: at beginning of bidi portion + if ( bRecurse && nIdx == nPos ) + { + // leave bidi portion + if ( nCrsrLevel == nDefaultDir ) + { + bRecurse = sal_False; + } + } + + // 2. special case: at beginning of portion after bidi portion + else if ( pLast && pLast->IsMultiPortion() && + ((SwMultiPortion*)pLast)->IsBidi() && nIdx == nPos ) + { + nPos = nPos - pLast->GetLen(); + + // enter bidi portion + if ( nCrsrLevel % 2 == nDefaultDir % 2 ) + { + bRecurse = sal_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 == nCrsrLevel ) + nPos = nPos + pLast->GetLen(); + } + } + + // go forward + if ( bRecurse ) + { + const SwLineLayout& rLine = ((SwMultiPortion*)pPor)->GetRoot(); + xub_StrLen nTmpPos = nPos - nIdx; + sal_Bool bTmpForward = ! bRight; + sal_uInt8 nTmpCrsrLevel = nCrsrLevel; + lcl_VisualMoveRecursion( rLine, 0, nTmpPos, bTmpForward, + nTmpCrsrLevel, 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() && nTmpCrsrLevel == nDefaultDir + 1 ) + { + nTmpPos = nTmpPos - pPor->GetLen(); + nTmpCrsrLevel = nDefaultDir; + bTmpForward = ! bTmpForward; + } + + nPos = nTmpPos + nIdx; + bRight = bTmpForward; + nCrsrLevel = nTmpCrsrLevel; + } + + // go backward + else + { + bRight = sal_False; + nCrsrLevel = nDefaultDir; + } + } +} + +void SwTxtFrm::PrepareVisualMove( xub_StrLen& nPos, sal_uInt8& nCrsrLevel, + sal_Bool& bForward, sal_Bool bInsertCrsr ) +{ + if( IsEmpty() || IsHiddenNow() ) + return; + + ((SwTxtFrm*)this)->GetFormatted(); + + SwTxtSizeInfo aInf( (SwTxtFrm*)this ); + SwTxtCursor aLine( ((SwTxtFrm*)this), &aInf ); + + if( nPos ) + aLine.CharCrsrToLine( nPos ); + else + aLine.Top(); + + const SwLineLayout* pLine = aLine.GetCurr(); + const xub_StrLen nStt = aLine.GetStart(); + const xub_StrLen 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 ( bInsertCrsr ) + { + lcl_VisualMoveRecursion( *pLine, nStt, nPos, bForward, + nCrsrLevel, IsRightToLeft() ? 1 : 0 ); + return; + } + + const sal_uInt8 nDefaultDir = static_cast<sal_uInt8>(IsRightToLeft() ? UBIDI_RTL : UBIDI_LTR); + const sal_Bool bVisualRight = ( nDefaultDir == UBIDI_LTR && bForward ) || + ( nDefaultDir == UBIDI_RTL && ! bForward ); + + // + // Bidi functions from icu 2.0 + // + const sal_Unicode* pLineString = GetTxtNode()->GetTxt().GetBuffer(); + pLine += nStt; + + UErrorCode nError = U_ZERO_ERROR; + UBiDi* pBidi = ubidi_openSized( nLen, 0, &nError ); + ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(pLineString), nLen, nDefaultDir, NULL, &nError ); // UChar != sal_Unicode in MinGW + + xub_StrLen nTmpPos; + sal_Bool bOutOfBounds = sal_False; + + if ( nPos < nStt + nLen ) + { + nTmpPos = (xub_StrLen)ubidi_getVisualIndex( pBidi, nPos, &nError ); + + // visual indices are always LTR aligned + if ( bVisualRight ) + { + if ( nTmpPos + 1 < nStt + nLen ) + ++nTmpPos; + else + { + nPos = nDefaultDir == UBIDI_RTL ? 0 : nStt + nLen; + bOutOfBounds = sal_True; + } + } + else + { + if ( nTmpPos ) + --nTmpPos; + else + { + nPos = nDefaultDir == UBIDI_RTL ? nStt + nLen : 0; + bOutOfBounds = sal_True; + } + } + } + else + { + nTmpPos = nDefaultDir == UBIDI_LTR ? nPos - 1 : 0; + } + + if ( ! bOutOfBounds ) + { + nPos = (xub_StrLen)ubidi_getLogicalIndex( pBidi, nTmpPos, &nError ); + + if ( bForward ) + { + if ( nPos ) + --nPos; + else + { + ++nPos; + bForward = ! bForward; + } + } + else + ++nPos; + } + + ubidi_close( pBidi ); +} + +/************************************************************************* + * SwTxtFrm::_UnitDown() + *************************************************************************/ + +sal_Bool SwTxtFrm::_UnitDown(SwPaM *pPam, const SwTwips nOffset, + sal_Bool bSetInReadOnly ) const +{ + + if ( IsInTab() && + pPam->GetNode( sal_True )->StartOfSectionNode() != + pPam->GetNode( sal_False )->StartOfSectionNode() ) + { + //Wenn der PaM in unterschiedlichen Boxen sitzt, so handelt es sich um + //eine Tabellenselektion; diese wird von der Basisklasse abgearbeitet. + return SwCntntFrm::UnitDown( pPam, nOffset, bSetInReadOnly ); + } + ((SwTxtFrm*)this)->GetFormatted(); + const xub_StrLen nPos = pPam->GetPoint()->nContent.GetIndex(); + SwRect aCharBox; + const SwCntntFrm *pTmpFollow = 0; + + if ( IsVertical() ) + ((SwTxtFrm*)this)->SwapWidthAndHeight(); + + if ( !IsEmpty() && !IsHiddenNow() ) + { + xub_StrLen nFormat = STRING_LEN; + do + { + if( nFormat != STRING_LEN && !IsFollow() && + !lcl_ChangeOffset( ((SwTxtFrm*)this), nFormat ) ) + break; + + SwTxtSizeInfo aInf( (SwTxtFrm*)this ); + SwTxtCursor aLine( ((SwTxtFrm*)this), &aInf ); + nFormat = aLine.GetEnd(); + + aLine.CharCrsrToLine( nPos ); + + const SwLineLayout* pNextLine = aLine.GetNextLine(); + const xub_StrLen nStart = aLine.GetStart(); + aLine.GetCharRect( &aCharBox, nPos ); + + sal_Bool bFirstOfDouble = ( aInf.IsMulti() && aInf.IsFirstMulti() ); + + if( pNextLine || bFirstOfDouble ) + { + aCharBox.SSize().Width() /= 2; +#if OSL_DEBUG_LEVEL > 1 + // siehe Kommentar in SwTxtFrm::GetCrsrOfst() + const sal_uLong nOldNode = pPam->GetPoint()->nNode.GetIndex(); +#endif + if ( pNextLine && ! bFirstOfDouble ) + aLine.NextLine(); + + xub_StrLen nTmpOfst = aLine.GetCrsrOfst( pPam->GetPoint(), + aCharBox.Pos(), sal_False ); +#if OSL_DEBUG_LEVEL > 1 + OSL_ENSURE( nOldNode == pPam->GetPoint()->nNode.GetIndex(), + "SwTxtFrm::UnitDown: illegal node change" ); +#endif + + // 7684: Wir stellen sicher, dass wir uns nach unten bewegen. + if( nTmpOfst <= nStart && ! bFirstOfDouble ) + nTmpOfst = nStart + 1; + pPam->GetPoint()->nContent = + SwIndex( ((SwTxtFrm*)this)->GetTxtNode(), nTmpOfst ); + + if ( IsVertical() ) + ((SwTxtFrm*)this)->SwapWidthAndHeight(); + + return sal_True; + } + if( 0 != ( pTmpFollow = GetFollow() ) ) + { // geschuetzte Follows auslassen + const SwCntntFrm* pTmp = pTmpFollow; + ViewShell *pSh = getRootFrm()->GetCurrShell(); + if( !pSh || !pSh->GetViewOptions()->IsCursorInProtectedArea() ) + { + while( pTmpFollow && pTmpFollow->IsProtected() ) + { + pTmp = pTmpFollow; + pTmpFollow = pTmpFollow->GetFollow(); + } + } + if( !pTmpFollow ) // nur noch geschuetzte + { + if ( IsVertical() ) + ((SwTxtFrm*)this)->SwapWidthAndHeight(); + return pTmp->SwCntntFrm::UnitDown( pPam, nOffset, bSetInReadOnly ); + } + + aLine.GetCharRect( &aCharBox, nPos ); + aCharBox.SSize().Width() /= 2; + } + else if( !IsFollow() ) + { + xub_StrLen nTmpLen = aInf.GetTxt().Len(); + if( aLine.GetEnd() < nTmpLen ) + { + if( nFormat <= GetOfst() ) + { + nFormat = Min( xub_StrLen( GetOfst() + MIN_OFFSET_STEP ), + nTmpLen ); + if( nFormat <= GetOfst() ) + break; + } + continue; + } + } + break; + } while( sal_True ); + } + else + pTmpFollow = GetFollow(); + + if ( IsVertical() ) + ((SwTxtFrm*)this)->SwapWidthAndHeight(); + + // Bei Follows schlagen wir eine Abkuerzung + if( pTmpFollow ) + { + aCharBox.Pos().Y() = pTmpFollow->Frm().Top() + 1; + return ((SwTxtFrm*)pTmpFollow)->GetKeyCrsrOfst( pPam->GetPoint(), + aCharBox.Pos() ); + } + return SwCntntFrm::UnitDown( pPam, nOffset, bSetInReadOnly ); +} + +/************************************************************************* + * virtual SwTxtFrm::UnitUp() + *************************************************************************/ + +sal_Bool SwTxtFrm::UnitUp(SwPaM *pPam, const SwTwips nOffset, + sal_Bool bSetInReadOnly ) const +{ + /* Im CrsrSh::Up() wird CntntNode::GetFrm() gerufen. + * Dies liefert _immer_ den Master zurueck. + * Um das Cursortravelling nicht zu belasten, korrigieren wir + * hier im SwTxtFrm. + * Wir ermittelt UnitUp fuer pFrm, pFrm ist entweder ein Master (=this) + * oder ein Follow (!=this) + */ + const SwTxtFrm *pFrm = GetAdjFrmAtPos( (SwTxtFrm*)this, *(pPam->GetPoint()), + SwTxtCursor::IsRightMargin() ); + const sal_Bool bRet = pFrm->_UnitUp( pPam, nOffset, bSetInReadOnly ); + + // 8626: kein SwTxtCursor::SetRightMargin( sal_False ); + // statt dessen steht ein SwSetToRightMargin im _UnitUp + return bRet; +} + +/************************************************************************* + * virtual SwTxtFrm::UnitDown() + *************************************************************************/ + +sal_Bool SwTxtFrm::UnitDown(SwPaM *pPam, const SwTwips nOffset, + sal_Bool bSetInReadOnly ) const +{ + const SwTxtFrm *pFrm = GetAdjFrmAtPos((SwTxtFrm*)this, *(pPam->GetPoint()), + SwTxtCursor::IsRightMargin() ); + const sal_Bool bRet = pFrm->_UnitDown( pPam, nOffset, bSetInReadOnly ); + SwTxtCursor::SetRightMargin( sal_False ); + return bRet; +} + +void SwTxtFrm::FillCrsrPos( SwFillData& rFill ) const +{ + if( !rFill.bColumn && GetUpper()->IsColBodyFrm() ) // ColumnFrms jetzt mit BodyFrm + { + const SwColumnFrm* pTmp = + (SwColumnFrm*)GetUpper()->GetUpper()->GetUpper()->Lower(); // die 1. Spalte + // der erste SwFrm im BodyFrm der ersten Spalte + const SwFrm* pFrm = ((SwLayoutFrm*)pTmp->Lower())->Lower(); + MSHORT nNextCol = 0; + // In welcher Spalte landen wir? + while( rFill.X() > pTmp->Frm().Right() && pTmp->GetNext() ) + { + pTmp = (SwColumnFrm*)pTmp->GetNext(); + if( ((SwLayoutFrm*)pTmp->Lower())->Lower() ) // ColumnFrms jetzt mit BodyFrm + { + pFrm = ((SwLayoutFrm*)pTmp->Lower())->Lower(); + nNextCol = 0; + } + else + ++nNextCol; // leere Spalten erfordern Spaltenumbrueche + } + if( pTmp != GetUpper()->GetUpper() ) // Sind wir in einer anderen Spalte gelandet? + { + if( !pFrm ) + return; + if( nNextCol ) + { + while( pFrm->GetNext() ) + pFrm = pFrm->GetNext(); + } + else + { + while( pFrm->GetNext() && pFrm->Frm().Bottom() < rFill.Y() ) + pFrm = pFrm->GetNext(); + } + // Kein Fuellen, wenn als letzter Frame in der anvisierten + // Spalte kein Absatz, sondern z.B. eine Tabelle steht + if( pFrm->IsTxtFrm() ) + { + rFill.Fill().nColumnCnt = nNextCol; + rFill.bColumn = sal_True; + if( rFill.pPos ) + { + SwTxtNode* pTxtNd = ((SwTxtFrm*)pFrm)->GetTxtNode(); + rFill.pPos->nNode = *pTxtNd; + rFill.pPos->nContent.Assign( pTxtNd, pTxtNd->GetTxt().Len() ); + } + if( nNextCol ) + { + rFill.aFrm = pTmp->Prt(); + rFill.aFrm += pTmp->Frm().Pos(); + } + else + rFill.aFrm = pFrm->Frm(); + ((SwTxtFrm*)pFrm)->FillCrsrPos( rFill ); + } + return; + } + } + sal_Bool bFill = sal_True; + SwFont *pFnt; + SwTxtFmtColl* pColl = GetTxtNode()->GetTxtColl(); + MSHORT nFirst = GetTxtNode()->GetSwAttrSet().GetULSpace().GetLower(); + SwTwips nDiff = rFill.Y() - Frm().Bottom(); + if( nDiff < nFirst ) + nDiff = -1; + else + pColl = &pColl->GetNextTxtFmtColl(); + SwAttrSet aSet( ((SwDoc*)GetTxtNode()->GetDoc())->GetAttrPool(), aTxtFmtCollSetRange ); + const SwAttrSet* pSet = &pColl->GetAttrSet(); + ViewShell *pSh = getRootFrm()->GetCurrShell(); + if( GetTxtNode()->HasSwAttrSet() ) + { + aSet.Put( *GetTxtNode()->GetpSwAttrSet() ); + aSet.SetParent( pSet ); + pSet = &aSet; + pFnt = new SwFont( pSet, GetNode()->getIDocumentSettingAccess() ); + } + else + { + SwFontAccess aFontAccess( pColl, pSh ); + pFnt = new SwFont( *aFontAccess.Get()->GetFont() ); + pFnt->ChkMagic( pSh, pFnt->GetActual() ); + } + OutputDevice* pOut = pSh->GetOut(); + if( !pSh->GetViewOptions()->getBrowseMode() || pSh->GetViewOptions()->IsPrtFormat() ) + pOut = GetTxtNode()->getIDocumentDeviceAccess()->getReferenceDevice( true ); + + pFnt->SetFntChg( sal_True ); + pFnt->ChgPhysFnt( pSh, *pOut ); + + SwTwips nLineHeight = pFnt->GetHeight( pSh, *pOut ); + + if( nLineHeight ) + { + const SvxULSpaceItem &rUL = pSet->GetULSpace(); + SwTwips nDist = Max( rUL.GetLower(), rUL.GetUpper() ); + if( rFill.Fill().nColumnCnt ) + { + rFill.aFrm.Height( nLineHeight ); + nDiff = rFill.Y() - rFill.Bottom(); + nFirst = 0; + } + else if( nDist < nFirst ) + nFirst = nFirst - (sal_uInt16)nDist; + else + nFirst = 0; + nDist = Max( nDist, long( GetLineSpace() ) ); + nDist += nLineHeight; + nDiff -= nFirst; + + if( nDiff > 0 ) + { + nDiff /= nDist; + rFill.Fill().nParaCnt = static_cast<sal_uInt16>(nDiff + 1); + rFill.nLineWidth = 0; + rFill.bInner = sal_False; + rFill.bEmpty = sal_True; + rFill.SetOrient( text::HoriOrientation::LEFT ); + } + else + nDiff = -1; + if( rFill.bInner ) + bFill = sal_False; + else + { + const SvxTabStopItem &rRuler = pSet->GetTabStops(); + const SvxLRSpaceItem &rLRSpace = pSet->GetLRSpace(); + + SwRect &rRect = rFill.Fill().aCrsr; + 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() + + GetTxtNode()->GetLeftMarginWithNum( sal_False ); + SwTwips nRight = rFill.Right() - rLRSpace.GetRight(); + SwTwips nCenter = ( nLeft + nRight ) / 2; + rRect.Left( nLeft ); + if( FILL_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 = sal_False; + } + else + { + SwTwips nSpace = 0; + if( FILL_TAB != rFill.Mode() ) + { +static sal_Char const sDoubleSpace[] = " "; + const XubString aTmp( sDoubleSpace, RTL_TEXTENCODING_MS_1252 ); + + SwDrawTextInfo aDrawInf( pSh, *pOut, 0, aTmp, 0, 2 ); + nSpace = pFnt->_GetTxtSize( aDrawInf ).Width()/2; + } + if( rFill.X() >= nRight ) + { + if( FILL_INDENT != rFill.Mode() && ( rFill.bEmpty || + rFill.X() > rFill.nLineWidth + FILL_MIN_DIST ) ) + { + rFill.SetOrient( text::HoriOrientation::RIGHT ); + rRect.Left( nRight ); + } + else + bFill = sal_False; + } + else if( FILL_INDENT == rFill.Mode() ) + { + SwTwips nIndent = rFill.X(); + if( !rFill.bEmpty || nIndent > nRight ) + bFill = sal_False; + else + { + nIndent -= rFill.Left(); + if( nIndent >= 0 && nSpace ) + { + nIndent /= nSpace; + nIndent *= nSpace; + rFill.SetTab( MSHORT( nIndent ) ); + rRect.Left( nIndent + rFill.Left() ); + } + else + bFill = sal_False; + } + } + else if( rFill.X() > nLeft ) + { + SwTwips nTxtLeft = rFill.Left() + rLRSpace.GetTxtLeft() + + GetTxtNode()->GetLeftMarginWithNum( sal_True ); + rFill.nLineWidth += rFill.bFirstLine ? nLeft : nTxtLeft; + SwTwips nLeftTab = nLeft; + SwTwips nRightTab = nLeft; + MSHORT nSpaceCnt = 0; + MSHORT nTabCnt = 0; + MSHORT nIdx = 0; + do + { + nLeftTab = nRightTab; + if( nIdx < rRuler.Count() ) + { + const SvxTabStop &rTabStop = rRuler.operator[](nIdx); + nRightTab = nTxtLeft + rTabStop.GetTabPos(); + if( nLeftTab < nTxtLeft && nRightTab > nTxtLeft ) + nRightTab = nTxtLeft; + else + ++nIdx; + if( nRightTab > rFill.nLineWidth ) + ++nTabCnt; + } + else + { + const SvxTabStopItem& rTab = + (const SvxTabStopItem &)pSet-> + GetPool()->GetDefaultItem( RES_PARATR_TABSTOP ); + MSHORT nDefTabDist = (MSHORT)rTab.GetStart()->GetTabPos(); + nRightTab = nLeftTab - nTxtLeft; + nRightTab /= nDefTabDist; + nRightTab = nRightTab * nDefTabDist + nTxtLeft; + 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( FILL_TAB != 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( 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 ); + if( bFill ) + { + if( Abs( rFill.X() - nCenter ) <= + Abs( rFill.X() - rRect.Left() ) ) + { + rFill.SetOrient( text::HoriOrientation::CENTER ); + rFill.SetTab( 0 ); + rFill.SetSpace( 0 ); + rRect.Left( nCenter ); + } + if( !rFill.bEmpty ) + rFill.nLineWidth += FILL_MIN_DIST; + if( rRect.Left() < rFill.nLineWidth ) + bFill = sal_False; + } + } + } + // Gehen wir ueber die Unterkante der Seite/Spalte etc. hinaus? + const SwFrm* pUp = GetUpper(); + if( pUp->IsInSct() ) + { + if( pUp->IsSctFrm() ) + pUp = pUp->GetUpper(); + else if( pUp->IsColBodyFrm() && + pUp->GetUpper()->GetUpper()->IsSctFrm() ) + pUp = pUp->GetUpper()->GetUpper()->GetUpper(); + } + SWRECTFN( this ) + SwTwips nLimit = (pUp->*fnRect->fnGetPrtBottom)(); + SwTwips nRectBottom = rRect.Bottom(); + if ( bVert ) + nRectBottom = SwitchHorizontalToVertical( nRectBottom ); + + if( (*fnRect->fnYDiff)( nLimit, nRectBottom ) < 0 ) + bFill = sal_False; + else + rRect.Width( 1 ); + } + } + else + bFill = sal_False; + ((SwCrsrMoveState*)rFill.pCMS)->bFillRet = bFill; + delete pFnt; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |