| /************************************************************** |
| * |
| * 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 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the 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 "txtcfg.hxx" |
| #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(); |
| } |
| ASSERT( 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. |
| ASSERT( !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 |
| { |
| ASSERT( ! 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(); |
| ASSERT( 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 ) |
| { |
| DBG_LOOP; |
| 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() |
| #ifdef DBG_UTIL |
| 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 ); |
| ASSERT( nOldNode == pPam->GetPoint()->nNode.GetIndex(), |
| "SwTxtFrm::UnitUp: illegal node change" ) |
| |
| // 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; |
| #ifdef DBG_UTIL |
| // 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 ); |
| ASSERT( nOldNode == pPam->GetPoint()->nNode.GetIndex(), |
| "SwTxtFrm::UnitDown: illegal node change" ) |
| |
| // 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 __READONLY_DATA 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; |
| } |