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>
36 #include <vcl/outdev.hxx>
37 #include <IDocumentSettingAccess.hxx>
39 #include "frame.hxx" // CalcFlyAdjust()
43 #include "porglue.hxx"
45 #include "porfly.hxx" // CalcFlyAdjust()
46 #include "pordrop.hxx" // CalcFlyAdjust()
47 #include "pormulti.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.
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() )
81 pPor
= pPor
->GetPortion();
83 pLay
= bSkip
? pLay
->GetNext() : 0;
89 if( !GetInfo().GetParaPortion()->HasFly() )
92 CalcFlyAdjust( pCurr
);
93 pCurr
->FinishSpaceAdd();
98 const SwLinePortion
*pTmpFly
= NULL
;
100 // 7701: beim letzten Fly soll Schluss sein
101 const SwLinePortion
*pPos
= pCurr
->GetFirstPortion();
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!
112 pPos
= pPos
->GetPortion();
114 // 8494: Wenn keiner gefunden wurde, ist sofort Schluss!
118 CalcFlyAdjust( pCurr
);
119 pCurr
->FinishSpaceAdd();
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
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
)
169 if ( nNext
== STRING_LEN
|| 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
;
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
;
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
;
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
)
232 if ( nNext
== STRING_LEN
|| 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
)
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
260 if ( !bAddSpaceChanged
)
261 break; // everything was OK
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())
298 rSI
.SetNoKashidaLine ( aItr
.GetStart(), aItr
.GetLength());
302 rSI
.ClearKashidaInvalid ( aItr
.GetStart(), aItr
.GetLength() );
303 rSI
.ClearNoKashidaLine( aItr
.GetStart(), aItr
.GetLength() );
308 // CalcRightMargin() setzt pCurrent->Width() auf die Zeilenbreite !
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
);
317 SwLinePortion
*pPos
= pCurrent
->GetPortion();
321 if ( bDoNotJustifyLinesWithManualBreak
&&
322 pPos
->IsBreakPortion() && !IsLastBlock() )
324 pCurrent
->FinishSpaceAdd();
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
337 if( pMulti
->HasTabulator() )
339 if ( nSpaceIdx
== pCurrent
->GetLLSpaceAddCount() )
340 pCurrent
->SetLLSpaceAdd( 0, nSpaceIdx
);
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 );
380 long nSpaceAdd
= nGluePortionWidth
/ nGluePortion
;
383 if( rSI
.CountKashida() && !bSkipKashida
)
385 if( !lcl_CheckKashidaWidth( rSI
, aInf
, aItr
, nKashidas
, nGluePortion
, nGluePortionWidth
, nSpaceAdd
))
388 // do regular blank justification
389 pCurrent
->FinishSpaceAdd();
390 GetInfo().SetIdx( nStart
);
391 CalcNewBlock( pCurrent
, pStopAt
, nReal
, true );
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() );
413 GetInfo().SetIdx( GetInfo().GetIdx() + pPos
->GetLen() );
414 if ( pPos
== pStopAt
)
416 pCurrent
->SetLLSpaceAdd( 0, nSpaceIdx
);
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;
437 long nKanaDiffSum
= 0;
438 SwTwips nRepaintOfst
= 0;
440 sal_Bool bNoCompression
= sal_False
;
443 // CalcRightMargin() setzt pCurrent->Width() auf die Zeilenbreite !
444 CalcRightMargin( pCurrent
, 0 );
446 SwLinePortion
* pPos
= pCurrent
->GetPortion();
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
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
);
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();
489 nRest
= ! bNoCompression
?
490 ((SwGluePortion
*)pPos
)->GetPrtGlue() :
493 bNoCompression
= sal_False
;
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
506 nCompress
= 10000 - nCompress
;
508 ( pCurrent
->GetKanaComp() )[ nKanaIdx
] = (USHORT
)nCompress
;
516 pPos
= pPos
->GetPortion();
521 USHORT nCompress
= ( pCurrent
->GetKanaComp() )[ nKanaIdx
];
522 pPos
= pCurrent
->GetPortion();
523 long nDecompress
= 0;
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() )
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
];
565 pPos
= pPos
->GetPortion();
571 /*************************************************************************
572 * SwTxtAdjuster::CalcRightMargin()
573 *************************************************************************/
575 SwMarginPortion
*SwTxtAdjuster::CalcRightMargin( SwLineLayout
*pCurrent
,
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() )
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
);
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
);
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
625 pCurrent
->PrtWidth( KSHORT( nRealWidth
) );
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
643 CalcRightMargin( pCurrent
);
645 SwLinePortion
*pPos
= pLeft
->GetPortion();
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
;
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
);
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
);
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
);
688 ((SwGluePortion
*)pPos
)->MoveAllGlue( pGlue
);
692 // Die letzte Textportion behaelt sein Glue
693 if( !pPos
->IsMarginPortion() )
694 ((SwGluePortion
*)pPos
)->MoveHalfGlue( pGlue
);
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 );
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 ) &&
745 CalcFlyAdjust( pCurrent );
746 pPara->GetRepaint()->SetOfst( 0 );
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();
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
829 if( !pCurr
->IsDummy() || NextLine() )
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
)
864 pPor
= pCurr
->GetFirstPortion();
865 const SwMarginPortion
*pMar
= pPor
->IsMarginPortion() ?
866 (SwMarginPortion
*)pPor
: 0;
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
;
886 pLeft
->MoveAllGlue( pRight
);
888 pLeft
->MoveGlue( pRight
, nGlue
);
890 aDbstream
<< "Drop adjusted: " << nGlue
<< endl
;
897 if( nLineNumber
!= GetLineNr() )
900 while( nLineNumber
!= GetLineNr() && Next() )
905 /*************************************************************************
906 * SwTxtAdjuster::CalcDropRepaint()
907 *************************************************************************/
909 void SwTxtAdjuster::CalcDropRepaint()
912 SwRepaint
&rRepaint
= *GetInfo().GetParaPortion()->GetRepaint();
913 if( rRepaint
.Top() > Y() )
915 for( MSHORT i
= 1; i
< GetDropLines(); ++i
)
917 const SwTwips nBottom
= Y() + GetLineHeight() - 1;
918 if( rRepaint
.Bottom() < nBottom
)
919 rRepaint
.Bottom( nBottom
);