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