summaryrefslogtreecommitdiff
path: root/sw/source/core/text/itradj.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sw/source/core/text/itradj.cxx')
-rw-r--r--sw/source/core/text/itradj.cxx919
1 files changed, 919 insertions, 0 deletions
diff --git a/sw/source/core/text/itradj.cxx b/sw/source/core/text/itradj.cxx
new file mode 100644
index 000000000000..b8e361bcae6c
--- /dev/null
+++ b/sw/source/core/text/itradj.cxx
@@ -0,0 +1,919 @@
+ /*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_sw.hxx"
+#ifndef _COM_SUN_STAR_I18N_SCRIPTTYPE_HDL_
+#include <com/sun/star/i18n/ScriptType.hdl>
+#endif
+#include <vcl/outdev.hxx>
+#include <IDocumentSettingAccess.hxx>
+
+#include "frame.hxx" // CalcFlyAdjust()
+#include "paratr.hxx"
+#include "txtcfg.hxx"
+#include "itrtxt.hxx"
+#include "porglue.hxx"
+#include "porlay.hxx"
+#include "porfly.hxx" // CalcFlyAdjust()
+#include "pordrop.hxx" // CalcFlyAdjust()
+#include "pormulti.hxx"
+#include <portab.hxx>
+
+#define MIN_TAB_WIDTH 60
+
+using namespace ::com::sun::star;
+
+/*************************************************************************
+ * SwTxtAdjuster::FormatBlock()
+ *************************************************************************/
+
+void SwTxtAdjuster::FormatBlock( )
+{
+ // In der letzten Zeile gibt's keinen Blocksatz.
+ // Und bei Tabulatoren aus Tradition auch nicht.
+ // 7701: wenn Flys im Spiel sind, geht's weiter
+
+ const SwLinePortion *pFly = 0;
+
+ sal_Bool bSkip = !IsLastBlock() &&
+ nStart + pCurr->GetLen() >= GetInfo().GetTxt().Len();
+
+ // ????: mehrzeilige Felder sind fies: wir muessen kontrollieren,
+ // ob es noch andere Textportions im Absatz gibt.
+ if( bSkip )
+ {
+ const SwLineLayout *pLay = pCurr->GetNext();
+ while( pLay && !pLay->GetLen() )
+ {
+ const SwLinePortion *pPor = pCurr->GetFirstPortion();
+ while( pPor && bSkip )
+ {
+ if( pPor->InTxtGrp() )
+ bSkip = sal_False;
+ pPor = pPor->GetPortion();
+ }
+ pLay = bSkip ? pLay->GetNext() : 0;
+ }
+ }
+
+ if( bSkip )
+ {
+ if( !GetInfo().GetParaPortion()->HasFly() )
+ {
+ if( IsLastCenter() )
+ CalcFlyAdjust( pCurr );
+ pCurr->FinishSpaceAdd();
+ return;
+ }
+ else
+ {
+ const SwLinePortion *pTmpFly = NULL;
+
+ // 7701: beim letzten Fly soll Schluss sein
+ const SwLinePortion *pPos = pCurr->GetFirstPortion();
+ while( pPos )
+ {
+ // Ich suche jetzt den letzten Fly, hinter dem noch Text ist:
+ if( pPos->IsFlyPortion() )
+ pTmpFly = pPos; // Ein Fly wurde gefunden
+ else if ( pTmpFly && pPos->InTxtGrp() )
+ {
+ pFly = pTmpFly; // Ein Fly mit nachfolgendem Text!
+ pTmpFly = NULL;
+ }
+ pPos = pPos->GetPortion();
+ }
+ // 8494: Wenn keiner gefunden wurde, ist sofort Schluss!
+ if( !pFly )
+ {
+ if( IsLastCenter() )
+ CalcFlyAdjust( pCurr );
+ pCurr->FinishSpaceAdd();
+ return;
+ }
+ }
+ }
+
+ const xub_StrLen nOldIdx = GetInfo().GetIdx();
+ GetInfo().SetIdx( nStart );
+ CalcNewBlock( pCurr, pFly );
+ GetInfo().SetIdx( nOldIdx );
+ GetInfo().GetParaPortion()->GetRepaint()->SetOfst(0);
+}
+
+/*************************************************************************
+ * lcl_CheckKashidaPositions()
+ *************************************************************************/
+bool lcl_CheckKashidaPositions( SwScriptInfo& rSI, SwTxtSizeInfo& rInf, SwTxtIter& rItr,
+ xub_StrLen& nKashidas, xub_StrLen& nGluePortion )
+{
+ // i60594 validate Kashida justification
+ xub_StrLen nIdx = rItr.GetStart();
+ xub_StrLen nEnd = rItr.GetEnd();
+
+ // Note on calling KashidaJustify():
+ // Kashida positions may be marked as invalid. Therefore KashidaJustify may return the clean
+ // total number of kashida positions, or the number of kashida positions after some positions
+ // have been dropped.
+ // Here we want the clean total, which is OK: We have called ClearKashidaInvalid() before.
+ nKashidas = rSI.KashidaJustify ( 0, 0, rItr.GetStart(), rItr.GetLength(), 0 );
+
+ if (!nKashidas) // nothing to do
+ return true;
+
+ // kashida positions found in SwScriptInfo are not necessarily valid in every font
+ // if two characters are replaced by a ligature glyph, there will be no place for a kashida
+ xub_StrLen* pKashidaPos = new xub_StrLen [ nKashidas ];
+ xub_StrLen* pKashidaPosDropped = new xub_StrLen [ nKashidas ];
+ rSI.GetKashidaPositions ( nIdx, rItr.GetLength(), pKashidaPos );
+ xub_StrLen nKashidaIdx = 0;
+ while ( nKashidas && nIdx < nEnd )
+ {
+ rItr.SeekAndChgAttrIter( nIdx, rInf.GetOut() );
+ xub_StrLen nNext = rItr.GetNextAttr();
+
+ // is there also a script change before?
+ // if there is, nNext should point to the script change
+ xub_StrLen nNextScript = rSI.NextScriptChg( nIdx );
+ if( nNextScript < nNext )
+ nNext = nNextScript;
+
+ if ( nNext == STRING_LEN || nNext > nEnd )
+ nNext = nEnd;
+ xub_StrLen nKashidasInAttr = rSI.KashidaJustify ( 0, 0, nIdx, nNext - nIdx );
+ if ( nKashidasInAttr )
+ {
+ xub_StrLen nKashidasDropped = 0;
+ if ( !SwScriptInfo::IsArabicText( rInf.GetTxt(), nIdx, nNext - nIdx ) )
+ {
+ nKashidasDropped = nKashidasInAttr;
+ nKashidas -= nKashidasDropped;
+ }
+ else
+ {
+ ULONG nOldLayout = rInf.GetOut()->GetLayoutMode();
+ rInf.GetOut()->SetLayoutMode ( nOldLayout | TEXT_LAYOUT_BIDI_RTL );
+ nKashidasDropped = rInf.GetOut()->ValidateKashidas ( rInf.GetTxt(), nIdx, nNext - nIdx,
+ nKashidasInAttr, pKashidaPos + nKashidaIdx,
+ pKashidaPosDropped );
+ rInf.GetOut()->SetLayoutMode ( nOldLayout );
+ if ( nKashidasDropped )
+ {
+ rSI.MarkKashidasInvalid ( nKashidasDropped, pKashidaPosDropped );
+ nKashidas -= nKashidasDropped;
+ nGluePortion -= nKashidasDropped;
+ }
+ }
+ nKashidaIdx += nKashidasInAttr;
+ }
+ nIdx = nNext;
+ }
+ delete[] pKashidaPos;
+ delete[] pKashidaPosDropped;
+
+ // return false if all kashidas have been eliminated
+ return (nKashidas > 0);
+}
+
+/*************************************************************************
+ * lcl_CheckKashidaWidth()
+ *************************************************************************/
+bool lcl_CheckKashidaWidth ( SwScriptInfo& rSI, SwTxtSizeInfo& rInf, SwTxtIter& rItr, xub_StrLen& nKashidas,
+ xub_StrLen& nGluePortion, const long nGluePortionWidth, long& nSpaceAdd )
+{
+ // check kashida width
+ // if width is smaller than minimal kashida width allowed by fonts in the current line
+ // drop one kashida after the other until kashida width is OK
+ bool bAddSpaceChanged;
+ while ( nKashidas )
+ {
+ bAddSpaceChanged = false;
+ xub_StrLen nIdx = rItr.GetStart();
+ xub_StrLen nEnd = rItr.GetEnd();
+ while ( nIdx < nEnd )
+ {
+ rItr.SeekAndChgAttrIter( nIdx, rInf.GetOut() );
+ xub_StrLen nNext = rItr.GetNextAttr();
+
+ // is there also a script change before?
+ // if there is, nNext should point to the script change
+ xub_StrLen nNextScript = rSI.NextScriptChg( nIdx );
+ if( nNextScript < nNext )
+ nNext = nNextScript;
+
+ if ( nNext == STRING_LEN || nNext > nEnd )
+ nNext = nEnd;
+ xub_StrLen nKashidasInAttr = rSI.KashidaJustify ( 0, 0, nIdx, nNext - nIdx );
+
+ long nFontMinKashida = rInf.GetOut()->GetMinKashida();
+ if ( nFontMinKashida && nKashidasInAttr && SwScriptInfo::IsArabicText( rInf.GetTxt(), nIdx, nNext - nIdx ) )
+ {
+ xub_StrLen nKashidasDropped = 0;
+ while ( nKashidas && nGluePortion && nKashidasInAttr &&
+ nSpaceAdd / SPACING_PRECISION_FACTOR < nFontMinKashida )
+ {
+ --nGluePortion;
+ --nKashidas;
+ --nKashidasInAttr;
+ ++nKashidasDropped;
+ if( !nKashidas || !nGluePortion ) // nothing left, return false to
+ return false; // do regular blank justification
+
+ nSpaceAdd = nGluePortionWidth / nGluePortion;
+ bAddSpaceChanged = true;
+ }
+ if( nKashidasDropped )
+ rSI.MarkKashidasInvalid( nKashidasDropped, nIdx, nNext - nIdx );
+ }
+ if ( bAddSpaceChanged )
+ break; // start all over again
+ nIdx = nNext;
+ }
+ if ( !bAddSpaceChanged )
+ break; // everything was OK
+ }
+ return true;
+}
+
+/*************************************************************************
+ * SwTxtAdjuster::CalcNewBlock()
+ *
+ * CalcNewBlock() darf erst nach CalcLine() gerufen werden !
+ * Aufgespannt wird immer zwischen zwei RandPortions oder FixPortions
+ * (Tabs und Flys). Dabei werden die Glues gezaehlt und ExpandBlock gerufen.
+ *************************************************************************/
+
+void SwTxtAdjuster::CalcNewBlock( SwLineLayout *pCurrent,
+ const SwLinePortion *pStopAt, SwTwips nReal, bool bSkipKashida )
+{
+ ASSERT( GetInfo().IsMulti() || SVX_ADJUST_BLOCK == GetAdjust(),
+ "CalcNewBlock: Why?" );
+ ASSERT( pCurrent->Height(), "SwTxtAdjuster::CalcBlockAdjust: missing CalcLine()" );
+
+ pCurrent->InitSpaceAdd();
+ xub_StrLen nGluePortion = 0;
+ xub_StrLen nCharCnt = 0;
+ MSHORT nSpaceIdx = 0;
+
+ // i60591: hennerdrews
+ SwScriptInfo& rSI = GetInfo().GetParaPortion()->GetScriptInfo();
+ SwTxtSizeInfo aInf ( GetTxtFrm() );
+ SwTxtIter aItr ( GetTxtFrm(), &aInf );
+
+ if ( rSI.CountKashida() )
+ {
+ while (aItr.GetCurr() != pCurrent && aItr.GetNext())
+ aItr.Next();
+
+ if( bSkipKashida )
+ {
+ rSI.SetNoKashidaLine ( aItr.GetStart(), aItr.GetLength());
+ }
+ else
+ {
+ rSI.ClearKashidaInvalid ( aItr.GetStart(), aItr.GetLength() );
+ rSI.ClearNoKashidaLine( aItr.GetStart(), aItr.GetLength() );
+ }
+ }
+
+ // Nicht vergessen:
+ // CalcRightMargin() setzt pCurrent->Width() auf die Zeilenbreite !
+ if (!bSkipKashida)
+ CalcRightMargin( pCurrent, nReal );
+
+ // --> FME 2005-06-08 #i49277#
+ const sal_Bool bDoNotJustifyLinesWithManualBreak =
+ GetTxtFrm()->GetNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::DO_NOT_JUSTIFY_LINES_WITH_MANUAL_BREAK);
+ // <--
+
+ SwLinePortion *pPos = pCurrent->GetPortion();
+
+ while( pPos )
+ {
+ if ( bDoNotJustifyLinesWithManualBreak &&
+ pPos->IsBreakPortion() && !IsLastBlock() )
+ {
+ pCurrent->FinishSpaceAdd();
+ break;
+ }
+
+ if ( pPos->InTxtGrp() )
+ nGluePortion = nGluePortion + ((SwTxtPortion*)pPos)->GetSpaceCnt( GetInfo(), nCharCnt );
+ else if( pPos->IsMultiPortion() )
+ {
+ SwMultiPortion* pMulti = (SwMultiPortion*)pPos;
+ // a multiportion with a tabulator inside breaks the text adjustment
+ // a ruby portion will not be stretched by text adjustment
+ // a double line portion takes additional space for each blank
+ // in the wider line
+ if( pMulti->HasTabulator() )
+ {
+ if ( nSpaceIdx == pCurrent->GetLLSpaceAddCount() )
+ pCurrent->SetLLSpaceAdd( 0, nSpaceIdx );
+
+ nSpaceIdx++;
+ nGluePortion = 0;
+ nCharCnt = 0;
+ }
+ else if( pMulti->IsDouble() )
+ nGluePortion = nGluePortion + ((SwDoubleLinePortion*)pMulti)->GetSpaceCnt();
+ else if ( pMulti->IsBidi() )
+ nGluePortion = nGluePortion + ((SwBidiPortion*)pMulti)->GetSpaceCnt( GetInfo() ); // i60594
+ }
+
+ if( pPos->InGlueGrp() )
+ {
+ if( pPos->InFixMargGrp() )
+ {
+ if ( nSpaceIdx == pCurrent->GetLLSpaceAddCount() )
+ pCurrent->SetLLSpaceAdd( 0, nSpaceIdx );
+
+ const long nGluePortionWidth = static_cast<SwGluePortion*>(pPos)->GetPrtGlue() *
+ SPACING_PRECISION_FACTOR;
+
+ xub_StrLen nKashidas = 0;
+ if( nGluePortion && rSI.CountKashida() && !bSkipKashida )
+ {
+ // kashida positions found in SwScriptInfo are not necessarily valid in every font
+ // if two characters are replaced by a ligature glyph, there will be no place for a kashida
+ if ( !lcl_CheckKashidaPositions ( rSI, aInf, aItr, nKashidas, nGluePortion ))
+ {
+ // all kashida positions are invalid
+ // do regular blank justification
+ pCurrent->FinishSpaceAdd();
+ GetInfo().SetIdx( nStart );
+ CalcNewBlock( pCurrent, pStopAt, nReal, true );
+ return;
+ }
+ }
+
+ if( nGluePortion )
+ {
+ long nSpaceAdd = nGluePortionWidth / nGluePortion;
+
+ // i60594
+ if( rSI.CountKashida() && !bSkipKashida )
+ {
+ if( !lcl_CheckKashidaWidth( rSI, aInf, aItr, nKashidas, nGluePortion, nGluePortionWidth, nSpaceAdd ))
+ {
+ // no kashidas left
+ // do regular blank justification
+ pCurrent->FinishSpaceAdd();
+ GetInfo().SetIdx( nStart );
+ CalcNewBlock( pCurrent, pStopAt, nReal, true );
+ return;
+ }
+ }
+
+ pCurrent->SetLLSpaceAdd( nSpaceAdd , nSpaceIdx );
+ pPos->Width( ( (SwGluePortion*)pPos )->GetFixWidth() );
+ }
+ else if ( IsOneBlock() && nCharCnt > 1 )
+ {
+ const long nSpaceAdd = - nGluePortionWidth / ( nCharCnt - 1 );
+ pCurrent->SetLLSpaceAdd( nSpaceAdd, nSpaceIdx );
+ pPos->Width( ( (SwGluePortion*)pPos )->GetFixWidth() );
+ }
+
+ nSpaceIdx++;
+ nGluePortion = 0;
+ nCharCnt = 0;
+ }
+ else
+ ++nGluePortion;
+ }
+ GetInfo().SetIdx( GetInfo().GetIdx() + pPos->GetLen() );
+ if ( pPos == pStopAt )
+ {
+ pCurrent->SetLLSpaceAdd( 0, nSpaceIdx );
+ break;
+ }
+ pPos = pPos->GetPortion();
+ }
+}
+
+/*************************************************************************
+ * SwTxtAdjuster::CalcKanaAdj()
+ *************************************************************************/
+
+SwTwips SwTxtAdjuster::CalcKanaAdj( SwLineLayout* pCurrent )
+{
+ ASSERT( pCurrent->Height(), "SwTxtAdjuster::CalcBlockAdjust: missing CalcLine()" );
+ ASSERT( !pCurrent->GetpKanaComp(), "pKanaComp already exists!!" );
+
+ SvUShorts *pNewKana = new SvUShorts;
+ pCurrent->SetKanaComp( pNewKana );
+
+ const USHORT nNull = 0;
+ MSHORT nKanaIdx = 0;
+ long nKanaDiffSum = 0;
+ SwTwips nRepaintOfst = 0;
+ SwTwips nX = 0;
+ sal_Bool bNoCompression = sal_False;
+
+ // Nicht vergessen:
+ // CalcRightMargin() setzt pCurrent->Width() auf die Zeilenbreite !
+ CalcRightMargin( pCurrent, 0 );
+
+ SwLinePortion* pPos = pCurrent->GetPortion();
+
+ while( pPos )
+ {
+ if ( pPos->InTxtGrp() )
+ {
+ // get maximum portion width from info structure, calculated
+ // during text formatting
+ USHORT nMaxWidthDiff = GetInfo().GetMaxWidthDiff( (ULONG)pPos );
+
+ // check, if information is stored under other key
+ if ( !nMaxWidthDiff && pPos == pCurrent->GetFirstPortion() )
+ nMaxWidthDiff = GetInfo().GetMaxWidthDiff( (ULONG)pCurrent );
+
+ // calculate difference between portion width and max. width
+ nKanaDiffSum += nMaxWidthDiff;
+
+ // we store the beginning of the first compressable portion
+ // for repaint
+ if ( nMaxWidthDiff && !nRepaintOfst )
+ nRepaintOfst = nX + GetLeftMargin();
+ }
+ else if( pPos->InGlueGrp() && pPos->InFixMargGrp() )
+ {
+ if ( nKanaIdx == pCurrent->GetKanaComp().Count() )
+ pCurrent->GetKanaComp().Insert( nNull, nKanaIdx );
+
+ USHORT nRest;
+
+ if ( pPos->InTabGrp() )
+ {
+ nRest = ! bNoCompression &&
+ ( pPos->Width() > MIN_TAB_WIDTH ) ?
+ pPos->Width() - MIN_TAB_WIDTH :
+ 0;
+
+ // for simplifying the handling of left, right ... tabs,
+ // we do expand portions, which are lying behind
+ // those special tabs
+ bNoCompression = !pPos->IsTabLeftPortion();
+ }
+ else
+ {
+ nRest = ! bNoCompression ?
+ ((SwGluePortion*)pPos)->GetPrtGlue() :
+ 0;
+
+ bNoCompression = sal_False;
+ }
+
+ if( nKanaDiffSum )
+ {
+ ULONG nCompress = ( 10000 * nRest ) / nKanaDiffSum;
+
+ if ( nCompress >= 10000 )
+ // kanas can be expanded to 100%, and there is still
+ // some space remaining
+ nCompress = 0;
+
+ else
+ nCompress = 10000 - nCompress;
+
+ ( pCurrent->GetKanaComp() )[ nKanaIdx ] = (USHORT)nCompress;
+ nKanaDiffSum = 0;
+ }
+
+ nKanaIdx++;
+ }
+
+ nX += pPos->Width();
+ pPos = pPos->GetPortion();
+ }
+
+ // set portion width
+ nKanaIdx = 0;
+ USHORT nCompress = ( pCurrent->GetKanaComp() )[ nKanaIdx ];
+ pPos = pCurrent->GetPortion();
+ long nDecompress = 0;
+ nKanaDiffSum = 0;
+
+ while( pPos )
+ {
+ if ( pPos->InTxtGrp() )
+ {
+ const USHORT nMinWidth = pPos->Width();
+
+ // get maximum portion width from info structure, calculated
+ // during text formatting
+ USHORT nMaxWidthDiff = GetInfo().GetMaxWidthDiff( (ULONG)pPos );
+
+ // check, if information is stored under other key
+ if ( !nMaxWidthDiff && pPos == pCurrent->GetFirstPortion() )
+ nMaxWidthDiff = GetInfo().GetMaxWidthDiff( (ULONG)pCurrent );
+ nKanaDiffSum += nMaxWidthDiff;
+ pPos->Width( nMinWidth +
+ ( ( 10000 - nCompress ) * nMaxWidthDiff ) / 10000 );
+ nDecompress += pPos->Width() - nMinWidth;
+ }
+ else if( pPos->InGlueGrp() && pPos->InFixMargGrp() )
+ {
+ if( nCompress )
+ {
+ nKanaDiffSum *= nCompress;
+ nKanaDiffSum /= 10000;
+ }
+
+ pPos->Width( static_cast<USHORT>(pPos->Width() - nDecompress) );
+
+ if ( pPos->InTabGrp() )
+ // set fix width to width
+ ((SwTabPortion*)pPos)->SetFixWidth( pPos->Width() );
+
+ const SvUShorts& rKanaComp = pCurrent->GetKanaComp();
+ if ( ++nKanaIdx < rKanaComp.Count() )
+ nCompress = ( pCurrent->GetKanaComp() )[ nKanaIdx ];
+
+ nKanaDiffSum = 0;
+ nDecompress = 0;
+ }
+ pPos = pPos->GetPortion();
+ }
+
+ return nRepaintOfst;
+}
+
+/*************************************************************************
+ * SwTxtAdjuster::CalcRightMargin()
+ *************************************************************************/
+
+SwMarginPortion *SwTxtAdjuster::CalcRightMargin( SwLineLayout *pCurrent,
+ SwTwips nReal )
+{
+ long nRealWidth;
+ const USHORT nRealHeight = GetLineHeight();
+ const USHORT nLineHeight = pCurrent->Height();
+
+ KSHORT nPrtWidth = pCurrent->PrtWidth();
+ SwLinePortion *pLast = pCurrent->FindLastPortion();
+
+ if( GetInfo().IsMulti() )
+ nRealWidth = nReal;
+ else
+ {
+ nRealWidth = GetLineWidth();
+ // Fuer jeden FlyFrm, der in den rechten Rand hineinragt,
+ // wird eine FlyPortion angelegt.
+ const long nLeftMar = GetLeftMargin();
+ SwRect aCurrRect( nLeftMar + nPrtWidth, Y() + nRealHeight - nLineHeight,
+ nRealWidth - nPrtWidth, nLineHeight );
+
+ SwFlyPortion *pFly = CalcFlyPortion( nRealWidth, aCurrRect );
+ while( pFly && long( nPrtWidth )< nRealWidth )
+ {
+ pLast->Append( pFly );
+ pLast = pFly;
+ if( pFly->Fix() > nPrtWidth )
+ pFly->Width( ( pFly->Fix() - nPrtWidth) + pFly->Width() + 1);
+ nPrtWidth += pFly->Width() + 1;
+ aCurrRect.Left( nLeftMar + nPrtWidth );
+ pFly = CalcFlyPortion( nRealWidth, aCurrRect );
+ }
+ if( pFly )
+ delete pFly;
+ }
+
+ SwMarginPortion *pRight = new SwMarginPortion( 0 );
+ pLast->Append( pRight );
+
+ if( long( nPrtWidth )< nRealWidth )
+ pRight->PrtWidth( KSHORT( nRealWidth - nPrtWidth ) );
+
+ // pCurrent->Width() wird auf die reale Groesse gesetzt,
+ // da jetzt die MarginPortions eingehaengt sind.
+ // Dieser Trick hat wundersame Auswirkungen.
+ // Wenn pCurrent->Width() == nRealWidth ist, dann wird das gesamte
+ // Adjustment implizit ausgecontert. GetLeftMarginAdjust() und
+ // IsBlocksatz() sind der Meinung, sie haetten eine mit Zeichen
+ // gefuellte Zeile.
+
+ pCurrent->PrtWidth( KSHORT( nRealWidth ) );
+ return pRight;
+}
+
+/*************************************************************************
+ * SwTxtAdjuster::CalcFlyAdjust()
+ *************************************************************************/
+
+void SwTxtAdjuster::CalcFlyAdjust( SwLineLayout *pCurrent )
+{
+ // 1) Es wird ein linker Rand eingefuegt:
+ SwMarginPortion *pLeft = pCurrent->CalcLeftMargin();
+ SwGluePortion *pGlue = pLeft; // die letzte GluePortion
+
+
+ // 2) Es wird ein rechter Rand angehaengt:
+ // CalcRightMargin berechnet auch eventuelle Ueberlappungen mit
+ // FlyFrms.
+ CalcRightMargin( pCurrent );
+
+ SwLinePortion *pPos = pLeft->GetPortion();
+ xub_StrLen nLen = 0;
+
+ // Wenn wir nur eine Zeile vorliegen haben und die Textportion zusammen
+ // haengend ist und wenn zentriert wird, dann ...
+
+ sal_Bool bComplete = 0 == nStart;
+ const sal_Bool bTabCompat = GetTxtFrm()->GetNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::TAB_COMPAT);
+ sal_Bool bMultiTab = sal_False;
+
+ while( pPos )
+ {
+ if ( pPos->IsMultiPortion() && ((SwMultiPortion*)pPos)->HasTabulator() )
+ bMultiTab = sal_True;
+ else if( pPos->InFixMargGrp() &&
+ ( bTabCompat ? ! pPos->InTabGrp() : ! bMultiTab ) )
+ {
+ // in tab compat mode we do not want to change tab portions
+ // in non tab compat mode we do not want to change margins if we
+ // found a multi portion with tabs
+ if( SVX_ADJUST_RIGHT == GetAdjust() )
+ ((SwGluePortion*)pPos)->MoveAllGlue( pGlue );
+ else
+ {
+ // Eine schlaue Idee von MA:
+ // Fuer die erste Textportion wird rechtsbuendig eingestellt,
+ // fuer die letzte linksbuendig.
+
+ // Die erste Textportion kriegt den ganzen Glue
+ // Aber nur, wenn wir mehr als eine Zeile besitzen.
+ if( bComplete && GetInfo().GetTxt().Len() == nLen )
+ ((SwGluePortion*)pPos)->MoveHalfGlue( pGlue );
+ else
+ {
+ if ( ! bTabCompat )
+ {
+ if( pLeft == pGlue )
+ {
+ // Wenn es nur einen linken und rechten Rand gibt,
+ // dann teilen sich die Raender den Glue.
+ if( nLen + pPos->GetLen() >= pCurrent->GetLen() )
+ ((SwGluePortion*)pPos)->MoveHalfGlue( pGlue );
+ else
+ ((SwGluePortion*)pPos)->MoveAllGlue( pGlue );
+ }
+ else
+ {
+ // Die letzte Textportion behaelt sein Glue
+ if( !pPos->IsMarginPortion() )
+ ((SwGluePortion*)pPos)->MoveHalfGlue( pGlue );
+ }
+ }
+ else
+ ((SwGluePortion*)pPos)->MoveHalfGlue( pGlue );
+ }
+ }
+
+ pGlue = (SwFlyPortion*)pPos;
+ bComplete = sal_False;
+ }
+ nLen = nLen + pPos->GetLen();
+ pPos = pPos->GetPortion();
+ }
+
+ if( ! bTabCompat && ! bMultiTab && SVX_ADJUST_RIGHT == GetAdjust() )
+ // portions are moved to the right if possible
+ pLeft->AdjustRight( pCurrent );
+}
+
+/*************************************************************************
+ * SwTxtAdjuster::CalcAdjLine()
+ *************************************************************************/
+
+void SwTxtAdjuster::CalcAdjLine( SwLineLayout *pCurrent )
+{
+ ASSERT( pCurrent->IsFormatAdj(), "CalcAdjLine: Why?" );
+
+ pCurrent->SetFormatAdj(sal_False);
+
+ SwParaPortion* pPara = GetInfo().GetParaPortion();
+
+ switch( GetAdjust() )
+ {
+ case SVX_ADJUST_RIGHT:
+ case SVX_ADJUST_CENTER:
+ {
+ CalcFlyAdjust( pCurrent );
+ pPara->GetRepaint()->SetOfst( 0 );
+ break;
+ }
+ case SVX_ADJUST_BLOCK:
+ {
+ // disabled for #i13507#
+ // 8311: In Zeilen mit LineBreaks gibt es keinen Blocksatz!
+/* if( pCurrent->GetLen() &&
+ CH_BREAK == GetInfo().GetChar( nStart + pCurrent->GetLen() - 1 ) &&
+ !IsLastBlock() )
+ {
+ if( IsLastCenter() )
+ {
+ CalcFlyAdjust( pCurrent );
+ pPara->GetRepaint()->SetOfst( 0 );
+ break;
+ }
+ return;
+ }
+*/ FormatBlock();
+ break;
+ }
+ default : return;
+ }
+}
+
+/*************************************************************************
+ * SwTxtAdjuster::CalcFlyPortion()
+ *
+ * Die Berechnung hat es in sich: nCurrWidth geibt die Breite _vor_ dem
+ * aufaddieren des Wortes das noch auf die Zeile passt! Aus diesem Grund
+ * stimmt die Breite der FlyPortion auch, wenn die Blockierungssituation
+ * bFirstWord && !WORDFITS eintritt.
+ *************************************************************************/
+
+SwFlyPortion *SwTxtAdjuster::CalcFlyPortion( const long nRealWidth,
+ const SwRect &rCurrRect )
+{
+ SwTxtFly aTxtFly( GetTxtFrm() );
+
+ const KSHORT nCurrWidth = pCurr->PrtWidth();
+ SwFlyPortion *pFlyPortion = 0;
+
+ SwRect aLineVert( rCurrRect );
+ if ( GetTxtFrm()->IsRightToLeft() )
+ GetTxtFrm()->SwitchLTRtoRTL( aLineVert );
+ if ( GetTxtFrm()->IsVertical() )
+ GetTxtFrm()->SwitchHorizontalToVertical( aLineVert );
+
+ // aFlyRect ist dokumentglobal !
+ SwRect aFlyRect( aTxtFly.GetFrm( aLineVert ) );
+
+ if ( GetTxtFrm()->IsRightToLeft() )
+ GetTxtFrm()->SwitchRTLtoLTR( aFlyRect );
+ if ( GetTxtFrm()->IsVertical() )
+ GetTxtFrm()->SwitchVerticalToHorizontal( aFlyRect );
+
+ // Wenn ein Frame ueberlappt, wird eine Portion eroeffnet.
+ if( aFlyRect.HasArea() )
+ {
+ // aLocal ist framelokal
+ SwRect aLocal( aFlyRect );
+ aLocal.Pos( aLocal.Left() - GetLeftMargin(), aLocal.Top() );
+ if( nCurrWidth > aLocal.Left() )
+ aLocal.Left( nCurrWidth );
+
+ // Wenn das Rechteck breiter als die Zeile ist, stutzen
+ // wir es ebenfalls zurecht.
+ KSHORT nLocalWidth = KSHORT( aLocal.Left() + aLocal.Width() );
+ if( nRealWidth < long( nLocalWidth ) )
+ aLocal.Width( nRealWidth - aLocal.Left() );
+ GetInfo().GetParaPortion()->SetFly( sal_True );
+ pFlyPortion = new SwFlyPortion( aLocal );
+ pFlyPortion->Height( KSHORT( rCurrRect.Height() ) );
+ // Die Width koennte kleiner sein als die FixWidth, daher:
+ pFlyPortion->AdjFixWidth();
+ }
+ return pFlyPortion;
+}
+
+/*************************************************************************
+ * SwTxtPainter::_CalcDropAdjust()
+ *************************************************************************/
+
+// 6721: Drops und Adjustment
+// CalcDropAdjust wird ggf. am Ende von Format() gerufen.
+
+void SwTxtAdjuster::CalcDropAdjust()
+{
+ ASSERT( 1<GetDropLines() && SVX_ADJUST_LEFT!=GetAdjust() && SVX_ADJUST_BLOCK!=GetAdjust(),
+ "CalcDropAdjust: No reason for DropAdjustment." )
+
+ const MSHORT nLineNumber = GetLineNr();
+
+ // 1) Dummies ueberspringen
+ Top();
+
+ if( !pCurr->IsDummy() || NextLine() )
+ {
+ // Erst adjustieren.
+ GetAdjusted();
+
+ SwLinePortion *pPor = pCurr->GetFirstPortion();
+
+ // 2) Sicherstellen, dass die DropPortion dabei ist.
+ // 3) pLeft: Die GluePor vor der DropPor
+ if( pPor->InGlueGrp() && pPor->GetPortion()
+ && pPor->GetPortion()->IsDropPortion() )
+ {
+ const SwLinePortion *pDropPor = (SwDropPortion*) pPor->GetPortion();
+ SwGluePortion *pLeft = (SwGluePortion*) pPor;
+
+ // 4) pRight: Die GluePor hinter der DropPor suchen
+ pPor = pPor->GetPortion();
+ while( pPor && !pPor->InFixMargGrp() )
+ pPor = pPor->GetPortion();
+
+ SwGluePortion *pRight = ( pPor && pPor->InGlueGrp() ) ?
+ (SwGluePortion*) pPor : 0;
+ if( pRight && pRight != pLeft )
+ {
+ // 5) nMinLeft berechnen. Wer steht am weitesten links?
+ const KSHORT nDropLineStart =
+ KSHORT(GetLineStart()) + pLeft->Width() + pDropPor->Width();
+ KSHORT nMinLeft = nDropLineStart;
+ for( MSHORT i = 1; i < GetDropLines(); ++i )
+ {
+ if( NextLine() )
+ {
+ // Erst adjustieren.
+ GetAdjusted();
+
+ pPor = pCurr->GetFirstPortion();
+ const SwMarginPortion *pMar = pPor->IsMarginPortion() ?
+ (SwMarginPortion*)pPor : 0;
+ if( !pMar )
+ nMinLeft = 0;
+ else
+ {
+ const KSHORT nLineStart =
+ KSHORT(GetLineStart()) + pMar->Width();
+ if( nMinLeft > nLineStart )
+ nMinLeft = nLineStart;
+ }
+ }
+ }
+
+ // 6) Den Glue zwischen pLeft und pRight neu verteilen.
+ if( nMinLeft < nDropLineStart )
+ {
+ // Glue wird immer von pLeft nach pRight abgegeben,
+ // damit der Text nach links wandert.
+ const short nGlue = nDropLineStart - nMinLeft;
+ if( !nMinLeft )
+ pLeft->MoveAllGlue( pRight );
+ else
+ pLeft->MoveGlue( pRight, nGlue );
+#ifdef DBGTXT
+ aDbstream << "Drop adjusted: " << nGlue << endl;
+#endif
+ }
+ }
+ }
+ }
+
+ if( nLineNumber != GetLineNr() )
+ {
+ Top();
+ while( nLineNumber != GetLineNr() && Next() )
+ ;
+ }
+}
+
+/*************************************************************************
+ * SwTxtAdjuster::CalcDropRepaint()
+ *************************************************************************/
+
+void SwTxtAdjuster::CalcDropRepaint()
+{
+ Top();
+ SwRepaint &rRepaint = *GetInfo().GetParaPortion()->GetRepaint();
+ if( rRepaint.Top() > Y() )
+ rRepaint.Top( Y() );
+ for( MSHORT i = 1; i < GetDropLines(); ++i )
+ NextLine();
+ const SwTwips nBottom = Y() + GetLineHeight() - 1;
+ if( rRepaint.Bottom() < nBottom )
+ rRepaint.Bottom( nBottom );
+}
+
+