android: Update app-specific/MIME type icons
[LibreOffice.git] / sw / source / core / text / itradj.cxx
blobb72a8d59f5f718eb1b9adec8e7f657b64cb79011
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include <o3tl/safeint.hxx>
24 #include <IDocumentSettingAccess.hxx>
25 #include <doc.hxx>
27 #include "itrtxt.hxx"
28 #include "porglue.hxx"
29 #include "porlay.hxx"
30 #include "porfly.hxx"
31 #include "pormulti.hxx"
32 #include "portab.hxx"
33 #include <memory>
35 #define MIN_TAB_WIDTH 60
37 using namespace ::com::sun::star;
39 void SwTextAdjuster::FormatBlock( )
41 // Block format does not apply to the last line.
42 // And for tabs it doesn't exist out of tradition
43 // If we have Flys we continue.
45 const SwLinePortion *pFly = nullptr;
47 bool bSkip = !IsLastBlock() &&
48 m_nStart + m_pCurr->GetLen() >= TextFrameIndex(GetInfo().GetText().getLength());
50 // Multi-line fields are tricky, because we need to check whether there are
51 // any other text portions in the paragraph.
52 if( bSkip )
54 const SwLineLayout *pLay = m_pCurr->GetNext();
55 while( pLay && !pLay->GetLen() )
57 const SwLinePortion *pPor = m_pCurr->GetFirstPortion();
58 while( pPor && bSkip )
60 if( pPor->InTextGrp() )
61 bSkip = false;
62 pPor = pPor->GetNextPortion();
64 pLay = bSkip ? pLay->GetNext() : nullptr;
68 if( bSkip )
70 if( !GetInfo().GetParaPortion()->HasFly() )
72 if( IsLastCenter() )
73 CalcFlyAdjust( m_pCurr );
74 m_pCurr->FinishSpaceAdd();
75 return;
77 else
79 const SwLinePortion *pTmpFly = nullptr;
81 // End at the last Fly
82 const SwLinePortion *pPos = m_pCurr->GetFirstPortion();
83 while( pPos )
85 // Look for the last Fly which has text coming after it:
86 if( pPos->IsFlyPortion() )
87 pTmpFly = pPos; // Found a Fly
88 else if ( pTmpFly && pPos->InTextGrp() )
90 pFly = pTmpFly; // A Fly with follow-up text!
91 pTmpFly = nullptr;
93 pPos = pPos->GetNextPortion();
95 // End if we didn't find one
96 if( !pFly )
98 if( IsLastCenter() )
99 CalcFlyAdjust( m_pCurr );
100 m_pCurr->FinishSpaceAdd();
101 return;
106 const TextFrameIndex nOldIdx = GetInfo().GetIdx();
107 GetInfo().SetIdx( m_nStart );
108 CalcNewBlock( m_pCurr, pFly );
109 GetInfo().SetIdx( nOldIdx );
110 GetInfo().GetParaPortion()->GetRepaint().SetOffset(0);
113 static bool lcl_CheckKashidaPositions( SwScriptInfo& rSI, SwTextSizeInfo& rInf, SwTextIter& rItr,
114 sal_Int32& rKashidas, TextFrameIndex& nGluePortion)
116 // i60594 validate Kashida justification
117 TextFrameIndex nIdx = rItr.GetStart();
118 TextFrameIndex nEnd = rItr.GetEnd();
120 // Note on calling KashidaJustify():
121 // Kashida positions may be marked as invalid. Therefore KashidaJustify may return the clean
122 // total number of kashida positions, or the number of kashida positions after some positions
123 // have been dropped.
124 // Here we want the clean total, which is OK: We have called ClearKashidaInvalid() before.
125 rKashidas = rSI.KashidaJustify(nullptr, nullptr, rItr.GetStart(), rItr.GetLength());
127 if (rKashidas <= 0) // nothing to do
128 return true;
130 // kashida positions found in SwScriptInfo are not necessarily valid in every font
131 // if two characters are replaced by a ligature glyph, there will be no place for a kashida
132 std::vector<TextFrameIndex> aKashidaPos;
133 rSI.GetKashidaPositions(nIdx, rItr.GetLength(), aKashidaPos);
134 assert(aKashidaPos.size() >= o3tl::make_unsigned(rKashidas));
135 std::vector<TextFrameIndex> aKashidaPosDropped(aKashidaPos.size());
136 sal_Int32 nKashidaIdx = 0;
137 while ( rKashidas && nIdx < nEnd )
139 rItr.SeekAndChgAttrIter( nIdx, rInf.GetOut() );
140 TextFrameIndex nNext = rItr.GetNextAttr();
142 // is there also a script change before?
143 // if there is, nNext should point to the script change
144 TextFrameIndex const nNextScript = rSI.NextScriptChg( nIdx );
145 if( nNextScript < nNext )
146 nNext = nNextScript;
148 if (nNext == TextFrameIndex(COMPLETE_STRING) || nNext > nEnd)
149 nNext = nEnd;
150 sal_Int32 nKashidasInAttr = rSI.KashidaJustify(nullptr, nullptr, nIdx, nNext - nIdx);
151 if (nKashidasInAttr > 0)
153 // Kashida glyph looks suspicious, skip Kashida justification
154 if ( rInf.GetOut()->GetMinKashida() <= 0 )
156 return false;
159 sal_Int32 nKashidasDropped = 0;
160 if ( !SwScriptInfo::IsArabicText( rInf.GetText(), nIdx, nNext - nIdx ) )
162 nKashidasDropped = nKashidasInAttr;
163 rKashidas -= nKashidasDropped;
165 else
167 vcl::text::ComplexTextLayoutFlags nOldLayout = rInf.GetOut()->GetLayoutMode();
168 rInf.GetOut()->SetLayoutMode ( nOldLayout | vcl::text::ComplexTextLayoutFlags::BiDiRtl );
169 nKashidasDropped = rInf.GetOut()->ValidateKashidas(
170 rInf.GetText(), sal_Int32(nIdx), sal_Int32(nNext - nIdx),
171 nKashidasInAttr,
172 reinterpret_cast<sal_Int32*>(aKashidaPos.data() + nKashidaIdx),
173 reinterpret_cast<sal_Int32*>(aKashidaPosDropped.data()));
174 rInf.GetOut()->SetLayoutMode ( nOldLayout );
175 if ( nKashidasDropped )
177 rSI.MarkKashidasInvalid(nKashidasDropped, aKashidaPosDropped.data());
178 rKashidas -= nKashidasDropped;
179 nGluePortion -= TextFrameIndex(nKashidasDropped);
182 nKashidaIdx += nKashidasInAttr;
184 nIdx = nNext;
187 // return false if all kashidas have been eliminated
188 return (rKashidas > 0);
191 static bool lcl_CheckKashidaWidth ( SwScriptInfo& rSI, SwTextSizeInfo& rInf, SwTextIter& rItr, sal_Int32& rKashidas,
192 TextFrameIndex& nGluePortion, const tools::Long nGluePortionWidth, tools::Long& nSpaceAdd )
194 // check kashida width
195 // if width is smaller than minimal kashida width allowed by fonts in the current line
196 // drop one kashida after the other until kashida width is OK
197 while (rKashidas)
199 bool bAddSpaceChanged = false;
200 TextFrameIndex nIdx = rItr.GetStart();
201 TextFrameIndex nEnd = rItr.GetEnd();
202 while ( nIdx < nEnd )
204 rItr.SeekAndChgAttrIter( nIdx, rInf.GetOut() );
205 TextFrameIndex nNext = rItr.GetNextAttr();
207 // is there also a script change before?
208 // if there is, nNext should point to the script change
209 TextFrameIndex const nNextScript = rSI.NextScriptChg( nIdx );
210 if( nNextScript < nNext )
211 nNext = nNextScript;
213 if (nNext == TextFrameIndex(COMPLETE_STRING) || nNext > nEnd)
214 nNext = nEnd;
215 sal_Int32 nKashidasInAttr = rSI.KashidaJustify(nullptr, nullptr, nIdx, nNext - nIdx);
217 tools::Long nFontMinKashida = rInf.GetOut()->GetMinKashida();
218 if ( nFontMinKashida && nKashidasInAttr > 0 && SwScriptInfo::IsArabicText( rInf.GetText(), nIdx, nNext - nIdx ) )
220 sal_Int32 nKashidasDropped = 0;
221 while ( rKashidas && nGluePortion && nKashidasInAttr > 0 &&
222 nSpaceAdd / SPACING_PRECISION_FACTOR < nFontMinKashida )
224 --nGluePortion;
225 --rKashidas;
226 --nKashidasInAttr;
227 ++nKashidasDropped;
228 if( !rKashidas || !nGluePortion ) // nothing left, return false to
229 return false; // do regular blank justification
231 nSpaceAdd = nGluePortionWidth / sal_Int32(nGluePortion);
232 bAddSpaceChanged = true;
234 if( nKashidasDropped )
235 rSI.MarkKashidasInvalid( nKashidasDropped, nIdx, nNext - nIdx );
237 if ( bAddSpaceChanged )
238 break; // start all over again
239 nIdx = nNext;
241 if ( !bAddSpaceChanged )
242 break; // everything was OK
244 return true;
247 // CalcNewBlock() must only be called _after_ CalcLine()!
248 // We always span between two RandPortions or FixPortions (Tabs and Flys).
249 // We count the Glues and call ExpandBlock.
250 void SwTextAdjuster::CalcNewBlock( SwLineLayout *pCurrent,
251 const SwLinePortion *pStopAt, SwTwips nReal, bool bSkipKashida )
253 OSL_ENSURE( GetInfo().IsMulti() || SvxAdjust::Block == GetAdjust(),
254 "CalcNewBlock: Why?" );
255 OSL_ENSURE( pCurrent->Height(), "SwTextAdjuster::CalcBlockAdjust: missing CalcLine()" );
257 pCurrent->InitSpaceAdd();
258 TextFrameIndex nGluePortion(0);
259 TextFrameIndex nCharCnt(0);
260 sal_uInt16 nSpaceIdx = 0;
262 // i60591: hennerdrews
263 SwScriptInfo& rSI = GetInfo().GetParaPortion()->GetScriptInfo();
264 SwTextSizeInfo aInf ( GetTextFrame() );
265 SwTextIter aItr ( GetTextFrame(), &aInf );
267 if ( rSI.CountKashida() )
269 while (aItr.GetCurr() != pCurrent && aItr.GetNext())
270 aItr.Next();
272 if( bSkipKashida )
274 rSI.SetNoKashidaLine ( aItr.GetStart(), aItr.GetLength());
276 else
278 rSI.ClearKashidaInvalid ( aItr.GetStart(), aItr.GetLength() );
279 rSI.ClearNoKashidaLine( aItr.GetStart(), aItr.GetLength() );
283 // Do not forget: CalcRightMargin() sets pCurrent->Width() to the line width!
284 if (!bSkipKashida)
285 CalcRightMargin( pCurrent, nReal );
287 // #i49277#
288 const bool bDoNotJustifyLinesWithManualBreak =
289 GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::DO_NOT_JUSTIFY_LINES_WITH_MANUAL_BREAK);
290 bool bDoNotJustifyTab = false;
292 SwLinePortion *pPos = pCurrent->GetNextPortion();
294 while( pPos )
296 if ( ( bDoNotJustifyLinesWithManualBreak || bDoNotJustifyTab ) &&
297 pPos->IsBreakPortion() && !IsLastBlock() )
299 pCurrent->FinishSpaceAdd();
300 break;
303 switch ( pPos->GetWhichPor() )
305 case PortionType::TabCenter :
306 case PortionType::TabRight :
307 case PortionType::TabDecimal :
308 bDoNotJustifyTab = true;
309 break;
310 case PortionType::TabLeft :
311 case PortionType::Break:
312 bDoNotJustifyTab = false;
313 break;
314 default: break;
317 if ( pPos->InTextGrp() )
318 nGluePortion = nGluePortion + static_cast<SwTextPortion*>(pPos)->GetSpaceCnt( GetInfo(), nCharCnt );
319 else if( pPos->IsMultiPortion() )
321 SwMultiPortion* pMulti = static_cast<SwMultiPortion*>(pPos);
322 // a multiportion with a tabulator inside breaks the text adjustment
323 // a ruby portion will not be stretched by text adjustment
324 // a double line portion takes additional space for each blank
325 // in the wider line
326 if( pMulti->HasTabulator() )
328 if ( nSpaceIdx == pCurrent->GetLLSpaceAddCount() )
329 pCurrent->SetLLSpaceAdd( 0, nSpaceIdx );
331 nSpaceIdx++;
332 nGluePortion = TextFrameIndex(0);
333 nCharCnt = TextFrameIndex(0);
335 else if( pMulti->IsDouble() )
336 nGluePortion = nGluePortion + static_cast<SwDoubleLinePortion*>(pMulti)->GetSpaceCnt();
337 else if ( pMulti->IsBidi() )
338 nGluePortion = nGluePortion + static_cast<SwBidiPortion*>(pMulti)->GetSpaceCnt( GetInfo() ); // i60594
341 if( pPos->InGlueGrp() )
343 if( pPos->InFixMargGrp() )
345 if ( nSpaceIdx == pCurrent->GetLLSpaceAddCount() )
346 pCurrent->SetLLSpaceAdd( 0, nSpaceIdx );
348 const tools::Long nGluePortionWidth = static_cast<SwGluePortion*>(pPos)->GetPrtGlue() *
349 SPACING_PRECISION_FACTOR;
351 sal_Int32 nKashidas = 0;
352 if( nGluePortion && rSI.CountKashida() && !bSkipKashida )
354 // kashida positions found in SwScriptInfo are not necessarily valid in every font
355 // if two characters are replaced by a ligature glyph, there will be no place for a kashida
356 if ( !lcl_CheckKashidaPositions ( rSI, aInf, aItr, nKashidas, nGluePortion ))
358 // all kashida positions are invalid
359 // do regular blank justification
360 pCurrent->FinishSpaceAdd();
361 GetInfo().SetIdx( m_nStart );
362 CalcNewBlock( pCurrent, pStopAt, nReal, true );
363 return;
367 if( nGluePortion )
369 tools::Long nSpaceAdd = nGluePortionWidth / sal_Int32(nGluePortion);
371 // i60594
372 if( rSI.CountKashida() && !bSkipKashida )
374 if( !lcl_CheckKashidaWidth( rSI, aInf, aItr, nKashidas, nGluePortion, nGluePortionWidth, nSpaceAdd ))
376 // no kashidas left
377 // do regular blank justification
378 pCurrent->FinishSpaceAdd();
379 GetInfo().SetIdx( m_nStart );
380 CalcNewBlock( pCurrent, pStopAt, nReal, true );
381 return;
385 pCurrent->SetLLSpaceAdd( nSpaceAdd , nSpaceIdx );
386 pPos->Width( static_cast<SwGluePortion*>(pPos)->GetFixWidth() );
388 else if (IsOneBlock() && nCharCnt > TextFrameIndex(1))
390 const tools::Long nSpaceAdd = - nGluePortionWidth / (sal_Int32(nCharCnt) - 1);
391 pCurrent->SetLLSpaceAdd( nSpaceAdd, nSpaceIdx );
392 pPos->Width( static_cast<SwGluePortion*>(pPos)->GetFixWidth() );
395 nSpaceIdx++;
396 nGluePortion = TextFrameIndex(0);
397 nCharCnt = TextFrameIndex(0);
399 else
400 ++nGluePortion;
402 GetInfo().SetIdx( GetInfo().GetIdx() + pPos->GetLen() );
403 if ( pPos == pStopAt )
405 pCurrent->SetLLSpaceAdd( 0, nSpaceIdx );
406 break;
408 pPos = pPos->GetNextPortion();
412 SwTwips SwTextAdjuster::CalcKanaAdj( SwLineLayout* pCurrent )
414 OSL_ENSURE( pCurrent->Height(), "SwTextAdjuster::CalcBlockAdjust: missing CalcLine()" );
415 OSL_ENSURE( !pCurrent->GetpKanaComp(), "pKanaComp already exists!!" );
417 pCurrent->SetKanaComp( std::make_unique<std::deque<sal_uInt16>>() );
419 const sal_uInt16 nNull = 0;
420 size_t nKanaIdx = 0;
421 tools::Long nKanaDiffSum = 0;
422 SwTwips nRepaintOfst = 0;
423 SwTwips nX = 0;
424 bool bNoCompression = false;
426 // Do not forget: CalcRightMargin() sets pCurrent->Width() to the line width!
427 CalcRightMargin( pCurrent );
429 SwLinePortion* pPos = pCurrent->GetNextPortion();
431 while( pPos )
433 if ( pPos->InTextGrp() )
435 // get maximum portion width from info structure, calculated
436 // during text formatting
437 sal_uInt16 nMaxWidthDiff = GetInfo().GetMaxWidthDiff( pPos );
439 // check, if information is stored under other key
440 if ( !nMaxWidthDiff && pPos == pCurrent->GetFirstPortion() )
441 nMaxWidthDiff = GetInfo().GetMaxWidthDiff( pCurrent );
443 // calculate difference between portion width and max. width
444 nKanaDiffSum += nMaxWidthDiff;
446 // we store the beginning of the first compressible portion
447 // for repaint
448 if ( nMaxWidthDiff && !nRepaintOfst )
449 nRepaintOfst = nX + GetLeftMargin();
451 else if( pPos->InGlueGrp() && pPos->InFixMargGrp() )
453 if ( nKanaIdx == pCurrent->GetKanaComp().size() )
454 pCurrent->GetKanaComp().push_back( nNull );
456 tools::Long nRest;
458 if ( pPos->InTabGrp() )
460 nRest = ! bNoCompression &&
461 ( pPos->Width() > MIN_TAB_WIDTH ) ?
462 pPos->Width() - MIN_TAB_WIDTH :
465 // for simplifying the handling of left, right ... tabs,
466 // we do expand portions, which are lying behind
467 // those special tabs
468 bNoCompression = !pPos->IsTabLeftPortion();
470 else
472 nRest = ! bNoCompression ?
473 static_cast<SwGluePortion*>(pPos)->GetPrtGlue() :
476 bNoCompression = false;
479 if( nKanaDiffSum )
481 sal_uLong nCompress = ( 10000 * nRest ) / nKanaDiffSum;
483 if ( nCompress >= 10000 )
484 // kanas can be expanded to 100%, and there is still
485 // some space remaining
486 nCompress = 0;
488 else
489 nCompress = 10000 - nCompress;
491 ( pCurrent->GetKanaComp() )[ nKanaIdx ] = o3tl::narrowing<sal_uInt16>(nCompress);
492 nKanaDiffSum = 0;
495 nKanaIdx++;
498 nX += pPos->Width();
499 pPos = pPos->GetNextPortion();
502 // set portion width
503 nKanaIdx = 0;
504 sal_uInt16 nCompress = ( pCurrent->GetKanaComp() )[ nKanaIdx ];
505 pPos = pCurrent->GetNextPortion();
506 tools::Long nDecompress = 0;
508 while( pPos )
510 if ( pPos->InTextGrp() )
512 const SwTwips nMinWidth = pPos->Width();
514 // get maximum portion width from info structure, calculated
515 // during text formatting
516 SwTwips nMaxWidthDiff = GetInfo().GetMaxWidthDiff( pPos );
518 // check, if information is stored under other key
519 if ( !nMaxWidthDiff && pPos == pCurrent->GetFirstPortion() )
520 nMaxWidthDiff = GetInfo().GetMaxWidthDiff( pCurrent );
521 pPos->Width( nMinWidth +
522 ( ( 10000 - nCompress ) * nMaxWidthDiff ) / 10000 );
523 nDecompress += pPos->Width() - nMinWidth;
525 else if( pPos->InGlueGrp() && pPos->InFixMargGrp() )
527 pPos->Width(pPos->Width() - nDecompress);
529 if ( pPos->InTabGrp() )
530 // set fix width to width
531 static_cast<SwTabPortion*>(pPos)->SetFixWidth( pPos->Width() );
533 if ( ++nKanaIdx < pCurrent->GetKanaComp().size() )
534 nCompress = ( pCurrent->GetKanaComp() )[ nKanaIdx ];
536 nDecompress = 0;
538 pPos = pPos->GetNextPortion();
541 return nRepaintOfst;
544 SwMarginPortion *SwTextAdjuster::CalcRightMargin( SwLineLayout *pCurrent,
545 SwTwips nReal )
547 tools::Long nRealWidth;
548 const sal_uInt16 nRealHeight = GetLineHeight();
549 const sal_uInt16 nLineHeight = pCurrent->Height();
551 sal_uInt16 nPrtWidth = pCurrent->PrtWidth();
552 SwLinePortion *pLast = pCurrent->FindLastPortion();
554 if( GetInfo().IsMulti() )
555 nRealWidth = nReal;
556 else
558 nRealWidth = GetLineWidth();
559 // For each FlyFrame extending into the right margin, we create a FlyPortion.
560 const tools::Long nLeftMar = GetLeftMargin();
561 SwRect aCurrRect( nLeftMar + nPrtWidth, Y() + nRealHeight - nLineHeight,
562 nRealWidth - nPrtWidth, nLineHeight );
564 SwFlyPortion *pFly = CalcFlyPortion( nRealWidth, aCurrRect );
565 while( pFly && tools::Long( nPrtWidth )< nRealWidth )
567 pLast->Append( pFly );
568 pLast = pFly;
569 if( pFly->GetFix() > nPrtWidth )
570 pFly->Width( ( pFly->GetFix() - nPrtWidth) + pFly->Width() + 1);
571 nPrtWidth += pFly->Width() + 1;
572 aCurrRect.Left( nLeftMar + nPrtWidth );
573 pFly = CalcFlyPortion( nRealWidth, aCurrRect );
575 delete pFly;
578 SwMarginPortion *pRight = new SwMarginPortion;
579 pLast->Append( pRight );
581 if( tools::Long( nPrtWidth )< nRealWidth )
582 pRight->PrtWidth( sal_uInt16( nRealWidth - nPrtWidth ) );
584 // pCurrent->Width() is set to the real size, because we attach the
585 // MarginPortions.
586 // This trick gives miraculous results:
587 // If pCurrent->Width() == nRealWidth, then the adjustment gets overruled
588 // implicitly. GetLeftMarginAdjust() and IsJustified() think they have a
589 // line filled with chars.
591 pCurrent->PrtWidth( sal_uInt16( nRealWidth ) );
592 return pRight;
595 void SwTextAdjuster::CalcFlyAdjust( SwLineLayout *pCurrent )
597 // 1) We insert a left margin:
598 SwMarginPortion *pLeft = pCurrent->CalcLeftMargin();
599 SwGluePortion *pGlue = pLeft; // the last GluePortion
601 // 2) We attach a right margin:
602 // CalcRightMargin also calculates a possible overlap with FlyFrames.
603 CalcRightMargin( pCurrent );
605 SwLinePortion *pPos = pLeft->GetNextPortion();
606 TextFrameIndex nLen(0);
608 // If we only have one line, the text portion is consecutive and we center, then ...
609 bool bComplete = TextFrameIndex(0) == m_nStart;
610 const bool bTabCompat = GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::TAB_COMPAT);
611 bool bMultiTab = false;
613 while( pPos )
615 if ( pPos->IsMultiPortion() && static_cast<SwMultiPortion*>(pPos)->HasTabulator() )
616 bMultiTab = true;
617 else if( pPos->InFixMargGrp() &&
618 ( bTabCompat ? ! pPos->InTabGrp() : ! bMultiTab ) )
620 // in tab compat mode we do not want to change tab portions
621 // in non tab compat mode we do not want to change margins if we
622 // found a multi portion with tabs
623 if( SvxAdjust::Right == GetAdjust() )
624 static_cast<SwGluePortion*>(pPos)->MoveAllGlue( pGlue );
625 else
627 // We set the first text portion to right-aligned and the last one
628 // to left-aligned.
629 // The first text portion gets the whole Glue, but only if we have
630 // more than one line.
631 if (bComplete && TextFrameIndex(GetInfo().GetText().getLength()) == nLen)
632 static_cast<SwGluePortion*>(pPos)->MoveHalfGlue( pGlue );
633 else
635 if ( ! bTabCompat )
637 if( pLeft == pGlue )
639 // If we only have a left and right margin, the
640 // margins share the Glue.
641 if( nLen + pPos->GetLen() >= pCurrent->GetLen() )
642 static_cast<SwGluePortion*>(pPos)->MoveHalfGlue( pGlue );
643 else
644 static_cast<SwGluePortion*>(pPos)->MoveAllGlue( pGlue );
646 else
648 // The last text portion retains its Glue.
649 if( !pPos->IsMarginPortion() )
650 static_cast<SwGluePortion*>(pPos)->MoveHalfGlue( pGlue );
653 else
654 static_cast<SwGluePortion*>(pPos)->MoveHalfGlue( pGlue );
658 pGlue = static_cast<SwGluePortion*>(pPos);
659 bComplete = false;
661 nLen = nLen + pPos->GetLen();
662 pPos = pPos->GetNextPortion();
665 if( ! bTabCompat && ! bMultiTab && SvxAdjust::Right == GetAdjust() )
666 // portions are moved to the right if possible
667 pLeft->AdjustRight( pCurrent );
670 void SwTextAdjuster::CalcAdjLine( SwLineLayout *pCurrent )
672 OSL_ENSURE( pCurrent->IsFormatAdj(), "CalcAdjLine: Why?" );
674 pCurrent->SetFormatAdj(false);
676 SwParaPortion* pPara = GetInfo().GetParaPortion();
678 switch( GetAdjust() )
680 case SvxAdjust::Right:
681 case SvxAdjust::Center:
683 CalcFlyAdjust( pCurrent );
684 pPara->GetRepaint().SetOffset( 0 );
685 break;
687 case SvxAdjust::Block:
689 FormatBlock();
690 break;
692 default : return;
696 // This is a quite complicated calculation: nCurrWidth is the width _before_
697 // adding the word, that still fits onto the line! For this reason the FlyPortion's
698 // width is still correct if we get a deadlock-situation of:
699 // bFirstWord && !WORDFITS
700 SwFlyPortion *SwTextAdjuster::CalcFlyPortion( const tools::Long nRealWidth,
701 const SwRect &rCurrRect )
703 SwTextFly aTextFly( GetTextFrame() );
705 const sal_uInt16 nCurrWidth = m_pCurr->PrtWidth();
706 SwFlyPortion *pFlyPortion = nullptr;
708 SwRect aLineVert( rCurrRect );
709 if ( GetTextFrame()->IsRightToLeft() )
710 GetTextFrame()->SwitchLTRtoRTL( aLineVert );
711 if ( GetTextFrame()->IsVertical() )
712 GetTextFrame()->SwitchHorizontalToVertical( aLineVert );
714 // aFlyRect is document-global!
715 SwRect aFlyRect( aTextFly.GetFrame( aLineVert ) );
717 if ( GetTextFrame()->IsRightToLeft() )
718 GetTextFrame()->SwitchRTLtoLTR( aFlyRect );
719 if ( GetTextFrame()->IsVertical() )
720 GetTextFrame()->SwitchVerticalToHorizontal( aFlyRect );
722 // If a Frame overlapps we open a Portion
723 if( aFlyRect.HasArea() )
725 // aLocal is frame-local
726 SwRect aLocal( aFlyRect );
727 aLocal.Pos( aLocal.Left() - GetLeftMargin(), aLocal.Top() );
728 if( nCurrWidth > aLocal.Left() )
729 aLocal.Left( nCurrWidth );
731 // If the rect is wider than the line, we adjust it to the right size
732 const tools::Long nLocalWidth = aLocal.Left() + aLocal.Width();
733 if( nRealWidth < nLocalWidth )
734 aLocal.Width( nRealWidth - aLocal.Left() );
735 GetInfo().GetParaPortion()->SetFly();
736 pFlyPortion = new SwFlyPortion( aLocal );
737 pFlyPortion->Height( sal_uInt16( rCurrRect.Height() ) );
738 // The Width could be smaller than the FixWidth, thus:
739 pFlyPortion->AdjFixWidth();
741 return pFlyPortion;
744 // CalcDropAdjust is called at the end by Format() if needed
745 void SwTextAdjuster::CalcDropAdjust()
747 OSL_ENSURE( 1<GetDropLines() && SvxAdjust::Left!=GetAdjust() && SvxAdjust::Block!=GetAdjust(),
748 "CalcDropAdjust: No reason for DropAdjustment." );
750 const sal_Int32 nLineNumber = GetLineNr();
752 // 1) Skip dummies
753 Top();
755 if( !m_pCurr->IsDummy() || NextLine() )
757 // Adjust first
758 GetAdjusted();
760 SwLinePortion *pPor = m_pCurr->GetFirstPortion();
762 // 2) Make sure we include the ropPortion
763 // 3) pLeft is the GluePor preceding the DropPor
764 if( pPor->InGlueGrp() && pPor->GetNextPortion()
765 && pPor->GetNextPortion()->IsDropPortion() )
767 const SwLinePortion *pDropPor = pPor->GetNextPortion();
768 SwGluePortion *pLeft = static_cast<SwGluePortion*>( pPor );
770 // 4) pRight: Find the GluePor coming after the DropPor
771 pPor = pPor->GetNextPortion();
772 while( pPor && !pPor->InFixMargGrp() )
773 pPor = pPor->GetNextPortion();
775 SwGluePortion *pRight = ( pPor && pPor->InGlueGrp() ) ?
776 static_cast<SwGluePortion*>(pPor) : nullptr;
777 if( pRight && pRight != pLeft )
779 // 5) Calculate nMinLeft. Who is the most to left?
780 const auto nDropLineStart =
781 GetLineStart() + pLeft->Width() + pDropPor->Width();
782 auto nMinLeft = nDropLineStart;
783 for( sal_Int32 i = 1; i < GetDropLines(); ++i )
785 if( NextLine() )
787 // Adjust first
788 GetAdjusted();
790 pPor = m_pCurr->GetFirstPortion();
791 const SwMarginPortion *pMar = pPor->IsMarginPortion() ?
792 static_cast<SwMarginPortion*>(pPor) : nullptr;
793 if( !pMar )
794 nMinLeft = 0;
795 else
797 const auto nLineStart =
798 GetLineStart() + pMar->Width();
799 if( nMinLeft > nLineStart )
800 nMinLeft = nLineStart;
805 // 6) Distribute the Glue anew between pLeft and pRight
806 if( nMinLeft < nDropLineStart )
808 // The Glue is always passed from pLeft to pRight, so that
809 // the text moves to the left.
810 const auto nGlue = nDropLineStart - nMinLeft;
811 if( !nMinLeft )
812 pLeft->MoveAllGlue( pRight );
813 else
814 pLeft->MoveGlue( pRight, nGlue );
820 if( nLineNumber != GetLineNr() )
822 Top();
823 while( nLineNumber != GetLineNr() && Next() )
828 void SwTextAdjuster::CalcDropRepaint()
830 Top();
831 SwRepaint &rRepaint = GetInfo().GetParaPortion()->GetRepaint();
832 if( rRepaint.Top() > Y() )
833 rRepaint.Top( Y() );
834 for( sal_Int32 i = 1; i < GetDropLines(); ++i )
835 NextLine();
836 const SwTwips nBottom = Y() + GetLineHeight() - 1;
837 if( rRepaint.Bottom() < nBottom )
838 rRepaint.Bottom( nBottom );
841 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */