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