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