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 <hintids.hxx> 33 34 #include <com/sun/star/i18n/ScriptType.hdl> 35 #include <editeng/twolinesitem.hxx> 36 #include <editeng/charrotateitem.hxx> 37 #include <vcl/outdev.hxx> 38 #include <fmtfld.hxx> 39 #include <fldbas.hxx> // SwField 40 #include <txatbase.hxx> 41 #include <fmtruby.hxx> // SwFmtRuby 42 #include <txtatr.hxx> // SwTxtRuby 43 #include <charfmt.hxx> 44 #include <txtinet.hxx> 45 #include <fchrfmt.hxx> 46 #include <layfrm.hxx> // GetUpper() 47 #include <SwPortionHandler.hxx> 48 #include <pormulti.hxx> // SwMultiPortion 49 #include <inftxt.hxx> // SwTxtSizeInfo 50 #include <itrpaint.hxx> // SwTxtPainter 51 #include <viewopt.hxx> // SwViewOptions 52 #include <itrform2.hxx> // SwTxtFormatter 53 #include <porfld.hxx> // SwFldPortion 54 #include <porglue.hxx> 55 #include <breakit.hxx> 56 #include <pagefrm.hxx> 57 #include <rowfrm.hxx> 58 #include <pagedesc.hxx> // SwPageDesc 59 #include <tgrditem.hxx> 60 #include <swtable.hxx> 61 #include <fmtfsize.hxx> 62 63 using namespace ::com::sun::star; 64 extern sal_Bool IsUnderlineBreak( const SwLinePortion& rPor, const SwFont& rFnt ); 65 66 /*-----------------10.10.00 15:23------------------- 67 * class SwMultiPortion 68 * 69 * A SwMultiPortion is not a simple portion, 70 * it's a container, which contains almost a SwLineLayoutPortion. 71 * This SwLineLayout could be followed by other textportions via pPortion 72 * and by another SwLineLayout via pNext to realize a doubleline portion. 73 * --------------------------------------------------*/ 74 75 SwMultiPortion::~SwMultiPortion() 76 { 77 delete pFldRest; 78 } 79 80 void SwMultiPortion::Paint( const SwTxtPaintInfo & ) const 81 { 82 ASSERT( sal_False, 83 "Don't try SwMultiPortion::Paint, try SwTxtPainter::PaintMultiPortion" ); 84 } 85 86 /*-----------------13.10.00 16:21------------------- 87 * Summarize the internal lines to calculate the (external) size. 88 * The internal line has to calculate first. 89 * --------------------------------------------------*/ 90 91 void SwMultiPortion::CalcSize( SwTxtFormatter& rLine, SwTxtFormatInfo &rInf ) 92 { 93 Width( 0 ); 94 Height( 0 ); 95 SetAscent( 0 ); 96 SetFlyInCntnt( sal_False ); 97 SwLineLayout *pLay = &GetRoot(); 98 do 99 { 100 pLay->CalcLine( rLine, rInf ); 101 if( rLine.IsFlyInCntBase() ) 102 SetFlyInCntnt( sal_True ); 103 if( IsRuby() && ( OnTop() == ( pLay == &GetRoot() ) ) ) 104 { 105 // An empty phonetic line don't need an ascent or a height. 106 if( !pLay->Width() ) 107 { 108 pLay->SetAscent( 0 ); 109 pLay->Height( 0 ); 110 } 111 if( OnTop() ) 112 SetAscent( GetAscent() + pLay->Height() ); 113 } 114 else 115 SetAscent( GetAscent() + pLay->GetAscent() ); 116 Height( Height() + pLay->Height() ); 117 if( Width() < pLay->Width() ) 118 Width( pLay->Width() ); 119 pLay = pLay->GetNext(); 120 } while ( pLay ); 121 if( HasBrackets() ) 122 { 123 KSHORT nTmp = ((SwDoubleLinePortion*)this)->GetBrackets()->nHeight; 124 if( nTmp > Height() ) 125 { 126 KSHORT nAdd = ( nTmp - Height() ) / 2; 127 GetRoot().SetAscent( GetRoot().GetAscent() + nAdd ); 128 GetRoot().Height( GetRoot().Height() + nAdd ); 129 Height( nTmp ); 130 } 131 nTmp = ((SwDoubleLinePortion*)this)->GetBrackets()->nAscent; 132 if( nTmp > GetAscent() ) 133 SetAscent( nTmp ); 134 } 135 } 136 137 long SwMultiPortion::CalcSpacing( long , const SwTxtSizeInfo & ) const 138 { 139 return 0; 140 } 141 142 sal_Bool SwMultiPortion::ChgSpaceAdd( SwLineLayout*, long ) const 143 { 144 return sal_False; 145 } 146 147 /************************************************************************* 148 * virtual SwMultiPortion::HandlePortion() 149 *************************************************************************/ 150 151 void SwMultiPortion::HandlePortion( SwPortionHandler& rPH ) const 152 { 153 rPH.Text( GetLen(), GetWhichPor() ); 154 } 155 156 /*-----------------01.11.00 14:21------------------- 157 * SwMultiPortion::ActualizeTabulator() 158 * sets the tabulator-flag, if there's any tabulator-portion inside. 159 * --------------------------------------------------*/ 160 161 void SwMultiPortion::ActualizeTabulator() 162 { 163 SwLinePortion* pPor = GetRoot().GetFirstPortion(); 164 // First line 165 for( bTab1 = bTab2 = sal_False; pPor; pPor = pPor->GetPortion() ) 166 if( pPor->InTabGrp() ) 167 SetTab1( sal_True ); 168 if( GetRoot().GetNext() ) 169 { 170 // Second line 171 pPor = GetRoot().GetNext()->GetFirstPortion(); 172 do 173 { 174 if( pPor->InTabGrp() ) 175 SetTab2( sal_True ); 176 pPor = pPor->GetPortion(); 177 } while ( pPor ); 178 } 179 } 180 181 /*-----------------16.02.01 12:07------------------- 182 * SwRotatedPortion::SwRotatedPortion(..) 183 * --------------------------------------------------*/ 184 185 SwRotatedPortion::SwRotatedPortion( const SwMultiCreator& rCreate, 186 xub_StrLen nEnd, sal_Bool bRTL ) : SwMultiPortion( nEnd ) 187 { 188 const SvxCharRotateItem* pRot = (SvxCharRotateItem*)rCreate.pItem; 189 if( !pRot ) 190 { 191 const SwTxtAttr& rAttr = *rCreate.pAttr; 192 const SfxPoolItem *const pItem = 193 CharFmt::GetItem(rAttr, RES_CHRATR_ROTATE); 194 if ( pItem ) 195 { 196 pRot = static_cast<const SvxCharRotateItem*>(pItem); 197 } 198 } 199 if( pRot ) 200 { 201 sal_uInt8 nDir; 202 if ( bRTL ) 203 nDir = pRot->IsBottomToTop() ? 3 : 1; 204 else 205 nDir = pRot->IsBottomToTop() ? 1 : 3; 206 207 SetDirection( nDir ); 208 } 209 } 210 211 /*--------------------------------------------------- 212 * SwBidiPortion::SwBidiPortion(..) 213 * --------------------------------------------------*/ 214 215 SwBidiPortion::SwBidiPortion( xub_StrLen nEnd, sal_uInt8 nLv ) 216 : SwMultiPortion( nEnd ), nLevel( nLv ) 217 { 218 SetBidi(); 219 220 if ( nLevel % 2 ) 221 SetDirection( DIR_RIGHT2LEFT ); 222 else 223 SetDirection( DIR_LEFT2RIGHT ); 224 } 225 226 227 long SwBidiPortion::CalcSpacing( long nSpaceAdd, const SwTxtSizeInfo& rInf ) const 228 { 229 return HasTabulator() ? 0 : GetSpaceCnt(rInf) * nSpaceAdd / SPACING_PRECISION_FACTOR; 230 } 231 232 sal_Bool SwBidiPortion::ChgSpaceAdd( SwLineLayout* pCurr, long nSpaceAdd ) const 233 { 234 sal_Bool bRet = sal_False; 235 if( !HasTabulator() && nSpaceAdd > 0 && !pCurr->IsSpaceAdd() ) 236 { 237 pCurr->CreateSpaceAdd(); 238 pCurr->SetLLSpaceAdd( nSpaceAdd, 0 ); 239 bRet = sal_True; 240 } 241 242 return bRet; 243 } 244 245 xub_StrLen SwBidiPortion::GetSpaceCnt( const SwTxtSizeInfo &rInf ) const 246 { 247 // Calculate number of blanks for justified alignment 248 SwLinePortion* pPor = GetRoot().GetFirstPortion(); 249 xub_StrLen nTmpStart = rInf.GetIdx(); 250 xub_StrLen nNull = 0; 251 xub_StrLen nBlanks; 252 253 for( nBlanks = 0; pPor; pPor = pPor->GetPortion() ) 254 { 255 if( pPor->InTxtGrp() ) 256 nBlanks = nBlanks + ((SwTxtPortion*)pPor)->GetSpaceCnt( rInf, nNull ); 257 else if ( pPor->IsMultiPortion() && 258 ((SwMultiPortion*)pPor)->IsBidi() ) 259 nBlanks = nBlanks + ((SwBidiPortion*)pPor)->GetSpaceCnt( rInf ); 260 261 ((SwTxtSizeInfo &)rInf).SetIdx( rInf.GetIdx() + pPor->GetLen() ); 262 } 263 ((SwTxtSizeInfo &)rInf).SetIdx( nTmpStart ); 264 return nBlanks; 265 } 266 267 /*-----------------01.11.00 14:22------------------- 268 * SwDoubleLinePortion::SwDoubleLinePortion(..) 269 * This constructor is for the continuation of a doubleline portion 270 * in the next line. 271 * It takes the same brackets and if the original has no content except 272 * brackets, these will be deleted. 273 * --------------------------------------------------*/ 274 275 SwDoubleLinePortion::SwDoubleLinePortion( SwDoubleLinePortion& rDouble, 276 xub_StrLen nEnd ) : 277 SwMultiPortion( nEnd ), 278 pBracket( 0 ) 279 { 280 SetDirection( rDouble.GetDirection() ); 281 SetDouble(); 282 if( rDouble.GetBrackets() ) 283 { 284 SetBrackets( rDouble ); 285 // An empty multiportion needs no brackets. 286 // Notice: GetLen() might be zero, if the multiportion contains 287 // the second part of a field and the width might be zero, if 288 // it contains a note only. In this cases the brackets are okay. 289 // But if the length and the width are both zero, the portion 290 // is really empty. 291 if( rDouble.Width() == rDouble.BracketWidth() ) 292 rDouble.ClearBrackets(); 293 } 294 } 295 296 /*-----------------01.11.00 14:22------------------- 297 * SwDoubleLinePortion::SwDoubleLinePortion(..) 298 * This constructor uses the textattribut to get the right brackets. 299 * The textattribut could be a 2-line-attribute or a character- or 300 * internetstyle, which contains the 2-line-attribute. 301 * --------------------------------------------------*/ 302 303 SwDoubleLinePortion::SwDoubleLinePortion( const SwMultiCreator& rCreate, 304 xub_StrLen nEnd ) : SwMultiPortion( nEnd ), pBracket( new SwBracket() ) 305 { 306 SetDouble(); 307 const SvxTwoLinesItem* pTwo = (SvxTwoLinesItem*)rCreate.pItem; 308 if( pTwo ) 309 pBracket->nStart = 0; 310 else 311 { 312 const SwTxtAttr& rAttr = *rCreate.pAttr; 313 pBracket->nStart = *rAttr.GetStart(); 314 315 const SfxPoolItem * const pItem = 316 CharFmt::GetItem( rAttr, RES_CHRATR_TWO_LINES ); 317 if ( pItem ) 318 { 319 pTwo = static_cast<const SvxTwoLinesItem*>(pItem); 320 } 321 } 322 if( pTwo ) 323 { 324 pBracket->cPre = pTwo->GetStartBracket(); 325 pBracket->cPost = pTwo->GetEndBracket(); 326 } 327 else 328 { 329 pBracket->cPre = 0; 330 pBracket->cPost = 0; 331 } 332 sal_uInt8 nTmp = SW_SCRIPTS; 333 if( pBracket->cPre > 255 ) 334 { 335 String aTxt( pBracket->cPre ); 336 nTmp = SwScriptInfo::WhichFont( 0, &aTxt, 0 ); 337 } 338 pBracket->nPreScript = nTmp; 339 nTmp = SW_SCRIPTS; 340 if( pBracket->cPost > 255 ) 341 { 342 String aTxt( pBracket->cPost ); 343 nTmp = SwScriptInfo::WhichFont( 0, &aTxt, 0 ); 344 } 345 pBracket->nPostScript = nTmp; 346 347 if( !pBracket->cPre && !pBracket->cPost ) 348 { 349 delete pBracket; 350 pBracket = 0; 351 } 352 353 // double line portions have the same direction as the frame directions 354 if ( rCreate.nLevel % 2 ) 355 SetDirection( DIR_RIGHT2LEFT ); 356 else 357 SetDirection( DIR_LEFT2RIGHT ); 358 } 359 360 361 /*-----------------25.10.00 09:51------------------- 362 * SwMultiPortion::PaintBracket paints the wished bracket, 363 * if the multiportion has surrounding brackets. 364 * The X-position of the SwTxtPaintInfo will be modified: 365 * the open bracket sets position behind itself, 366 * the close bracket in front of itself. 367 * --------------------------------------------------*/ 368 369 void SwDoubleLinePortion::PaintBracket( SwTxtPaintInfo &rInf, 370 long nSpaceAdd, 371 sal_Bool bOpen ) const 372 { 373 sal_Unicode cCh = bOpen ? pBracket->cPre : pBracket->cPost; 374 if( !cCh ) 375 return; 376 KSHORT nChWidth = bOpen ? PreWidth() : PostWidth(); 377 if( !nChWidth ) 378 return; 379 if( !bOpen ) 380 rInf.X( rInf.X() + Width() - PostWidth() + 381 ( nSpaceAdd > 0 ? CalcSpacing( nSpaceAdd, rInf ) : 0 ) ); 382 383 SwBlankPortion aBlank( cCh, sal_True ); 384 aBlank.SetAscent( pBracket->nAscent ); 385 aBlank.Width( nChWidth ); 386 aBlank.Height( pBracket->nHeight ); 387 { 388 SwFont* pTmpFnt = new SwFont( *rInf.GetFont() ); 389 sal_uInt8 nAct = bOpen ? pBracket->nPreScript : pBracket->nPostScript; 390 if( SW_SCRIPTS > nAct ) 391 pTmpFnt->SetActual( nAct ); 392 pTmpFnt->SetProportion( 100 ); 393 SwFontSave aSave( rInf, pTmpFnt ); 394 aBlank.Paint( rInf ); 395 delete pTmpFnt; 396 } 397 if( bOpen ) 398 rInf.X( rInf.X() + PreWidth() ); 399 } 400 401 /*-----------------25.10.00 16:26------------------- 402 * SwDoubleLinePortion::SetBrackets creates the bracket-structur 403 * and fills it, if not both characters are 0x00. 404 * --------------------------------------------------*/ 405 406 void SwDoubleLinePortion::SetBrackets( const SwDoubleLinePortion& rDouble ) 407 { 408 if( rDouble.pBracket ) 409 { 410 pBracket = new SwBracket; 411 pBracket->cPre = rDouble.pBracket->cPre; 412 pBracket->cPost = rDouble.pBracket->cPost; 413 pBracket->nPreScript = rDouble.pBracket->nPreScript; 414 pBracket->nPostScript = rDouble.pBracket->nPostScript; 415 pBracket->nStart = rDouble.pBracket->nStart; 416 } 417 } 418 419 /*-----------------25.10.00 16:29------------------- 420 * SwDoubleLinePortion::FormatBrackets 421 * calculates the size of the brackets => pBracket, 422 * reduces the nMaxWidth-parameter ( minus bracket-width ) 423 * and moves the rInf-x-position behind the opening bracket. 424 * --------------------------------------------------*/ 425 426 void SwDoubleLinePortion::FormatBrackets( SwTxtFormatInfo &rInf, SwTwips& nMaxWidth ) 427 { 428 nMaxWidth -= rInf.X(); 429 SwFont* pTmpFnt = new SwFont( *rInf.GetFont() ); 430 pTmpFnt->SetProportion( 100 ); 431 pBracket->nAscent = 0; 432 pBracket->nHeight = 0; 433 if( pBracket->cPre ) 434 { 435 String aStr( pBracket->cPre ); 436 sal_uInt8 nActualScr = pTmpFnt->GetActual(); 437 if( SW_SCRIPTS > pBracket->nPreScript ) 438 pTmpFnt->SetActual( pBracket->nPreScript ); 439 SwFontSave aSave( rInf, pTmpFnt ); 440 SwPosSize aSize = rInf.GetTxtSize( aStr ); 441 pBracket->nAscent = rInf.GetAscent(); 442 pBracket->nHeight = aSize.Height(); 443 pTmpFnt->SetActual( nActualScr ); 444 if( nMaxWidth > aSize.Width() ) 445 { 446 pBracket->nPreWidth = aSize.Width(); 447 nMaxWidth -= aSize.Width(); 448 rInf.X( rInf.X() + aSize.Width() ); 449 } 450 else 451 { 452 pBracket->nPreWidth = 0; 453 nMaxWidth = 0; 454 } 455 } 456 else 457 pBracket->nPreWidth = 0; 458 if( pBracket->cPost ) 459 { 460 String aStr( pBracket->cPost ); 461 if( SW_SCRIPTS > pBracket->nPostScript ) 462 pTmpFnt->SetActual( pBracket->nPostScript ); 463 SwFontSave aSave( rInf, pTmpFnt ); 464 SwPosSize aSize = rInf.GetTxtSize( aStr ); 465 KSHORT nTmpAsc = rInf.GetAscent(); 466 if( nTmpAsc > pBracket->nAscent ) 467 { 468 pBracket->nHeight += nTmpAsc - pBracket->nAscent; 469 pBracket->nAscent = nTmpAsc; 470 } 471 if( aSize.Height() > pBracket->nHeight ) 472 pBracket->nHeight = aSize.Height(); 473 if( nMaxWidth > aSize.Width() ) 474 { 475 pBracket->nPostWidth = aSize.Width(); 476 nMaxWidth -= aSize.Width(); 477 } 478 else 479 { 480 pBracket->nPostWidth = 0; 481 nMaxWidth = 0; 482 } 483 } 484 else 485 pBracket->nPostWidth = 0; 486 nMaxWidth += rInf.X(); 487 } 488 489 /*-----------------26.10.00 10:36------------------- 490 * SwDoubleLinePortion::CalcBlanks 491 * calculates the number of blanks in each line and 492 * the difference of the width of the two lines. 493 * These results are used from the text adjustment. 494 * --------------------------------------------------*/ 495 496 void SwDoubleLinePortion::CalcBlanks( SwTxtFormatInfo &rInf ) 497 { 498 SwLinePortion* pPor = GetRoot().GetFirstPortion(); 499 xub_StrLen nNull = 0; 500 xub_StrLen nStart = rInf.GetIdx(); 501 SetTab1( sal_False ); 502 SetTab2( sal_False ); 503 for( nBlank1 = 0; pPor; pPor = pPor->GetPortion() ) 504 { 505 if( pPor->InTxtGrp() ) 506 nBlank1 = nBlank1 + ((SwTxtPortion*)pPor)->GetSpaceCnt( rInf, nNull ); 507 rInf.SetIdx( rInf.GetIdx() + pPor->GetLen() ); 508 if( pPor->InTabGrp() ) 509 SetTab1( sal_True ); 510 } 511 nLineDiff = GetRoot().Width(); 512 if( GetRoot().GetNext() ) 513 { 514 pPor = GetRoot().GetNext()->GetFirstPortion(); 515 nLineDiff -= GetRoot().GetNext()->Width(); 516 } 517 for( nBlank2 = 0; pPor; pPor = pPor->GetPortion() ) 518 { 519 if( pPor->InTxtGrp() ) 520 nBlank2 = nBlank2 + ((SwTxtPortion*)pPor)->GetSpaceCnt( rInf, nNull ); 521 rInf.SetIdx( rInf.GetIdx() + pPor->GetLen() ); 522 if( pPor->InTabGrp() ) 523 SetTab2( sal_True ); 524 } 525 rInf.SetIdx( nStart ); 526 } 527 528 long SwDoubleLinePortion::CalcSpacing( long nSpaceAdd, const SwTxtSizeInfo & ) const 529 { 530 return HasTabulator() ? 0 : GetSpaceCnt() * nSpaceAdd / SPACING_PRECISION_FACTOR; 531 } 532 533 /*-----------------01.11.00 14:29------------------- 534 * SwDoubleLinePortion::ChangeSpaceAdd(..) 535 * merges the spaces for text adjustment from the inner and outer part. 536 * Inside the doubleline portion the wider line has no spaceadd-array, the 537 * smaller line has such an array to reach width of the wider line. 538 * If the surrounding line has text adjustment and the doubleline portion 539 * contains no tabulator, it is necessary to create/manipulate the inner 540 * space arrays. 541 * --------------------------------------------------*/ 542 543 sal_Bool SwDoubleLinePortion::ChgSpaceAdd( SwLineLayout* pCurr, 544 long nSpaceAdd ) const 545 { 546 sal_Bool bRet = sal_False; 547 if( !HasTabulator() && nSpaceAdd > 0 ) 548 { 549 if( !pCurr->IsSpaceAdd() ) 550 { 551 // The wider line gets the spaceadd from the surrounding line direct 552 pCurr->CreateSpaceAdd(); 553 pCurr->SetLLSpaceAdd( nSpaceAdd, 0 ); 554 bRet = sal_True; 555 } 556 else 557 { 558 xub_StrLen nMyBlank = GetSmallerSpaceCnt(); 559 xub_StrLen nOther = GetSpaceCnt(); 560 SwTwips nMultiSpace = pCurr->GetLLSpaceAdd( 0 ) * nMyBlank + nOther * nSpaceAdd; 561 562 if( nMyBlank ) 563 nMultiSpace /= nMyBlank; 564 565 if( nMultiSpace < KSHRT_MAX * SPACING_PRECISION_FACTOR ) 566 { 567 // pCurr->SetLLSpaceAdd( nMultiSpace, 0 ); 568 // --> FME 2006-07-11 #i65711# SetLLSpaceAdd replaces the first value, 569 // instead we want to insert a new first value: 570 std::vector<long>* pVec = pCurr->GetpLLSpaceAdd(); 571 pVec->insert( pVec->begin(), nMultiSpace ); 572 // <-- 573 bRet = sal_True; 574 } 575 } 576 } 577 return bRet; 578 } 579 /*-----------------01.11.00 14:29------------------- 580 * SwDoubleLinePortion::ResetSpaceAdd(..) 581 * cancels the manipulation from SwDoubleLinePortion::ChangeSpaceAdd(..) 582 * --------------------------------------------------*/ 583 584 void SwDoubleLinePortion::ResetSpaceAdd( SwLineLayout* pCurr ) 585 { 586 pCurr->RemoveFirstLLSpaceAdd();; 587 if( !pCurr->GetLLSpaceAddCount() ) 588 pCurr->FinishSpaceAdd(); 589 } 590 591 SwDoubleLinePortion::~SwDoubleLinePortion() 592 { 593 delete pBracket; 594 } 595 596 /*-----------------13.11.00 14:50------------------- 597 * SwRubyPortion::SwRubyPortion(..) 598 * constructs a ruby portion, i.e. an additional text is displayed 599 * beside the main text, e.g. phonetic characters. 600 * --------------------------------------------------*/ 601 602 603 SwRubyPortion::SwRubyPortion( const SwRubyPortion& rRuby, xub_StrLen nEnd ) : 604 SwMultiPortion( nEnd ), 605 nRubyOffset( rRuby.GetRubyOffset() ), 606 nAdjustment( rRuby.GetAdjustment() ) 607 { 608 SetDirection( rRuby.GetDirection() ), 609 SetTop( rRuby.OnTop() ); 610 SetRuby(); 611 } 612 613 /*-----------------13.11.00 14:50------------------- 614 * SwRubyPortion::SwRubyPortion(..) 615 * constructs a ruby portion, i.e. an additional text is displayed 616 * beside the main text, e.g. phonetic characters. 617 * --------------------------------------------------*/ 618 619 SwRubyPortion::SwRubyPortion( const SwMultiCreator& rCreate, const SwFont& rFnt, 620 const IDocumentSettingAccess& rIDocumentSettingAccess, 621 xub_StrLen nEnd, xub_StrLen nOffs, 622 const sal_Bool* pForceRubyPos ) 623 : SwMultiPortion( nEnd ) 624 { 625 SetRuby(); 626 ASSERT( SW_MC_RUBY == rCreate.nId, "Ruby expected" ); 627 ASSERT( RES_TXTATR_CJK_RUBY == rCreate.pAttr->Which(), "Wrong attribute" ); 628 const SwFmtRuby& rRuby = rCreate.pAttr->GetRuby(); 629 nAdjustment = rRuby.GetAdjustment(); 630 nRubyOffset = nOffs; 631 632 // in grid mode we force the ruby text to the upper or lower line 633 if ( pForceRubyPos ) 634 SetTop( *pForceRubyPos ); 635 else 636 SetTop( ! rRuby.GetPosition() ); 637 638 const SwCharFmt* pFmt = ((SwTxtRuby*)rCreate.pAttr)->GetCharFmt(); 639 SwFont *pRubyFont; 640 if( pFmt ) 641 { 642 const SwAttrSet& rSet = pFmt->GetAttrSet(); 643 pRubyFont = new SwFont( rFnt ); 644 pRubyFont->SetDiffFnt( &rSet, &rIDocumentSettingAccess ); 645 646 // we do not allow a vertical font for the ruby text 647 pRubyFont->SetVertical( rFnt.GetOrientation() ); 648 } 649 else 650 pRubyFont = NULL; 651 652 String aStr( rRuby.GetText(), nOffs, STRING_LEN ); 653 SwFldPortion *pFld = new SwFldPortion( aStr, pRubyFont ); 654 pFld->SetNextOffset( nOffs ); 655 pFld->SetFollow( sal_True ); 656 657 if( OnTop() ) 658 GetRoot().SetPortion( pFld ); 659 else 660 { 661 GetRoot().SetNext( new SwLineLayout() ); 662 GetRoot().GetNext()->SetPortion( pFld ); 663 } 664 665 // ruby portions have the same direction as the frame directions 666 if ( rCreate.nLevel % 2 ) 667 { 668 // switch right and left ruby adjustment in rtl environment 669 if ( 0 == nAdjustment ) 670 nAdjustment = 2; 671 else if ( 2 == nAdjustment ) 672 nAdjustment = 0; 673 674 SetDirection( DIR_RIGHT2LEFT ); 675 } 676 else 677 SetDirection( DIR_LEFT2RIGHT ); 678 } 679 680 /*-----------------13.11.00 14:56------------------- 681 * SwRubyPortion::_Adjust(..) 682 * In ruby portion there are different alignments for 683 * the ruby text and the main text. 684 * Left, right, centered and two possibilities of block adjustment 685 * The block adjustment is realized by spacing between the characteres, 686 * either with a half space or no space in front of the first letter and 687 * a half space at the end of the last letter. 688 * Notice: the smaller line will be manipulated, normally it's the ruby line, 689 * but it could be the main text, too. 690 * If there is a tabulator in smaller line, no adjustment is possible. 691 * --------------------------------------------------*/ 692 693 void SwRubyPortion::_Adjust( SwTxtFormatInfo &rInf ) 694 { 695 SwTwips nLineDiff = GetRoot().Width() - GetRoot().GetNext()->Width(); 696 xub_StrLen nOldIdx = rInf.GetIdx(); 697 if( !nLineDiff ) 698 return; 699 SwLineLayout *pCurr; 700 if( nLineDiff < 0 ) 701 { // The first line has to be adjusted. 702 if( GetTab1() ) 703 return; 704 pCurr = &GetRoot(); 705 nLineDiff = -nLineDiff; 706 } 707 else 708 { // The second line has to be adjusted. 709 if( GetTab2() ) 710 return; 711 pCurr = GetRoot().GetNext(); 712 rInf.SetIdx( nOldIdx + GetRoot().GetLen() ); 713 } 714 KSHORT nLeft = 0; // the space in front of the first letter 715 KSHORT nRight = 0; // the space at the end of the last letter 716 sal_uInt16 nSub = 0; 717 switch ( nAdjustment ) 718 { 719 case 1: nRight = static_cast<sal_uInt16>(nLineDiff / 2); // no break 720 case 2: nLeft = static_cast<sal_uInt16>(nLineDiff - nRight); break; 721 case 3: nSub = 1; // no break 722 case 4: 723 { 724 xub_StrLen nCharCnt = 0; 725 SwLinePortion *pPor; 726 for( pPor = pCurr->GetFirstPortion(); pPor; pPor = pPor->GetPortion() ) 727 { 728 if( pPor->InTxtGrp() ) 729 ((SwTxtPortion*)pPor)->GetSpaceCnt( rInf, nCharCnt ); 730 rInf.SetIdx( rInf.GetIdx() + pPor->GetLen() ); 731 } 732 if( nCharCnt > nSub ) 733 { 734 SwTwips nCalc = nLineDiff / ( nCharCnt - nSub ); 735 short nTmp; 736 if( nCalc < SHRT_MAX ) 737 nTmp = -short(nCalc); 738 else 739 nTmp = SHRT_MIN; 740 741 pCurr->CreateSpaceAdd( SPACING_PRECISION_FACTOR * nTmp ); 742 nLineDiff -= nCalc * ( nCharCnt - 1 ); 743 } 744 if( nLineDiff > 1 ) 745 { 746 nRight = static_cast<sal_uInt16>(nLineDiff / 2); 747 nLeft = static_cast<sal_uInt16>(nLineDiff - nRight); 748 } 749 break; 750 } 751 default: ASSERT( sal_False, "New ruby adjustment" ); 752 } 753 if( nLeft || nRight ) 754 { 755 if( !pCurr->GetPortion() ) 756 pCurr->SetPortion( new SwTxtPortion( *pCurr ) ); 757 SwMarginPortion *pMarg = new SwMarginPortion( 0 ); 758 if( nLeft ) 759 { 760 pMarg->AddPrtWidth( nLeft ); 761 pMarg->SetPortion( pCurr->GetPortion() ); 762 pCurr->SetPortion( pMarg ); 763 } 764 if( nRight ) 765 { 766 pMarg = new SwMarginPortion( 0 ); 767 pMarg->AddPrtWidth( nRight ); 768 pCurr->FindLastPortion()->Append( pMarg ); 769 } 770 } 771 772 pCurr->Width( Width() ); 773 rInf.SetIdx( nOldIdx ); 774 } 775 776 /*-----------------08.11.00 14:14------------------- 777 * CalcRubyOffset() 778 * has to change the nRubyOffset, if there's a fieldportion 779 * in the phonetic line. 780 * The nRubyOffset is the position in the rubystring, where the 781 * next SwRubyPortion has start the displaying of the phonetics. 782 * --------------------------------------------------*/ 783 784 void SwRubyPortion::CalcRubyOffset() 785 { 786 const SwLineLayout *pCurr = &GetRoot(); 787 if( !OnTop() ) 788 { 789 pCurr = pCurr->GetNext(); 790 if( !pCurr ) 791 return; 792 } 793 const SwLinePortion *pPor = pCurr->GetFirstPortion(); 794 const SwFldPortion *pFld = NULL; 795 while( pPor ) 796 { 797 if( pPor->InFldGrp() ) 798 pFld = (SwFldPortion*)pPor; 799 pPor = pPor->GetPortion(); 800 } 801 if( pFld ) 802 { 803 if( pFld->HasFollow() ) 804 nRubyOffset = pFld->GetNextOffset(); 805 else 806 nRubyOffset = STRING_LEN; 807 } 808 } 809 810 /*-----------------13.10.00 16:22------------------- 811 * SwTxtSizeInfo::GetMultiCreator(..) 812 * If we (e.g. the position rPos) are inside a two-line-attribute or 813 * a ruby-attribute, the attribute will be returned in a SwMultiCreator-struct, 814 * otherwise the function returns zero. 815 * The rPos parameter is set to the end of the multiportion, 816 * normally this is the end of the attribute, 817 * but sometimes it is the start of another attribute, which finished or 818 * interrupts the first attribute. 819 * E.g. a ruby portion interrupts a 2-line-attribute, a 2-line-attribute 820 * with different brackets interrupts another 2-line-attribute. 821 * --------------------------------------------------*/ 822 823 /*-----------------13.11.00 15:38------------------- 824 * lcl_Has2Lines(..) 825 * is a little help function for GetMultiCreator(..) 826 * It extracts the 2-line-format from a 2-line-attribute or a character style. 827 * The rValue is set to sal_True, if the 2-line-attribute's value is set and 828 * no 2-line-format reference is passed. If there is a 2-line-format reference, 829 * then the rValue is set only, if the 2-line-attribute's value is set _and_ 830 * the 2-line-formats has the same brackets. 831 * --------------------------------------------------*/ 832 833 sal_Bool lcl_Has2Lines( const SwTxtAttr& rAttr, const SvxTwoLinesItem* &rpRef, 834 sal_Bool &rValue ) 835 { 836 const SfxPoolItem* pItem = CharFmt::GetItem( rAttr, RES_CHRATR_TWO_LINES ); 837 if( pItem ) 838 { 839 rValue = ((SvxTwoLinesItem*)pItem)->GetValue(); 840 if( !rpRef ) 841 rpRef = (SvxTwoLinesItem*)pItem; 842 else if( ((SvxTwoLinesItem*)pItem)->GetEndBracket() != 843 rpRef->GetEndBracket() || 844 ((SvxTwoLinesItem*)pItem)->GetStartBracket() != 845 rpRef->GetStartBracket() ) 846 rValue = sal_False; 847 return sal_True; 848 } 849 return sal_False; 850 } 851 852 /*-----------------16.02.01 16:39------------------- 853 * lcl_HasRotation(..) 854 * is a little help function for GetMultiCreator(..) 855 * It extracts the charrotation from a charrotate-attribute or a character style. 856 * The rValue is set to sal_True, if the charrotate-attribute's value is set and 857 * no charrotate-format reference is passed. 858 * If there is a charrotate-format reference, then the rValue is set only, 859 * if the charrotate-attribute's value is set _and_ identical 860 * to the charrotate-format's value. 861 * --------------------------------------------------*/ 862 863 sal_Bool lcl_HasRotation( const SwTxtAttr& rAttr, 864 const SvxCharRotateItem* &rpRef, sal_Bool &rValue ) 865 { 866 const SfxPoolItem* pItem = CharFmt::GetItem( rAttr, RES_CHRATR_ROTATE ); 867 if ( pItem ) 868 { 869 rValue = 0 != ((SvxCharRotateItem*)pItem)->GetValue(); 870 if( !rpRef ) 871 rpRef = (SvxCharRotateItem*)pItem; 872 else if( ((SvxCharRotateItem*)pItem)->GetValue() != 873 rpRef->GetValue() ) 874 rValue = sal_False; 875 return sal_True; 876 } 877 878 return sal_False; 879 } 880 881 SwMultiCreator* SwTxtSizeInfo::GetMultiCreator( xub_StrLen &rPos, 882 SwMultiPortion* pMulti ) const 883 { 884 SwScriptInfo& rSI = ((SwParaPortion*)GetParaPortion())->GetScriptInfo(); 885 886 // get the last embedding level 887 sal_uInt8 nCurrLevel; 888 if ( pMulti ) 889 { 890 ASSERT( pMulti->IsBidi(), "Nested MultiPortion is not BidiPortion" ) 891 // level associated with bidi-portion; 892 nCurrLevel = ((SwBidiPortion*)pMulti)->GetLevel(); 893 } 894 else 895 // no nested bidi portion required 896 nCurrLevel = GetTxtFrm()->IsRightToLeft() ? 1 : 0; 897 898 // check if there is a field at rPos: 899 sal_uInt8 nNextLevel = nCurrLevel; 900 sal_Bool bFldBidi = sal_False; 901 902 if ( CH_TXTATR_BREAKWORD == GetChar( rPos ) ) 903 { 904 bFldBidi = sal_True; 905 /* 906 // examining the script of the field text should be sufficient 907 // for 99% of all cases 908 XubString aTxt = GetTxtFrm()->GetTxtNode()->GetExpandTxt( rPos, 1 ); 909 910 if ( pBreakIt->GetBreakIter().is() && aTxt.Len() ) 911 { 912 sal_Bool bFldDir = ( i18n::ScriptType::COMPLEX == 913 pBreakIt->GetRealScriptOfText( aTxt, 0 ) ); 914 sal_Bool bCurrDir = ( 0 != ( nCurrLevel % 2 ) ); 915 if ( bFldDir != bCurrDir ) 916 { 917 nNextLevel = nCurrLevel + 1; 918 bFldBidi = sal_True; 919 } 920 }*/ 921 } 922 else 923 nNextLevel = rSI.DirType( rPos ); 924 925 if ( GetTxt().Len() != rPos && nNextLevel > nCurrLevel ) 926 { 927 rPos = bFldBidi ? rPos + 1 : rSI.NextDirChg( rPos, &nCurrLevel ); 928 if ( STRING_LEN == rPos ) 929 return NULL; 930 SwMultiCreator *pRet = new SwMultiCreator; 931 pRet->pItem = NULL; 932 pRet->pAttr = NULL; 933 pRet->nId = SW_MC_BIDI; 934 pRet->nLevel = nCurrLevel + 1; 935 return pRet; 936 } 937 938 // a bidi portion can only contain other bidi portions 939 if ( pMulti ) 940 return NULL; 941 942 const SvxCharRotateItem* pRotate = NULL; 943 const SfxPoolItem* pRotItem; 944 if( SFX_ITEM_SET == pFrm->GetTxtNode()->GetSwAttrSet(). 945 GetItemState( RES_CHRATR_ROTATE, sal_True, &pRotItem ) && 946 ((SvxCharRotateItem*)pRotItem)->GetValue() ) 947 pRotate = (SvxCharRotateItem*)pRotItem; 948 else 949 pRotItem = NULL; 950 const SvxTwoLinesItem* p2Lines = NULL; 951 const SfxPoolItem* pItem; 952 if( SFX_ITEM_SET == pFrm->GetTxtNode()->GetSwAttrSet(). 953 GetItemState( RES_CHRATR_TWO_LINES, sal_True, &pItem ) && 954 ((SvxTwoLinesItem*)pItem)->GetValue() ) 955 p2Lines = (SvxTwoLinesItem*)pItem; 956 else 957 pItem = NULL; 958 959 const SwpHints *pHints = pFrm->GetTxtNode()->GetpSwpHints(); 960 if( !pHints && !p2Lines && !pRotate ) 961 return NULL; 962 const SwTxtAttr *pRuby = NULL; 963 sal_Bool bTwo = sal_False; 964 sal_Bool bRot = sal_False; 965 sal_uInt16 n2Lines = USHRT_MAX; 966 sal_uInt16 nRotate = USHRT_MAX; 967 sal_uInt16 nCount = pHints ? pHints->Count() : 0; 968 sal_uInt16 i; 969 for( i = 0; i < nCount; ++i ) 970 { 971 const SwTxtAttr *pTmp = (*pHints)[i]; 972 xub_StrLen nStart = *pTmp->GetStart(); 973 if( rPos < nStart ) 974 break; 975 if( *pTmp->GetAnyEnd() > rPos ) 976 { 977 if( RES_TXTATR_CJK_RUBY == pTmp->Which() ) 978 pRuby = pTmp; 979 else 980 { 981 const SvxCharRotateItem* pRoTmp = NULL; 982 if( lcl_HasRotation( *pTmp, pRoTmp, bRot ) ) 983 { 984 nRotate = bRot ? i : nCount; 985 pRotate = pRoTmp; 986 } 987 const SvxTwoLinesItem* p2Tmp = NULL; 988 if( lcl_Has2Lines( *pTmp, p2Tmp, bTwo ) ) 989 { 990 n2Lines = bTwo ? i : nCount; 991 p2Lines = p2Tmp; 992 } 993 } 994 } 995 } 996 if( pRuby ) 997 { // The winner is ... a ruby attribute and so 998 // the end of the multiportion is the end of the ruby attribute. 999 rPos = *pRuby->GetEnd(); 1000 SwMultiCreator *pRet = new SwMultiCreator; 1001 pRet->pItem = NULL; 1002 pRet->pAttr = pRuby; 1003 pRet->nId = SW_MC_RUBY; 1004 pRet->nLevel = GetTxtFrm()->IsRightToLeft() ? 1 : 0; 1005 return pRet; 1006 } 1007 if( n2Lines < nCount || ( pItem && pItem == p2Lines && 1008 rPos < GetTxt().Len() ) ) 1009 { // The winner is a 2-line-attribute, 1010 // the end of the multiportion depends on the following attributes... 1011 SwMultiCreator *pRet = new SwMultiCreator; 1012 1013 // We note the endpositions of the 2-line attributes in aEnd as stack 1014 SvXub_StrLens aEnd; 1015 1016 // The bOn flag signs the state of the last 2-line attribute in the 1017 // aEnd-stack, it is compatible with the winner-attribute or 1018 // it interrupts the other attribute. 1019 sal_Bool bOn = sal_True; 1020 1021 if( n2Lines < nCount ) 1022 { 1023 pRet->pItem = NULL; 1024 pRet->pAttr = (*pHints)[n2Lines]; 1025 aEnd.push_front( *pRet->pAttr->GetEnd() ); 1026 if( pItem ) 1027 { 1028 aEnd.front() = GetTxt().Len(); 1029 bOn = ((SvxTwoLinesItem*)pItem)->GetEndBracket() == 1030 p2Lines->GetEndBracket() && 1031 ((SvxTwoLinesItem*)pItem)->GetStartBracket() == 1032 p2Lines->GetStartBracket(); 1033 } 1034 } 1035 else 1036 { 1037 pRet->pItem = pItem; 1038 pRet->pAttr = NULL; 1039 aEnd.push_front( GetTxt().Len() ); 1040 } 1041 pRet->nId = SW_MC_DOUBLE; 1042 pRet->nLevel = GetTxtFrm()->IsRightToLeft() ? 1 : 0; 1043 1044 // n2Lines is the index of the last 2-line-attribute, which contains 1045 // the actual position. 1046 i = 0; 1047 // At this moment we know that at position rPos the "winner"-attribute 1048 // causes a 2-line-portion. The end of the attribute is the end of the 1049 // portion, if there's no interrupting attribute. 1050 // There are two kinds of interruptors: 1051 // - ruby attributes stops the 2-line-attribute, the end of the 1052 // multiline is the start of the ruby attribute 1053 // - 2-line-attributes with value "Off" or with different brackets, 1054 // these attributes may interrupt the winner, but they could be 1055 // neutralized by another 2-line-attribute starting at the same 1056 // position with the same brackets as the winner-attribute. 1057 1058 // In the following loop rPos is the critical position and it will be 1059 // evaluated, if at rPos starts a interrupting or a maintaining 1060 // continuity attribute. 1061 while( i < nCount ) 1062 { 1063 const SwTxtAttr *pTmp = (*pHints)[i++]; 1064 if( *pTmp->GetAnyEnd() <= rPos ) 1065 continue; 1066 if( rPos < *pTmp->GetStart() ) 1067 { 1068 // If bOn is sal_False and the next attribute starts later than rPos 1069 // the winner attribute is interrupted at rPos. 1070 // If the start of the next atribute is behind the end of 1071 // the last attribute on the aEnd-stack, this is the endposition 1072 // on the stack is the end of the 2-line portion. 1073 if( !bOn || aEnd.back() < *pTmp->GetStart() ) 1074 break; 1075 // At this moment, bOn is sal_True and the next attribute starts 1076 // behind rPos, so we could move rPos to the next startpoint 1077 rPos = *pTmp->GetStart(); 1078 // We clean up the aEnd-stack, endpositions equal to rPos are 1079 // superfluous. 1080 while( !aEnd.empty() && aEnd.back() <= rPos ) 1081 { 1082 bOn = !bOn; 1083 aEnd.pop_back(); 1084 } 1085 // If the endstack is empty, we simulate an attribute with 1086 // state sal_True and endposition rPos 1087 if( aEnd.empty() ) 1088 { 1089 aEnd.push_front( rPos ); 1090 bOn = sal_True; 1091 } 1092 } 1093 // A ruby attribute stops the 2-line immediately 1094 if( RES_TXTATR_CJK_RUBY == pTmp->Which() ) 1095 return pRet; 1096 if( lcl_Has2Lines( *pTmp, p2Lines, bTwo ) ) 1097 { // We have an interesting attribute.. 1098 if( bTwo == bOn ) 1099 { // .. with the same state, so the last attribute could 1100 // be continued. 1101 if( aEnd.back() < *pTmp->GetEnd() ) 1102 aEnd.back() = *pTmp->GetEnd(); 1103 } 1104 else 1105 { // .. with a different state. 1106 bOn = bTwo; 1107 // If this is smaller than the last on the stack, we put 1108 // it on the stack. If it has the same endposition, the last 1109 // could be removed. 1110 if( aEnd.back() > *pTmp->GetEnd() ) 1111 aEnd.push_back( *pTmp->GetEnd() ); 1112 else if( aEnd.size() > 1 ) 1113 aEnd.pop_back(); 1114 else 1115 aEnd.back() = *pTmp->GetEnd(); 1116 } 1117 } 1118 } 1119 if( bOn && !aEnd.empty() ) 1120 rPos = aEnd.back(); 1121 return pRet; 1122 } 1123 if( nRotate < nCount || ( pRotItem && pRotItem == pRotate && 1124 rPos < GetTxt().Len() ) ) 1125 { // The winner is a rotate-attribute, 1126 // the end of the multiportion depends on the following attributes... 1127 SwMultiCreator *pRet = new SwMultiCreator; 1128 pRet->nId = SW_MC_ROTATE; 1129 1130 // We note the endpositions of the 2-line attributes in aEnd as stack 1131 SvXub_StrLens aEnd; 1132 1133 // The bOn flag signs the state of the last 2-line attribute in the 1134 // aEnd-stack, which could interrupts the winning rotation attribute. 1135 sal_Bool bOn = pItem ? sal_True : sal_False; 1136 aEnd.push_front( GetTxt().Len() ); 1137 // n2Lines is the index of the last 2-line-attribute, which contains 1138 // the actual position. 1139 i = 0; 1140 xub_StrLen n2Start = rPos; 1141 while( i < nCount ) 1142 { 1143 const SwTxtAttr *pTmp = (*pHints)[i++]; 1144 if( *pTmp->GetAnyEnd() <= n2Start ) 1145 continue; 1146 if( n2Start < *pTmp->GetStart() ) 1147 { 1148 if( bOn || aEnd.back() < *pTmp->GetStart() ) 1149 break; 1150 n2Start = *pTmp->GetStart(); 1151 while( !aEnd.empty() && aEnd.back() <= n2Start ) 1152 { 1153 bOn = !bOn; 1154 aEnd.pop_back(); 1155 } 1156 if( aEnd.empty() ) 1157 { 1158 aEnd.push_front( n2Start ); 1159 bOn = sal_False; 1160 } 1161 } 1162 // A ruby attribute stops immediately 1163 if( RES_TXTATR_CJK_RUBY == pTmp->Which() ) 1164 { 1165 bOn = sal_True; 1166 break; 1167 } 1168 p2Lines = NULL; 1169 if( lcl_Has2Lines( *pTmp, p2Lines, bTwo ) ) 1170 { 1171 if( bTwo == bOn ) 1172 { 1173 if( aEnd.back() < *pTmp->GetEnd() ) 1174 aEnd.back() = *pTmp->GetEnd(); 1175 } 1176 else 1177 { 1178 bOn = bTwo; 1179 if( aEnd.back() > *pTmp->GetEnd() ) 1180 aEnd.push_back( *pTmp->GetEnd() ); 1181 else if( aEnd.size() > 1 ) 1182 aEnd.pop_back(); 1183 else 1184 aEnd.back() = *pTmp->GetEnd(); 1185 } 1186 } 1187 } 1188 if( !bOn && !aEnd.empty() ) 1189 n2Start = aEnd.back(); 1190 1191 if( !aEnd.empty() ) 1192 aEnd.clear(); 1193 1194 bOn = sal_True; 1195 if( nRotate < nCount ) 1196 { 1197 pRet->pItem = NULL; 1198 pRet->pAttr = (*pHints)[nRotate]; 1199 aEnd.push_front( *pRet->pAttr->GetEnd() ); 1200 if( pRotItem ) 1201 { 1202 aEnd.front() = GetTxt().Len(); 1203 bOn = ((SvxCharRotateItem*)pRotItem)->GetValue() == 1204 pRotate->GetValue(); 1205 } 1206 } 1207 else 1208 { 1209 pRet->pItem = pRotItem; 1210 pRet->pAttr = NULL; 1211 aEnd.push_front( GetTxt().Len() ); 1212 } 1213 i = 0; 1214 while( i < nCount ) 1215 { 1216 const SwTxtAttr *pTmp = (*pHints)[i++]; 1217 if( *pTmp->GetAnyEnd() <= rPos ) 1218 continue; 1219 if( rPos < *pTmp->GetStart() ) 1220 { 1221 if( !bOn || aEnd.back() < *pTmp->GetStart() ) 1222 break; 1223 rPos = *pTmp->GetStart(); 1224 while( !aEnd.empty() && aEnd.back() <= rPos ) 1225 { 1226 bOn = !bOn; 1227 aEnd.pop_back(); 1228 } 1229 if( aEnd.empty() ) 1230 { 1231 aEnd.push_front( rPos ); 1232 bOn = sal_True; 1233 } 1234 } 1235 if( RES_TXTATR_CJK_RUBY == pTmp->Which() ) 1236 { 1237 bOn = sal_False; 1238 break; 1239 } 1240 if( lcl_HasRotation( *pTmp, pRotate, bTwo ) ) 1241 { 1242 if( bTwo == bOn ) 1243 { 1244 if( aEnd.back() < *pTmp->GetEnd() ) 1245 aEnd.back() = *pTmp->GetEnd(); 1246 } 1247 else 1248 { 1249 bOn = bTwo; 1250 if( aEnd.back() > *pTmp->GetEnd() ) 1251 aEnd.push_back( *pTmp->GetEnd() ); 1252 else if( aEnd.size() > 1 ) 1253 aEnd.pop_back(); 1254 else 1255 aEnd.back() = *pTmp->GetEnd(); 1256 } 1257 } 1258 } 1259 if( bOn && !aEnd.empty() ) 1260 rPos = aEnd.back(); 1261 if( rPos > n2Start ) 1262 rPos = n2Start; 1263 return pRet; 1264 } 1265 return NULL; 1266 } 1267 1268 /*-----------------01.11.00 14:52------------------- 1269 * SwSpaceManipulator 1270 * is a little helper class to manage the spaceadd-arrays of the text adjustment 1271 * during a PaintMultiPortion. 1272 * The constructor prepares the array for the first line of multiportion, 1273 * the SecondLine-function restores the values for the first line and prepares 1274 * the second line. 1275 * The destructor restores the values of the last manipulation. 1276 * --------------------------------------------------*/ 1277 1278 class SwSpaceManipulator 1279 { 1280 SwTxtPaintInfo& rInfo; 1281 SwMultiPortion& rMulti; 1282 std::vector<long>* pOldSpaceAdd; 1283 MSHORT nOldSpIdx; 1284 long nSpaceAdd; 1285 sal_Bool bSpaceChg : 1; 1286 sal_uInt8 nOldDir : 2; 1287 public: 1288 SwSpaceManipulator( SwTxtPaintInfo& rInf, SwMultiPortion& rMult ); 1289 ~SwSpaceManipulator(); 1290 void SecondLine(); 1291 inline long GetSpaceAdd() const { return nSpaceAdd; } 1292 }; 1293 1294 SwSpaceManipulator::SwSpaceManipulator( SwTxtPaintInfo& rInf, 1295 SwMultiPortion& rMult ) : 1296 rInfo( rInf ), rMulti( rMult ) 1297 { 1298 pOldSpaceAdd = rInfo.GetpSpaceAdd(); 1299 nOldSpIdx = rInfo.GetSpaceIdx(); 1300 nOldDir = rInfo.GetDirection(); 1301 rInfo.SetDirection( rMulti.GetDirection() ); 1302 bSpaceChg = sal_False; 1303 1304 if( rMulti.IsDouble() ) 1305 { 1306 nSpaceAdd = ( pOldSpaceAdd && !rMulti.HasTabulator() ) ? 1307 rInfo.GetSpaceAdd() : 0; 1308 if( rMulti.GetRoot().IsSpaceAdd() ) 1309 { 1310 rInfo.SetpSpaceAdd( rMulti.GetRoot().GetpLLSpaceAdd() ); 1311 rInfo.ResetSpaceIdx(); 1312 bSpaceChg = rMulti.ChgSpaceAdd( &rMulti.GetRoot(), nSpaceAdd ); 1313 } 1314 else if( rMulti.HasTabulator() ) 1315 rInfo.SetpSpaceAdd( NULL ); 1316 } 1317 else if ( ! rMulti.IsBidi() ) 1318 { 1319 rInfo.SetpSpaceAdd( rMulti.GetRoot().GetpLLSpaceAdd() ); 1320 rInfo.ResetSpaceIdx(); 1321 } 1322 } 1323 1324 void SwSpaceManipulator::SecondLine() 1325 { 1326 if( bSpaceChg ) 1327 { 1328 rInfo.RemoveFirstSpaceAdd(); 1329 bSpaceChg = sal_False; 1330 } 1331 SwLineLayout *pLay = rMulti.GetRoot().GetNext(); 1332 if( pLay->IsSpaceAdd() ) 1333 { 1334 rInfo.SetpSpaceAdd( pLay->GetpLLSpaceAdd() ); 1335 rInfo.ResetSpaceIdx(); 1336 bSpaceChg = rMulti.ChgSpaceAdd( pLay, nSpaceAdd ); 1337 } 1338 else 1339 { 1340 rInfo.SetpSpaceAdd( (!rMulti.IsDouble() || rMulti.HasTabulator() ) ? 1341 0 : pOldSpaceAdd ); 1342 rInfo.SetSpaceIdx( nOldSpIdx); 1343 } 1344 } 1345 1346 SwSpaceManipulator::~SwSpaceManipulator() 1347 { 1348 if( bSpaceChg ) 1349 { 1350 rInfo.RemoveFirstSpaceAdd(); 1351 bSpaceChg = sal_False; 1352 } 1353 rInfo.SetpSpaceAdd( pOldSpaceAdd ); 1354 rInfo.SetSpaceIdx( nOldSpIdx); 1355 rInfo.SetDirection( nOldDir ); 1356 } 1357 1358 /*-----------------13.10.00 16:24------------------- 1359 * SwTxtPainter::PaintMultiPortion manages the paint for a SwMultiPortion. 1360 * External, for the calling function, it seems to be a normal Paint-function, 1361 * internal it is like a SwTxtFrm::Paint with multiple DrawTextLines 1362 * --------------------------------------------------*/ 1363 1364 void SwTxtPainter::PaintMultiPortion( const SwRect &rPaint, 1365 SwMultiPortion& rMulti, const SwMultiPortion* pEnvPor ) 1366 { 1367 GETGRID( pFrm->FindPageFrm() ) 1368 const sal_Bool bHasGrid = pGrid && GetInfo().SnapToGrid(); 1369 sal_uInt16 nGridWidth = 0; 1370 sal_uInt16 nRubyHeight = 0; 1371 sal_Bool bRubyTop = sal_False; 1372 1373 if ( bHasGrid ) 1374 { 1375 nGridWidth = pGrid->GetBaseHeight(); 1376 nRubyHeight = pGrid->GetRubyHeight(); 1377 bRubyTop = ! pGrid->GetRubyTextBelow(); 1378 } 1379 1380 // do not allow grid mode for first line in ruby portion 1381 const sal_Bool bRubyInGrid = bHasGrid && rMulti.IsRuby(); 1382 1383 const sal_uInt16 nOldHeight = rMulti.Height(); 1384 const sal_Bool bOldGridModeAllowed = GetInfo().SnapToGrid(); 1385 1386 if ( bRubyInGrid ) 1387 { 1388 GetInfo().SetSnapToGrid( ! bRubyTop ); 1389 rMulti.Height( pCurr->Height() ); 1390 } 1391 1392 SwLayoutModeModifier aLayoutModeModifier( *GetInfo().GetOut() ); 1393 sal_uInt8 nEnvDir = 0; 1394 sal_uInt8 nThisDir = 0; 1395 sal_uInt8 nFrmDir = 0; 1396 if ( rMulti.IsBidi() ) 1397 { 1398 // these values are needed for the calculation of the x coordinate 1399 // and the layout mode 1400 ASSERT( ! pEnvPor || pEnvPor->IsBidi(), 1401 "Oh no, I expected a BidiPortion" ) 1402 nFrmDir = GetInfo().GetTxtFrm()->IsRightToLeft() ? 1 : 0; 1403 nEnvDir = pEnvPor ? ((SwBidiPortion*)pEnvPor)->GetLevel() % 2 : nFrmDir; 1404 nThisDir = ((SwBidiPortion&)rMulti).GetLevel() % 2; 1405 } 1406 1407 #if OSL_DEBUG_LEVEL > 1 1408 // only paint first level bidi portions 1409 if( rMulti.Width() > 1 && ! pEnvPor ) 1410 GetInfo().DrawViewOpt( rMulti, POR_FLD ); 1411 #endif 1412 1413 if ( bRubyInGrid ) 1414 rMulti.Height( nOldHeight ); 1415 1416 // do we have to repaint a post it portion? 1417 if( GetInfo().OnWin() && rMulti.GetPortion() && 1418 ! rMulti.GetPortion()->Width() ) 1419 rMulti.GetPortion()->PrePaint( GetInfo(), &rMulti ); 1420 1421 // old values must be saved and restored at the end 1422 xub_StrLen nOldLen = GetInfo().GetLen(); 1423 KSHORT nOldX = KSHORT(GetInfo().X()); 1424 long nOldY = GetInfo().Y(); 1425 xub_StrLen nOldIdx = GetInfo().GetIdx(); 1426 1427 SwSpaceManipulator aManip( GetInfo(), rMulti ); 1428 1429 SwFontSave *pFontSave; 1430 SwFont* pTmpFnt; 1431 1432 if( rMulti.IsDouble() ) 1433 { 1434 pTmpFnt = new SwFont( *GetInfo().GetFont() ); 1435 if( rMulti.IsDouble() ) 1436 { 1437 SetPropFont( 50 ); 1438 pTmpFnt->SetProportion( GetPropFont() ); 1439 } 1440 pFontSave = new SwFontSave( GetInfo(), pTmpFnt, this ); 1441 } 1442 else 1443 { 1444 pFontSave = NULL; 1445 pTmpFnt = NULL; 1446 } 1447 1448 if( rMulti.HasBrackets() ) 1449 { 1450 xub_StrLen nTmpOldIdx = GetInfo().GetIdx(); 1451 GetInfo().SetIdx(((SwDoubleLinePortion&)rMulti).GetBrackets()->nStart); 1452 SeekAndChg( GetInfo() ); 1453 ((SwDoubleLinePortion&)rMulti).PaintBracket( GetInfo(), 0, sal_True ); 1454 GetInfo().SetIdx( nTmpOldIdx ); 1455 } 1456 1457 KSHORT nTmpX = KSHORT(GetInfo().X()); 1458 1459 SwLineLayout* pLay = &rMulti.GetRoot();// the first line of the multiportion 1460 SwLinePortion* pPor = pLay->GetFirstPortion();//first portion of these line 1461 SwTwips nOfst = 0; 1462 1463 // GetInfo().Y() is the baseline from the surrounding line. We must switch 1464 // this temporary to the baseline of the inner lines of the multiportion. 1465 if( rMulti.HasRotation() ) 1466 { 1467 if( rMulti.IsRevers() ) 1468 { 1469 GetInfo().Y( nOldY - rMulti.GetAscent() ); 1470 nOfst = nTmpX + rMulti.Width(); 1471 } 1472 else 1473 { 1474 GetInfo().Y( nOldY - rMulti.GetAscent() + rMulti.Height() ); 1475 nOfst = nTmpX; 1476 } 1477 } 1478 else if ( rMulti.IsBidi() ) 1479 { 1480 // does the current bidi portion has the same direction 1481 // as its environment? 1482 if ( nEnvDir != nThisDir ) 1483 { 1484 // different directions, we have to adjust the x coordinate 1485 SwTwips nMultiWidth = rMulti.Width() + 1486 rMulti.CalcSpacing( GetInfo().GetSpaceAdd(), GetInfo() ); 1487 1488 if ( nFrmDir == nThisDir ) 1489 GetInfo().X( GetInfo().X() - nMultiWidth ); 1490 else 1491 GetInfo().X( GetInfo().X() + nMultiWidth ); 1492 } 1493 1494 nOfst = nOldY - rMulti.GetAscent(); 1495 1496 // set layout mode 1497 aLayoutModeModifier.Modify( nThisDir ); 1498 } 1499 else 1500 nOfst = nOldY - rMulti.GetAscent(); 1501 1502 sal_Bool bRest = pLay->IsRest(); 1503 sal_Bool bFirst = sal_True; 1504 1505 ASSERT( 0 == GetInfo().GetUnderFnt() || rMulti.IsBidi(), 1506 " Only BiDi portions are allowed to use the common underlining font" ) 1507 1508 do 1509 { 1510 if ( bHasGrid ) 1511 { 1512 if( rMulti.HasRotation() ) 1513 { 1514 const sal_uInt16 nAdjustment = ( pLay->Height() - pPor->Height() ) / 2 + 1515 pPor->GetAscent(); 1516 if( rMulti.IsRevers() ) 1517 GetInfo().X( nOfst - nAdjustment ); 1518 else 1519 GetInfo().X( nOfst + nAdjustment ); 1520 } 1521 else 1522 { 1523 // special treatment for ruby portions in grid mode 1524 SwTwips nAdjustment = 0; 1525 if ( rMulti.IsRuby() ) 1526 { 1527 if ( bRubyTop != ( pLay == &rMulti.GetRoot() ) ) 1528 // adjust base text 1529 nAdjustment = ( pCurr->Height() - nRubyHeight - pPor->Height() ) / 2; 1530 else if ( bRubyTop ) 1531 // adjust upper ruby text 1532 nAdjustment = nRubyHeight - pPor->Height(); 1533 // else adjust lower ruby text 1534 } 1535 1536 GetInfo().Y( nOfst + nAdjustment + pPor->GetAscent() ); 1537 } 1538 } 1539 else if( rMulti.HasRotation() ) 1540 { 1541 if( rMulti.IsRevers() ) 1542 GetInfo().X( nOfst - AdjustBaseLine( *pLay, pPor, 0, 0, sal_True ) ); 1543 else 1544 GetInfo().X( nOfst + AdjustBaseLine( *pLay, pPor ) ); 1545 } 1546 else 1547 GetInfo().Y( nOfst + AdjustBaseLine( *pLay, pPor ) ); 1548 1549 sal_Bool bSeeked = sal_True; 1550 GetInfo().SetLen( pPor->GetLen() ); 1551 1552 if( bRest && pPor->InFldGrp() && !pPor->GetLen() ) 1553 { 1554 if( ((SwFldPortion*)pPor)->HasFont() ) 1555 bSeeked = sal_False; 1556 else 1557 SeekAndChgBefore( GetInfo() ); 1558 } 1559 else if( pPor->InTxtGrp() || pPor->InFldGrp() || pPor->InTabGrp() ) 1560 SeekAndChg( GetInfo() ); 1561 else if ( !bFirst && pPor->IsBreakPortion() && GetInfo().GetOpt().IsParagraph() ) 1562 { 1563 if( GetRedln() ) 1564 SeekAndChg( GetInfo() ); 1565 else 1566 SeekAndChgBefore( GetInfo() ); 1567 } 1568 else 1569 bSeeked = sal_False; 1570 1571 SwLinePortion *pNext = pPor->GetPortion(); 1572 if(GetInfo().OnWin() && pNext && !pNext->Width() ) 1573 { 1574 if ( !bSeeked ) 1575 SeekAndChg( GetInfo() ); 1576 pNext->PrePaint( GetInfo(), pPor ); 1577 } 1578 1579 CheckSpecialUnderline( pPor ); 1580 SwUnderlineFont* pUnderLineFnt = GetInfo().GetUnderFnt(); 1581 if ( pUnderLineFnt ) 1582 { 1583 if ( rMulti.IsDouble() ) 1584 pUnderLineFnt->GetFont().SetProportion( 50 ); 1585 pUnderLineFnt->SetPos( GetInfo().GetPos() ); 1586 } 1587 1588 if ( rMulti.IsBidi() ) 1589 { 1590 // we do not allow any rotation inside a bidi portion 1591 SwFont* pTmpFont = GetInfo().GetFont(); 1592 pTmpFont->SetVertical( 0, GetInfo().GetTxtFrm()->IsVertical() ); 1593 } 1594 1595 if( pPor->IsMultiPortion() && ((SwMultiPortion*)pPor)->IsBidi() ) 1596 { 1597 // but we do allow nested bidi portions 1598 ASSERT( rMulti.IsBidi(), "Only nesting of bidi portions is allowed" ) 1599 PaintMultiPortion( rPaint, (SwMultiPortion&)*pPor, &rMulti ); 1600 } 1601 else 1602 pPor->Paint( GetInfo() ); 1603 1604 if( GetFnt()->IsURL() && pPor->InTxtGrp() ) 1605 GetInfo().NotifyURL( *pPor ); 1606 1607 bFirst &= !pPor->GetLen(); 1608 if( pNext || !pPor->IsMarginPortion() ) 1609 pPor->Move( GetInfo() ); 1610 1611 pPor = pNext; 1612 1613 // If there's no portion left, we go to the next line 1614 if( !pPor && pLay->GetNext() ) 1615 { 1616 pLay = pLay->GetNext(); 1617 pPor = pLay->GetFirstPortion(); 1618 bRest = pLay->IsRest(); 1619 aManip.SecondLine(); 1620 1621 // delete underline font 1622 delete GetInfo().GetUnderFnt(); 1623 GetInfo().SetUnderFnt( 0 ); 1624 1625 if( rMulti.HasRotation() ) 1626 { 1627 if( rMulti.IsRevers() ) 1628 { 1629 nOfst += pLay->Height(); 1630 GetInfo().Y( nOldY - rMulti.GetAscent() ); 1631 } 1632 else 1633 { 1634 nOfst -= pLay->Height(); 1635 GetInfo().Y( nOldY - rMulti.GetAscent() + rMulti.Height() ); 1636 } 1637 } 1638 else if ( bHasGrid && rMulti.IsRuby() ) 1639 { 1640 GetInfo().X( nTmpX ); 1641 if ( bRubyTop ) 1642 { 1643 nOfst += nRubyHeight; 1644 GetInfo().SetSnapToGrid( sal_True ); 1645 } 1646 else 1647 { 1648 nOfst += pCurr->Height() - nRubyHeight; 1649 GetInfo().SetSnapToGrid( sal_False ); 1650 } 1651 } else 1652 { 1653 GetInfo().X( nTmpX ); 1654 // We switch to the baseline of the next inner line 1655 nOfst += rMulti.GetRoot().Height(); 1656 } 1657 } 1658 } while( pPor ); 1659 1660 if ( bRubyInGrid ) 1661 GetInfo().SetSnapToGrid( bOldGridModeAllowed ); 1662 1663 // delete underline font 1664 if ( ! rMulti.IsBidi() ) 1665 { 1666 delete GetInfo().GetUnderFnt(); 1667 GetInfo().SetUnderFnt( 0 ); 1668 } 1669 1670 GetInfo().SetIdx( nOldIdx ); 1671 GetInfo().Y( nOldY ); 1672 1673 if( rMulti.HasBrackets() ) 1674 { 1675 xub_StrLen nTmpOldIdx = GetInfo().GetIdx(); 1676 GetInfo().SetIdx(((SwDoubleLinePortion&)rMulti).GetBrackets()->nStart); 1677 SeekAndChg( GetInfo() ); 1678 GetInfo().X( nOldX ); 1679 ((SwDoubleLinePortion&)rMulti).PaintBracket( GetInfo(), 1680 aManip.GetSpaceAdd(), sal_False ); 1681 GetInfo().SetIdx( nTmpOldIdx ); 1682 } 1683 // Restore the saved values 1684 GetInfo().X( nOldX ); 1685 GetInfo().SetLen( nOldLen ); 1686 delete pFontSave; 1687 delete pTmpFnt; 1688 SetPropFont( 0 ); 1689 } 1690 1691 sal_Bool lcl_ExtractFieldFollow( SwLineLayout* pLine, SwLinePortion* &rpFld ) 1692 { 1693 SwLinePortion* pLast = pLine; 1694 rpFld = pLine->GetPortion(); 1695 while( rpFld && !rpFld->InFldGrp() ) 1696 { 1697 pLast = rpFld; 1698 rpFld = rpFld->GetPortion(); 1699 } 1700 sal_Bool bRet = rpFld != 0; 1701 if( bRet ) 1702 { 1703 if( ((SwFldPortion*)rpFld)->IsFollow() ) 1704 { 1705 rpFld->Truncate(); 1706 pLast->SetPortion( NULL ); 1707 } 1708 else 1709 rpFld = NULL; 1710 } 1711 pLine->Truncate(); 1712 return bRet; 1713 } 1714 1715 /*---------------------------------------------------- 1716 * lcl_TruncateMultiPortion 1717 * If a multi portion completely has to go to the 1718 * next line, this function is called to trunctate 1719 * the rest of the remaining multi portion 1720 * --------------------------------------------------*/ 1721 1722 void lcl_TruncateMultiPortion( SwMultiPortion& rMulti, SwTxtFormatInfo& rInf, 1723 xub_StrLen nStartIdx ) 1724 { 1725 rMulti.GetRoot().Truncate(); 1726 rMulti.GetRoot().SetLen(0); 1727 rMulti.GetRoot().Width(0); 1728 // rMulti.CalcSize( *this, aInf ); 1729 if ( rMulti.GetRoot().GetNext() ) 1730 { 1731 rMulti.GetRoot().GetNext()->Truncate(); 1732 rMulti.GetRoot().GetNext()->SetLen( 0 ); 1733 rMulti.GetRoot().GetNext()->Width( 0 ); 1734 } 1735 rMulti.Width( 0 ); 1736 rMulti.SetLen(0); 1737 rInf.SetIdx( nStartIdx ); 1738 } 1739 1740 /*----------------------------------------------------------------------------- 1741 * SwTxtFormatter::BuildMultiPortion 1742 * manages the formatting of a SwMultiPortion. External, for the calling 1743 * function, it seems to be a normal Format-function, internal it is like a 1744 * SwTxtFrm::_Format with multiple BuildPortions 1745 *---------------------------------------------------------------------------*/ 1746 1747 sal_Bool SwTxtFormatter::BuildMultiPortion( SwTxtFormatInfo &rInf, 1748 SwMultiPortion& rMulti ) 1749 { 1750 SwTwips nMaxWidth = rInf.Width(); 1751 KSHORT nOldX = 0; 1752 1753 if( rMulti.HasBrackets() ) 1754 { 1755 xub_StrLen nOldIdx = rInf.GetIdx(); 1756 rInf.SetIdx( ((SwDoubleLinePortion&)rMulti).GetBrackets()->nStart ); 1757 SeekAndChg( rInf ); 1758 nOldX = KSHORT(GetInfo().X()); 1759 ((SwDoubleLinePortion&)rMulti).FormatBrackets( rInf, nMaxWidth ); 1760 rInf.SetIdx( nOldIdx ); 1761 } 1762 1763 SeekAndChg( rInf ); 1764 SwFontSave *pFontSave; 1765 if( rMulti.IsDouble() ) 1766 { 1767 SwFont* pTmpFnt = new SwFont( *rInf.GetFont() ); 1768 if( rMulti.IsDouble() ) 1769 { 1770 SetPropFont( 50 ); 1771 pTmpFnt->SetProportion( GetPropFont() ); 1772 } 1773 pFontSave = new SwFontSave( rInf, pTmpFnt, this ); 1774 } 1775 else 1776 pFontSave = NULL; 1777 1778 SwLayoutModeModifier aLayoutModeModifier( *GetInfo().GetOut() ); 1779 if ( rMulti.IsBidi() ) 1780 { 1781 // set layout mode 1782 aLayoutModeModifier.Modify( ! rInf.GetTxtFrm()->IsRightToLeft() ); 1783 } 1784 1785 SwTwips nTmpX = 0; 1786 1787 if( rMulti.HasRotation() ) 1788 { 1789 // For nMaxWidth we take the height of the body frame. 1790 // #i25067#: If the current frame is inside a table, we restrict 1791 // nMaxWidth to the current frame height, unless the frame size 1792 // attribute is set to variable size: 1793 1794 // We set nTmpX (which is used for portion calculating) to the 1795 // current Y value 1796 const SwPageFrm* pPage = pFrm->FindPageFrm(); 1797 ASSERT( pPage, "No page in frame!"); 1798 const SwLayoutFrm* pUpperFrm = pPage; 1799 1800 if ( pFrm->IsInTab() ) 1801 { 1802 pUpperFrm = pFrm->GetUpper(); 1803 while ( pUpperFrm && !pUpperFrm->IsCellFrm() ) 1804 pUpperFrm = pUpperFrm->GetUpper(); 1805 ASSERT( pUpperFrm, "pFrm is in table but does not have an upper cell frame" ) 1806 const SwTableLine* pLine = ((SwRowFrm*)pUpperFrm->GetUpper())->GetTabLine(); 1807 const SwFmtFrmSize& rFrmFmtSize = pLine->GetFrmFmt()->GetFrmSize(); 1808 if ( ATT_VAR_SIZE == rFrmFmtSize.GetHeightSizeType() ) 1809 pUpperFrm = pPage; 1810 } 1811 if ( pUpperFrm == pPage && !pFrm->IsInFtn() ) 1812 pUpperFrm = pPage->FindBodyCont(); 1813 1814 nMaxWidth = pUpperFrm ? 1815 ( rInf.GetTxtFrm()->IsVertical() ? 1816 pUpperFrm->Prt().Width() : 1817 pUpperFrm->Prt().Height() ) : 1818 USHRT_MAX; 1819 } 1820 else 1821 nTmpX = rInf.X(); 1822 1823 SwMultiPortion* pOldMulti = pMulti; 1824 1825 pMulti = &rMulti; 1826 SwLineLayout *pOldCurr = pCurr; 1827 xub_StrLen nOldStart = GetStart(); 1828 SwTwips nMinWidth = nTmpX + 1; 1829 SwTwips nActWidth = nMaxWidth; 1830 const xub_StrLen nStartIdx = rInf.GetIdx(); 1831 xub_StrLen nMultiLen = rMulti.GetLen(); 1832 1833 SwLinePortion *pFirstRest; 1834 SwLinePortion *pSecondRest; 1835 if( rMulti.IsFormatted() ) 1836 { 1837 if( !lcl_ExtractFieldFollow( &rMulti.GetRoot(), pFirstRest ) 1838 && rMulti.IsDouble() && rMulti.GetRoot().GetNext() ) 1839 lcl_ExtractFieldFollow( rMulti.GetRoot().GetNext(), pFirstRest ); 1840 if( !rMulti.IsDouble() && rMulti.GetRoot().GetNext() ) 1841 lcl_ExtractFieldFollow( rMulti.GetRoot().GetNext(), pSecondRest ); 1842 else 1843 pSecondRest = NULL; 1844 } 1845 else 1846 { 1847 pFirstRest = rMulti.GetRoot().GetPortion(); 1848 pSecondRest = rMulti.GetRoot().GetNext() ? 1849 rMulti.GetRoot().GetNext()->GetPortion() : NULL; 1850 if( pFirstRest ) 1851 rMulti.GetRoot().SetPortion( NULL ); 1852 if( pSecondRest ) 1853 rMulti.GetRoot().GetNext()->SetPortion( NULL ); 1854 rMulti.SetFormatted(); 1855 nMultiLen = nMultiLen - rInf.GetIdx(); 1856 } 1857 1858 // save some values 1859 const XubString* pOldTxt = &(rInf.GetTxt()); 1860 const SwTwips nOldPaintOfst = rInf.GetPaintOfst(); 1861 1862 XubString aMultiStr( rInf.GetTxt(), 0, nMultiLen + rInf.GetIdx() ); 1863 rInf.SetTxt( aMultiStr ); 1864 SwTxtFormatInfo aInf( rInf, rMulti.GetRoot(), nActWidth ); 1865 // Do we allow break cuts? The FirstMulti-Flag is evaluated during 1866 // line break determination. 1867 sal_Bool bFirstMulti = rInf.GetIdx() != rInf.GetLineStart(); 1868 1869 SwLinePortion *pNextFirst = NULL; 1870 SwLinePortion *pNextSecond = NULL; 1871 sal_Bool bRet = sal_False; 1872 1873 GETGRID( pFrm->FindPageFrm() ) 1874 const sal_Bool bHasGrid = pGrid && GRID_LINES_CHARS == pGrid->GetGridType(); 1875 1876 sal_uInt16 nGridWidth = 0; 1877 sal_uInt16 nRubyHeight = 0; 1878 sal_Bool bRubyTop = sal_False; 1879 1880 if ( bHasGrid ) 1881 { 1882 nGridWidth = pGrid->GetBaseHeight(); 1883 nRubyHeight = pGrid->GetRubyHeight(); 1884 bRubyTop = ! pGrid->GetRubyTextBelow(); 1885 } 1886 1887 do 1888 { 1889 pCurr = &rMulti.GetRoot(); 1890 nStart = nStartIdx; 1891 bRet = sal_False; 1892 FormatReset( aInf ); 1893 aInf.X( nTmpX ); 1894 aInf.Width( KSHORT(nActWidth) ); 1895 aInf.RealWidth( KSHORT(nActWidth) ); 1896 aInf.SetFirstMulti( bFirstMulti ); 1897 aInf.SetNumDone( rInf.IsNumDone() ); 1898 aInf.SetFtnDone( rInf.IsFtnDone() ); 1899 1900 if( pFirstRest ) 1901 { 1902 ASSERT( pFirstRest->InFldGrp(), "BuildMulti: Fieldrest expected"); 1903 SwFldPortion *pFld = 1904 ((SwFldPortion*)pFirstRest)->Clone( 1905 ((SwFldPortion*)pFirstRest)->GetExp() ); 1906 pFld->SetFollow( sal_True ); 1907 aInf.SetRest( pFld ); 1908 } 1909 aInf.SetRuby( rMulti.IsRuby() && rMulti.OnTop() ); 1910 1911 // in grid mode we temporarily have to disable the grid for the ruby line 1912 const sal_Bool bOldGridModeAllowed = GetInfo().SnapToGrid(); 1913 if ( bHasGrid && aInf.IsRuby() && bRubyTop ) 1914 aInf.SetSnapToGrid( sal_False ); 1915 1916 // If there's no more rubytext, then buildportion is forbidden 1917 if( pFirstRest || !aInf.IsRuby() ) 1918 BuildPortions( aInf ); 1919 1920 aInf.SetSnapToGrid( bOldGridModeAllowed ); 1921 1922 rMulti.CalcSize( *this, aInf ); 1923 pCurr->SetRealHeight( pCurr->Height() ); 1924 1925 if( rMulti.IsBidi() ) 1926 { 1927 pNextFirst = aInf.GetRest(); 1928 break; 1929 } 1930 1931 if( rMulti.HasRotation() && !rMulti.IsDouble() ) 1932 break; 1933 // second line has to be formatted 1934 else if( pCurr->GetLen()<nMultiLen || rMulti.IsRuby() || aInf.GetRest()) 1935 { 1936 xub_StrLen nFirstLen = pCurr->GetLen(); 1937 delete pCurr->GetNext(); 1938 pCurr->SetNext( new SwLineLayout() ); 1939 pCurr = pCurr->GetNext(); 1940 nStart = aInf.GetIdx(); 1941 aInf.X( nTmpX ); 1942 SwTxtFormatInfo aTmp( aInf, *pCurr, nActWidth ); 1943 if( rMulti.IsRuby() ) 1944 { 1945 aTmp.SetRuby( !rMulti.OnTop() ); 1946 pNextFirst = aInf.GetRest(); 1947 if( pSecondRest ) 1948 { 1949 ASSERT( pSecondRest->InFldGrp(), "Fieldrest expected"); 1950 SwFldPortion *pFld = ((SwFldPortion*)pSecondRest)->Clone( 1951 ((SwFldPortion*)pSecondRest)->GetExp() ); 1952 pFld->SetFollow( sal_True ); 1953 aTmp.SetRest( pFld ); 1954 } 1955 if( !rMulti.OnTop() && nFirstLen < nMultiLen ) 1956 bRet = sal_True; 1957 } 1958 else 1959 aTmp.SetRest( aInf.GetRest() ); 1960 aInf.SetRest( NULL ); 1961 1962 // in grid mode we temporarily have to disable the grid for the ruby line 1963 if ( bHasGrid && aTmp.IsRuby() && ! bRubyTop ) 1964 aTmp.SetSnapToGrid( sal_False ); 1965 1966 BuildPortions( aTmp ); 1967 1968 aTmp.SetSnapToGrid( bOldGridModeAllowed ); 1969 1970 rMulti.CalcSize( *this, aInf ); 1971 rMulti.GetRoot().SetRealHeight( rMulti.GetRoot().Height() ); 1972 pCurr->SetRealHeight( pCurr->Height() ); 1973 if( rMulti.IsRuby() ) 1974 { 1975 pNextSecond = aTmp.GetRest(); 1976 if( pNextFirst ) 1977 bRet = sal_True; 1978 } 1979 else 1980 pNextFirst = aTmp.GetRest(); 1981 if( ( !aTmp.IsRuby() && nFirstLen + pCurr->GetLen() < nMultiLen ) 1982 || aTmp.GetRest() ) 1983 // our guess for width of multiportion was too small, 1984 // text did not fit into multiportion 1985 bRet = sal_True; 1986 } 1987 if( rMulti.IsRuby() ) 1988 break; 1989 if( bRet ) 1990 { 1991 // our guess for multiportion width was too small, 1992 // we set min to act 1993 nMinWidth = nActWidth; 1994 nActWidth = ( 3 * nMaxWidth + nMinWidth + 3 ) / 4; 1995 if ( nActWidth == nMaxWidth && rInf.GetLineStart() == rInf.GetIdx() ) 1996 // we have too less space, we must allow break cuts 1997 // ( the first multi flag is considered during TxtPortion::_Format() ) 1998 bFirstMulti = sal_False; 1999 if( nActWidth <= nMinWidth ) 2000 break; 2001 } 2002 else 2003 { 2004 // For Solaris, this optimisation can causes trouble: 2005 // Setting this to the portion width ( = rMulti.Width() ) 2006 // can make GetTextBreak inside SwTxtGuess::Guess return to small 2007 // values. Therefore we add some extra twips. 2008 if( nActWidth > nTmpX + rMulti.Width() + 6 ) 2009 nActWidth = nTmpX + rMulti.Width() + 6; 2010 nMaxWidth = nActWidth; 2011 nActWidth = ( 3 * nMaxWidth + nMinWidth + 3 ) / 4; 2012 if( nActWidth >= nMaxWidth ) 2013 break; 2014 // we do not allow break cuts during formatting 2015 bFirstMulti = sal_True; 2016 } 2017 delete pNextFirst; 2018 pNextFirst = NULL; 2019 } while ( sal_True ); 2020 2021 pMulti = pOldMulti; 2022 2023 pCurr = pOldCurr; 2024 nStart = nOldStart; 2025 SetPropFont( 0 ); 2026 2027 rMulti.SetLen( rMulti.GetRoot().GetLen() + ( rMulti.GetRoot().GetNext() ? 2028 rMulti.GetRoot().GetNext()->GetLen() : 0 ) ); 2029 2030 if( rMulti.IsDouble() ) 2031 { 2032 ((SwDoubleLinePortion&)rMulti).CalcBlanks( rInf ); 2033 if( ((SwDoubleLinePortion&)rMulti).GetLineDiff() ) 2034 { 2035 SwLineLayout* pLine = &rMulti.GetRoot(); 2036 if( ((SwDoubleLinePortion&)rMulti).GetLineDiff() > 0 ) 2037 { 2038 rInf.SetIdx( nStartIdx + pLine->GetLen() ); 2039 pLine = pLine->GetNext(); 2040 } 2041 if( pLine ) 2042 { 2043 GetInfo().SetMulti( sal_True ); 2044 CalcNewBlock( pLine, NULL, rMulti.Width() ); 2045 GetInfo().SetMulti( sal_False ); 2046 } 2047 rInf.SetIdx( nStartIdx ); 2048 } 2049 if( ((SwDoubleLinePortion&)rMulti).GetBrackets() ) 2050 { 2051 rMulti.Width( rMulti.Width() + 2052 ((SwDoubleLinePortion&)rMulti).BracketWidth() ); 2053 GetInfo().X( nOldX ); 2054 } 2055 } 2056 else 2057 { 2058 rMulti.ActualizeTabulator(); 2059 if( rMulti.IsRuby() ) 2060 { 2061 ((SwRubyPortion&)rMulti).Adjust( rInf ); 2062 ((SwRubyPortion&)rMulti).CalcRubyOffset(); 2063 } 2064 } 2065 if( rMulti.HasRotation() ) 2066 { 2067 SwTwips nH = rMulti.Width(); 2068 SwTwips nAsc = rMulti.GetAscent() + ( nH - rMulti.Height() )/2; 2069 if( nAsc > nH ) 2070 nAsc = nH; 2071 else if( nAsc < 0 ) 2072 nAsc = 0; 2073 rMulti.Width( rMulti.Height() ); 2074 rMulti.Height( KSHORT(nH) ); 2075 rMulti.SetAscent( KSHORT(nAsc) ); 2076 bRet = ( rInf.GetPos().X() + rMulti.Width() > rInf.Width() ) && 2077 nStartIdx != rInf.GetLineStart(); 2078 } 2079 else if ( rMulti.IsBidi() ) 2080 { 2081 bRet = rMulti.GetLen() < nMultiLen || pNextFirst; 2082 } 2083 2084 // line break has to be performed! 2085 if( bRet ) 2086 { 2087 ASSERT( !pNextFirst || pNextFirst->InFldGrp(), 2088 "BuildMultiPortion: Surprising restportion, field expected" ); 2089 SwMultiPortion *pTmp; 2090 if( rMulti.IsDouble() ) 2091 pTmp = new SwDoubleLinePortion( ((SwDoubleLinePortion&)rMulti), 2092 nMultiLen + rInf.GetIdx() ); 2093 else if( rMulti.IsRuby() ) 2094 { 2095 ASSERT( !pNextSecond || pNextSecond->InFldGrp(), 2096 "BuildMultiPortion: Surprising restportion, field expected" ); 2097 2098 if ( rInf.GetIdx() == rInf.GetLineStart() ) 2099 { 2100 // the ruby portion has to be split in two portions 2101 pTmp = new SwRubyPortion( ((SwRubyPortion&)rMulti), 2102 nMultiLen + rInf.GetIdx() ); 2103 2104 if( pNextSecond ) 2105 { 2106 pTmp->GetRoot().SetNext( new SwLineLayout() ); 2107 pTmp->GetRoot().GetNext()->SetPortion( pNextSecond ); 2108 } 2109 pTmp->SetFollowFld(); 2110 } 2111 else 2112 { 2113 // we try to keep our ruby portion together 2114 lcl_TruncateMultiPortion( rMulti, rInf, nStartIdx ); 2115 pTmp = 0; 2116 } 2117 } 2118 else if( rMulti.HasRotation() ) 2119 { 2120 // we try to keep our rotated portion together 2121 lcl_TruncateMultiPortion( rMulti, rInf, nStartIdx ); 2122 pTmp = new SwRotatedPortion( nMultiLen + rInf.GetIdx(), 2123 rMulti.GetDirection() ); 2124 } 2125 // during a recursion of BuildMultiPortions we may not build 2126 // a new SwBidiPortion, this would cause a memory leak 2127 else if( rMulti.IsBidi() && ! pMulti ) 2128 { 2129 if ( ! rMulti.GetLen() ) 2130 lcl_TruncateMultiPortion( rMulti, rInf, nStartIdx ); 2131 2132 // If there is a HolePortion at the end of the bidi portion, 2133 // it has to be moved behind the bidi portion. Otherwise 2134 // the visual cursor travelling gets into trouble. 2135 SwLineLayout& aRoot = rMulti.GetRoot(); 2136 SwLinePortion* pPor = aRoot.GetFirstPortion(); 2137 while ( pPor ) 2138 { 2139 if ( pPor->GetPortion() && pPor->GetPortion()->IsHolePortion() ) 2140 { 2141 SwLinePortion* pHolePor = pPor->GetPortion(); 2142 pPor->SetPortion( NULL ); 2143 aRoot.SetLen( aRoot.GetLen() - pHolePor->GetLen() ); 2144 rMulti.SetLen( rMulti.GetLen() - pHolePor->GetLen() ); 2145 rMulti.SetPortion( pHolePor ); 2146 break; 2147 } 2148 pPor = pPor->GetPortion(); 2149 } 2150 2151 pTmp = new SwBidiPortion( nMultiLen + rInf.GetIdx(), 2152 ((SwBidiPortion&)rMulti).GetLevel() ); 2153 } 2154 else 2155 pTmp = NULL; 2156 2157 if ( ! rMulti.GetLen() && rInf.GetLast() ) 2158 { 2159 SeekAndChgBefore( rInf ); 2160 rInf.GetLast()->FormatEOL( rInf ); 2161 } 2162 2163 if( pNextFirst && pTmp ) 2164 { 2165 pTmp->SetFollowFld(); 2166 pTmp->GetRoot().SetPortion( pNextFirst ); 2167 } 2168 else 2169 // A follow field portion is still waiting. If nobody wants it, 2170 // we delete it. 2171 delete pNextFirst; 2172 2173 rInf.SetRest( pTmp ); 2174 } 2175 2176 rInf.SetTxt( *pOldTxt ); 2177 rInf.SetPaintOfst( nOldPaintOfst ); 2178 rInf.SetStop( aInf.IsStop() ); 2179 rInf.SetNumDone( sal_True ); 2180 rInf.SetFtnDone( sal_True ); 2181 SeekAndChg( rInf ); 2182 delete pFirstRest; 2183 delete pSecondRest; 2184 delete pFontSave; 2185 return bRet; 2186 } 2187 2188 /*-----------------08.11.00 09:29------------------- 2189 * SwTxtFormatter::MakeRestPortion(..) 2190 * When a fieldportion at the end of line breaks and needs a following 2191 * fieldportion in the next line, then the "restportion" of the formatinfo 2192 * has to be set. Normally this happens during the formatting of the first 2193 * part of the fieldportion. 2194 * But sometimes the formatting starts at the line with the following part, 2195 * exspecally when the following part is on the next page. 2196 * In this case the MakeRestPortion-function has to create the following part. 2197 * The first parameter is the line that contains possibly a first part 2198 * of a field. When the function finds such field part, it creates the right 2199 * restportion. This may be a multiportion, e.g. if the field is surrounded by 2200 * a doubleline- or ruby-portion. 2201 * The second parameter is the start index of the line. 2202 * --------------------------------------------------*/ 2203 2204 SwLinePortion* SwTxtFormatter::MakeRestPortion( const SwLineLayout* pLine, 2205 xub_StrLen nPosition ) 2206 { 2207 if( !nPosition ) 2208 return NULL; 2209 xub_StrLen nMultiPos = nPosition - pLine->GetLen(); 2210 const SwMultiPortion *pTmpMulti = NULL; 2211 const SwMultiPortion *pHelpMulti = NULL; 2212 const SwLinePortion* pPor = pLine->GetFirstPortion(); 2213 SwFldPortion *pFld = NULL; 2214 while( pPor ) 2215 { 2216 if( pPor->GetLen() ) 2217 { 2218 if( !pHelpMulti ) 2219 { 2220 nMultiPos = nMultiPos + pPor->GetLen(); 2221 pTmpMulti = NULL; 2222 } 2223 } 2224 if( pPor->InFldGrp() ) 2225 { 2226 if( !pHelpMulti ) 2227 pTmpMulti = NULL; 2228 pFld = (SwFldPortion*)pPor; 2229 } 2230 else if( pPor->IsMultiPortion() ) 2231 { 2232 ASSERT( !pHelpMulti || pHelpMulti->IsBidi(), 2233 "Nested multiportions are forbidden." ); 2234 2235 pFld = NULL; 2236 pTmpMulti = (SwMultiPortion*)pPor; 2237 } 2238 pPor = pPor->GetPortion(); 2239 // If the last portion is a multi-portion, we enter it 2240 // and look for a field portion inside. 2241 // If we are already in a multiportion, we could change to the 2242 // next line 2243 if( !pPor && pTmpMulti ) 2244 { 2245 if( pHelpMulti ) 2246 { // We're already inside the multiportion, let's take the second 2247 // line, if we are in a double line portion 2248 if( !pHelpMulti->IsRuby() ) 2249 pPor = pHelpMulti->GetRoot().GetNext(); 2250 pTmpMulti = NULL; 2251 } 2252 else 2253 { // Now we enter a multiportion, in a ruby portion we take the 2254 // main line, not the phonetic line, in a doublelineportion we 2255 // starts with the first line. 2256 pHelpMulti = pTmpMulti; 2257 nMultiPos = nMultiPos - pHelpMulti->GetLen(); 2258 if( pHelpMulti->IsRuby() && pHelpMulti->OnTop() ) 2259 pPor = pHelpMulti->GetRoot().GetNext(); 2260 else 2261 pPor = pHelpMulti->GetRoot().GetFirstPortion(); 2262 } 2263 } 2264 } 2265 if( pFld && !pFld->HasFollow() ) 2266 pFld = NULL; 2267 2268 SwLinePortion *pRest = NULL; 2269 if( pFld ) 2270 { 2271 const SwTxtAttr *pHint = GetAttr( nPosition - 1 ); 2272 if( pHint && pHint->Which() == RES_TXTATR_FIELD ) 2273 { 2274 pRest = NewFldPortion( GetInfo(), pHint ); 2275 if( pRest->InFldGrp() ) 2276 ((SwFldPortion*)pRest)->TakeNextOffset( pFld ); 2277 else 2278 { 2279 delete pRest; 2280 pRest = NULL; 2281 } 2282 } 2283 } 2284 if( !pHelpMulti ) 2285 return pRest; 2286 2287 nPosition = nMultiPos + pHelpMulti->GetLen(); 2288 SwMultiCreator* pCreate = GetInfo().GetMultiCreator( nMultiPos, 0 ); 2289 2290 if ( !pCreate ) 2291 { 2292 ASSERT( !pHelpMulti->GetLen(), "Multiportion without attribut?" ); 2293 if ( nMultiPos ) 2294 --nMultiPos; 2295 pCreate = GetInfo().GetMultiCreator( --nMultiPos, 0 ); 2296 } 2297 2298 if( pRest || nMultiPos > nPosition || ( pHelpMulti->IsRuby() && 2299 ((SwRubyPortion*)pHelpMulti)->GetRubyOffset() < STRING_LEN ) ) 2300 { 2301 SwMultiPortion* pTmp; 2302 if( pHelpMulti->IsDouble() ) 2303 pTmp = new SwDoubleLinePortion( *pCreate, nMultiPos ); 2304 else if( pHelpMulti->IsBidi() ) 2305 pTmp = new SwBidiPortion( nMultiPos, pCreate->nLevel ); 2306 else if( pHelpMulti->IsRuby() ) 2307 { 2308 sal_Bool bRubyTop; 2309 sal_Bool* pRubyPos = 0; 2310 2311 if ( GetInfo().SnapToGrid() ) 2312 { 2313 GETGRID( pFrm->FindPageFrm() ) 2314 if ( pGrid ) 2315 { 2316 bRubyTop = ! pGrid->GetRubyTextBelow(); 2317 pRubyPos = &bRubyTop; 2318 } 2319 } 2320 2321 pTmp = new SwRubyPortion( *pCreate, *GetInfo().GetFont(), 2322 *pFrm->GetTxtNode()->getIDocumentSettingAccess(), 2323 nMultiPos, ((SwRubyPortion*)pHelpMulti)->GetRubyOffset(), 2324 pRubyPos ); 2325 } 2326 else if( pHelpMulti->HasRotation() ) 2327 pTmp = new SwRotatedPortion( nMultiPos, pHelpMulti->GetDirection() ); 2328 else 2329 { 2330 delete pCreate; 2331 return pRest; 2332 } 2333 delete pCreate; 2334 pTmp->SetFollowFld(); 2335 if( pRest ) 2336 { 2337 SwLineLayout *pLay = &pTmp->GetRoot(); 2338 if( pTmp->IsRuby() && pTmp->OnTop() ) 2339 { 2340 pLay->SetNext( new SwLineLayout() ); 2341 pLay = pLay->GetNext(); 2342 } 2343 pLay->SetPortion( pRest ); 2344 } 2345 return pTmp; 2346 } 2347 return pRest; 2348 } 2349 2350 2351 2352 /*-----------------23.10.00 10:47------------------- 2353 * SwTxtCursorSave notes the start and current line of a SwTxtCursor, 2354 * sets them to the values for GetCrsrOfst inside a multiportion 2355 * and restores them in the destructor. 2356 * --------------------------------------------------*/ 2357 2358 SwTxtCursorSave::SwTxtCursorSave( SwTxtCursor* pTxtCursor, 2359 SwMultiPortion* pMulti, 2360 SwTwips nY, 2361 sal_uInt16& nX, 2362 xub_StrLen nCurrStart, 2363 long nSpaceAdd ) 2364 { 2365 pTxtCrsr = pTxtCursor; 2366 nStart = pTxtCursor->nStart; 2367 pTxtCursor->nStart = nCurrStart; 2368 pCurr = pTxtCursor->pCurr; 2369 pTxtCursor->pCurr = &pMulti->GetRoot(); 2370 while( pTxtCursor->Y() + pTxtCursor->GetLineHeight() < nY && 2371 pTxtCursor->Next() ) 2372 ; // nothing 2373 nWidth = pTxtCursor->pCurr->Width(); 2374 nOldProp = pTxtCursor->GetPropFont(); 2375 2376 if ( pMulti->IsDouble() || pMulti->IsBidi() ) 2377 { 2378 bSpaceChg = pMulti->ChgSpaceAdd( pTxtCursor->pCurr, nSpaceAdd ); 2379 2380 sal_uInt16 nSpaceCnt; 2381 if ( pMulti->IsDouble() ) 2382 { 2383 pTxtCursor->SetPropFont( 50 ); 2384 nSpaceCnt = ((SwDoubleLinePortion*)pMulti)->GetSpaceCnt(); 2385 } 2386 else 2387 { 2388 const xub_StrLen nOldIdx = pTxtCursor->GetInfo().GetIdx(); 2389 pTxtCursor->GetInfo().SetIdx ( nCurrStart ); 2390 nSpaceCnt = ((SwBidiPortion*)pMulti)->GetSpaceCnt(pTxtCursor->GetInfo()); 2391 pTxtCursor->GetInfo().SetIdx ( nOldIdx ); 2392 } 2393 2394 if( nSpaceAdd > 0 && !pMulti->HasTabulator() ) 2395 pTxtCursor->pCurr->Width( static_cast<sal_uInt16>(nWidth + nSpaceAdd * nSpaceCnt / SPACING_PRECISION_FACTOR ) ); 2396 2397 // For a BidiPortion we have to calculate the offset from the 2398 // end of the portion 2399 if ( nX && pMulti->IsBidi() ) 2400 nX = pTxtCursor->pCurr->Width() - nX; 2401 } 2402 else 2403 bSpaceChg = sal_False; 2404 } 2405 2406 SwTxtCursorSave::~SwTxtCursorSave() 2407 { 2408 if( bSpaceChg ) 2409 SwDoubleLinePortion::ResetSpaceAdd( pTxtCrsr->pCurr ); 2410 pTxtCrsr->pCurr->Width( KSHORT(nWidth) ); 2411 pTxtCrsr->pCurr = pCurr; 2412 pTxtCrsr->nStart = nStart; 2413 pTxtCrsr->SetPropFont( nOldProp ); 2414 } 2415 2416