Update ooo320-m1
[ooovba.git] / sw / source / core / text / frmcrsr.cxx
blob2fb972d2be1d1ed3270bd62ca38e771ffe9117ee
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: frmcrsr.cxx,v $
10 * $Revision: 1.44.212.1 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_sw.hxx"
35 #include "ndtxt.hxx" // GetNode()
36 #include "pam.hxx" // SwPosition
37 #include "frmtool.hxx"
38 #include "viewopt.hxx"
39 #include "paratr.hxx"
40 #include "pagefrm.hxx"
41 #include "colfrm.hxx"
42 #include "txttypes.hxx"
43 #include <sfx2/printer.hxx>
44 #include <svx/lrspitem.hxx>
45 #include <svx/tstpitem.hxx>
46 #include <svx/ulspitem.hxx>
47 #include <svx/lspcitem.hxx>
48 #include <pormulti.hxx> // SwMultiPortion
49 #include <doc.hxx>
50 #include <sortedobjs.hxx>
52 #include <unicode/ubidi.h>
54 #include "txtcfg.hxx"
55 #include "txtfrm.hxx" // SwTxtFrm
56 #include "inftxt.hxx" // SwTxtSizeInfo
57 #include "itrtxt.hxx" // SwTxtCursor
58 #include "crstate.hxx" // SwTxtCursor
59 #include "viewsh.hxx" // InvalidateWindows
60 #include "swfntcch.hxx" // SwFontAccess
61 #include "flyfrm.hxx"
63 #if OSL_DEBUG_LEVEL > 1
64 #include "txtpaint.hxx"
65 #endif
67 #define MIN_OFFSET_STEP 10
69 using namespace ::com::sun::star;
73 * 1170-SurvivalKit: Wie gelangt man hinter das letzte Zeichen der Zeile.
74 * - RightMargin verzichtet auf den Positionsausgleich mit -1
75 * - GetCharRect liefert bei MV_RIGHTMARGIN ein GetEndCharRect
76 * - GetEndCharRect setzt bRightMargin auf sal_True
77 * - SwTxtCursor::bRightMargin wird per CharCrsrToLine auf sal_False gesetzt
80 /*************************************************************************
81 * GetAdjFrmAtPos()
82 *************************************************************************/
84 SwTxtFrm *GetAdjFrmAtPos( SwTxtFrm *pFrm, const SwPosition &rPos,
85 const sal_Bool bRightMargin, const sal_Bool bNoScroll = TRUE )
87 // 8810: vgl. 1170, RightMargin in der letzten Masterzeile...
88 const xub_StrLen nOffset = rPos.nContent.GetIndex();
89 SwTxtFrm *pFrmAtPos = pFrm;
90 if( !bNoScroll || pFrm->GetFollow() )
92 pFrmAtPos = pFrm->GetFrmAtPos( rPos );
93 if( nOffset < pFrmAtPos->GetOfst() &&
94 !pFrmAtPos->IsFollow() )
96 xub_StrLen nNew = nOffset;
97 if( nNew < MIN_OFFSET_STEP )
98 nNew = 0;
99 else
100 nNew -= MIN_OFFSET_STEP;
101 lcl_ChangeOffset( pFrmAtPos, nNew );
104 while( pFrm != pFrmAtPos )
106 pFrm = pFrmAtPos;
107 pFrm->GetFormatted();
108 pFrmAtPos = (SwTxtFrm*)pFrm->GetFrmAtPos( rPos );
111 if( nOffset && bRightMargin )
113 while( pFrmAtPos && pFrmAtPos->GetOfst() == nOffset &&
114 pFrmAtPos->IsFollow() )
116 pFrmAtPos->GetFormatted();
117 pFrmAtPos = pFrmAtPos->FindMaster();
119 ASSERT( pFrmAtPos, "+GetCharRect: no frame with my rightmargin" );
121 return pFrmAtPos ? pFrmAtPos : pFrm;
124 sal_Bool lcl_ChangeOffset( SwTxtFrm* pFrm, xub_StrLen nNew )
126 // In Bereichen und ausserhalb von Flies wird nicht mehr gescrollt.
127 ASSERT( !pFrm->IsFollow(), "Illegal Scrolling by Follow!" );
128 if( pFrm->GetOfst() != nNew && !pFrm->IsInSct() )
130 SwFlyFrm *pFly = pFrm->FindFlyFrm();
131 // Vorsicht, wenn z.B. bei einem spaltigen Rahmen die Groesse noch invalide ist,
132 // duerfen wir nicht mal eben herumscrollen
133 if ( ( pFly && pFly->IsValid() &&
134 !pFly->GetNextLink() && !pFly->GetPrevLink() ) ||
135 ( !pFly && pFrm->IsInTab() ) )
137 ViewShell* pVsh = pFrm->GetShell();
138 if( pVsh )
140 if( pVsh->GetNext() != pVsh ||
141 ( pFrm->GetDrawObjs() && pFrm->GetDrawObjs()->Count() ) )
143 if( !pFrm->GetOfst() )
144 return sal_False;
145 nNew = 0;
147 pFrm->SetOfst( nNew );
148 pFrm->SetPara( 0 );
149 pFrm->GetFormatted();
150 if( pFrm->Frm().HasArea() )
151 pFrm->GetShell()->InvalidateWindows( pFrm->Frm() );
152 return sal_True;
156 return sal_False;
159 /*************************************************************************
160 * GetFrmAtOfst(), GetFrmAtPos()
161 *************************************************************************/
163 // OD 07.10.2003 #110978#
164 SwTxtFrm& SwTxtFrm::GetFrmAtOfst( const xub_StrLen nWhere )
166 SwTxtFrm* pRet = this;
167 while( pRet->HasFollow() && nWhere >= pRet->GetFollow()->GetOfst() )
168 pRet = pRet->GetFollow();
169 return *pRet;
172 SwTxtFrm *SwTxtFrm::GetFrmAtPos( const SwPosition &rPos )
174 SwTxtFrm *pFoll = (SwTxtFrm*)this;
175 while( pFoll->GetFollow() )
177 if( rPos.nContent.GetIndex() > pFoll->GetFollow()->GetOfst() )
178 pFoll = pFoll->GetFollow();
179 else
181 if( rPos.nContent.GetIndex() == pFoll->GetFollow()->GetOfst()
182 && !SwTxtCursor::IsRightMargin() )
183 pFoll = pFoll->GetFollow();
184 else
185 break;
188 return pFoll;
191 /*************************************************************************
192 * SwTxtFrm::GetCharRect()
193 *************************************************************************/
196 * GetCharRect() findet die Characterzelle des Characters, dass
197 * durch aPos beschrieben wird. GetCrsrOfst() findet den
198 * umgekehrten Weg: Von einer Dokumentkoordinate zu einem Pam.
199 * Beide sind virtuell in der Framebasisklasse und werden deshalb
200 * immer angezogen.
203 sal_Bool SwTxtFrm::GetCharRect( SwRect& rOrig, const SwPosition &rPos,
204 SwCrsrMoveState *pCMS ) const
206 ASSERT( ! IsVertical() || ! IsSwapped(),"SwTxtFrm::GetCharRect with swapped frame" );
208 if( IsLocked() || IsHiddenNow() )
209 return sal_False;
211 //Erstmal den richtigen Frm finden, dabei muss beachtet werden, dass:
212 //- die gecachten Informationen verworfen sein koennen (GetPara() == 0)
213 //- das ein Follow gemeint sein kann
214 //- das die Kette der Follows dynamisch waechst; der in den wir
215 // schliesslich gelangen muss aber Formatiert sein.
217 // opt: reading ahead erspart uns ein GetAdjFrmAtPos
218 const sal_Bool bRightMargin = pCMS && ( MV_RIGHTMARGIN == pCMS->eState );
219 const sal_Bool bNoScroll = pCMS && pCMS->bNoScroll;
220 SwTxtFrm *pFrm = GetAdjFrmAtPos( (SwTxtFrm*)this, rPos, bRightMargin,
221 bNoScroll );
222 pFrm->GetFormatted();
223 const SwFrm* pTmpFrm = (SwFrm*)pFrm->GetUpper();
225 SWRECTFN ( pFrm )
226 const SwTwips nUpperMaxY = (pTmpFrm->*fnRect->fnGetPrtBottom)();
227 const SwTwips nFrmMaxY = (pFrm->*fnRect->fnGetPrtBottom)();
229 // nMaxY is an absolute value
230 SwTwips nMaxY = bVert ?
231 Max( nFrmMaxY, nUpperMaxY ) :
232 Min( nFrmMaxY, nUpperMaxY );
234 sal_Bool bRet = sal_False;
236 if ( pFrm->IsEmpty() || ! (pFrm->Prt().*fnRect->fnGetHeight)() )
238 Point aPnt1 = pFrm->Frm().Pos() + pFrm->Prt().Pos();
239 SwTxtNode* pTxtNd = ((SwTxtFrm*)this)->GetTxtNode();
240 short nFirstOffset;
241 pTxtNd->GetFirstLineOfsWithNum( nFirstOffset );
243 Point aPnt2;
244 if ( bVert )
246 if( nFirstOffset > 0 )
247 aPnt1.Y() += nFirstOffset;
249 if ( aPnt1.X() < nMaxY )
250 aPnt1.X() = nMaxY;
251 aPnt2.X() = aPnt1.X() + pFrm->Prt().Width();
252 aPnt2.Y() = aPnt1.Y();
253 if( aPnt2.X() < nMaxY )
254 aPnt2.X() = nMaxY;
256 else
258 if( nFirstOffset > 0 )
259 aPnt1.X() += nFirstOffset;
261 if( aPnt1.Y() > nMaxY )
262 aPnt1.Y() = nMaxY;
263 aPnt2.X() = aPnt1.X();
264 aPnt2.Y() = aPnt1.Y() + pFrm->Prt().Height();
265 if( aPnt2.Y() > nMaxY )
266 aPnt2.Y() = nMaxY;
269 rOrig = SwRect( aPnt1, aPnt2 );
271 if ( pCMS )
273 pCMS->aRealHeight.X() = 0;
274 pCMS->aRealHeight.Y() = bVert ? -rOrig.Width() : rOrig.Height();
277 if ( pFrm->IsRightToLeft() )
278 pFrm->SwitchLTRtoRTL( rOrig );
280 bRet = sal_True;
282 else
284 if( !pFrm->HasPara() )
285 return sal_False;
287 SwFrmSwapper aSwapper( pFrm, sal_True );
288 if ( bVert )
289 nMaxY = pFrm->SwitchVerticalToHorizontal( nMaxY );
291 sal_Bool bGoOn = sal_True;
292 xub_StrLen nOffset = rPos.nContent.GetIndex();
293 xub_StrLen nNextOfst;
298 SwTxtSizeInfo aInf( pFrm );
299 SwTxtCursor aLine( pFrm, &aInf );
300 nNextOfst = aLine.GetEnd();
301 // Siehe Kommentar in AdjustFrm
302 // 1170: das letzte Zeichen der Zeile mitnehmen?
303 bRet = bRightMargin ? aLine.GetEndCharRect( &rOrig, nOffset, pCMS, nMaxY )
304 : aLine.GetCharRect( &rOrig, nOffset, pCMS, nMaxY );
307 if ( pFrm->IsRightToLeft() )
308 pFrm->SwitchLTRtoRTL( rOrig );
310 if ( bVert )
311 pFrm->SwitchHorizontalToVertical( rOrig );
313 if( pFrm->IsUndersized() && pCMS && !pFrm->GetNext() &&
314 (rOrig.*fnRect->fnGetBottom)() == nUpperMaxY &&
315 pFrm->GetOfst() < nOffset &&
316 !pFrm->IsFollow() && !bNoScroll &&
317 pFrm->GetTxtNode()->GetTxt().Len() != nNextOfst )
318 bGoOn = lcl_ChangeOffset( pFrm, nNextOfst );
319 else
320 bGoOn = sal_False;
321 } while ( bGoOn );
323 if ( pCMS )
325 if ( pFrm->IsRightToLeft() )
327 if( pCMS->b2Lines && pCMS->p2Lines)
329 pFrm->SwitchLTRtoRTL( pCMS->p2Lines->aLine );
330 pFrm->SwitchLTRtoRTL( pCMS->p2Lines->aPortion );
334 if ( bVert )
336 if ( pCMS->bRealHeight )
338 pCMS->aRealHeight.Y() = -pCMS->aRealHeight.Y();
339 if ( pCMS->aRealHeight.Y() < 0 )
341 // writing direction is from top to bottom
342 pCMS->aRealHeight.X() = ( rOrig.Width() -
343 pCMS->aRealHeight.X() +
344 pCMS->aRealHeight.Y() );
347 if( pCMS->b2Lines && pCMS->p2Lines)
349 pFrm->SwitchHorizontalToVertical( pCMS->p2Lines->aLine );
350 pFrm->SwitchHorizontalToVertical( pCMS->p2Lines->aPortion );
356 if( bRet )
358 SwPageFrm *pPage = pFrm->FindPageFrm();
359 ASSERT( pPage, "Text esaped from page?" );
360 const SwTwips nOrigTop = (rOrig.*fnRect->fnGetTop)();
361 const SwTwips nPageTop = (pPage->Frm().*fnRect->fnGetTop)();
362 const SwTwips nPageBott = (pPage->Frm().*fnRect->fnGetBottom)();
364 // Following situation: if the frame is in an invalid sectionframe,
365 // it's possible that the frame is outside the page. If we restrict
366 // the cursor position to the page area, we enforce the formatting
367 // of the page, of the section frame and the frame himself.
368 if( (*fnRect->fnYDiff)( nPageTop, nOrigTop ) > 0 )
369 (rOrig.*fnRect->fnSetTop)( nPageTop );
371 if ( (*fnRect->fnYDiff)( nOrigTop, nPageBott ) > 0 )
372 (rOrig.*fnRect->fnSetTop)( nPageBott );
375 return bRet;
378 /*************************************************************************
379 * SwTxtFrm::GetAutoPos()
380 *************************************************************************/
383 * GetAutoPos() findet die Characterzelle des Characters, dass
384 * durch aPos beschrieben wird und wird von autopositionierten Rahmen genutzt.
387 sal_Bool SwTxtFrm::GetAutoPos( SwRect& rOrig, const SwPosition &rPos ) const
389 if( IsHiddenNow() )
390 return sal_False;
392 xub_StrLen nOffset = rPos.nContent.GetIndex();
393 SwTxtFrm* pFrm = &(const_cast<SwTxtFrm*>(this)->GetFrmAtOfst( nOffset ));
395 pFrm->GetFormatted();
396 const SwFrm* pTmpFrm = (SwFrm*)pFrm->GetUpper();
398 SWRECTFN( pTmpFrm )
399 SwTwips nUpperMaxY = (pTmpFrm->*fnRect->fnGetPrtBottom)();
401 // nMaxY is in absolute value
402 SwTwips nMaxY = bVert ?
403 Max( (pFrm->*fnRect->fnGetPrtBottom)(), nUpperMaxY ) :
404 Min( (pFrm->*fnRect->fnGetPrtBottom)(), nUpperMaxY );
406 if ( pFrm->IsEmpty() || ! (pFrm->Prt().*fnRect->fnGetHeight)() )
408 Point aPnt1 = pFrm->Frm().Pos() + pFrm->Prt().Pos();
409 Point aPnt2;
410 if ( bVert )
412 if ( aPnt1.X() < nMaxY )
413 aPnt1.X() = nMaxY;
414 aPnt2.X() = aPnt1.X() + pFrm->Prt().Width();
415 aPnt2.Y() = aPnt1.Y();
416 if( aPnt2.X() < nMaxY )
417 aPnt2.X() = nMaxY;
419 else
421 if( aPnt1.Y() > nMaxY )
422 aPnt1.Y() = nMaxY;
423 aPnt2.X() = aPnt1.X();
424 aPnt2.Y() = aPnt1.Y() + pFrm->Prt().Height();
425 if( aPnt2.Y() > nMaxY )
426 aPnt2.Y() = nMaxY;
428 rOrig = SwRect( aPnt1, aPnt2 );
429 return sal_True;
431 else
433 if( !pFrm->HasPara() )
434 return sal_False;
436 SwFrmSwapper aSwapper( pFrm, sal_True );
437 if ( bVert )
438 nMaxY = pFrm->SwitchVerticalToHorizontal( nMaxY );
440 SwTxtSizeInfo aInf( pFrm );
441 SwTxtCursor aLine( pFrm, &aInf );
442 SwCrsrMoveState aTmpState( MV_SETONLYTEXT );
443 aTmpState.bRealHeight = TRUE;
444 if( aLine.GetCharRect( &rOrig, nOffset, &aTmpState, nMaxY ) )
446 if( aTmpState.aRealHeight.X() >= 0 )
448 rOrig.Pos().Y() += aTmpState.aRealHeight.X();
449 rOrig.Height( aTmpState.aRealHeight.Y() );
452 if ( pFrm->IsRightToLeft() )
453 pFrm->SwitchLTRtoRTL( rOrig );
455 if ( bVert )
456 pFrm->SwitchHorizontalToVertical( rOrig );
458 return sal_True;
460 return sal_False;
464 /** determine top of line for given position in the text frame
466 OD 11.11.2003 #i22341#
467 OD 2004-03-18 #114789# - corrections:
468 - Top of first paragraph line is the top of the printing area of the text frame
469 - If a proportional line spacing is applied use top of anchor character as
470 top of the line.
472 @author OD
474 bool SwTxtFrm::GetTopOfLine( SwTwips& _onTopOfLine,
475 const SwPosition& _rPos ) const
477 bool bRet = true;
479 // get position offset
480 xub_StrLen nOffset = _rPos.nContent.GetIndex();
482 if ( GetTxt().Len() < nOffset )
484 bRet = false;
486 else
488 SWRECTFN( this )
489 if ( IsEmpty() || !(Prt().*fnRect->fnGetHeight)() )
491 // OD 2004-03-18 #i11860# - consider upper space amount considered
492 // for previous frame and the page grid.
493 _onTopOfLine = (this->*fnRect->fnGetPrtTop)();
495 else
497 // determine formatted text frame that contains the requested position
498 SwTxtFrm* pFrm = &(const_cast<SwTxtFrm*>(this)->GetFrmAtOfst( nOffset ));
499 pFrm->GetFormatted();
500 SWREFRESHFN( pFrm )
501 // OD 2004-03-18 #114789# - If proportional line spacing is applied
502 // to the text frame, the top of the anchor character is also the
503 // top of the line.
504 // Otherwise the line layout determines the top of the line
505 const SvxLineSpacingItem& rSpace = GetAttrSet()->GetLineSpacing();
506 if ( rSpace.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_PROP )
508 SwRect aCharRect;
509 if ( GetAutoPos( aCharRect, _rPos ) )
511 _onTopOfLine = (aCharRect.*fnRect->fnGetTop)();
513 else
515 bRet = false;
518 else
520 // assure that text frame is in a horizontal layout
521 SwFrmSwapper aSwapper( pFrm, sal_True );
522 // determine text line that contains the requested position
523 SwTxtSizeInfo aInf( pFrm );
524 SwTxtCursor aLine( pFrm, &aInf );
525 aLine.CharCrsrToLine( nOffset );
526 // determine top of line
527 _onTopOfLine = aLine.Y();
528 if ( bVert )
530 _onTopOfLine = pFrm->SwitchHorizontalToVertical( _onTopOfLine );
536 return bRet;
539 /*************************************************************************
540 * SwTxtFrm::_GetCrsrOfst()
541 *************************************************************************/
543 // Minimaler Abstand von nichtleeren Zeilen etwas weniger als 2 cm
544 #define FILL_MIN_DIST 1100
546 struct SwFillData
548 SwRect aFrm;
549 const SwCrsrMoveState *pCMS;
550 SwPosition* pPos;
551 const Point& rPoint;
552 SwTwips nLineWidth;
553 sal_Bool bFirstLine : 1;
554 sal_Bool bInner : 1;
555 sal_Bool bColumn : 1;
556 sal_Bool bEmpty : 1;
557 SwFillData( const SwCrsrMoveState *pC, SwPosition* pP, const SwRect& rR,
558 const Point& rPt ) : aFrm( rR ), pCMS( pC ), pPos( pP ), rPoint( rPt ),
559 nLineWidth( 0 ), bFirstLine( sal_True ), bInner( sal_False ), bColumn( sal_False ),
560 bEmpty( sal_True ){}
561 SwFillMode Mode() const { return pCMS->pFill->eMode; }
562 long X() const { return rPoint.X(); }
563 long Y() const { return rPoint.Y(); }
564 long Left() const { return aFrm.Left(); }
565 long Right() const { return aFrm.Right(); }
566 long Bottom() const { return aFrm.Bottom(); }
567 SwRect& Frm() { return aFrm; }
568 SwFillCrsrPos &Fill() const { return *pCMS->pFill; }
569 void SetTab( MSHORT nNew ) { pCMS->pFill->nTabCnt = nNew; }
570 void SetSpace( MSHORT nNew ) { pCMS->pFill->nSpaceCnt = nNew; }
571 void SetOrient( const sal_Int16 eNew ){ pCMS->pFill->eOrient = eNew; }
574 sal_Bool SwTxtFrm::_GetCrsrOfst(SwPosition* pPos, const Point& rPoint,
575 const sal_Bool bChgFrm, SwCrsrMoveState* pCMS ) const
577 // 8804: _GetCrsrOfst wird vom GetCrsrOfst und GetKeyCrsrOfst gerufen.
578 // In keinem Fall nur ein return sal_False.
580 if( IsLocked() || IsHiddenNow() )
581 return sal_False;
583 ((SwTxtFrm*)this)->GetFormatted();
585 Point aOldPoint( rPoint );
587 if ( IsVertical() )
589 SwitchVerticalToHorizontal( (Point&)rPoint );
590 ((SwTxtFrm*)this)->SwapWidthAndHeight();
593 if ( IsRightToLeft() )
594 SwitchRTLtoLTR( (Point&)rPoint );
596 SwFillData *pFillData = ( pCMS && pCMS->pFill ) ?
597 new SwFillData( pCMS, pPos, Frm(), rPoint ) : NULL;
599 if ( IsEmpty() )
601 SwTxtNode* pTxtNd = ((SwTxtFrm*)this)->GetTxtNode();
602 pPos->nNode = *pTxtNd;
603 pPos->nContent.Assign( pTxtNd, 0 );
604 if( pCMS && pCMS->bFieldInfo )
606 SwTwips nDiff = rPoint.X() - Frm().Left() - Prt().Left();
607 if( nDiff > 50 || nDiff < 0 )
608 ((SwCrsrMoveState*)pCMS)->bPosCorr = sal_True;
611 else
613 SwTxtSizeInfo aInf( (SwTxtFrm*)this );
614 SwTxtCursor aLine( ((SwTxtFrm*)this), &aInf );
616 // Siehe Kommentar in AdjustFrm()
617 SwTwips nMaxY = Frm().Top() + Prt().Top() + Prt().Height();
618 aLine.TwipsToLine( rPoint.Y() );
619 while( aLine.Y() + aLine.GetLineHeight() > nMaxY )
621 DBG_LOOP;
622 if( !aLine.Prev() )
623 break;
626 if( aLine.GetDropLines() >= aLine.GetLineNr() && 1 != aLine.GetLineNr()
627 && rPoint.X() < aLine.FirstLeft() + aLine.GetDropLeft() )
628 while( aLine.GetLineNr() > 1 )
629 aLine.Prev();
631 xub_StrLen nOffset = aLine.GetCrsrOfst( pPos, rPoint, bChgFrm, pCMS );
633 if( pCMS && pCMS->eState == MV_NONE && aLine.GetEnd() == nOffset )
634 ((SwCrsrMoveState*)pCMS)->eState = MV_RIGHTMARGIN;
636 // 6776: pPos ist ein reiner IN-Parameter, der nicht ausgewertet werden darf.
637 // Das pIter->GetCrsrOfst returnt aus einer Verschachtelung mit STRING_LEN.
638 // Wenn SwTxtIter::GetCrsrOfst von sich aus weitere GetCrsrOfst
639 // ruft, so aendert sich nNode der Position. In solchen Faellen
640 // darf pPos nicht berechnet werden.
641 if( STRING_LEN != nOffset )
643 SwTxtNode* pTxtNd = ((SwTxtFrm*)this)->GetTxtNode();
644 pPos->nNode = *pTxtNd;
645 pPos->nContent.Assign( pTxtNd, nOffset );
646 if( pFillData )
648 if( pTxtNd->GetTxt().Len() > nOffset ||
649 rPoint.Y() < Frm().Top() )
650 pFillData->bInner = sal_True;
651 pFillData->bFirstLine = aLine.GetLineNr() < 2;
652 if( pTxtNd->GetTxt().Len() )
654 pFillData->bEmpty = sal_False;
655 pFillData->nLineWidth = aLine.GetCurr()->Width();
660 sal_Bool bChgFillData = sal_False;
661 if( pFillData && FindPageFrm()->Frm().IsInside( aOldPoint ) )
663 FillCrsrPos( *pFillData );
664 bChgFillData = sal_True;
667 if ( IsVertical() )
669 if ( bChgFillData )
670 SwitchHorizontalToVertical( pFillData->Fill().aCrsr.Pos() );
671 ((SwTxtFrm*)this)->SwapWidthAndHeight();
674 if ( IsRightToLeft() && bChgFillData )
676 SwitchLTRtoRTL( pFillData->Fill().aCrsr.Pos() );
677 const sal_Int16 eOrient = pFillData->pCMS->pFill->eOrient;
679 if ( text::HoriOrientation::LEFT == eOrient )
680 pFillData->SetOrient( text::HoriOrientation::RIGHT );
681 else if ( text::HoriOrientation::RIGHT == eOrient )
682 pFillData->SetOrient( text::HoriOrientation::LEFT );
685 (Point&)rPoint = aOldPoint;
686 delete pFillData;
688 return sal_True;
691 /*************************************************************************
692 * virtual SwTxtFrm::GetCrsrOfst()
693 *************************************************************************/
695 sal_Bool SwTxtFrm::GetCrsrOfst(SwPosition* pPos, Point& rPoint,
696 SwCrsrMoveState* pCMS ) const
698 MSHORT nChgFrm = 2;
699 if( pCMS )
701 if( MV_UPDOWN == pCMS->eState )
702 nChgFrm = 0;
703 else if( MV_SETONLYTEXT == pCMS->eState ||
704 MV_TBLSEL == pCMS->eState )
705 nChgFrm = 1;
707 return _GetCrsrOfst( pPos, rPoint, nChgFrm != 0, pCMS );
710 /*************************************************************************
711 * SwTxtFrm::LeftMargin()
712 *************************************************************************/
715 * Layout-orientierte Cursorbewegungen
719 * an den Zeilenanfang
722 sal_Bool SwTxtFrm::LeftMargin(SwPaM *pPam) const
724 if( ((const SwNode*)pPam->GetNode()) != GetNode() )
725 pPam->GetPoint()->nNode = *((SwTxtFrm*)this)->GetTxtNode();
727 SwTxtFrm *pFrm = GetAdjFrmAtPos( (SwTxtFrm*)this, *pPam->GetPoint(),
728 SwTxtCursor::IsRightMargin() );
729 pFrm->GetFormatted();
730 xub_StrLen nIndx;
731 if ( pFrm->IsEmpty() )
732 nIndx = 0;
733 else
735 SwTxtSizeInfo aInf( pFrm );
736 SwTxtCursor aLine( pFrm, &aInf );
738 aLine.CharCrsrToLine(pPam->GetPoint()->nContent.GetIndex());
739 nIndx = aLine.GetStart();
740 if( pFrm->GetOfst() && !pFrm->IsFollow() && !aLine.GetPrev() )
742 lcl_ChangeOffset( pFrm, 0 );
743 nIndx = 0;
746 pPam->GetPoint()->nContent = SwIndex( pFrm->GetTxtNode(), nIndx );
747 SwTxtCursor::SetRightMargin( sal_False );
748 return sal_True;
751 /*************************************************************************
752 * SwTxtFrm::RightMargin()
753 *************************************************************************/
756 * An das Zeilenende:Das ist die Position vor dem letzten
757 * Character in der Zeile. Ausnahme: In der letzten Zeile soll
758 * der Cursor auch hinter dem letzten Character stehen koennen,
759 * um Text anhaengen zu koennen.
763 sal_Bool SwTxtFrm::RightMargin(SwPaM *pPam, sal_Bool bAPI) const
765 if( ((const SwNode*)pPam->GetNode()) != GetNode() )
766 pPam->GetPoint()->nNode = *((SwTxtFrm*)this)->GetTxtNode();
768 SwTxtFrm *pFrm = GetAdjFrmAtPos( (SwTxtFrm*)this, *pPam->GetPoint(),
769 SwTxtCursor::IsRightMargin() );
770 pFrm->GetFormatted();
771 xub_StrLen nRightMargin;
772 if ( IsEmpty() )
773 nRightMargin = 0;
774 else
776 SwTxtSizeInfo aInf( pFrm );
777 SwTxtCursor aLine( pFrm, &aInf );
779 aLine.CharCrsrToLine(pPam->GetPoint()->nContent.GetIndex());
780 nRightMargin = aLine.GetStart() + aLine.GetCurr()->GetLen();
782 // Harte Zeilenumbrueche lassen wir hinter uns.
783 if( aLine.GetCurr()->GetLen() &&
784 CH_BREAK == aInf.GetTxt().GetChar( nRightMargin - 1 ) )
785 --nRightMargin;
786 if( !bAPI && (aLine.GetNext() || pFrm->GetFollow()) )
788 while( nRightMargin > aLine.GetStart() &&
789 ' ' == aInf.GetTxt().GetChar( nRightMargin - 1 ) )
790 --nRightMargin;
793 pPam->GetPoint()->nContent = SwIndex( pFrm->GetTxtNode(), nRightMargin );
794 SwTxtCursor::SetRightMargin( !bAPI );
795 return sal_True;
798 /*************************************************************************
799 * SwTxtFrm::_UnitUp()
800 *************************************************************************/
802 //Die beiden folgenden Methoden versuchen zunaechst den Crsr in die
803 //nachste/folgende Zeile zu setzen. Gibt es im Frame keine vorhergehende/
804 //folgende Zeile, so wird der Aufruf an die Basisklasse weitergeleitet.
805 //Die Horizontale Ausrichtung des Crsr wird hinterher von der CrsrShell
806 //vorgenommen.
808 class SwSetToRightMargin
810 sal_Bool bRight;
811 public:
812 inline SwSetToRightMargin() : bRight( sal_False ) { }
813 inline ~SwSetToRightMargin() { SwTxtCursor::SetRightMargin( bRight ); }
814 inline void SetRight( const sal_Bool bNew ) { bRight = bNew; }
817 sal_Bool SwTxtFrm::_UnitUp( SwPaM *pPam, const SwTwips nOffset,
818 sal_Bool bSetInReadOnly ) const
820 // 8626: Im Notfall den RightMargin setzen.
821 SwSetToRightMargin aSet;
823 if( IsInTab() &&
824 pPam->GetNode( sal_True )->StartOfSectionNode() !=
825 pPam->GetNode( sal_False )->StartOfSectionNode() )
827 //Wenn der PaM in unterschiedlichen Boxen sitzt, so handelt es sich um
828 //eine Tabellenselektion; diese wird von der Basisklasse abgearbeitet.
829 return SwCntntFrm::UnitUp( pPam, nOffset, bSetInReadOnly );
832 ((SwTxtFrm*)this)->GetFormatted();
833 const xub_StrLen nPos = pPam->GetPoint()->nContent.GetIndex();
834 SwRect aCharBox;
836 if( !IsEmpty() && !IsHiddenNow() )
838 xub_StrLen nFormat = STRING_LEN;
841 if( nFormat != STRING_LEN && !IsFollow() )
842 lcl_ChangeOffset( ((SwTxtFrm*)this), nFormat );
844 SwTxtSizeInfo aInf( (SwTxtFrm*)this );
845 SwTxtCursor aLine( ((SwTxtFrm*)this), &aInf );
847 // 8116: Flys ohne Umlauf und IsDummy(); hier wegoptimiert
848 if( nPos )
849 aLine.CharCrsrToLine( nPos );
850 else
851 aLine.Top();
853 const SwLineLayout *pPrevLine = aLine.GetPrevLine();
854 const xub_StrLen nStart = aLine.GetStart();
855 aLine.GetCharRect( &aCharBox, nPos );
857 sal_Bool bSecondOfDouble = ( aInf.IsMulti() && ! aInf.IsFirstMulti() );
858 sal_Bool bPrevLine = ( pPrevLine && pPrevLine != aLine.GetCurr() );
860 if( !pPrevLine && !bSecondOfDouble && GetOfst() && !IsFollow() )
862 nFormat = GetOfst();
863 xub_StrLen nDiff = aLine.GetLength();
864 if( !nDiff )
865 nDiff = MIN_OFFSET_STEP;
866 if( nFormat > nDiff )
867 nFormat = nFormat - nDiff;
868 else
869 nFormat = 0;
870 continue;
873 // we select the target line for the cursor, in case we are in a
874 // double line portion, prev line = curr line
875 if( bPrevLine && !bSecondOfDouble )
877 aLine.PrevLine();
878 while ( aLine.GetStart() == nStart &&
879 0 != ( pPrevLine = aLine.GetPrevLine() ) &&
880 pPrevLine != aLine.GetCurr() )
881 aLine.PrevLine();
884 if ( bPrevLine || bSecondOfDouble )
886 aCharBox.SSize().Width() /= 2;
887 aCharBox.Pos().X() = aCharBox.Pos().X() - 150;
889 // siehe Kommentar in SwTxtFrm::GetCrsrOfst()
890 #ifndef PRODUCT
891 const ULONG nOldNode = pPam->GetPoint()->nNode.GetIndex();
892 #endif
893 // Der Node soll nicht gewechselt werden
894 xub_StrLen nTmpOfst = aLine.GetCrsrOfst( pPam->GetPoint(),
895 aCharBox.Pos(), sal_False );
896 ASSERT( nOldNode == pPam->GetPoint()->nNode.GetIndex(),
897 "SwTxtFrm::UnitUp: illegal node change" )
899 // 7684: Wir stellen sicher, dass wir uns nach oben bewegen.
900 if( nTmpOfst >= nStart && nStart && !bSecondOfDouble )
902 nTmpOfst = nStart;
903 aSet.SetRight( sal_True );
905 pPam->GetPoint()->nContent =
906 SwIndex( ((SwTxtFrm*)this)->GetTxtNode(), nTmpOfst );
907 return sal_True;
910 if ( IsFollow() )
912 aLine.GetCharRect( &aCharBox, nPos );
913 aCharBox.SSize().Width() /= 2;
915 break;
916 } while ( sal_True );
918 /* Wenn this ein Follow ist und ein Prev miszlang, so
919 * muessen wir in die letzte Zeile des Master ... und der sind wir.
920 * Oder wir sind ein Follow mit Follow, dann muessen wir uns den
921 * Master extra besorgen...
923 if ( IsFollow() )
925 const SwTxtFrm *pTmpPrev = FindMaster();
926 xub_StrLen nOffs = GetOfst();
927 if( pTmpPrev )
929 ViewShell *pSh = GetShell();
930 sal_Bool bProtectedAllowed = pSh && pSh->GetViewOptions()->IsCursorInProtectedArea();
931 const SwTxtFrm *pPrevPrev = pTmpPrev;
932 // Hier werden geschuetzte Frames und Frame ohne Inhalt ausgelassen
933 while( pPrevPrev && ( pPrevPrev->GetOfst() == nOffs ||
934 ( !bProtectedAllowed && pPrevPrev->IsProtected() ) ) )
936 pTmpPrev = pPrevPrev;
937 nOffs = pTmpPrev->GetOfst();
938 if ( pPrevPrev->IsFollow() )
939 pPrevPrev = pTmpPrev->FindMaster();
940 else
941 pPrevPrev = NULL;
943 if ( !pPrevPrev )
944 return pTmpPrev->SwCntntFrm::UnitUp( pPam, nOffset, bSetInReadOnly );
945 aCharBox.Pos().Y() = pPrevPrev->Frm().Bottom() - 1;
946 return pPrevPrev->GetKeyCrsrOfst( pPam->GetPoint(), aCharBox.Pos() );
949 return SwCntntFrm::UnitUp( pPam, nOffset, bSetInReadOnly );
953 // Used for Bidi. nPos is the logical position in the string, bLeft indicates
954 // if left arrow or right arrow was pressed. The return values are:
955 // nPos: the new visual position
956 // bLeft: whether the break iterator has to add or subtract from the
957 // current position
958 void lcl_VisualMoveRecursion( const SwLineLayout& rCurrLine, xub_StrLen nIdx,
959 xub_StrLen& nPos, sal_Bool& bRight,
960 BYTE& nCrsrLevel, BYTE nDefaultDir )
962 const SwLinePortion* pPor = rCurrLine.GetFirstPortion();
963 const SwLinePortion* pLast = 0;
965 // what's the current portion
966 while ( pPor && nIdx + pPor->GetLen() <= nPos )
968 nIdx = nIdx + pPor->GetLen();
969 pLast = pPor;
970 pPor = pPor->GetPortion();
973 if ( bRight )
975 sal_Bool bRecurse = pPor && pPor->IsMultiPortion() &&
976 ((SwMultiPortion*)pPor)->IsBidi();
978 // 1. special case: at beginning of bidi portion
979 if ( bRecurse && nIdx == nPos )
981 nPos = nPos + pPor->GetLen();
983 // leave bidi portion
984 if ( nCrsrLevel != nDefaultDir )
986 bRecurse = sal_False;
988 else
989 // special case:
990 // buffer: abcXYZ123 in LTR paragraph
991 // view: abc123ZYX
992 // cursor is between c and X in the buffer and cursor level = 0
993 nCrsrLevel++;
996 // 2. special case: at beginning of portion after bidi portion
997 else if ( pLast && pLast->IsMultiPortion() &&
998 ((SwMultiPortion*)pLast)->IsBidi() && nIdx == nPos )
1000 // enter bidi portion
1001 if ( nCrsrLevel != nDefaultDir )
1003 bRecurse = sal_True;
1004 nIdx = nIdx - pLast->GetLen();
1005 pPor = pLast;
1009 // Recursion
1010 if ( bRecurse )
1012 const SwLineLayout& rLine = ((SwMultiPortion*)pPor)->GetRoot();
1013 xub_StrLen nTmpPos = nPos - nIdx;
1014 sal_Bool bTmpForward = ! bRight;
1015 BYTE nTmpCrsrLevel = nCrsrLevel;
1016 lcl_VisualMoveRecursion( rLine, 0, nTmpPos, bTmpForward,
1017 nTmpCrsrLevel, nDefaultDir + 1 );
1019 nPos = nTmpPos + nIdx;
1020 bRight = bTmpForward;
1021 nCrsrLevel = nTmpCrsrLevel;
1024 // go forward
1025 else
1027 bRight = sal_True;
1028 nCrsrLevel = nDefaultDir;
1032 else
1034 sal_Bool bRecurse = pPor && pPor->IsMultiPortion() && ((SwMultiPortion*)pPor)->IsBidi();
1036 // 1. special case: at beginning of bidi portion
1037 if ( bRecurse && nIdx == nPos )
1039 // leave bidi portion
1040 if ( nCrsrLevel == nDefaultDir )
1042 bRecurse = sal_False;
1046 // 2. special case: at beginning of portion after bidi portion
1047 else if ( pLast && pLast->IsMultiPortion() &&
1048 ((SwMultiPortion*)pLast)->IsBidi() && nIdx == nPos )
1050 nPos = nPos - pLast->GetLen();
1052 // enter bidi portion
1053 if ( nCrsrLevel % 2 == nDefaultDir % 2 )
1055 bRecurse = sal_True;
1056 nIdx = nIdx - pLast->GetLen();
1057 pPor = pLast;
1059 // special case:
1060 // buffer: abcXYZ123 in LTR paragraph
1061 // view: abc123ZYX
1062 // cursor is behind 3 in the buffer and cursor level = 2
1063 if ( nDefaultDir + 2 == nCrsrLevel )
1064 nPos = nPos + pLast->GetLen();
1068 // go forward
1069 if ( bRecurse )
1071 const SwLineLayout& rLine = ((SwMultiPortion*)pPor)->GetRoot();
1072 xub_StrLen nTmpPos = nPos - nIdx;
1073 sal_Bool bTmpForward = ! bRight;
1074 BYTE nTmpCrsrLevel = nCrsrLevel;
1075 lcl_VisualMoveRecursion( rLine, 0, nTmpPos, bTmpForward,
1076 nTmpCrsrLevel, nDefaultDir + 1 );
1078 // special case:
1079 // buffer: abcXYZ123 in LTR paragraph
1080 // view: abc123ZYX
1081 // cursor is between Z and 1 in the buffer and cursor level = 2
1082 if ( nTmpPos == pPor->GetLen() && nTmpCrsrLevel == nDefaultDir + 1 )
1084 nTmpPos = nTmpPos - pPor->GetLen();
1085 nTmpCrsrLevel = nDefaultDir;
1086 bTmpForward = ! bTmpForward;
1089 nPos = nTmpPos + nIdx;
1090 bRight = bTmpForward;
1091 nCrsrLevel = nTmpCrsrLevel;
1094 // go backward
1095 else
1097 bRight = sal_False;
1098 nCrsrLevel = nDefaultDir;
1103 void SwTxtFrm::PrepareVisualMove( xub_StrLen& nPos, BYTE& nCrsrLevel,
1104 sal_Bool& bForward, sal_Bool bInsertCrsr )
1106 if( IsEmpty() || IsHiddenNow() )
1107 return;
1109 ((SwTxtFrm*)this)->GetFormatted();
1111 SwTxtSizeInfo aInf( (SwTxtFrm*)this );
1112 SwTxtCursor aLine( ((SwTxtFrm*)this), &aInf );
1114 if( nPos )
1115 aLine.CharCrsrToLine( nPos );
1116 else
1117 aLine.Top();
1119 const SwLineLayout* pLine = aLine.GetCurr();
1120 const xub_StrLen nStt = aLine.GetStart();
1121 const xub_StrLen nLen = pLine->GetLen();
1123 // We have to distinguish between an insert and overwrite cursor:
1124 // The insert cursor position depends on the cursor level:
1125 // buffer: abcXYZdef in LTR paragraph
1126 // display: abcZYXdef
1127 // If cursor is between c and X in the buffer and cursor level is 0,
1128 // the cursor blinks between c and Z and -> sets the cursor between Z and Y.
1129 // If the cursor level is 1, the cursor blinks between X and d and
1130 // -> sets the cursor between d and e.
1131 // The overwrite cursor simply travels to the next visual character.
1132 if ( bInsertCrsr )
1134 lcl_VisualMoveRecursion( *pLine, nStt, nPos, bForward,
1135 nCrsrLevel, IsRightToLeft() ? 1 : 0 );
1136 return;
1139 const BYTE nDefaultDir = static_cast<BYTE>(IsRightToLeft() ? UBIDI_RTL : UBIDI_LTR);
1140 const sal_Bool bVisualRight = ( nDefaultDir == UBIDI_LTR && bForward ) ||
1141 ( nDefaultDir == UBIDI_RTL && ! bForward );
1144 // Bidi functions from icu 2.0
1146 const sal_Unicode* pLineString = GetTxtNode()->GetTxt().GetBuffer();
1147 pLine += nStt;
1149 UErrorCode nError = U_ZERO_ERROR;
1150 UBiDi* pBidi = ubidi_openSized( nLen, 0, &nError );
1151 ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(pLineString), nLen, nDefaultDir, NULL, &nError ); // UChar != sal_Unicode in MinGW
1153 xub_StrLen nTmpPos;
1154 sal_Bool bOutOfBounds = sal_False;
1156 if ( nPos < nStt + nLen )
1158 nTmpPos = (xub_StrLen)ubidi_getVisualIndex( pBidi, nPos, &nError );
1160 // visual indices are always LTR aligned
1161 if ( bVisualRight )
1163 if ( nTmpPos + 1 < nStt + nLen )
1164 ++nTmpPos;
1165 else
1167 nPos = nDefaultDir == UBIDI_RTL ? 0 : nStt + nLen;
1168 bOutOfBounds = sal_True;
1171 else
1173 if ( nTmpPos )
1174 --nTmpPos;
1175 else
1177 nPos = nDefaultDir == UBIDI_RTL ? nStt + nLen : 0;
1178 bOutOfBounds = sal_True;
1182 else
1184 nTmpPos = nDefaultDir == UBIDI_LTR ? nPos - 1 : 0;
1187 if ( ! bOutOfBounds )
1189 nPos = (xub_StrLen)ubidi_getLogicalIndex( pBidi, nTmpPos, &nError );
1191 if ( bForward )
1193 if ( nPos )
1194 --nPos;
1195 else
1197 ++nPos;
1198 bForward = ! bForward;
1201 else
1202 ++nPos;
1205 ubidi_close( pBidi );
1208 /*************************************************************************
1209 * SwTxtFrm::_UnitDown()
1210 *************************************************************************/
1212 sal_Bool SwTxtFrm::_UnitDown(SwPaM *pPam, const SwTwips nOffset,
1213 sal_Bool bSetInReadOnly ) const
1216 if ( IsInTab() &&
1217 pPam->GetNode( sal_True )->StartOfSectionNode() !=
1218 pPam->GetNode( sal_False )->StartOfSectionNode() )
1220 //Wenn der PaM in unterschiedlichen Boxen sitzt, so handelt es sich um
1221 //eine Tabellenselektion; diese wird von der Basisklasse abgearbeitet.
1222 return SwCntntFrm::UnitDown( pPam, nOffset, bSetInReadOnly );
1224 ((SwTxtFrm*)this)->GetFormatted();
1225 const xub_StrLen nPos = pPam->GetPoint()->nContent.GetIndex();
1226 SwRect aCharBox;
1227 const SwCntntFrm *pTmpFollow = 0;
1229 if ( IsVertical() )
1230 ((SwTxtFrm*)this)->SwapWidthAndHeight();
1232 if ( !IsEmpty() && !IsHiddenNow() )
1234 xub_StrLen nFormat = STRING_LEN;
1237 if( nFormat != STRING_LEN && !IsFollow() &&
1238 !lcl_ChangeOffset( ((SwTxtFrm*)this), nFormat ) )
1239 break;
1241 SwTxtSizeInfo aInf( (SwTxtFrm*)this );
1242 SwTxtCursor aLine( ((SwTxtFrm*)this), &aInf );
1243 nFormat = aLine.GetEnd();
1245 aLine.CharCrsrToLine( nPos );
1247 const SwLineLayout* pNextLine = aLine.GetNextLine();
1248 const xub_StrLen nStart = aLine.GetStart();
1249 aLine.GetCharRect( &aCharBox, nPos );
1251 sal_Bool bFirstOfDouble = ( aInf.IsMulti() && aInf.IsFirstMulti() );
1253 if( pNextLine || bFirstOfDouble )
1255 aCharBox.SSize().Width() /= 2;
1256 #ifndef PRODUCT
1257 // siehe Kommentar in SwTxtFrm::GetCrsrOfst()
1258 const ULONG nOldNode = pPam->GetPoint()->nNode.GetIndex();
1259 #endif
1260 if ( pNextLine && ! bFirstOfDouble )
1261 aLine.NextLine();
1263 xub_StrLen nTmpOfst = aLine.GetCrsrOfst( pPam->GetPoint(),
1264 aCharBox.Pos(), sal_False );
1265 ASSERT( nOldNode == pPam->GetPoint()->nNode.GetIndex(),
1266 "SwTxtFrm::UnitDown: illegal node change" )
1268 // 7684: Wir stellen sicher, dass wir uns nach unten bewegen.
1269 if( nTmpOfst <= nStart && ! bFirstOfDouble )
1270 nTmpOfst = nStart + 1;
1271 pPam->GetPoint()->nContent =
1272 SwIndex( ((SwTxtFrm*)this)->GetTxtNode(), nTmpOfst );
1274 if ( IsVertical() )
1275 ((SwTxtFrm*)this)->SwapWidthAndHeight();
1277 return sal_True;
1279 if( 0 != ( pTmpFollow = GetFollow() ) )
1280 { // geschuetzte Follows auslassen
1281 const SwCntntFrm* pTmp = pTmpFollow;
1282 ViewShell *pSh = GetShell();
1283 if( !pSh || !pSh->GetViewOptions()->IsCursorInProtectedArea() )
1285 while( pTmpFollow && pTmpFollow->IsProtected() )
1287 pTmp = pTmpFollow;
1288 pTmpFollow = pTmpFollow->GetFollow();
1291 if( !pTmpFollow ) // nur noch geschuetzte
1293 if ( IsVertical() )
1294 ((SwTxtFrm*)this)->SwapWidthAndHeight();
1295 return pTmp->SwCntntFrm::UnitDown( pPam, nOffset, bSetInReadOnly );
1298 aLine.GetCharRect( &aCharBox, nPos );
1299 aCharBox.SSize().Width() /= 2;
1301 else if( !IsFollow() )
1303 xub_StrLen nTmpLen = aInf.GetTxt().Len();
1304 if( aLine.GetEnd() < nTmpLen )
1306 if( nFormat <= GetOfst() )
1308 nFormat = Min( xub_StrLen( GetOfst() + MIN_OFFSET_STEP ),
1309 nTmpLen );
1310 if( nFormat <= GetOfst() )
1311 break;
1313 continue;
1316 break;
1317 } while( sal_True );
1319 else
1320 pTmpFollow = GetFollow();
1322 if ( IsVertical() )
1323 ((SwTxtFrm*)this)->SwapWidthAndHeight();
1325 // Bei Follows schlagen wir eine Abkuerzung
1326 if( pTmpFollow )
1328 aCharBox.Pos().Y() = pTmpFollow->Frm().Top() + 1;
1329 return ((SwTxtFrm*)pTmpFollow)->GetKeyCrsrOfst( pPam->GetPoint(),
1330 aCharBox.Pos() );
1332 return SwCntntFrm::UnitDown( pPam, nOffset, bSetInReadOnly );
1335 /*************************************************************************
1336 * virtual SwTxtFrm::UnitUp()
1337 *************************************************************************/
1339 sal_Bool SwTxtFrm::UnitUp(SwPaM *pPam, const SwTwips nOffset,
1340 sal_Bool bSetInReadOnly ) const
1342 /* Im CrsrSh::Up() wird CntntNode::GetFrm() gerufen.
1343 * Dies liefert _immer_ den Master zurueck.
1344 * Um das Cursortravelling nicht zu belasten, korrigieren wir
1345 * hier im SwTxtFrm.
1346 * Wir ermittelt UnitUp fuer pFrm, pFrm ist entweder ein Master (=this)
1347 * oder ein Follow (!=this)
1349 const SwTxtFrm *pFrm = GetAdjFrmAtPos( (SwTxtFrm*)this, *(pPam->GetPoint()),
1350 SwTxtCursor::IsRightMargin() );
1351 const sal_Bool bRet = pFrm->_UnitUp( pPam, nOffset, bSetInReadOnly );
1353 // 8626: kein SwTxtCursor::SetRightMargin( sal_False );
1354 // statt dessen steht ein SwSetToRightMargin im _UnitUp
1355 return bRet;
1358 /*************************************************************************
1359 * virtual SwTxtFrm::UnitDown()
1360 *************************************************************************/
1362 sal_Bool SwTxtFrm::UnitDown(SwPaM *pPam, const SwTwips nOffset,
1363 sal_Bool bSetInReadOnly ) const
1365 const SwTxtFrm *pFrm = GetAdjFrmAtPos((SwTxtFrm*)this, *(pPam->GetPoint()),
1366 SwTxtCursor::IsRightMargin() );
1367 const sal_Bool bRet = pFrm->_UnitDown( pPam, nOffset, bSetInReadOnly );
1368 SwTxtCursor::SetRightMargin( sal_False );
1369 return bRet;
1372 void SwTxtFrm::FillCrsrPos( SwFillData& rFill ) const
1374 if( !rFill.bColumn && GetUpper()->IsColBodyFrm() ) // ColumnFrms jetzt mit BodyFrm
1376 const SwColumnFrm* pTmp =
1377 (SwColumnFrm*)GetUpper()->GetUpper()->GetUpper()->Lower(); // die 1. Spalte
1378 // der erste SwFrm im BodyFrm der ersten Spalte
1379 const SwFrm* pFrm = ((SwLayoutFrm*)pTmp->Lower())->Lower();
1380 MSHORT nNextCol = 0;
1381 // In welcher Spalte landen wir?
1382 while( rFill.X() > pTmp->Frm().Right() && pTmp->GetNext() )
1384 pTmp = (SwColumnFrm*)pTmp->GetNext();
1385 if( ((SwLayoutFrm*)pTmp->Lower())->Lower() ) // ColumnFrms jetzt mit BodyFrm
1387 pFrm = ((SwLayoutFrm*)pTmp->Lower())->Lower();
1388 nNextCol = 0;
1390 else
1391 ++nNextCol; // leere Spalten erfordern Spaltenumbrueche
1393 if( pTmp != GetUpper()->GetUpper() ) // Sind wir in einer anderen Spalte gelandet?
1395 if( !pFrm )
1396 return;
1397 if( nNextCol )
1399 while( pFrm->GetNext() )
1400 pFrm = pFrm->GetNext();
1402 else
1404 while( pFrm->GetNext() && pFrm->Frm().Bottom() < rFill.Y() )
1405 pFrm = pFrm->GetNext();
1407 // Kein Fuellen, wenn als letzter Frame in der anvisierten
1408 // Spalte kein Absatz, sondern z.B. eine Tabelle steht
1409 if( pFrm->IsTxtFrm() )
1411 rFill.Fill().nColumnCnt = nNextCol;
1412 rFill.bColumn = sal_True;
1413 if( rFill.pPos )
1415 SwTxtNode* pTxtNd = ((SwTxtFrm*)pFrm)->GetTxtNode();
1416 rFill.pPos->nNode = *pTxtNd;
1417 rFill.pPos->nContent.Assign( pTxtNd, pTxtNd->GetTxt().Len() );
1419 if( nNextCol )
1421 rFill.aFrm = pTmp->Prt();
1422 rFill.aFrm += pTmp->Frm().Pos();
1424 else
1425 rFill.aFrm = pFrm->Frm();
1426 ((SwTxtFrm*)pFrm)->FillCrsrPos( rFill );
1428 return;
1431 sal_Bool bFill = sal_True;
1432 SwFont *pFnt;
1433 SwTxtFmtColl* pColl = GetTxtNode()->GetTxtColl();
1434 MSHORT nFirst = GetTxtNode()->GetSwAttrSet().GetULSpace().GetLower();
1435 SwTwips nDiff = rFill.Y() - Frm().Bottom();
1436 if( nDiff < nFirst )
1437 nDiff = -1;
1438 else
1439 pColl = &pColl->GetNextTxtFmtColl();
1440 SwAttrSet aSet( ((SwDoc*)GetTxtNode()->GetDoc())->GetAttrPool(), aTxtFmtCollSetRange );
1441 const SwAttrSet* pSet = &pColl->GetAttrSet();
1442 ViewShell *pSh = GetShell();
1443 if( GetTxtNode()->HasSwAttrSet() )
1445 aSet.Put( *GetTxtNode()->GetpSwAttrSet() );
1446 aSet.SetParent( pSet );
1447 pSet = &aSet;
1448 pFnt = new SwFont( pSet, GetNode()->getIDocumentSettingAccess() );
1450 else
1452 SwFontAccess aFontAccess( pColl, pSh );
1453 pFnt = new SwFont( *aFontAccess.Get()->GetFont() );
1454 pFnt->ChkMagic( pSh, pFnt->GetActual() );
1456 OutputDevice* pOut = pSh->GetOut();
1457 if ( !GetTxtNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::BROWSE_MODE) ||
1458 ( pSh->GetViewOptions()->IsPrtFormat() ) )
1459 pOut = GetTxtNode()->getIDocumentDeviceAccess()->getReferenceDevice( true );
1461 pFnt->SetFntChg( sal_True );
1462 pFnt->ChgPhysFnt( pSh, *pOut );
1464 SwTwips nLineHeight = pFnt->GetHeight( pSh, *pOut );
1466 if( nLineHeight )
1468 const SvxULSpaceItem &rUL = pSet->GetULSpace();
1469 SwTwips nDist = Max( rUL.GetLower(), rUL.GetUpper() );
1470 if( rFill.Fill().nColumnCnt )
1472 rFill.aFrm.Height( nLineHeight );
1473 nDiff = rFill.Y() - rFill.Bottom();
1474 nFirst = 0;
1476 else if( nDist < nFirst )
1477 nFirst = nFirst - (USHORT)nDist;
1478 else
1479 nFirst = 0;
1480 nDist = Max( nDist, long( GetLineSpace() ) );
1481 nDist += nLineHeight;
1482 nDiff -= nFirst;
1484 if( nDiff > 0 )
1486 nDiff /= nDist;
1487 rFill.Fill().nParaCnt = static_cast<USHORT>(nDiff + 1);
1488 rFill.nLineWidth = 0;
1489 rFill.bInner = sal_False;
1490 rFill.bEmpty = sal_True;
1491 rFill.SetOrient( text::HoriOrientation::LEFT );
1493 else
1494 nDiff = -1;
1495 if( rFill.bInner )
1496 bFill = sal_False;
1497 else
1499 const SvxTabStopItem &rRuler = pSet->GetTabStops();
1500 const SvxLRSpaceItem &rLRSpace = pSet->GetLRSpace();
1502 SwRect &rRect = rFill.Fill().aCrsr;
1503 rRect.Top( rFill.Bottom() + (nDiff+1) * nDist - nLineHeight );
1504 if( nFirst && nDiff > -1 )
1505 rRect.Top( rRect.Top() + nFirst );
1506 rRect.Height( nLineHeight );
1507 SwTwips nLeft = rFill.Left() + rLRSpace.GetLeft() +
1508 GetTxtNode()->GetLeftMarginWithNum( sal_False );
1509 SwTwips nRight = rFill.Right() - rLRSpace.GetRight();
1510 SwTwips nCenter = ( nLeft + nRight ) / 2;
1511 rRect.Left( nLeft );
1512 if( FILL_MARGIN == rFill.Mode() )
1514 if( rFill.bEmpty )
1516 rFill.SetOrient( text::HoriOrientation::LEFT );
1517 if( rFill.X() < nCenter )
1519 if( rFill.X() > ( nLeft + 2 * nCenter ) / 3 )
1521 rFill.SetOrient( text::HoriOrientation::CENTER );
1522 rRect.Left( nCenter );
1525 else if( rFill.X() > ( nRight + 2 * nCenter ) / 3 )
1527 rFill.SetOrient( text::HoriOrientation::RIGHT );
1528 rRect.Left( nRight );
1530 else
1532 rFill.SetOrient( text::HoriOrientation::CENTER );
1533 rRect.Left( nCenter );
1536 else
1537 bFill = sal_False;
1539 else
1541 SwTwips nSpace = 0;
1542 if( FILL_TAB != rFill.Mode() )
1544 static sal_Char __READONLY_DATA sDoubleSpace[] = " ";
1545 const XubString aTmp( sDoubleSpace, RTL_TEXTENCODING_MS_1252 );
1547 SwDrawTextInfo aDrawInf( pSh, *pOut, 0, aTmp, 0, 2 );
1548 nSpace = pFnt->_GetTxtSize( aDrawInf ).Width()/2;
1550 if( rFill.X() >= nRight )
1552 if( FILL_INDENT != rFill.Mode() && ( rFill.bEmpty ||
1553 rFill.X() > rFill.nLineWidth + FILL_MIN_DIST ) )
1555 rFill.SetOrient( text::HoriOrientation::RIGHT );
1556 rRect.Left( nRight );
1558 else
1559 bFill = sal_False;
1561 else if( FILL_INDENT == rFill.Mode() )
1563 SwTwips nIndent = rFill.X();
1564 if( !rFill.bEmpty || nIndent > nRight )
1565 bFill = sal_False;
1566 else
1568 nIndent -= rFill.Left();
1569 if( nIndent >= 0 && nSpace )
1571 nIndent /= nSpace;
1572 nIndent *= nSpace;
1573 rFill.SetTab( MSHORT( nIndent ) );
1574 rRect.Left( nIndent + rFill.Left() );
1576 else
1577 bFill = sal_False;
1580 else if( rFill.X() > nLeft )
1582 SwTwips nTxtLeft = rFill.Left() + rLRSpace.GetTxtLeft() +
1583 GetTxtNode()->GetLeftMarginWithNum( sal_True );
1584 rFill.nLineWidth += rFill.bFirstLine ? nLeft : nTxtLeft;
1585 SwTwips nLeftTab = nLeft;
1586 SwTwips nRightTab = nLeft;
1587 MSHORT nSpaceCnt = 0;
1588 MSHORT nTabCnt = 0;
1589 MSHORT nIdx = 0;
1592 nLeftTab = nRightTab;
1593 if( nIdx < rRuler.Count() )
1595 const SvxTabStop &rTabStop = rRuler.operator[](nIdx);
1596 nRightTab = nTxtLeft + rTabStop.GetTabPos();
1597 if( nLeftTab < nTxtLeft && nRightTab > nTxtLeft )
1598 nRightTab = nTxtLeft;
1599 else
1600 ++nIdx;
1601 if( nRightTab > rFill.nLineWidth )
1602 ++nTabCnt;
1604 else
1606 const SvxTabStopItem& rTab =
1607 (const SvxTabStopItem &)pSet->
1608 GetPool()->GetDefaultItem( RES_PARATR_TABSTOP );
1609 MSHORT nDefTabDist = (MSHORT)rTab.GetStart()->GetTabPos();
1610 nRightTab = nLeftTab - nTxtLeft;
1611 nRightTab /= nDefTabDist;
1612 nRightTab = nRightTab * nDefTabDist + nTxtLeft;
1613 while ( nRightTab <= nLeftTab )
1614 nRightTab += nDefTabDist;
1615 if( nRightTab > rFill.nLineWidth )
1616 ++nTabCnt;
1617 while ( nRightTab < rFill.X() )
1619 nRightTab += nDefTabDist;
1620 if( nRightTab > rFill.nLineWidth )
1621 ++nTabCnt;
1623 if( nLeftTab < nRightTab - nDefTabDist )
1624 nLeftTab = nRightTab - nDefTabDist;
1626 if( nRightTab > nRight )
1627 nRightTab = nRight;
1629 while( rFill.X() > nRightTab );
1630 --nTabCnt;
1631 if( FILL_TAB != rFill.Mode() )
1633 if( nSpace > 0 )
1635 if( !nTabCnt )
1636 nLeftTab = rFill.nLineWidth;
1637 while( nLeftTab < rFill.X() )
1639 nLeftTab += nSpace;
1640 ++nSpaceCnt;
1642 if( nSpaceCnt )
1644 nLeftTab -= nSpace;
1645 --nSpaceCnt;
1647 if( rFill.X() - nLeftTab > nRightTab - rFill.X() )
1649 nSpaceCnt = 0;
1650 ++nTabCnt;
1651 rRect.Left( nRightTab );
1653 else
1655 if( rFill.X() - nLeftTab > nSpace/2 )
1657 ++nSpaceCnt;
1658 rRect.Left( nLeftTab + nSpace );
1660 else
1661 rRect.Left( nLeftTab );
1664 else if( rFill.X() - nLeftTab < nRightTab - rFill.X() )
1665 rRect.Left( nLeftTab );
1666 else
1668 if( nRightTab >= nRight )
1670 rFill.SetOrient( text::HoriOrientation::RIGHT );
1671 rRect.Left( nRight );
1672 nTabCnt = 0;
1673 nSpaceCnt = 0;
1675 else
1677 rRect.Left( nRightTab );
1678 ++nTabCnt;
1682 else
1684 if( rFill.X() - nLeftTab < nRightTab - rFill.X() )
1685 rRect.Left( nLeftTab );
1686 else
1688 if( nRightTab >= nRight )
1690 rFill.SetOrient( text::HoriOrientation::RIGHT );
1691 rRect.Left( nRight );
1692 nTabCnt = 0;
1693 nSpaceCnt = 0;
1695 else
1697 rRect.Left( nRightTab );
1698 ++nTabCnt;
1702 rFill.SetTab( nTabCnt );
1703 rFill.SetSpace( nSpaceCnt );
1704 if( bFill )
1706 if( Abs( rFill.X() - nCenter ) <=
1707 Abs( rFill.X() - rRect.Left() ) )
1709 rFill.SetOrient( text::HoriOrientation::CENTER );
1710 rFill.SetTab( 0 );
1711 rFill.SetSpace( 0 );
1712 rRect.Left( nCenter );
1714 if( !rFill.bEmpty )
1715 rFill.nLineWidth += FILL_MIN_DIST;
1716 if( rRect.Left() < rFill.nLineWidth )
1717 bFill = sal_False;
1721 // Gehen wir ueber die Unterkante der Seite/Spalte etc. hinaus?
1722 const SwFrm* pUp = GetUpper();
1723 if( pUp->IsInSct() )
1725 if( pUp->IsSctFrm() )
1726 pUp = pUp->GetUpper();
1727 else if( pUp->IsColBodyFrm() &&
1728 pUp->GetUpper()->GetUpper()->IsSctFrm() )
1729 pUp = pUp->GetUpper()->GetUpper()->GetUpper();
1731 SWRECTFN( this )
1732 SwTwips nLimit = (pUp->*fnRect->fnGetPrtBottom)();
1733 SwTwips nRectBottom = rRect.Bottom();
1734 if ( bVert )
1735 nRectBottom = SwitchHorizontalToVertical( nRectBottom );
1737 if( (*fnRect->fnYDiff)( nLimit, nRectBottom ) < 0 )
1738 bFill = sal_False;
1739 else
1740 rRect.Width( 1 );
1743 else
1744 bFill = sal_False;
1745 ((SwCrsrMoveState*)rFill.pCMS)->bFillRet = bFill;
1746 delete pFnt;