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 #ifndef _COM_SUN_STAR_I18N_SCRIPTTYPE_HDL_ 27 #include <com/sun/star/i18n/ScriptType.hdl> 28 #endif 29 #include <vcl/outdev.hxx> 30 #include <IDocumentSettingAccess.hxx> 31 32 #include "frame.hxx" // CalcFlyAdjust() 33 #include "paratr.hxx" 34 #include "txtcfg.hxx" 35 #include "itrtxt.hxx" 36 #include "porglue.hxx" 37 #include "porlay.hxx" 38 #include "porfly.hxx" // CalcFlyAdjust() 39 #include "pordrop.hxx" // CalcFlyAdjust() 40 #include "pormulti.hxx" 41 #include <portab.hxx> 42 43 #define MIN_TAB_WIDTH 60 44 45 using namespace ::com::sun::star; 46 47 /************************************************************************* 48 * SwTxtAdjuster::FormatBlock() 49 *************************************************************************/ 50 51 void SwTxtAdjuster::FormatBlock( ) 52 { 53 // In der letzten Zeile gibt's keinen Blocksatz. 54 // Und bei Tabulatoren aus Tradition auch nicht. 55 // 7701: wenn Flys im Spiel sind, geht's weiter 56 57 const SwLinePortion *pFly = 0; 58 59 sal_Bool bSkip = !IsLastBlock() && 60 nStart + pCurr->GetLen() >= GetInfo().GetTxt().Len(); 61 62 // ????: mehrzeilige Felder sind fies: wir muessen kontrollieren, 63 // ob es noch andere Textportions im Absatz gibt. 64 if( bSkip ) 65 { 66 const SwLineLayout *pLay = pCurr->GetNext(); 67 while( pLay && !pLay->GetLen() ) 68 { 69 const SwLinePortion *pPor = pCurr->GetFirstPortion(); 70 while( pPor && bSkip ) 71 { 72 if( pPor->InTxtGrp() ) 73 bSkip = sal_False; 74 pPor = pPor->GetPortion(); 75 } 76 pLay = bSkip ? pLay->GetNext() : 0; 77 } 78 } 79 80 if( bSkip ) 81 { 82 if( !GetInfo().GetParaPortion()->HasFly() ) 83 { 84 if( IsLastCenter() ) 85 CalcFlyAdjust( pCurr ); 86 pCurr->FinishSpaceAdd(); 87 return; 88 } 89 else 90 { 91 const SwLinePortion *pTmpFly = NULL; 92 93 // 7701: beim letzten Fly soll Schluss sein 94 const SwLinePortion *pPos = pCurr->GetFirstPortion(); 95 while( pPos ) 96 { 97 // Ich suche jetzt den letzten Fly, hinter dem noch Text ist: 98 if( pPos->IsFlyPortion() ) 99 pTmpFly = pPos; // Ein Fly wurde gefunden 100 else if ( pTmpFly && pPos->InTxtGrp() ) 101 { 102 pFly = pTmpFly; // Ein Fly mit nachfolgendem Text! 103 pTmpFly = NULL; 104 } 105 pPos = pPos->GetPortion(); 106 } 107 // 8494: Wenn keiner gefunden wurde, ist sofort Schluss! 108 if( !pFly ) 109 { 110 if( IsLastCenter() ) 111 CalcFlyAdjust( pCurr ); 112 pCurr->FinishSpaceAdd(); 113 return; 114 } 115 } 116 } 117 118 const xub_StrLen nOldIdx = GetInfo().GetIdx(); 119 GetInfo().SetIdx( nStart ); 120 CalcNewBlock( pCurr, pFly ); 121 GetInfo().SetIdx( nOldIdx ); 122 GetInfo().GetParaPortion()->GetRepaint()->SetOfst(0); 123 } 124 125 /************************************************************************* 126 * lcl_CheckKashidaPositions() 127 *************************************************************************/ 128 bool lcl_CheckKashidaPositions( SwScriptInfo& rSI, SwTxtSizeInfo& rInf, SwTxtIter& rItr, 129 xub_StrLen& nKashidas, xub_StrLen& nGluePortion ) 130 { 131 // i60594 validate Kashida justification 132 xub_StrLen nIdx = rItr.GetStart(); 133 xub_StrLen nEnd = rItr.GetEnd(); 134 135 // Note on calling KashidaJustify(): 136 // Kashida positions may be marked as invalid. Therefore KashidaJustify may return the clean 137 // total number of kashida positions, or the number of kashida positions after some positions 138 // have been dropped. 139 // Here we want the clean total, which is OK: We have called ClearKashidaInvalid() before. 140 nKashidas = rSI.KashidaJustify ( 0, 0, rItr.GetStart(), rItr.GetLength(), 0 ); 141 142 if (!nKashidas) // nothing to do 143 return true; 144 145 // kashida positions found in SwScriptInfo are not necessarily valid in every font 146 // if two characters are replaced by a ligature glyph, there will be no place for a kashida 147 xub_StrLen* pKashidaPos = new xub_StrLen [ nKashidas ]; 148 xub_StrLen* pKashidaPosDropped = new xub_StrLen [ nKashidas ]; 149 rSI.GetKashidaPositions ( nIdx, rItr.GetLength(), pKashidaPos ); 150 xub_StrLen nKashidaIdx = 0; 151 while ( nKashidas && nIdx < nEnd ) 152 { 153 rItr.SeekAndChgAttrIter( nIdx, rInf.GetOut() ); 154 xub_StrLen nNext = rItr.GetNextAttr(); 155 156 // is there also a script change before? 157 // if there is, nNext should point to the script change 158 xub_StrLen nNextScript = rSI.NextScriptChg( nIdx ); 159 if( nNextScript < nNext ) 160 nNext = nNextScript; 161 162 if ( nNext == STRING_LEN || nNext > nEnd ) 163 nNext = nEnd; 164 xub_StrLen nKashidasInAttr = rSI.KashidaJustify ( 0, 0, nIdx, nNext - nIdx ); 165 if ( nKashidasInAttr ) 166 { 167 xub_StrLen nKashidasDropped = 0; 168 if ( !SwScriptInfo::IsArabicText( rInf.GetTxt(), nIdx, nNext - nIdx ) ) 169 { 170 nKashidasDropped = nKashidasInAttr; 171 nKashidas -= nKashidasDropped; 172 } 173 else 174 { 175 sal_uLong nOldLayout = rInf.GetOut()->GetLayoutMode(); 176 rInf.GetOut()->SetLayoutMode ( nOldLayout | TEXT_LAYOUT_BIDI_RTL ); 177 nKashidasDropped = rInf.GetOut()->ValidateKashidas ( rInf.GetTxt(), nIdx, nNext - nIdx, 178 nKashidasInAttr, pKashidaPos + nKashidaIdx, 179 pKashidaPosDropped ); 180 rInf.GetOut()->SetLayoutMode ( nOldLayout ); 181 if ( nKashidasDropped ) 182 { 183 rSI.MarkKashidasInvalid ( nKashidasDropped, pKashidaPosDropped ); 184 nKashidas -= nKashidasDropped; 185 nGluePortion -= nKashidasDropped; 186 } 187 } 188 nKashidaIdx += nKashidasInAttr; 189 } 190 nIdx = nNext; 191 } 192 delete[] pKashidaPos; 193 delete[] pKashidaPosDropped; 194 195 // return false if all kashidas have been eliminated 196 return (nKashidas > 0); 197 } 198 199 /************************************************************************* 200 * lcl_CheckKashidaWidth() 201 *************************************************************************/ 202 bool lcl_CheckKashidaWidth ( SwScriptInfo& rSI, SwTxtSizeInfo& rInf, SwTxtIter& rItr, xub_StrLen& nKashidas, 203 xub_StrLen& nGluePortion, const long nGluePortionWidth, long& nSpaceAdd ) 204 { 205 // check kashida width 206 // if width is smaller than minimal kashida width allowed by fonts in the current line 207 // drop one kashida after the other until kashida width is OK 208 bool bAddSpaceChanged; 209 while ( nKashidas ) 210 { 211 bAddSpaceChanged = false; 212 xub_StrLen nIdx = rItr.GetStart(); 213 xub_StrLen nEnd = rItr.GetEnd(); 214 while ( nIdx < nEnd ) 215 { 216 rItr.SeekAndChgAttrIter( nIdx, rInf.GetOut() ); 217 xub_StrLen nNext = rItr.GetNextAttr(); 218 219 // is there also a script change before? 220 // if there is, nNext should point to the script change 221 xub_StrLen nNextScript = rSI.NextScriptChg( nIdx ); 222 if( nNextScript < nNext ) 223 nNext = nNextScript; 224 225 if ( nNext == STRING_LEN || nNext > nEnd ) 226 nNext = nEnd; 227 xub_StrLen nKashidasInAttr = rSI.KashidaJustify ( 0, 0, nIdx, nNext - nIdx ); 228 229 long nFontMinKashida = rInf.GetOut()->GetMinKashida(); 230 if ( nFontMinKashida && nKashidasInAttr && SwScriptInfo::IsArabicText( rInf.GetTxt(), nIdx, nNext - nIdx ) ) 231 { 232 xub_StrLen nKashidasDropped = 0; 233 while ( nKashidas && nGluePortion && nKashidasInAttr && 234 nSpaceAdd / SPACING_PRECISION_FACTOR < nFontMinKashida ) 235 { 236 --nGluePortion; 237 --nKashidas; 238 --nKashidasInAttr; 239 ++nKashidasDropped; 240 if( !nKashidas || !nGluePortion ) // nothing left, return false to 241 return false; // do regular blank justification 242 243 nSpaceAdd = nGluePortionWidth / nGluePortion; 244 bAddSpaceChanged = true; 245 } 246 if( nKashidasDropped ) 247 rSI.MarkKashidasInvalid( nKashidasDropped, nIdx, nNext - nIdx ); 248 } 249 if ( bAddSpaceChanged ) 250 break; // start all over again 251 nIdx = nNext; 252 } 253 if ( !bAddSpaceChanged ) 254 break; // everything was OK 255 } 256 return true; 257 } 258 259 /************************************************************************* 260 * SwTxtAdjuster::CalcNewBlock() 261 * 262 * CalcNewBlock() darf erst nach CalcLine() gerufen werden ! 263 * Aufgespannt wird immer zwischen zwei RandPortions oder FixPortions 264 * (Tabs und Flys). Dabei werden die Glues gezaehlt und ExpandBlock gerufen. 265 *************************************************************************/ 266 267 void SwTxtAdjuster::CalcNewBlock( SwLineLayout *pCurrent, 268 const SwLinePortion *pStopAt, SwTwips nReal, bool bSkipKashida ) 269 { 270 ASSERT( GetInfo().IsMulti() || SVX_ADJUST_BLOCK == GetAdjust(), 271 "CalcNewBlock: Why?" ); 272 ASSERT( pCurrent->Height(), "SwTxtAdjuster::CalcBlockAdjust: missing CalcLine()" ); 273 274 pCurrent->InitSpaceAdd(); 275 xub_StrLen nGluePortion = 0; 276 xub_StrLen nCharCnt = 0; 277 MSHORT nSpaceIdx = 0; 278 279 // i60591: hennerdrews 280 SwScriptInfo& rSI = GetInfo().GetParaPortion()->GetScriptInfo(); 281 SwTxtSizeInfo aInf ( GetTxtFrm() ); 282 SwTxtIter aItr ( GetTxtFrm(), &aInf ); 283 284 if ( rSI.CountKashida() ) 285 { 286 while (aItr.GetCurr() != pCurrent && aItr.GetNext()) 287 aItr.Next(); 288 289 if( bSkipKashida ) 290 { 291 rSI.SetNoKashidaLine ( aItr.GetStart(), aItr.GetLength()); 292 } 293 else 294 { 295 rSI.ClearKashidaInvalid ( aItr.GetStart(), aItr.GetLength() ); 296 rSI.ClearNoKashidaLine( aItr.GetStart(), aItr.GetLength() ); 297 } 298 } 299 300 // Nicht vergessen: 301 // CalcRightMargin() setzt pCurrent->Width() auf die Zeilenbreite ! 302 if (!bSkipKashida) 303 CalcRightMargin( pCurrent, nReal ); 304 305 // --> FME 2005-06-08 #i49277# 306 const sal_Bool bDoNotJustifyLinesWithManualBreak = 307 GetTxtFrm()->GetNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::DO_NOT_JUSTIFY_LINES_WITH_MANUAL_BREAK); 308 // <-- 309 310 SwLinePortion *pPos = pCurrent->GetPortion(); 311 312 while( pPos ) 313 { 314 if ( bDoNotJustifyLinesWithManualBreak && 315 pPos->IsBreakPortion() && !IsLastBlock() ) 316 { 317 pCurrent->FinishSpaceAdd(); 318 break; 319 } 320 321 if ( pPos->InTxtGrp() ) 322 nGluePortion = nGluePortion + ((SwTxtPortion*)pPos)->GetSpaceCnt( GetInfo(), nCharCnt ); 323 else if( pPos->IsMultiPortion() ) 324 { 325 SwMultiPortion* pMulti = (SwMultiPortion*)pPos; 326 // a multiportion with a tabulator inside breaks the text adjustment 327 // a ruby portion will not be stretched by text adjustment 328 // a double line portion takes additional space for each blank 329 // in the wider line 330 if( pMulti->HasTabulator() ) 331 { 332 if ( nSpaceIdx == pCurrent->GetLLSpaceAddCount() ) 333 pCurrent->SetLLSpaceAdd( 0, nSpaceIdx ); 334 335 nSpaceIdx++; 336 nGluePortion = 0; 337 nCharCnt = 0; 338 } 339 else if( pMulti->IsDouble() ) 340 nGluePortion = nGluePortion + ((SwDoubleLinePortion*)pMulti)->GetSpaceCnt(); 341 else if ( pMulti->IsBidi() ) 342 nGluePortion = nGluePortion + ((SwBidiPortion*)pMulti)->GetSpaceCnt( GetInfo() ); // i60594 343 } 344 345 if( pPos->InGlueGrp() ) 346 { 347 if( pPos->InFixMargGrp() ) 348 { 349 if ( nSpaceIdx == pCurrent->GetLLSpaceAddCount() ) 350 pCurrent->SetLLSpaceAdd( 0, nSpaceIdx ); 351 352 const long nGluePortionWidth = static_cast<SwGluePortion*>(pPos)->GetPrtGlue() * 353 SPACING_PRECISION_FACTOR; 354 355 xub_StrLen nKashidas = 0; 356 if( nGluePortion && rSI.CountKashida() && !bSkipKashida ) 357 { 358 // kashida positions found in SwScriptInfo are not necessarily valid in every font 359 // if two characters are replaced by a ligature glyph, there will be no place for a kashida 360 if ( !lcl_CheckKashidaPositions ( rSI, aInf, aItr, nKashidas, nGluePortion )) 361 { 362 // all kashida positions are invalid 363 // do regular blank justification 364 pCurrent->FinishSpaceAdd(); 365 GetInfo().SetIdx( nStart ); 366 CalcNewBlock( pCurrent, pStopAt, nReal, true ); 367 return; 368 } 369 } 370 371 if( nGluePortion ) 372 { 373 long nSpaceAdd = nGluePortionWidth / nGluePortion; 374 375 // i60594 376 if( rSI.CountKashida() && !bSkipKashida ) 377 { 378 if( !lcl_CheckKashidaWidth( rSI, aInf, aItr, nKashidas, nGluePortion, nGluePortionWidth, nSpaceAdd )) 379 { 380 // no kashidas left 381 // do regular blank justification 382 pCurrent->FinishSpaceAdd(); 383 GetInfo().SetIdx( nStart ); 384 CalcNewBlock( pCurrent, pStopAt, nReal, true ); 385 return; 386 } 387 } 388 389 pCurrent->SetLLSpaceAdd( nSpaceAdd , nSpaceIdx ); 390 pPos->Width( ( (SwGluePortion*)pPos )->GetFixWidth() ); 391 } 392 else if ( IsOneBlock() && nCharCnt > 1 ) 393 { 394 const long nSpaceAdd = - nGluePortionWidth / ( nCharCnt - 1 ); 395 pCurrent->SetLLSpaceAdd( nSpaceAdd, nSpaceIdx ); 396 pPos->Width( ( (SwGluePortion*)pPos )->GetFixWidth() ); 397 } 398 399 nSpaceIdx++; 400 nGluePortion = 0; 401 nCharCnt = 0; 402 } 403 else 404 ++nGluePortion; 405 } 406 GetInfo().SetIdx( GetInfo().GetIdx() + pPos->GetLen() ); 407 if ( pPos == pStopAt ) 408 { 409 pCurrent->SetLLSpaceAdd( 0, nSpaceIdx ); 410 break; 411 } 412 pPos = pPos->GetPortion(); 413 } 414 } 415 416 /************************************************************************* 417 * SwTxtAdjuster::CalcKanaAdj() 418 *************************************************************************/ 419 420 SwTwips SwTxtAdjuster::CalcKanaAdj( SwLineLayout* pCurrent ) 421 { 422 ASSERT( pCurrent->Height(), "SwTxtAdjuster::CalcBlockAdjust: missing CalcLine()" ); 423 ASSERT( !pCurrent->GetpKanaComp(), "pKanaComp already exists!!" ); 424 425 SvUShorts *pNewKana = new SvUShorts; 426 pCurrent->SetKanaComp( pNewKana ); 427 428 const sal_uInt16 nNull = 0; 429 MSHORT nKanaIdx = 0; 430 long nKanaDiffSum = 0; 431 SwTwips nRepaintOfst = 0; 432 SwTwips nX = 0; 433 sal_Bool bNoCompression = sal_False; 434 435 // Nicht vergessen: 436 // CalcRightMargin() setzt pCurrent->Width() auf die Zeilenbreite ! 437 CalcRightMargin( pCurrent, 0 ); 438 439 SwLinePortion* pPos = pCurrent->GetPortion(); 440 441 while( pPos ) 442 { 443 if ( pPos->InTxtGrp() ) 444 { 445 // get maximum portion width from info structure, calculated 446 // during text formatting 447 sal_uInt16 nMaxWidthDiff = GetInfo().GetMaxWidthDiff( (sal_uLong)pPos ); 448 449 // check, if information is stored under other key 450 if ( !nMaxWidthDiff && pPos == pCurrent->GetFirstPortion() ) 451 nMaxWidthDiff = GetInfo().GetMaxWidthDiff( (sal_uLong)pCurrent ); 452 453 // calculate difference between portion width and max. width 454 nKanaDiffSum += nMaxWidthDiff; 455 456 // we store the beginning of the first compressable portion 457 // for repaint 458 if ( nMaxWidthDiff && !nRepaintOfst ) 459 nRepaintOfst = nX + GetLeftMargin(); 460 } 461 else if( pPos->InGlueGrp() && pPos->InFixMargGrp() ) 462 { 463 if ( nKanaIdx == pCurrent->GetKanaComp().Count() ) 464 pCurrent->GetKanaComp().Insert( nNull, nKanaIdx ); 465 466 sal_uInt16 nRest; 467 468 if ( pPos->InTabGrp() ) 469 { 470 nRest = ! bNoCompression && 471 ( pPos->Width() > MIN_TAB_WIDTH ) ? 472 pPos->Width() - MIN_TAB_WIDTH : 473 0; 474 475 // for simplifying the handling of left, right ... tabs, 476 // we do expand portions, which are lying behind 477 // those special tabs 478 bNoCompression = !pPos->IsTabLeftPortion(); 479 } 480 else 481 { 482 nRest = ! bNoCompression ? 483 ((SwGluePortion*)pPos)->GetPrtGlue() : 484 0; 485 486 bNoCompression = sal_False; 487 } 488 489 if( nKanaDiffSum ) 490 { 491 sal_uLong nCompress = ( 10000 * nRest ) / nKanaDiffSum; 492 493 if ( nCompress >= 10000 ) 494 // kanas can be expanded to 100%, and there is still 495 // some space remaining 496 nCompress = 0; 497 498 else 499 nCompress = 10000 - nCompress; 500 501 ( pCurrent->GetKanaComp() )[ nKanaIdx ] = (sal_uInt16)nCompress; 502 nKanaDiffSum = 0; 503 } 504 505 nKanaIdx++; 506 } 507 508 nX += pPos->Width(); 509 pPos = pPos->GetPortion(); 510 } 511 512 // set portion width 513 nKanaIdx = 0; 514 sal_uInt16 nCompress = ( pCurrent->GetKanaComp() )[ nKanaIdx ]; 515 pPos = pCurrent->GetPortion(); 516 long nDecompress = 0; 517 nKanaDiffSum = 0; 518 519 while( pPos ) 520 { 521 if ( pPos->InTxtGrp() ) 522 { 523 const sal_uInt16 nMinWidth = pPos->Width(); 524 525 // get maximum portion width from info structure, calculated 526 // during text formatting 527 sal_uInt16 nMaxWidthDiff = GetInfo().GetMaxWidthDiff( (sal_uLong)pPos ); 528 529 // check, if information is stored under other key 530 if ( !nMaxWidthDiff && pPos == pCurrent->GetFirstPortion() ) 531 nMaxWidthDiff = GetInfo().GetMaxWidthDiff( (sal_uLong)pCurrent ); 532 nKanaDiffSum += nMaxWidthDiff; 533 pPos->Width( nMinWidth + 534 ( ( 10000 - nCompress ) * nMaxWidthDiff ) / 10000 ); 535 nDecompress += pPos->Width() - nMinWidth; 536 } 537 else if( pPos->InGlueGrp() && pPos->InFixMargGrp() ) 538 { 539 if( nCompress ) 540 { 541 nKanaDiffSum *= nCompress; 542 nKanaDiffSum /= 10000; 543 } 544 545 pPos->Width( static_cast<sal_uInt16>(pPos->Width() - nDecompress) ); 546 547 if ( pPos->InTabGrp() ) 548 // set fix width to width 549 ((SwTabPortion*)pPos)->SetFixWidth( pPos->Width() ); 550 551 const SvUShorts& rKanaComp = pCurrent->GetKanaComp(); 552 if ( ++nKanaIdx < rKanaComp.Count() ) 553 nCompress = ( pCurrent->GetKanaComp() )[ nKanaIdx ]; 554 555 nKanaDiffSum = 0; 556 nDecompress = 0; 557 } 558 pPos = pPos->GetPortion(); 559 } 560 561 return nRepaintOfst; 562 } 563 564 /************************************************************************* 565 * SwTxtAdjuster::CalcRightMargin() 566 *************************************************************************/ 567 568 SwMarginPortion *SwTxtAdjuster::CalcRightMargin( SwLineLayout *pCurrent, 569 SwTwips nReal ) 570 { 571 long nRealWidth; 572 const sal_uInt16 nRealHeight = GetLineHeight(); 573 const sal_uInt16 nLineHeight = pCurrent->Height(); 574 575 KSHORT nPrtWidth = pCurrent->PrtWidth(); 576 SwLinePortion *pLast = pCurrent->FindLastPortion(); 577 578 if( GetInfo().IsMulti() ) 579 nRealWidth = nReal; 580 else 581 { 582 nRealWidth = GetLineWidth(); 583 // Fuer jeden FlyFrm, der in den rechten Rand hineinragt, 584 // wird eine FlyPortion angelegt. 585 const long nLeftMar = GetLeftMargin(); 586 SwRect aCurrRect( nLeftMar + nPrtWidth, Y() + nRealHeight - nLineHeight, 587 nRealWidth - nPrtWidth, nLineHeight ); 588 589 SwFlyPortion *pFly = CalcFlyPortion( nRealWidth, aCurrRect ); 590 while( pFly && long( nPrtWidth )< nRealWidth ) 591 { 592 pLast->Append( pFly ); 593 pLast = pFly; 594 if( pFly->Fix() > nPrtWidth ) 595 pFly->Width( ( pFly->Fix() - nPrtWidth) + pFly->Width() + 1); 596 nPrtWidth += pFly->Width() + 1; 597 aCurrRect.Left( nLeftMar + nPrtWidth ); 598 pFly = CalcFlyPortion( nRealWidth, aCurrRect ); 599 } 600 if( pFly ) 601 delete pFly; 602 } 603 604 SwMarginPortion *pRight = new SwMarginPortion( 0 ); 605 pLast->Append( pRight ); 606 607 if( long( nPrtWidth )< nRealWidth ) 608 pRight->PrtWidth( KSHORT( nRealWidth - nPrtWidth ) ); 609 610 // pCurrent->Width() wird auf die reale Groesse gesetzt, 611 // da jetzt die MarginPortions eingehaengt sind. 612 // Dieser Trick hat wundersame Auswirkungen. 613 // Wenn pCurrent->Width() == nRealWidth ist, dann wird das gesamte 614 // Adjustment implizit ausgecontert. GetLeftMarginAdjust() und 615 // IsBlocksatz() sind der Meinung, sie haetten eine mit Zeichen 616 // gefuellte Zeile. 617 618 pCurrent->PrtWidth( KSHORT( nRealWidth ) ); 619 return pRight; 620 } 621 622 /************************************************************************* 623 * SwTxtAdjuster::CalcFlyAdjust() 624 *************************************************************************/ 625 626 void SwTxtAdjuster::CalcFlyAdjust( SwLineLayout *pCurrent ) 627 { 628 // 1) Es wird ein linker Rand eingefuegt: 629 SwMarginPortion *pLeft = pCurrent->CalcLeftMargin(); 630 SwGluePortion *pGlue = pLeft; // die letzte GluePortion 631 632 633 // 2) Es wird ein rechter Rand angehaengt: 634 // CalcRightMargin berechnet auch eventuelle Ueberlappungen mit 635 // FlyFrms. 636 CalcRightMargin( pCurrent ); 637 638 SwLinePortion *pPos = pLeft->GetPortion(); 639 xub_StrLen nLen = 0; 640 641 // Wenn wir nur eine Zeile vorliegen haben und die Textportion zusammen 642 // haengend ist und wenn zentriert wird, dann ... 643 644 sal_Bool bComplete = 0 == nStart; 645 const sal_Bool bTabCompat = GetTxtFrm()->GetNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::TAB_COMPAT); 646 sal_Bool bMultiTab = sal_False; 647 648 while( pPos ) 649 { 650 if ( pPos->IsMultiPortion() && ((SwMultiPortion*)pPos)->HasTabulator() ) 651 bMultiTab = sal_True; 652 else if( pPos->InFixMargGrp() && 653 ( bTabCompat ? ! pPos->InTabGrp() : ! bMultiTab ) ) 654 { 655 // in tab compat mode we do not want to change tab portions 656 // in non tab compat mode we do not want to change margins if we 657 // found a multi portion with tabs 658 if( SVX_ADJUST_RIGHT == GetAdjust() ) 659 ((SwGluePortion*)pPos)->MoveAllGlue( pGlue ); 660 else 661 { 662 // Eine schlaue Idee von MA: 663 // Fuer die erste Textportion wird rechtsbuendig eingestellt, 664 // fuer die letzte linksbuendig. 665 666 // Die erste Textportion kriegt den ganzen Glue 667 // Aber nur, wenn wir mehr als eine Zeile besitzen. 668 if( bComplete && GetInfo().GetTxt().Len() == nLen ) 669 ((SwGluePortion*)pPos)->MoveHalfGlue( pGlue ); 670 else 671 { 672 if ( ! bTabCompat ) 673 { 674 if( pLeft == pGlue ) 675 { 676 // Wenn es nur einen linken und rechten Rand gibt, 677 // dann teilen sich die Raender den Glue. 678 if( nLen + pPos->GetLen() >= pCurrent->GetLen() ) 679 ((SwGluePortion*)pPos)->MoveHalfGlue( pGlue ); 680 else 681 ((SwGluePortion*)pPos)->MoveAllGlue( pGlue ); 682 } 683 else 684 { 685 // Die letzte Textportion behaelt sein Glue 686 if( !pPos->IsMarginPortion() ) 687 ((SwGluePortion*)pPos)->MoveHalfGlue( pGlue ); 688 } 689 } 690 else 691 ((SwGluePortion*)pPos)->MoveHalfGlue( pGlue ); 692 } 693 } 694 695 pGlue = (SwFlyPortion*)pPos; 696 bComplete = sal_False; 697 } 698 nLen = nLen + pPos->GetLen(); 699 pPos = pPos->GetPortion(); 700 } 701 702 if( ! bTabCompat && ! bMultiTab && SVX_ADJUST_RIGHT == GetAdjust() ) 703 // portions are moved to the right if possible 704 pLeft->AdjustRight( pCurrent ); 705 } 706 707 /************************************************************************* 708 * SwTxtAdjuster::CalcAdjLine() 709 *************************************************************************/ 710 711 void SwTxtAdjuster::CalcAdjLine( SwLineLayout *pCurrent ) 712 { 713 ASSERT( pCurrent->IsFormatAdj(), "CalcAdjLine: Why?" ); 714 715 pCurrent->SetFormatAdj(sal_False); 716 717 SwParaPortion* pPara = GetInfo().GetParaPortion(); 718 719 switch( GetAdjust() ) 720 { 721 case SVX_ADJUST_RIGHT: 722 case SVX_ADJUST_CENTER: 723 { 724 CalcFlyAdjust( pCurrent ); 725 pPara->GetRepaint()->SetOfst( 0 ); 726 break; 727 } 728 case SVX_ADJUST_BLOCK: 729 { 730 // disabled for #i13507# 731 // 8311: In Zeilen mit LineBreaks gibt es keinen Blocksatz! 732 /* if( pCurrent->GetLen() && 733 CH_BREAK == GetInfo().GetChar( nStart + pCurrent->GetLen() - 1 ) && 734 !IsLastBlock() ) 735 { 736 if( IsLastCenter() ) 737 { 738 CalcFlyAdjust( pCurrent ); 739 pPara->GetRepaint()->SetOfst( 0 ); 740 break; 741 } 742 return; 743 } 744 */ FormatBlock(); 745 break; 746 } 747 default : return; 748 } 749 } 750 751 /************************************************************************* 752 * SwTxtAdjuster::CalcFlyPortion() 753 * 754 * Die Berechnung hat es in sich: nCurrWidth geibt die Breite _vor_ dem 755 * aufaddieren des Wortes das noch auf die Zeile passt! Aus diesem Grund 756 * stimmt die Breite der FlyPortion auch, wenn die Blockierungssituation 757 * bFirstWord && !WORDFITS eintritt. 758 *************************************************************************/ 759 760 SwFlyPortion *SwTxtAdjuster::CalcFlyPortion( const long nRealWidth, 761 const SwRect &rCurrRect ) 762 { 763 SwTxtFly aTxtFly( GetTxtFrm() ); 764 765 const KSHORT nCurrWidth = pCurr->PrtWidth(); 766 SwFlyPortion *pFlyPortion = 0; 767 768 SwRect aLineVert( rCurrRect ); 769 if ( GetTxtFrm()->IsRightToLeft() ) 770 GetTxtFrm()->SwitchLTRtoRTL( aLineVert ); 771 if ( GetTxtFrm()->IsVertical() ) 772 GetTxtFrm()->SwitchHorizontalToVertical( aLineVert ); 773 774 // aFlyRect ist dokumentglobal ! 775 SwRect aFlyRect( aTxtFly.GetFrm( aLineVert ) ); 776 777 if ( GetTxtFrm()->IsRightToLeft() ) 778 GetTxtFrm()->SwitchRTLtoLTR( aFlyRect ); 779 if ( GetTxtFrm()->IsVertical() ) 780 GetTxtFrm()->SwitchVerticalToHorizontal( aFlyRect ); 781 782 // Wenn ein Frame ueberlappt, wird eine Portion eroeffnet. 783 if( aFlyRect.HasArea() ) 784 { 785 // aLocal ist framelokal 786 SwRect aLocal( aFlyRect ); 787 aLocal.Pos( aLocal.Left() - GetLeftMargin(), aLocal.Top() ); 788 if( nCurrWidth > aLocal.Left() ) 789 aLocal.Left( nCurrWidth ); 790 791 // Wenn das Rechteck breiter als die Zeile ist, stutzen 792 // wir es ebenfalls zurecht. 793 KSHORT nLocalWidth = KSHORT( aLocal.Left() + aLocal.Width() ); 794 if( nRealWidth < long( nLocalWidth ) ) 795 aLocal.Width( nRealWidth - aLocal.Left() ); 796 GetInfo().GetParaPortion()->SetFly( sal_True ); 797 pFlyPortion = new SwFlyPortion( aLocal ); 798 pFlyPortion->Height( KSHORT( rCurrRect.Height() ) ); 799 // Die Width koennte kleiner sein als die FixWidth, daher: 800 pFlyPortion->AdjFixWidth(); 801 } 802 return pFlyPortion; 803 } 804 805 /************************************************************************* 806 * SwTxtPainter::_CalcDropAdjust() 807 *************************************************************************/ 808 809 // 6721: Drops und Adjustment 810 // CalcDropAdjust wird ggf. am Ende von Format() gerufen. 811 812 void SwTxtAdjuster::CalcDropAdjust() 813 { 814 ASSERT( 1<GetDropLines() && SVX_ADJUST_LEFT!=GetAdjust() && SVX_ADJUST_BLOCK!=GetAdjust(), 815 "CalcDropAdjust: No reason for DropAdjustment." ) 816 817 const MSHORT nLineNumber = GetLineNr(); 818 819 // 1) Dummies ueberspringen 820 Top(); 821 822 if( !pCurr->IsDummy() || NextLine() ) 823 { 824 // Erst adjustieren. 825 GetAdjusted(); 826 827 SwLinePortion *pPor = pCurr->GetFirstPortion(); 828 829 // 2) Sicherstellen, dass die DropPortion dabei ist. 830 // 3) pLeft: Die GluePor vor der DropPor 831 if( pPor->InGlueGrp() && pPor->GetPortion() 832 && pPor->GetPortion()->IsDropPortion() ) 833 { 834 const SwLinePortion *pDropPor = (SwDropPortion*) pPor->GetPortion(); 835 SwGluePortion *pLeft = (SwGluePortion*) pPor; 836 837 // 4) pRight: Die GluePor hinter der DropPor suchen 838 pPor = pPor->GetPortion(); 839 while( pPor && !pPor->InFixMargGrp() ) 840 pPor = pPor->GetPortion(); 841 842 SwGluePortion *pRight = ( pPor && pPor->InGlueGrp() ) ? 843 (SwGluePortion*) pPor : 0; 844 if( pRight && pRight != pLeft ) 845 { 846 // 5) nMinLeft berechnen. Wer steht am weitesten links? 847 const KSHORT nDropLineStart = 848 KSHORT(GetLineStart()) + pLeft->Width() + pDropPor->Width(); 849 KSHORT nMinLeft = nDropLineStart; 850 for( MSHORT i = 1; i < GetDropLines(); ++i ) 851 { 852 if( NextLine() ) 853 { 854 // Erst adjustieren. 855 GetAdjusted(); 856 857 pPor = pCurr->GetFirstPortion(); 858 const SwMarginPortion *pMar = pPor->IsMarginPortion() ? 859 (SwMarginPortion*)pPor : 0; 860 if( !pMar ) 861 nMinLeft = 0; 862 else 863 { 864 const KSHORT nLineStart = 865 KSHORT(GetLineStart()) + pMar->Width(); 866 if( nMinLeft > nLineStart ) 867 nMinLeft = nLineStart; 868 } 869 } 870 } 871 872 // 6) Den Glue zwischen pLeft und pRight neu verteilen. 873 if( nMinLeft < nDropLineStart ) 874 { 875 // Glue wird immer von pLeft nach pRight abgegeben, 876 // damit der Text nach links wandert. 877 const short nGlue = nDropLineStart - nMinLeft; 878 if( !nMinLeft ) 879 pLeft->MoveAllGlue( pRight ); 880 else 881 pLeft->MoveGlue( pRight, nGlue ); 882 #ifdef DBGTXT 883 aDbstream << "Drop adjusted: " << nGlue << endl; 884 #endif 885 } 886 } 887 } 888 } 889 890 if( nLineNumber != GetLineNr() ) 891 { 892 Top(); 893 while( nLineNumber != GetLineNr() && Next() ) 894 ; 895 } 896 } 897 898 /************************************************************************* 899 * SwTxtAdjuster::CalcDropRepaint() 900 *************************************************************************/ 901 902 void SwTxtAdjuster::CalcDropRepaint() 903 { 904 Top(); 905 SwRepaint &rRepaint = *GetInfo().GetParaPortion()->GetRepaint(); 906 if( rRepaint.Top() > Y() ) 907 rRepaint.Top( Y() ); 908 for( MSHORT i = 1; i < GetDropLines(); ++i ) 909 NextLine(); 910 const SwTwips nBottom = Y() + GetLineHeight() - 1; 911 if( rRepaint.Bottom() < nBottom ) 912 rRepaint.Bottom( nBottom ); 913 } 914 915 916