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
FormatBlock()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 *************************************************************************/
lcl_CheckKashidaPositions(SwScriptInfo & rSI,SwTxtSizeInfo & rInf,SwTxtIter & rItr,xub_StrLen & nKashidas,xub_StrLen & nGluePortion)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 *************************************************************************/
lcl_CheckKashidaWidth(SwScriptInfo & rSI,SwTxtSizeInfo & rInf,SwTxtIter & rItr,xub_StrLen & nKashidas,xub_StrLen & nGluePortion,const long nGluePortionWidth,long & nSpaceAdd)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
CalcNewBlock(SwLineLayout * pCurrent,const SwLinePortion * pStopAt,SwTwips nReal,bool bSkipKashida)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
CalcKanaAdj(SwLineLayout * pCurrent)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
CalcRightMargin(SwLineLayout * pCurrent,SwTwips nReal)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
CalcFlyAdjust(SwLineLayout * pCurrent)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
CalcAdjLine(SwLineLayout * pCurrent)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
CalcFlyPortion(const long nRealWidth,const SwRect & rCurrRect)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
CalcDropAdjust()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
CalcDropRepaint()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