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