1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 // MARKER(update_precomp.py): autogen include statement, do not remove 29 #include "precompiled_sw.hxx" 30 31 32 #include "ndtxt.hxx" // GetNode() 33 #include "pam.hxx" // SwPosition 34 #include "frmtool.hxx" 35 #include "viewopt.hxx" 36 #include "paratr.hxx" 37 #include "rootfrm.hxx" 38 #include "pagefrm.hxx" 39 #include "colfrm.hxx" 40 #include "txttypes.hxx" 41 #include <sfx2/printer.hxx> 42 #include <editeng/lrspitem.hxx> 43 #include <editeng/tstpitem.hxx> 44 #include <editeng/ulspitem.hxx> 45 #include <editeng/lspcitem.hxx> 46 #include <pormulti.hxx> // SwMultiPortion 47 #include <doc.hxx> 48 #include <sortedobjs.hxx> 49 50 #include <unicode/ubidi.h> 51 52 #include "txtcfg.hxx" 53 #include "txtfrm.hxx" // SwTxtFrm 54 #include "inftxt.hxx" // SwTxtSizeInfo 55 #include "itrtxt.hxx" // SwTxtCursor 56 #include "crstate.hxx" // SwTxtCursor 57 #include "viewsh.hxx" // InvalidateWindows 58 #include "swfntcch.hxx" // SwFontAccess 59 #include "flyfrm.hxx" 60 61 #if OSL_DEBUG_LEVEL > 1 62 #include "txtpaint.hxx" 63 #endif 64 65 #define MIN_OFFSET_STEP 10 66 67 using namespace ::com::sun::star; 68 69 70 /* 71 * 1170-SurvivalKit: Wie gelangt man hinter das letzte Zeichen der Zeile. 72 * - RightMargin verzichtet auf den Positionsausgleich mit -1 73 * - GetCharRect liefert bei MV_RIGHTMARGIN ein GetEndCharRect 74 * - GetEndCharRect setzt bRightMargin auf sal_True 75 * - SwTxtCursor::bRightMargin wird per CharCrsrToLine auf sal_False gesetzt 76 */ 77 78 /************************************************************************* 79 * GetAdjFrmAtPos() 80 *************************************************************************/ 81 82 SwTxtFrm *GetAdjFrmAtPos( SwTxtFrm *pFrm, const SwPosition &rPos, 83 const sal_Bool bRightMargin, const sal_Bool bNoScroll = sal_True ) 84 { 85 // 8810: vgl. 1170, RightMargin in der letzten Masterzeile... 86 const xub_StrLen nOffset = rPos.nContent.GetIndex(); 87 SwTxtFrm *pFrmAtPos = pFrm; 88 if( !bNoScroll || pFrm->GetFollow() ) 89 { 90 pFrmAtPos = pFrm->GetFrmAtPos( rPos ); 91 if( nOffset < pFrmAtPos->GetOfst() && 92 !pFrmAtPos->IsFollow() ) 93 { 94 xub_StrLen nNew = nOffset; 95 if( nNew < MIN_OFFSET_STEP ) 96 nNew = 0; 97 else 98 nNew -= MIN_OFFSET_STEP; 99 lcl_ChangeOffset( pFrmAtPos, nNew ); 100 } 101 } 102 while( pFrm != pFrmAtPos ) 103 { 104 pFrm = pFrmAtPos; 105 pFrm->GetFormatted(); 106 pFrmAtPos = (SwTxtFrm*)pFrm->GetFrmAtPos( rPos ); 107 } 108 109 if( nOffset && bRightMargin ) 110 { 111 while( pFrmAtPos && pFrmAtPos->GetOfst() == nOffset && 112 pFrmAtPos->IsFollow() ) 113 { 114 pFrmAtPos->GetFormatted(); 115 pFrmAtPos = pFrmAtPos->FindMaster(); 116 } 117 ASSERT( pFrmAtPos, "+GetCharRect: no frame with my rightmargin" ); 118 } 119 return pFrmAtPos ? pFrmAtPos : pFrm; 120 } 121 122 sal_Bool lcl_ChangeOffset( SwTxtFrm* pFrm, xub_StrLen nNew ) 123 { 124 // In Bereichen und ausserhalb von Flies wird nicht mehr gescrollt. 125 ASSERT( !pFrm->IsFollow(), "Illegal Scrolling by Follow!" ); 126 if( pFrm->GetOfst() != nNew && !pFrm->IsInSct() ) 127 { 128 SwFlyFrm *pFly = pFrm->FindFlyFrm(); 129 // Vorsicht, wenn z.B. bei einem spaltigen Rahmen die Groesse noch invalide ist, 130 // duerfen wir nicht mal eben herumscrollen 131 if ( ( pFly && pFly->IsValid() && 132 !pFly->GetNextLink() && !pFly->GetPrevLink() ) || 133 ( !pFly && pFrm->IsInTab() ) ) 134 { 135 ViewShell* pVsh = pFrm->getRootFrm()->GetCurrShell(); 136 if( pVsh ) 137 { 138 if( pVsh->GetNext() != pVsh || 139 ( pFrm->GetDrawObjs() && pFrm->GetDrawObjs()->Count() ) ) 140 { 141 if( !pFrm->GetOfst() ) 142 return sal_False; 143 nNew = 0; 144 } 145 pFrm->SetOfst( nNew ); 146 pFrm->SetPara( 0 ); 147 pFrm->GetFormatted(); 148 if( pFrm->Frm().HasArea() ) 149 pFrm->getRootFrm()->GetCurrShell()->InvalidateWindows( pFrm->Frm() ); 150 return sal_True; 151 } 152 } 153 } 154 return sal_False; 155 } 156 157 /************************************************************************* 158 * GetFrmAtOfst(), GetFrmAtPos() 159 *************************************************************************/ 160 161 // OD 07.10.2003 #110978# 162 SwTxtFrm& SwTxtFrm::GetFrmAtOfst( const xub_StrLen nWhere ) 163 { 164 SwTxtFrm* pRet = this; 165 while( pRet->HasFollow() && nWhere >= pRet->GetFollow()->GetOfst() ) 166 pRet = pRet->GetFollow(); 167 return *pRet; 168 } 169 170 SwTxtFrm *SwTxtFrm::GetFrmAtPos( const SwPosition &rPos ) 171 { 172 SwTxtFrm *pFoll = (SwTxtFrm*)this; 173 while( pFoll->GetFollow() ) 174 { 175 if( rPos.nContent.GetIndex() > pFoll->GetFollow()->GetOfst() ) 176 pFoll = pFoll->GetFollow(); 177 else 178 { 179 if( rPos.nContent.GetIndex() == pFoll->GetFollow()->GetOfst() 180 && !SwTxtCursor::IsRightMargin() ) 181 pFoll = pFoll->GetFollow(); 182 else 183 break; 184 } 185 } 186 return pFoll; 187 } 188 189 /************************************************************************* 190 * SwTxtFrm::GetCharRect() 191 *************************************************************************/ 192 193 /* 194 * GetCharRect() findet die Characterzelle des Characters, dass 195 * durch aPos beschrieben wird. GetCrsrOfst() findet den 196 * umgekehrten Weg: Von einer Dokumentkoordinate zu einem Pam. 197 * Beide sind virtuell in der Framebasisklasse und werden deshalb 198 * immer angezogen. 199 */ 200 201 sal_Bool SwTxtFrm::GetCharRect( SwRect& rOrig, const SwPosition &rPos, 202 SwCrsrMoveState *pCMS ) const 203 { 204 ASSERT( ! IsVertical() || ! IsSwapped(),"SwTxtFrm::GetCharRect with swapped frame" ); 205 206 if( IsLocked() || IsHiddenNow() ) 207 return sal_False; 208 209 //Erstmal den richtigen Frm finden, dabei muss beachtet werden, dass: 210 //- die gecachten Informationen verworfen sein koennen (GetPara() == 0) 211 //- das ein Follow gemeint sein kann 212 //- das die Kette der Follows dynamisch waechst; der in den wir 213 // schliesslich gelangen muss aber Formatiert sein. 214 215 // opt: reading ahead erspart uns ein GetAdjFrmAtPos 216 const sal_Bool bRightMargin = pCMS && ( MV_RIGHTMARGIN == pCMS->eState ); 217 const sal_Bool bNoScroll = pCMS && pCMS->bNoScroll; 218 SwTxtFrm *pFrm = GetAdjFrmAtPos( (SwTxtFrm*)this, rPos, bRightMargin, 219 bNoScroll ); 220 pFrm->GetFormatted(); 221 const SwFrm* pTmpFrm = (SwFrm*)pFrm->GetUpper(); 222 223 SWRECTFN ( pFrm ) 224 const SwTwips nUpperMaxY = (pTmpFrm->*fnRect->fnGetPrtBottom)(); 225 const SwTwips nFrmMaxY = (pFrm->*fnRect->fnGetPrtBottom)(); 226 227 // nMaxY is an absolute value 228 //Badaa: 2008-04-18 * Support for Classical Mongolian Script (SCMS) joint with Jiayanmin 229 SwTwips nMaxY = bVert ? 230 ( bVertL2R ? Min( nFrmMaxY, nUpperMaxY ) : Max( nFrmMaxY, nUpperMaxY ) ) : 231 Min( nFrmMaxY, nUpperMaxY ); 232 233 sal_Bool bRet = sal_False; 234 235 if ( pFrm->IsEmpty() || ! (pFrm->Prt().*fnRect->fnGetHeight)() ) 236 { 237 Point aPnt1 = pFrm->Frm().Pos() + pFrm->Prt().Pos(); 238 SwTxtNode* pTxtNd = ((SwTxtFrm*)this)->GetTxtNode(); 239 short nFirstOffset; 240 pTxtNd->GetFirstLineOfsWithNum( nFirstOffset ); 241 242 Point aPnt2; 243 if ( bVert ) 244 { 245 if( nFirstOffset > 0 ) 246 aPnt1.Y() += nFirstOffset; 247 //Badaa: 2008-04-18 * Support for Classical Mongolian Script (SCMS) joint with Jiayanmin 248 if ( aPnt1.X() < nMaxY && !bVertL2R ) 249 aPnt1.X() = nMaxY; 250 aPnt2.X() = aPnt1.X() + pFrm->Prt().Width(); 251 aPnt2.Y() = aPnt1.Y(); 252 if( aPnt2.X() < nMaxY ) 253 aPnt2.X() = nMaxY; 254 } 255 else 256 { 257 if( nFirstOffset > 0 ) 258 aPnt1.X() += nFirstOffset; 259 260 if( aPnt1.Y() > nMaxY ) 261 aPnt1.Y() = nMaxY; 262 aPnt2.X() = aPnt1.X(); 263 aPnt2.Y() = aPnt1.Y() + pFrm->Prt().Height(); 264 if( aPnt2.Y() > nMaxY ) 265 aPnt2.Y() = nMaxY; 266 } 267 268 rOrig = SwRect( aPnt1, aPnt2 ); 269 270 if ( pCMS ) 271 { 272 pCMS->aRealHeight.X() = 0; 273 pCMS->aRealHeight.Y() = bVert ? -rOrig.Width() : rOrig.Height(); 274 } 275 276 if ( pFrm->IsRightToLeft() ) 277 pFrm->SwitchLTRtoRTL( rOrig ); 278 279 bRet = sal_True; 280 } 281 else 282 { 283 if( !pFrm->HasPara() ) 284 return sal_False; 285 286 SwFrmSwapper aSwapper( pFrm, sal_True ); 287 if ( bVert ) 288 nMaxY = pFrm->SwitchVerticalToHorizontal( nMaxY ); 289 290 sal_Bool bGoOn = sal_True; 291 xub_StrLen nOffset = rPos.nContent.GetIndex(); 292 xub_StrLen nNextOfst; 293 294 do 295 { 296 { 297 SwTxtSizeInfo aInf( pFrm ); 298 SwTxtCursor aLine( pFrm, &aInf ); 299 nNextOfst = aLine.GetEnd(); 300 // Siehe Kommentar in AdjustFrm 301 // 1170: das letzte Zeichen der Zeile mitnehmen? 302 bRet = bRightMargin ? aLine.GetEndCharRect( &rOrig, nOffset, pCMS, nMaxY ) 303 : aLine.GetCharRect( &rOrig, nOffset, pCMS, nMaxY ); 304 } 305 306 if ( pFrm->IsRightToLeft() ) 307 pFrm->SwitchLTRtoRTL( rOrig ); 308 309 if ( bVert ) 310 pFrm->SwitchHorizontalToVertical( rOrig ); 311 312 if( pFrm->IsUndersized() && pCMS && !pFrm->GetNext() && 313 (rOrig.*fnRect->fnGetBottom)() == nUpperMaxY && 314 pFrm->GetOfst() < nOffset && 315 !pFrm->IsFollow() && !bNoScroll && 316 pFrm->GetTxtNode()->GetTxt().Len() != nNextOfst ) 317 bGoOn = lcl_ChangeOffset( pFrm, nNextOfst ); 318 else 319 bGoOn = sal_False; 320 } while ( bGoOn ); 321 322 if ( pCMS ) 323 { 324 if ( pFrm->IsRightToLeft() ) 325 { 326 if( pCMS->b2Lines && pCMS->p2Lines) 327 { 328 pFrm->SwitchLTRtoRTL( pCMS->p2Lines->aLine ); 329 pFrm->SwitchLTRtoRTL( pCMS->p2Lines->aPortion ); 330 } 331 } 332 333 if ( bVert ) 334 { 335 if ( pCMS->bRealHeight ) 336 { 337 pCMS->aRealHeight.Y() = -pCMS->aRealHeight.Y(); 338 if ( pCMS->aRealHeight.Y() < 0 ) 339 { 340 // writing direction is from top to bottom 341 pCMS->aRealHeight.X() = ( rOrig.Width() - 342 pCMS->aRealHeight.X() + 343 pCMS->aRealHeight.Y() ); 344 } 345 } 346 if( pCMS->b2Lines && pCMS->p2Lines) 347 { 348 pFrm->SwitchHorizontalToVertical( pCMS->p2Lines->aLine ); 349 pFrm->SwitchHorizontalToVertical( pCMS->p2Lines->aPortion ); 350 } 351 } 352 353 } 354 } 355 if( bRet ) 356 { 357 SwPageFrm *pPage = pFrm->FindPageFrm(); 358 ASSERT( pPage, "Text esaped from page?" ); 359 const SwTwips nOrigTop = (rOrig.*fnRect->fnGetTop)(); 360 const SwTwips nPageTop = (pPage->Frm().*fnRect->fnGetTop)(); 361 const SwTwips nPageBott = (pPage->Frm().*fnRect->fnGetBottom)(); 362 363 // Following situation: if the frame is in an invalid sectionframe, 364 // it's possible that the frame is outside the page. If we restrict 365 // the cursor position to the page area, we enforce the formatting 366 // of the page, of the section frame and the frame himself. 367 if( (*fnRect->fnYDiff)( nPageTop, nOrigTop ) > 0 ) 368 (rOrig.*fnRect->fnSetTop)( nPageTop ); 369 370 if ( (*fnRect->fnYDiff)( nOrigTop, nPageBott ) > 0 ) 371 (rOrig.*fnRect->fnSetTop)( nPageBott ); 372 } 373 374 return bRet; 375 } 376 377 /************************************************************************* 378 * SwTxtFrm::GetAutoPos() 379 *************************************************************************/ 380 381 /* 382 * GetAutoPos() findet die Characterzelle des Characters, dass 383 * durch aPos beschrieben wird und wird von autopositionierten Rahmen genutzt. 384 */ 385 386 sal_Bool SwTxtFrm::GetAutoPos( SwRect& rOrig, const SwPosition &rPos ) const 387 { 388 if( IsHiddenNow() ) 389 return sal_False; 390 391 xub_StrLen nOffset = rPos.nContent.GetIndex(); 392 SwTxtFrm* pFrm = &(const_cast<SwTxtFrm*>(this)->GetFrmAtOfst( nOffset )); 393 394 pFrm->GetFormatted(); 395 const SwFrm* pTmpFrm = (SwFrm*)pFrm->GetUpper(); 396 397 SWRECTFN( pTmpFrm ) 398 SwTwips nUpperMaxY = (pTmpFrm->*fnRect->fnGetPrtBottom)(); 399 400 // nMaxY is in absolute value 401 //Badaa: 2008-04-18 * Support for Classical Mongolian Script (SCMS) joint with Jiayanmin 402 SwTwips nMaxY = bVert ? 403 ( bVertL2R ? Min( (pFrm->*fnRect->fnGetPrtBottom)(), nUpperMaxY ) : Max( (pFrm->*fnRect->fnGetPrtBottom)(), nUpperMaxY ) ) : 404 Min( (pFrm->*fnRect->fnGetPrtBottom)(), nUpperMaxY ); 405 406 if ( pFrm->IsEmpty() || ! (pFrm->Prt().*fnRect->fnGetHeight)() ) 407 { 408 Point aPnt1 = pFrm->Frm().Pos() + pFrm->Prt().Pos(); 409 Point aPnt2; 410 if ( bVert ) 411 { 412 if ( aPnt1.X() < nMaxY && !bVertL2R ) 413 aPnt1.X() = nMaxY; 414 415 aPnt2.X() = aPnt1.X() + pFrm->Prt().Width(); 416 aPnt2.Y() = aPnt1.Y(); 417 if( aPnt2.X() < nMaxY ) 418 aPnt2.X() = nMaxY; 419 } 420 else 421 { 422 if( aPnt1.Y() > nMaxY ) 423 aPnt1.Y() = nMaxY; 424 aPnt2.X() = aPnt1.X(); 425 aPnt2.Y() = aPnt1.Y() + pFrm->Prt().Height(); 426 if( aPnt2.Y() > nMaxY ) 427 aPnt2.Y() = nMaxY; 428 } 429 rOrig = SwRect( aPnt1, aPnt2 ); 430 return sal_True; 431 } 432 else 433 { 434 if( !pFrm->HasPara() ) 435 return sal_False; 436 437 SwFrmSwapper aSwapper( pFrm, sal_True ); 438 if ( bVert ) 439 nMaxY = pFrm->SwitchVerticalToHorizontal( nMaxY ); 440 441 SwTxtSizeInfo aInf( pFrm ); 442 SwTxtCursor aLine( pFrm, &aInf ); 443 SwCrsrMoveState aTmpState( MV_SETONLYTEXT ); 444 aTmpState.bRealHeight = sal_True; 445 if( aLine.GetCharRect( &rOrig, nOffset, &aTmpState, nMaxY ) ) 446 { 447 if( aTmpState.aRealHeight.X() >= 0 ) 448 { 449 rOrig.Pos().Y() += aTmpState.aRealHeight.X(); 450 rOrig.Height( aTmpState.aRealHeight.Y() ); 451 } 452 453 if ( pFrm->IsRightToLeft() ) 454 pFrm->SwitchLTRtoRTL( rOrig ); 455 456 if ( bVert ) 457 pFrm->SwitchHorizontalToVertical( rOrig ); 458 459 return sal_True; 460 } 461 return sal_False; 462 } 463 } 464 465 /** determine top of line for given position in the text frame 466 467 OD 11.11.2003 #i22341# 468 OD 2004-03-18 #114789# - corrections: 469 - Top of first paragraph line is the top of the printing area of the text frame 470 - If a proportional line spacing is applied use top of anchor character as 471 top of the line. 472 473 @author OD 474 */ 475 bool SwTxtFrm::GetTopOfLine( SwTwips& _onTopOfLine, 476 const SwPosition& _rPos ) const 477 { 478 bool bRet = true; 479 480 // get position offset 481 xub_StrLen nOffset = _rPos.nContent.GetIndex(); 482 483 if ( GetTxt().Len() < nOffset ) 484 { 485 bRet = false; 486 } 487 else 488 { 489 SWRECTFN( this ) 490 if ( IsEmpty() || !(Prt().*fnRect->fnGetHeight)() ) 491 { 492 // OD 2004-03-18 #i11860# - consider upper space amount considered 493 // for previous frame and the page grid. 494 _onTopOfLine = (this->*fnRect->fnGetPrtTop)(); 495 } 496 else 497 { 498 // determine formatted text frame that contains the requested position 499 SwTxtFrm* pFrm = &(const_cast<SwTxtFrm*>(this)->GetFrmAtOfst( nOffset )); 500 pFrm->GetFormatted(); 501 SWREFRESHFN( pFrm ) 502 // OD 2004-03-18 #114789# - If proportional line spacing is applied 503 // to the text frame, the top of the anchor character is also the 504 // top of the line. 505 // Otherwise the line layout determines the top of the line 506 const SvxLineSpacingItem& rSpace = GetAttrSet()->GetLineSpacing(); 507 if ( rSpace.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_PROP ) 508 { 509 SwRect aCharRect; 510 if ( GetAutoPos( aCharRect, _rPos ) ) 511 { 512 _onTopOfLine = (aCharRect.*fnRect->fnGetTop)(); 513 } 514 else 515 { 516 bRet = false; 517 } 518 } 519 else 520 { 521 // assure that text frame is in a horizontal layout 522 SwFrmSwapper aSwapper( pFrm, sal_True ); 523 // determine text line that contains the requested position 524 SwTxtSizeInfo aInf( pFrm ); 525 SwTxtCursor aLine( pFrm, &aInf ); 526 aLine.CharCrsrToLine( nOffset ); 527 // determine top of line 528 _onTopOfLine = aLine.Y(); 529 if ( bVert ) 530 { 531 _onTopOfLine = pFrm->SwitchHorizontalToVertical( _onTopOfLine ); 532 } 533 } 534 } 535 } 536 537 return bRet; 538 } 539 540 /************************************************************************* 541 * SwTxtFrm::_GetCrsrOfst() 542 *************************************************************************/ 543 544 // Minimaler Abstand von nichtleeren Zeilen etwas weniger als 2 cm 545 #define FILL_MIN_DIST 1100 546 547 struct SwFillData 548 { 549 SwRect aFrm; 550 const SwCrsrMoveState *pCMS; 551 SwPosition* pPos; 552 const Point& rPoint; 553 SwTwips nLineWidth; 554 sal_Bool bFirstLine : 1; 555 sal_Bool bInner : 1; 556 sal_Bool bColumn : 1; 557 sal_Bool bEmpty : 1; 558 SwFillData( const SwCrsrMoveState *pC, SwPosition* pP, const SwRect& rR, 559 const Point& rPt ) : aFrm( rR ), pCMS( pC ), pPos( pP ), rPoint( rPt ), 560 nLineWidth( 0 ), bFirstLine( sal_True ), bInner( sal_False ), bColumn( sal_False ), 561 bEmpty( sal_True ){} 562 SwFillMode Mode() const { return pCMS->pFill->eMode; } 563 long X() const { return rPoint.X(); } 564 long Y() const { return rPoint.Y(); } 565 long Left() const { return aFrm.Left(); } 566 long Right() const { return aFrm.Right(); } 567 long Bottom() const { return aFrm.Bottom(); } 568 SwRect& Frm() { return aFrm; } 569 SwFillCrsrPos &Fill() const { return *pCMS->pFill; } 570 void SetTab( MSHORT nNew ) { pCMS->pFill->nTabCnt = nNew; } 571 void SetSpace( MSHORT nNew ) { pCMS->pFill->nSpaceCnt = nNew; } 572 void SetOrient( const sal_Int16 eNew ){ pCMS->pFill->eOrient = eNew; } 573 }; 574 575 sal_Bool SwTxtFrm::_GetCrsrOfst(SwPosition* pPos, const Point& rPoint, 576 const sal_Bool bChgFrm, SwCrsrMoveState* pCMS ) const 577 { 578 // 8804: _GetCrsrOfst wird vom GetCrsrOfst und GetKeyCrsrOfst gerufen. 579 // In keinem Fall nur ein return sal_False. 580 581 if( IsLocked() || IsHiddenNow() ) 582 return sal_False; 583 584 ((SwTxtFrm*)this)->GetFormatted(); 585 586 Point aOldPoint( rPoint ); 587 588 if ( IsVertical() ) 589 { 590 SwitchVerticalToHorizontal( (Point&)rPoint ); 591 ((SwTxtFrm*)this)->SwapWidthAndHeight(); 592 } 593 594 if ( IsRightToLeft() ) 595 SwitchRTLtoLTR( (Point&)rPoint ); 596 597 SwFillData *pFillData = ( pCMS && pCMS->pFill ) ? 598 new SwFillData( pCMS, pPos, Frm(), rPoint ) : NULL; 599 600 if ( IsEmpty() ) 601 { 602 SwTxtNode* pTxtNd = ((SwTxtFrm*)this)->GetTxtNode(); 603 pPos->nNode = *pTxtNd; 604 pPos->nContent.Assign( pTxtNd, 0 ); 605 if( pCMS && pCMS->bFieldInfo ) 606 { 607 SwTwips nDiff = rPoint.X() - Frm().Left() - Prt().Left(); 608 if( nDiff > 50 || nDiff < 0 ) 609 ((SwCrsrMoveState*)pCMS)->bPosCorr = sal_True; 610 } 611 } 612 else 613 { 614 SwTxtSizeInfo aInf( (SwTxtFrm*)this ); 615 SwTxtCursor aLine( ((SwTxtFrm*)this), &aInf ); 616 617 // Siehe Kommentar in AdjustFrm() 618 SwTwips nMaxY = Frm().Top() + Prt().Top() + Prt().Height(); 619 aLine.TwipsToLine( rPoint.Y() ); 620 while( aLine.Y() + aLine.GetLineHeight() > nMaxY ) 621 { 622 DBG_LOOP; 623 if( !aLine.Prev() ) 624 break; 625 } 626 627 if( aLine.GetDropLines() >= aLine.GetLineNr() && 1 != aLine.GetLineNr() 628 && rPoint.X() < aLine.FirstLeft() + aLine.GetDropLeft() ) 629 while( aLine.GetLineNr() > 1 ) 630 aLine.Prev(); 631 632 xub_StrLen nOffset = aLine.GetCrsrOfst( pPos, rPoint, bChgFrm, pCMS ); 633 634 if( pCMS && pCMS->eState == MV_NONE && aLine.GetEnd() == nOffset ) 635 ((SwCrsrMoveState*)pCMS)->eState = MV_RIGHTMARGIN; 636 637 // 6776: pPos ist ein reiner IN-Parameter, der nicht ausgewertet werden darf. 638 // Das pIter->GetCrsrOfst returnt aus einer Verschachtelung mit STRING_LEN. 639 // Wenn SwTxtIter::GetCrsrOfst von sich aus weitere GetCrsrOfst 640 // ruft, so aendert sich nNode der Position. In solchen Faellen 641 // darf pPos nicht berechnet werden. 642 if( STRING_LEN != nOffset ) 643 { 644 SwTxtNode* pTxtNd = ((SwTxtFrm*)this)->GetTxtNode(); 645 pPos->nNode = *pTxtNd; 646 pPos->nContent.Assign( pTxtNd, nOffset ); 647 if( pFillData ) 648 { 649 if( pTxtNd->GetTxt().Len() > nOffset || 650 rPoint.Y() < Frm().Top() ) 651 pFillData->bInner = sal_True; 652 pFillData->bFirstLine = aLine.GetLineNr() < 2; 653 if( pTxtNd->GetTxt().Len() ) 654 { 655 pFillData->bEmpty = sal_False; 656 pFillData->nLineWidth = aLine.GetCurr()->Width(); 657 } 658 } 659 } 660 } 661 sal_Bool bChgFillData = sal_False; 662 if( pFillData && FindPageFrm()->Frm().IsInside( aOldPoint ) ) 663 { 664 FillCrsrPos( *pFillData ); 665 bChgFillData = sal_True; 666 } 667 668 if ( IsVertical() ) 669 { 670 if ( bChgFillData ) 671 SwitchHorizontalToVertical( pFillData->Fill().aCrsr.Pos() ); 672 ((SwTxtFrm*)this)->SwapWidthAndHeight(); 673 } 674 675 if ( IsRightToLeft() && bChgFillData ) 676 { 677 SwitchLTRtoRTL( pFillData->Fill().aCrsr.Pos() ); 678 const sal_Int16 eOrient = pFillData->pCMS->pFill->eOrient; 679 680 if ( text::HoriOrientation::LEFT == eOrient ) 681 pFillData->SetOrient( text::HoriOrientation::RIGHT ); 682 else if ( text::HoriOrientation::RIGHT == eOrient ) 683 pFillData->SetOrient( text::HoriOrientation::LEFT ); 684 } 685 686 (Point&)rPoint = aOldPoint; 687 delete pFillData; 688 689 return sal_True; 690 } 691 692 /************************************************************************* 693 * virtual SwTxtFrm::GetCrsrOfst() 694 *************************************************************************/ 695 696 sal_Bool SwTxtFrm::GetCrsrOfst(SwPosition* pPos, Point& rPoint, 697 SwCrsrMoveState* pCMS ) const 698 { 699 MSHORT nChgFrm = 2; 700 if( pCMS ) 701 { 702 if( MV_UPDOWN == pCMS->eState ) 703 nChgFrm = 0; 704 else if( MV_SETONLYTEXT == pCMS->eState || 705 MV_TBLSEL == pCMS->eState ) 706 nChgFrm = 1; 707 } 708 return _GetCrsrOfst( pPos, rPoint, nChgFrm != 0, pCMS ); 709 } 710 711 /************************************************************************* 712 * SwTxtFrm::LeftMargin() 713 *************************************************************************/ 714 715 /* 716 * Layout-orientierte Cursorbewegungen 717 */ 718 719 /* 720 * an den Zeilenanfang 721 */ 722 723 sal_Bool SwTxtFrm::LeftMargin(SwPaM *pPam) const 724 { 725 if( ((const SwNode*)pPam->GetNode()) != GetNode() ) 726 pPam->GetPoint()->nNode = *((SwTxtFrm*)this)->GetTxtNode(); 727 728 SwTxtFrm *pFrm = GetAdjFrmAtPos( (SwTxtFrm*)this, *pPam->GetPoint(), 729 SwTxtCursor::IsRightMargin() ); 730 pFrm->GetFormatted(); 731 xub_StrLen nIndx; 732 if ( pFrm->IsEmpty() ) 733 nIndx = 0; 734 else 735 { 736 SwTxtSizeInfo aInf( pFrm ); 737 SwTxtCursor aLine( pFrm, &aInf ); 738 739 aLine.CharCrsrToLine(pPam->GetPoint()->nContent.GetIndex()); 740 nIndx = aLine.GetStart(); 741 if( pFrm->GetOfst() && !pFrm->IsFollow() && !aLine.GetPrev() ) 742 { 743 lcl_ChangeOffset( pFrm, 0 ); 744 nIndx = 0; 745 } 746 } 747 pPam->GetPoint()->nContent = SwIndex( pFrm->GetTxtNode(), nIndx ); 748 SwTxtCursor::SetRightMargin( sal_False ); 749 return sal_True; 750 } 751 752 /************************************************************************* 753 * SwTxtFrm::RightMargin() 754 *************************************************************************/ 755 756 /* 757 * An das Zeilenende:Das ist die Position vor dem letzten 758 * Character in der Zeile. Ausnahme: In der letzten Zeile soll 759 * der Cursor auch hinter dem letzten Character stehen koennen, 760 * um Text anhaengen zu koennen. 761 * 762 */ 763 764 sal_Bool SwTxtFrm::RightMargin(SwPaM *pPam, sal_Bool bAPI) const 765 { 766 if( ((const SwNode*)pPam->GetNode()) != GetNode() ) 767 pPam->GetPoint()->nNode = *((SwTxtFrm*)this)->GetTxtNode(); 768 769 SwTxtFrm *pFrm = GetAdjFrmAtPos( (SwTxtFrm*)this, *pPam->GetPoint(), 770 SwTxtCursor::IsRightMargin() ); 771 pFrm->GetFormatted(); 772 xub_StrLen nRightMargin; 773 if ( IsEmpty() ) 774 nRightMargin = 0; 775 else 776 { 777 SwTxtSizeInfo aInf( pFrm ); 778 SwTxtCursor aLine( pFrm, &aInf ); 779 780 aLine.CharCrsrToLine(pPam->GetPoint()->nContent.GetIndex()); 781 nRightMargin = aLine.GetStart() + aLine.GetCurr()->GetLen(); 782 783 // Harte Zeilenumbrueche lassen wir hinter uns. 784 if( aLine.GetCurr()->GetLen() && 785 CH_BREAK == aInf.GetTxt().GetChar( nRightMargin - 1 ) ) 786 --nRightMargin; 787 else if( !bAPI && (aLine.GetNext() || pFrm->GetFollow()) ) 788 { 789 while( nRightMargin > aLine.GetStart() && 790 ' ' == aInf.GetTxt().GetChar( nRightMargin - 1 ) ) 791 --nRightMargin; 792 } 793 } 794 pPam->GetPoint()->nContent = SwIndex( pFrm->GetTxtNode(), nRightMargin ); 795 SwTxtCursor::SetRightMargin( !bAPI ); 796 return sal_True; 797 } 798 799 /************************************************************************* 800 * SwTxtFrm::_UnitUp() 801 *************************************************************************/ 802 803 //Die beiden folgenden Methoden versuchen zunaechst den Crsr in die 804 //nachste/folgende Zeile zu setzen. Gibt es im Frame keine vorhergehende/ 805 //folgende Zeile, so wird der Aufruf an die Basisklasse weitergeleitet. 806 //Die Horizontale Ausrichtung des Crsr wird hinterher von der CrsrShell 807 //vorgenommen. 808 809 class SwSetToRightMargin 810 { 811 sal_Bool bRight; 812 public: 813 inline SwSetToRightMargin() : bRight( sal_False ) { } 814 inline ~SwSetToRightMargin() { SwTxtCursor::SetRightMargin( bRight ); } 815 inline void SetRight( const sal_Bool bNew ) { bRight = bNew; } 816 }; 817 818 sal_Bool SwTxtFrm::_UnitUp( SwPaM *pPam, const SwTwips nOffset, 819 sal_Bool bSetInReadOnly ) const 820 { 821 // 8626: Im Notfall den RightMargin setzen. 822 SwSetToRightMargin aSet; 823 824 if( IsInTab() && 825 pPam->GetNode( sal_True )->StartOfSectionNode() != 826 pPam->GetNode( sal_False )->StartOfSectionNode() ) 827 { 828 //Wenn der PaM in unterschiedlichen Boxen sitzt, so handelt es sich um 829 //eine Tabellenselektion; diese wird von der Basisklasse abgearbeitet. 830 return SwCntntFrm::UnitUp( pPam, nOffset, bSetInReadOnly ); 831 } 832 833 ((SwTxtFrm*)this)->GetFormatted(); 834 const xub_StrLen nPos = pPam->GetPoint()->nContent.GetIndex(); 835 SwRect aCharBox; 836 837 if( !IsEmpty() && !IsHiddenNow() ) 838 { 839 xub_StrLen nFormat = STRING_LEN; 840 do 841 { 842 if( nFormat != STRING_LEN && !IsFollow() ) 843 lcl_ChangeOffset( ((SwTxtFrm*)this), nFormat ); 844 845 SwTxtSizeInfo aInf( (SwTxtFrm*)this ); 846 SwTxtCursor aLine( ((SwTxtFrm*)this), &aInf ); 847 848 // 8116: Flys ohne Umlauf und IsDummy(); hier wegoptimiert 849 if( nPos ) 850 aLine.CharCrsrToLine( nPos ); 851 else 852 aLine.Top(); 853 854 const SwLineLayout *pPrevLine = aLine.GetPrevLine(); 855 const xub_StrLen nStart = aLine.GetStart(); 856 aLine.GetCharRect( &aCharBox, nPos ); 857 858 sal_Bool bSecondOfDouble = ( aInf.IsMulti() && ! aInf.IsFirstMulti() ); 859 sal_Bool bPrevLine = ( pPrevLine && pPrevLine != aLine.GetCurr() ); 860 861 if( !pPrevLine && !bSecondOfDouble && GetOfst() && !IsFollow() ) 862 { 863 nFormat = GetOfst(); 864 xub_StrLen nDiff = aLine.GetLength(); 865 if( !nDiff ) 866 nDiff = MIN_OFFSET_STEP; 867 if( nFormat > nDiff ) 868 nFormat = nFormat - nDiff; 869 else 870 nFormat = 0; 871 continue; 872 } 873 874 // we select the target line for the cursor, in case we are in a 875 // double line portion, prev line = curr line 876 if( bPrevLine && !bSecondOfDouble ) 877 { 878 aLine.PrevLine(); 879 while ( aLine.GetStart() == nStart && 880 0 != ( pPrevLine = aLine.GetPrevLine() ) && 881 pPrevLine != aLine.GetCurr() ) 882 aLine.PrevLine(); 883 } 884 885 if ( bPrevLine || bSecondOfDouble ) 886 { 887 aCharBox.SSize().Width() /= 2; 888 aCharBox.Pos().X() = aCharBox.Pos().X() - 150; 889 890 // siehe Kommentar in SwTxtFrm::GetCrsrOfst() 891 #ifdef DBG_UTIL 892 const sal_uLong nOldNode = pPam->GetPoint()->nNode.GetIndex(); 893 #endif 894 // Der Node soll nicht gewechselt werden 895 xub_StrLen nTmpOfst = aLine.GetCrsrOfst( pPam->GetPoint(), 896 aCharBox.Pos(), sal_False ); 897 ASSERT( nOldNode == pPam->GetPoint()->nNode.GetIndex(), 898 "SwTxtFrm::UnitUp: illegal node change" ) 899 900 // 7684: Wir stellen sicher, dass wir uns nach oben bewegen. 901 if( nTmpOfst >= nStart && nStart && !bSecondOfDouble ) 902 { 903 nTmpOfst = nStart; 904 aSet.SetRight( sal_True ); 905 } 906 pPam->GetPoint()->nContent = 907 SwIndex( ((SwTxtFrm*)this)->GetTxtNode(), nTmpOfst ); 908 return sal_True; 909 } 910 911 if ( IsFollow() ) 912 { 913 aLine.GetCharRect( &aCharBox, nPos ); 914 aCharBox.SSize().Width() /= 2; 915 } 916 break; 917 } while ( sal_True ); 918 } 919 /* Wenn this ein Follow ist und ein Prev miszlang, so 920 * muessen wir in die letzte Zeile des Master ... und der sind wir. 921 * Oder wir sind ein Follow mit Follow, dann muessen wir uns den 922 * Master extra besorgen... 923 */ 924 if ( IsFollow() ) 925 { 926 const SwTxtFrm *pTmpPrev = FindMaster(); 927 xub_StrLen nOffs = GetOfst(); 928 if( pTmpPrev ) 929 { 930 ViewShell *pSh = getRootFrm()->GetCurrShell(); 931 sal_Bool bProtectedAllowed = pSh && pSh->GetViewOptions()->IsCursorInProtectedArea(); 932 const SwTxtFrm *pPrevPrev = pTmpPrev; 933 // Hier werden geschuetzte Frames und Frame ohne Inhalt ausgelassen 934 while( pPrevPrev && ( pPrevPrev->GetOfst() == nOffs || 935 ( !bProtectedAllowed && pPrevPrev->IsProtected() ) ) ) 936 { 937 pTmpPrev = pPrevPrev; 938 nOffs = pTmpPrev->GetOfst(); 939 if ( pPrevPrev->IsFollow() ) 940 pPrevPrev = pTmpPrev->FindMaster(); 941 else 942 pPrevPrev = NULL; 943 } 944 if ( !pPrevPrev ) 945 return pTmpPrev->SwCntntFrm::UnitUp( pPam, nOffset, bSetInReadOnly ); 946 aCharBox.Pos().Y() = pPrevPrev->Frm().Bottom() - 1; 947 return pPrevPrev->GetKeyCrsrOfst( pPam->GetPoint(), aCharBox.Pos() ); 948 } 949 } 950 return SwCntntFrm::UnitUp( pPam, nOffset, bSetInReadOnly ); 951 } 952 953 // 954 // Used for Bidi. nPos is the logical position in the string, bLeft indicates 955 // if left arrow or right arrow was pressed. The return values are: 956 // nPos: the new visual position 957 // bLeft: whether the break iterator has to add or subtract from the 958 // current position 959 void lcl_VisualMoveRecursion( const SwLineLayout& rCurrLine, xub_StrLen nIdx, 960 xub_StrLen& nPos, sal_Bool& bRight, 961 sal_uInt8& nCrsrLevel, sal_uInt8 nDefaultDir ) 962 { 963 const SwLinePortion* pPor = rCurrLine.GetFirstPortion(); 964 const SwLinePortion* pLast = 0; 965 966 // what's the current portion 967 while ( pPor && nIdx + pPor->GetLen() <= nPos ) 968 { 969 nIdx = nIdx + pPor->GetLen(); 970 pLast = pPor; 971 pPor = pPor->GetPortion(); 972 } 973 974 if ( bRight ) 975 { 976 sal_Bool bRecurse = pPor && pPor->IsMultiPortion() && 977 ((SwMultiPortion*)pPor)->IsBidi(); 978 979 // 1. special case: at beginning of bidi portion 980 if ( bRecurse && nIdx == nPos ) 981 { 982 nPos = nPos + pPor->GetLen(); 983 984 // leave bidi portion 985 if ( nCrsrLevel != nDefaultDir ) 986 { 987 bRecurse = sal_False; 988 } 989 else 990 // special case: 991 // buffer: abcXYZ123 in LTR paragraph 992 // view: abc123ZYX 993 // cursor is between c and X in the buffer and cursor level = 0 994 nCrsrLevel++; 995 } 996 997 // 2. special case: at beginning of portion after bidi portion 998 else if ( pLast && pLast->IsMultiPortion() && 999 ((SwMultiPortion*)pLast)->IsBidi() && nIdx == nPos ) 1000 { 1001 // enter bidi portion 1002 if ( nCrsrLevel != nDefaultDir ) 1003 { 1004 bRecurse = sal_True; 1005 nIdx = nIdx - pLast->GetLen(); 1006 pPor = pLast; 1007 } 1008 } 1009 1010 // Recursion 1011 if ( bRecurse ) 1012 { 1013 const SwLineLayout& rLine = ((SwMultiPortion*)pPor)->GetRoot(); 1014 xub_StrLen nTmpPos = nPos - nIdx; 1015 sal_Bool bTmpForward = ! bRight; 1016 sal_uInt8 nTmpCrsrLevel = nCrsrLevel; 1017 lcl_VisualMoveRecursion( rLine, 0, nTmpPos, bTmpForward, 1018 nTmpCrsrLevel, nDefaultDir + 1 ); 1019 1020 nPos = nTmpPos + nIdx; 1021 bRight = bTmpForward; 1022 nCrsrLevel = nTmpCrsrLevel; 1023 } 1024 1025 // go forward 1026 else 1027 { 1028 bRight = sal_True; 1029 nCrsrLevel = nDefaultDir; 1030 } 1031 1032 } 1033 else 1034 { 1035 sal_Bool bRecurse = pPor && pPor->IsMultiPortion() && ((SwMultiPortion*)pPor)->IsBidi(); 1036 1037 // 1. special case: at beginning of bidi portion 1038 if ( bRecurse && nIdx == nPos ) 1039 { 1040 // leave bidi portion 1041 if ( nCrsrLevel == nDefaultDir ) 1042 { 1043 bRecurse = sal_False; 1044 } 1045 } 1046 1047 // 2. special case: at beginning of portion after bidi portion 1048 else if ( pLast && pLast->IsMultiPortion() && 1049 ((SwMultiPortion*)pLast)->IsBidi() && nIdx == nPos ) 1050 { 1051 nPos = nPos - pLast->GetLen(); 1052 1053 // enter bidi portion 1054 if ( nCrsrLevel % 2 == nDefaultDir % 2 ) 1055 { 1056 bRecurse = sal_True; 1057 nIdx = nIdx - pLast->GetLen(); 1058 pPor = pLast; 1059 1060 // special case: 1061 // buffer: abcXYZ123 in LTR paragraph 1062 // view: abc123ZYX 1063 // cursor is behind 3 in the buffer and cursor level = 2 1064 if ( nDefaultDir + 2 == nCrsrLevel ) 1065 nPos = nPos + pLast->GetLen(); 1066 } 1067 } 1068 1069 // go forward 1070 if ( bRecurse ) 1071 { 1072 const SwLineLayout& rLine = ((SwMultiPortion*)pPor)->GetRoot(); 1073 xub_StrLen nTmpPos = nPos - nIdx; 1074 sal_Bool bTmpForward = ! bRight; 1075 sal_uInt8 nTmpCrsrLevel = nCrsrLevel; 1076 lcl_VisualMoveRecursion( rLine, 0, nTmpPos, bTmpForward, 1077 nTmpCrsrLevel, nDefaultDir + 1 ); 1078 1079 // special case: 1080 // buffer: abcXYZ123 in LTR paragraph 1081 // view: abc123ZYX 1082 // cursor is between Z and 1 in the buffer and cursor level = 2 1083 if ( nTmpPos == pPor->GetLen() && nTmpCrsrLevel == nDefaultDir + 1 ) 1084 { 1085 nTmpPos = nTmpPos - pPor->GetLen(); 1086 nTmpCrsrLevel = nDefaultDir; 1087 bTmpForward = ! bTmpForward; 1088 } 1089 1090 nPos = nTmpPos + nIdx; 1091 bRight = bTmpForward; 1092 nCrsrLevel = nTmpCrsrLevel; 1093 } 1094 1095 // go backward 1096 else 1097 { 1098 bRight = sal_False; 1099 nCrsrLevel = nDefaultDir; 1100 } 1101 } 1102 } 1103 1104 void SwTxtFrm::PrepareVisualMove( xub_StrLen& nPos, sal_uInt8& nCrsrLevel, 1105 sal_Bool& bForward, sal_Bool bInsertCrsr ) 1106 { 1107 if( IsEmpty() || IsHiddenNow() ) 1108 return; 1109 1110 ((SwTxtFrm*)this)->GetFormatted(); 1111 1112 SwTxtSizeInfo aInf( (SwTxtFrm*)this ); 1113 SwTxtCursor aLine( ((SwTxtFrm*)this), &aInf ); 1114 1115 if( nPos ) 1116 aLine.CharCrsrToLine( nPos ); 1117 else 1118 aLine.Top(); 1119 1120 const SwLineLayout* pLine = aLine.GetCurr(); 1121 const xub_StrLen nStt = aLine.GetStart(); 1122 const xub_StrLen nLen = pLine->GetLen(); 1123 1124 // We have to distinguish between an insert and overwrite cursor: 1125 // The insert cursor position depends on the cursor level: 1126 // buffer: abcXYZdef in LTR paragraph 1127 // display: abcZYXdef 1128 // If cursor is between c and X in the buffer and cursor level is 0, 1129 // the cursor blinks between c and Z and -> sets the cursor between Z and Y. 1130 // If the cursor level is 1, the cursor blinks between X and d and 1131 // -> sets the cursor between d and e. 1132 // The overwrite cursor simply travels to the next visual character. 1133 if ( bInsertCrsr ) 1134 { 1135 lcl_VisualMoveRecursion( *pLine, nStt, nPos, bForward, 1136 nCrsrLevel, IsRightToLeft() ? 1 : 0 ); 1137 return; 1138 } 1139 1140 const sal_uInt8 nDefaultDir = static_cast<sal_uInt8>(IsRightToLeft() ? UBIDI_RTL : UBIDI_LTR); 1141 const sal_Bool bVisualRight = ( nDefaultDir == UBIDI_LTR && bForward ) || 1142 ( nDefaultDir == UBIDI_RTL && ! bForward ); 1143 1144 // 1145 // Bidi functions from icu 2.0 1146 // 1147 const sal_Unicode* pLineString = GetTxtNode()->GetTxt().GetBuffer(); 1148 pLine += nStt; 1149 1150 UErrorCode nError = U_ZERO_ERROR; 1151 UBiDi* pBidi = ubidi_openSized( nLen, 0, &nError ); 1152 ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(pLineString), nLen, nDefaultDir, NULL, &nError ); // UChar != sal_Unicode in MinGW 1153 1154 xub_StrLen nTmpPos; 1155 sal_Bool bOutOfBounds = sal_False; 1156 1157 if ( nPos < nStt + nLen ) 1158 { 1159 nTmpPos = (xub_StrLen)ubidi_getVisualIndex( pBidi, nPos, &nError ); 1160 1161 // visual indices are always LTR aligned 1162 if ( bVisualRight ) 1163 { 1164 if ( nTmpPos + 1 < nStt + nLen ) 1165 ++nTmpPos; 1166 else 1167 { 1168 nPos = nDefaultDir == UBIDI_RTL ? 0 : nStt + nLen; 1169 bOutOfBounds = sal_True; 1170 } 1171 } 1172 else 1173 { 1174 if ( nTmpPos ) 1175 --nTmpPos; 1176 else 1177 { 1178 nPos = nDefaultDir == UBIDI_RTL ? nStt + nLen : 0; 1179 bOutOfBounds = sal_True; 1180 } 1181 } 1182 } 1183 else 1184 { 1185 nTmpPos = nDefaultDir == UBIDI_LTR ? nPos - 1 : 0; 1186 } 1187 1188 if ( ! bOutOfBounds ) 1189 { 1190 nPos = (xub_StrLen)ubidi_getLogicalIndex( pBidi, nTmpPos, &nError ); 1191 1192 if ( bForward ) 1193 { 1194 if ( nPos ) 1195 --nPos; 1196 else 1197 { 1198 ++nPos; 1199 bForward = ! bForward; 1200 } 1201 } 1202 else 1203 ++nPos; 1204 } 1205 1206 ubidi_close( pBidi ); 1207 } 1208 1209 /************************************************************************* 1210 * SwTxtFrm::_UnitDown() 1211 *************************************************************************/ 1212 1213 sal_Bool SwTxtFrm::_UnitDown(SwPaM *pPam, const SwTwips nOffset, 1214 sal_Bool bSetInReadOnly ) const 1215 { 1216 1217 if ( IsInTab() && 1218 pPam->GetNode( sal_True )->StartOfSectionNode() != 1219 pPam->GetNode( sal_False )->StartOfSectionNode() ) 1220 { 1221 //Wenn der PaM in unterschiedlichen Boxen sitzt, so handelt es sich um 1222 //eine Tabellenselektion; diese wird von der Basisklasse abgearbeitet. 1223 return SwCntntFrm::UnitDown( pPam, nOffset, bSetInReadOnly ); 1224 } 1225 ((SwTxtFrm*)this)->GetFormatted(); 1226 const xub_StrLen nPos = pPam->GetPoint()->nContent.GetIndex(); 1227 SwRect aCharBox; 1228 const SwCntntFrm *pTmpFollow = 0; 1229 1230 if ( IsVertical() ) 1231 ((SwTxtFrm*)this)->SwapWidthAndHeight(); 1232 1233 if ( !IsEmpty() && !IsHiddenNow() ) 1234 { 1235 xub_StrLen nFormat = STRING_LEN; 1236 do 1237 { 1238 if( nFormat != STRING_LEN && !IsFollow() && 1239 !lcl_ChangeOffset( ((SwTxtFrm*)this), nFormat ) ) 1240 break; 1241 1242 SwTxtSizeInfo aInf( (SwTxtFrm*)this ); 1243 SwTxtCursor aLine( ((SwTxtFrm*)this), &aInf ); 1244 nFormat = aLine.GetEnd(); 1245 1246 aLine.CharCrsrToLine( nPos ); 1247 1248 const SwLineLayout* pNextLine = aLine.GetNextLine(); 1249 const xub_StrLen nStart = aLine.GetStart(); 1250 aLine.GetCharRect( &aCharBox, nPos ); 1251 1252 sal_Bool bFirstOfDouble = ( aInf.IsMulti() && aInf.IsFirstMulti() ); 1253 1254 if( pNextLine || bFirstOfDouble ) 1255 { 1256 aCharBox.SSize().Width() /= 2; 1257 #ifdef DBG_UTIL 1258 // siehe Kommentar in SwTxtFrm::GetCrsrOfst() 1259 const sal_uLong nOldNode = pPam->GetPoint()->nNode.GetIndex(); 1260 #endif 1261 if ( pNextLine && ! bFirstOfDouble ) 1262 aLine.NextLine(); 1263 1264 xub_StrLen nTmpOfst = aLine.GetCrsrOfst( pPam->GetPoint(), 1265 aCharBox.Pos(), sal_False ); 1266 ASSERT( nOldNode == pPam->GetPoint()->nNode.GetIndex(), 1267 "SwTxtFrm::UnitDown: illegal node change" ) 1268 1269 // 7684: Wir stellen sicher, dass wir uns nach unten bewegen. 1270 if( nTmpOfst <= nStart && ! bFirstOfDouble ) 1271 nTmpOfst = nStart + 1; 1272 pPam->GetPoint()->nContent = 1273 SwIndex( ((SwTxtFrm*)this)->GetTxtNode(), nTmpOfst ); 1274 1275 if ( IsVertical() ) 1276 ((SwTxtFrm*)this)->SwapWidthAndHeight(); 1277 1278 return sal_True; 1279 } 1280 if( 0 != ( pTmpFollow = GetFollow() ) ) 1281 { // geschuetzte Follows auslassen 1282 const SwCntntFrm* pTmp = pTmpFollow; 1283 ViewShell *pSh = getRootFrm()->GetCurrShell(); 1284 if( !pSh || !pSh->GetViewOptions()->IsCursorInProtectedArea() ) 1285 { 1286 while( pTmpFollow && pTmpFollow->IsProtected() ) 1287 { 1288 pTmp = pTmpFollow; 1289 pTmpFollow = pTmpFollow->GetFollow(); 1290 } 1291 } 1292 if( !pTmpFollow ) // nur noch geschuetzte 1293 { 1294 if ( IsVertical() ) 1295 ((SwTxtFrm*)this)->SwapWidthAndHeight(); 1296 return pTmp->SwCntntFrm::UnitDown( pPam, nOffset, bSetInReadOnly ); 1297 } 1298 1299 aLine.GetCharRect( &aCharBox, nPos ); 1300 aCharBox.SSize().Width() /= 2; 1301 } 1302 else if( !IsFollow() ) 1303 { 1304 xub_StrLen nTmpLen = aInf.GetTxt().Len(); 1305 if( aLine.GetEnd() < nTmpLen ) 1306 { 1307 if( nFormat <= GetOfst() ) 1308 { 1309 nFormat = Min( xub_StrLen( GetOfst() + MIN_OFFSET_STEP ), 1310 nTmpLen ); 1311 if( nFormat <= GetOfst() ) 1312 break; 1313 } 1314 continue; 1315 } 1316 } 1317 break; 1318 } while( sal_True ); 1319 } 1320 else 1321 pTmpFollow = GetFollow(); 1322 1323 if ( IsVertical() ) 1324 ((SwTxtFrm*)this)->SwapWidthAndHeight(); 1325 1326 // Bei Follows schlagen wir eine Abkuerzung 1327 if( pTmpFollow ) 1328 { 1329 aCharBox.Pos().Y() = pTmpFollow->Frm().Top() + 1; 1330 return ((SwTxtFrm*)pTmpFollow)->GetKeyCrsrOfst( pPam->GetPoint(), 1331 aCharBox.Pos() ); 1332 } 1333 return SwCntntFrm::UnitDown( pPam, nOffset, bSetInReadOnly ); 1334 } 1335 1336 /************************************************************************* 1337 * virtual SwTxtFrm::UnitUp() 1338 *************************************************************************/ 1339 1340 sal_Bool SwTxtFrm::UnitUp(SwPaM *pPam, const SwTwips nOffset, 1341 sal_Bool bSetInReadOnly ) const 1342 { 1343 /* Im CrsrSh::Up() wird CntntNode::GetFrm() gerufen. 1344 * Dies liefert _immer_ den Master zurueck. 1345 * Um das Cursortravelling nicht zu belasten, korrigieren wir 1346 * hier im SwTxtFrm. 1347 * Wir ermittelt UnitUp fuer pFrm, pFrm ist entweder ein Master (=this) 1348 * oder ein Follow (!=this) 1349 */ 1350 const SwTxtFrm *pFrm = GetAdjFrmAtPos( (SwTxtFrm*)this, *(pPam->GetPoint()), 1351 SwTxtCursor::IsRightMargin() ); 1352 const sal_Bool bRet = pFrm->_UnitUp( pPam, nOffset, bSetInReadOnly ); 1353 1354 // 8626: kein SwTxtCursor::SetRightMargin( sal_False ); 1355 // statt dessen steht ein SwSetToRightMargin im _UnitUp 1356 return bRet; 1357 } 1358 1359 /************************************************************************* 1360 * virtual SwTxtFrm::UnitDown() 1361 *************************************************************************/ 1362 1363 sal_Bool SwTxtFrm::UnitDown(SwPaM *pPam, const SwTwips nOffset, 1364 sal_Bool bSetInReadOnly ) const 1365 { 1366 const SwTxtFrm *pFrm = GetAdjFrmAtPos((SwTxtFrm*)this, *(pPam->GetPoint()), 1367 SwTxtCursor::IsRightMargin() ); 1368 const sal_Bool bRet = pFrm->_UnitDown( pPam, nOffset, bSetInReadOnly ); 1369 SwTxtCursor::SetRightMargin( sal_False ); 1370 return bRet; 1371 } 1372 1373 void SwTxtFrm::FillCrsrPos( SwFillData& rFill ) const 1374 { 1375 if( !rFill.bColumn && GetUpper()->IsColBodyFrm() ) // ColumnFrms jetzt mit BodyFrm 1376 { 1377 const SwColumnFrm* pTmp = 1378 (SwColumnFrm*)GetUpper()->GetUpper()->GetUpper()->Lower(); // die 1. Spalte 1379 // der erste SwFrm im BodyFrm der ersten Spalte 1380 const SwFrm* pFrm = ((SwLayoutFrm*)pTmp->Lower())->Lower(); 1381 MSHORT nNextCol = 0; 1382 // In welcher Spalte landen wir? 1383 while( rFill.X() > pTmp->Frm().Right() && pTmp->GetNext() ) 1384 { 1385 pTmp = (SwColumnFrm*)pTmp->GetNext(); 1386 if( ((SwLayoutFrm*)pTmp->Lower())->Lower() ) // ColumnFrms jetzt mit BodyFrm 1387 { 1388 pFrm = ((SwLayoutFrm*)pTmp->Lower())->Lower(); 1389 nNextCol = 0; 1390 } 1391 else 1392 ++nNextCol; // leere Spalten erfordern Spaltenumbrueche 1393 } 1394 if( pTmp != GetUpper()->GetUpper() ) // Sind wir in einer anderen Spalte gelandet? 1395 { 1396 if( !pFrm ) 1397 return; 1398 if( nNextCol ) 1399 { 1400 while( pFrm->GetNext() ) 1401 pFrm = pFrm->GetNext(); 1402 } 1403 else 1404 { 1405 while( pFrm->GetNext() && pFrm->Frm().Bottom() < rFill.Y() ) 1406 pFrm = pFrm->GetNext(); 1407 } 1408 // Kein Fuellen, wenn als letzter Frame in der anvisierten 1409 // Spalte kein Absatz, sondern z.B. eine Tabelle steht 1410 if( pFrm->IsTxtFrm() ) 1411 { 1412 rFill.Fill().nColumnCnt = nNextCol; 1413 rFill.bColumn = sal_True; 1414 if( rFill.pPos ) 1415 { 1416 SwTxtNode* pTxtNd = ((SwTxtFrm*)pFrm)->GetTxtNode(); 1417 rFill.pPos->nNode = *pTxtNd; 1418 rFill.pPos->nContent.Assign( pTxtNd, pTxtNd->GetTxt().Len() ); 1419 } 1420 if( nNextCol ) 1421 { 1422 rFill.aFrm = pTmp->Prt(); 1423 rFill.aFrm += pTmp->Frm().Pos(); 1424 } 1425 else 1426 rFill.aFrm = pFrm->Frm(); 1427 ((SwTxtFrm*)pFrm)->FillCrsrPos( rFill ); 1428 } 1429 return; 1430 } 1431 } 1432 sal_Bool bFill = sal_True; 1433 SwFont *pFnt; 1434 SwTxtFmtColl* pColl = GetTxtNode()->GetTxtColl(); 1435 MSHORT nFirst = GetTxtNode()->GetSwAttrSet().GetULSpace().GetLower(); 1436 SwTwips nDiff = rFill.Y() - Frm().Bottom(); 1437 if( nDiff < nFirst ) 1438 nDiff = -1; 1439 else 1440 pColl = &pColl->GetNextTxtFmtColl(); 1441 SwAttrSet aSet( ((SwDoc*)GetTxtNode()->GetDoc())->GetAttrPool(), aTxtFmtCollSetRange ); 1442 const SwAttrSet* pSet = &pColl->GetAttrSet(); 1443 ViewShell *pSh = getRootFrm()->GetCurrShell(); 1444 if( GetTxtNode()->HasSwAttrSet() ) 1445 { 1446 aSet.Put( *GetTxtNode()->GetpSwAttrSet() ); 1447 aSet.SetParent( pSet ); 1448 pSet = &aSet; 1449 pFnt = new SwFont( pSet, GetNode()->getIDocumentSettingAccess() ); 1450 } 1451 else 1452 { 1453 SwFontAccess aFontAccess( pColl, pSh ); 1454 pFnt = new SwFont( *aFontAccess.Get()->GetFont() ); 1455 pFnt->ChkMagic( pSh, pFnt->GetActual() ); 1456 } 1457 OutputDevice* pOut = pSh->GetOut(); 1458 if( !pSh->GetViewOptions()->getBrowseMode() || pSh->GetViewOptions()->IsPrtFormat() ) 1459 pOut = GetTxtNode()->getIDocumentDeviceAccess()->getReferenceDevice( true ); 1460 1461 pFnt->SetFntChg( sal_True ); 1462 pFnt->ChgPhysFnt( pSh, *pOut ); 1463 1464 SwTwips nLineHeight = pFnt->GetHeight( pSh, *pOut ); 1465 1466 if( nLineHeight ) 1467 { 1468 const SvxULSpaceItem &rUL = pSet->GetULSpace(); 1469 SwTwips nDist = Max( rUL.GetLower(), rUL.GetUpper() ); 1470 if( rFill.Fill().nColumnCnt ) 1471 { 1472 rFill.aFrm.Height( nLineHeight ); 1473 nDiff = rFill.Y() - rFill.Bottom(); 1474 nFirst = 0; 1475 } 1476 else if( nDist < nFirst ) 1477 nFirst = nFirst - (sal_uInt16)nDist; 1478 else 1479 nFirst = 0; 1480 nDist = Max( nDist, long( GetLineSpace() ) ); 1481 nDist += nLineHeight; 1482 nDiff -= nFirst; 1483 1484 if( nDiff > 0 ) 1485 { 1486 nDiff /= nDist; 1487 rFill.Fill().nParaCnt = static_cast<sal_uInt16>(nDiff + 1); 1488 rFill.nLineWidth = 0; 1489 rFill.bInner = sal_False; 1490 rFill.bEmpty = sal_True; 1491 rFill.SetOrient( text::HoriOrientation::LEFT ); 1492 } 1493 else 1494 nDiff = -1; 1495 if( rFill.bInner ) 1496 bFill = sal_False; 1497 else 1498 { 1499 const SvxTabStopItem &rRuler = pSet->GetTabStops(); 1500 const SvxLRSpaceItem &rLRSpace = pSet->GetLRSpace(); 1501 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() ) 1513 { 1514 if( rFill.bEmpty ) 1515 { 1516 rFill.SetOrient( text::HoriOrientation::LEFT ); 1517 if( rFill.X() < nCenter ) 1518 { 1519 if( rFill.X() > ( nLeft + 2 * nCenter ) / 3 ) 1520 { 1521 rFill.SetOrient( text::HoriOrientation::CENTER ); 1522 rRect.Left( nCenter ); 1523 } 1524 } 1525 else if( rFill.X() > ( nRight + 2 * nCenter ) / 3 ) 1526 { 1527 rFill.SetOrient( text::HoriOrientation::RIGHT ); 1528 rRect.Left( nRight ); 1529 } 1530 else 1531 { 1532 rFill.SetOrient( text::HoriOrientation::CENTER ); 1533 rRect.Left( nCenter ); 1534 } 1535 } 1536 else 1537 bFill = sal_False; 1538 } 1539 else 1540 { 1541 SwTwips nSpace = 0; 1542 if( FILL_TAB != rFill.Mode() ) 1543 { 1544 static sal_Char __READONLY_DATA sDoubleSpace[] = " "; 1545 const XubString aTmp( sDoubleSpace, RTL_TEXTENCODING_MS_1252 ); 1546 1547 SwDrawTextInfo aDrawInf( pSh, *pOut, 0, aTmp, 0, 2 ); 1548 nSpace = pFnt->_GetTxtSize( aDrawInf ).Width()/2; 1549 } 1550 if( rFill.X() >= nRight ) 1551 { 1552 if( FILL_INDENT != rFill.Mode() && ( rFill.bEmpty || 1553 rFill.X() > rFill.nLineWidth + FILL_MIN_DIST ) ) 1554 { 1555 rFill.SetOrient( text::HoriOrientation::RIGHT ); 1556 rRect.Left( nRight ); 1557 } 1558 else 1559 bFill = sal_False; 1560 } 1561 else if( FILL_INDENT == rFill.Mode() ) 1562 { 1563 SwTwips nIndent = rFill.X(); 1564 if( !rFill.bEmpty || nIndent > nRight ) 1565 bFill = sal_False; 1566 else 1567 { 1568 nIndent -= rFill.Left(); 1569 if( nIndent >= 0 && nSpace ) 1570 { 1571 nIndent /= nSpace; 1572 nIndent *= nSpace; 1573 rFill.SetTab( MSHORT( nIndent ) ); 1574 rRect.Left( nIndent + rFill.Left() ); 1575 } 1576 else 1577 bFill = sal_False; 1578 } 1579 } 1580 else if( rFill.X() > nLeft ) 1581 { 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; 1590 do 1591 { 1592 nLeftTab = nRightTab; 1593 if( nIdx < rRuler.Count() ) 1594 { 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; 1603 } 1604 else 1605 { 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() ) 1618 { 1619 nRightTab += nDefTabDist; 1620 if( nRightTab > rFill.nLineWidth ) 1621 ++nTabCnt; 1622 } 1623 if( nLeftTab < nRightTab - nDefTabDist ) 1624 nLeftTab = nRightTab - nDefTabDist; 1625 } 1626 if( nRightTab > nRight ) 1627 nRightTab = nRight; 1628 } 1629 while( rFill.X() > nRightTab ); 1630 --nTabCnt; 1631 if( FILL_TAB != rFill.Mode() ) 1632 { 1633 if( nSpace > 0 ) 1634 { 1635 if( !nTabCnt ) 1636 nLeftTab = rFill.nLineWidth; 1637 while( nLeftTab < rFill.X() ) 1638 { 1639 nLeftTab += nSpace; 1640 ++nSpaceCnt; 1641 } 1642 if( nSpaceCnt ) 1643 { 1644 nLeftTab -= nSpace; 1645 --nSpaceCnt; 1646 } 1647 if( rFill.X() - nLeftTab > nRightTab - rFill.X() ) 1648 { 1649 nSpaceCnt = 0; 1650 ++nTabCnt; 1651 rRect.Left( nRightTab ); 1652 } 1653 else 1654 { 1655 if( rFill.X() - nLeftTab > nSpace/2 ) 1656 { 1657 ++nSpaceCnt; 1658 rRect.Left( nLeftTab + nSpace ); 1659 } 1660 else 1661 rRect.Left( nLeftTab ); 1662 } 1663 } 1664 else if( rFill.X() - nLeftTab < nRightTab - rFill.X() ) 1665 rRect.Left( nLeftTab ); 1666 else 1667 { 1668 if( nRightTab >= nRight ) 1669 { 1670 rFill.SetOrient( text::HoriOrientation::RIGHT ); 1671 rRect.Left( nRight ); 1672 nTabCnt = 0; 1673 nSpaceCnt = 0; 1674 } 1675 else 1676 { 1677 rRect.Left( nRightTab ); 1678 ++nTabCnt; 1679 } 1680 } 1681 } 1682 else 1683 { 1684 if( rFill.X() - nLeftTab < nRightTab - rFill.X() ) 1685 rRect.Left( nLeftTab ); 1686 else 1687 { 1688 if( nRightTab >= nRight ) 1689 { 1690 rFill.SetOrient( text::HoriOrientation::RIGHT ); 1691 rRect.Left( nRight ); 1692 nTabCnt = 0; 1693 nSpaceCnt = 0; 1694 } 1695 else 1696 { 1697 rRect.Left( nRightTab ); 1698 ++nTabCnt; 1699 } 1700 } 1701 } 1702 rFill.SetTab( nTabCnt ); 1703 rFill.SetSpace( nSpaceCnt ); 1704 if( bFill ) 1705 { 1706 if( Abs( rFill.X() - nCenter ) <= 1707 Abs( rFill.X() - rRect.Left() ) ) 1708 { 1709 rFill.SetOrient( text::HoriOrientation::CENTER ); 1710 rFill.SetTab( 0 ); 1711 rFill.SetSpace( 0 ); 1712 rRect.Left( nCenter ); 1713 } 1714 if( !rFill.bEmpty ) 1715 rFill.nLineWidth += FILL_MIN_DIST; 1716 if( rRect.Left() < rFill.nLineWidth ) 1717 bFill = sal_False; 1718 } 1719 } 1720 } 1721 // Gehen wir ueber die Unterkante der Seite/Spalte etc. hinaus? 1722 const SwFrm* pUp = GetUpper(); 1723 if( pUp->IsInSct() ) 1724 { 1725 if( pUp->IsSctFrm() ) 1726 pUp = pUp->GetUpper(); 1727 else if( pUp->IsColBodyFrm() && 1728 pUp->GetUpper()->GetUpper()->IsSctFrm() ) 1729 pUp = pUp->GetUpper()->GetUpper()->GetUpper(); 1730 } 1731 SWRECTFN( this ) 1732 SwTwips nLimit = (pUp->*fnRect->fnGetPrtBottom)(); 1733 SwTwips nRectBottom = rRect.Bottom(); 1734 if ( bVert ) 1735 nRectBottom = SwitchHorizontalToVertical( nRectBottom ); 1736 1737 if( (*fnRect->fnYDiff)( nLimit, nRectBottom ) < 0 ) 1738 bFill = sal_False; 1739 else 1740 rRect.Width( 1 ); 1741 } 1742 } 1743 else 1744 bFill = sal_False; 1745 ((SwCrsrMoveState*)rFill.pCMS)->bFillRet = bFill; 1746 delete pFnt; 1747 } 1748