blob: 1f8431f1c4b55a55b4a15fc720482eb33256cfbd [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the 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
{
sal_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 sal_uInt16 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
sal_uInt16 nMaxWidthDiff = GetInfo().GetMaxWidthDiff( (sal_uLong)pPos );
// check, if information is stored under other key
if ( !nMaxWidthDiff && pPos == pCurrent->GetFirstPortion() )
nMaxWidthDiff = GetInfo().GetMaxWidthDiff( (sal_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 );
sal_uInt16 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 )
{
sal_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 ] = (sal_uInt16)nCompress;
nKanaDiffSum = 0;
}
nKanaIdx++;
}
nX += pPos->Width();
pPos = pPos->GetPortion();
}
// set portion width
nKanaIdx = 0;
sal_uInt16 nCompress = ( pCurrent->GetKanaComp() )[ nKanaIdx ];
pPos = pCurrent->GetPortion();
long nDecompress = 0;
nKanaDiffSum = 0;
while( pPos )
{
if ( pPos->InTxtGrp() )
{
const sal_uInt16 nMinWidth = pPos->Width();
// get maximum portion width from info structure, calculated
// during text formatting
sal_uInt16 nMaxWidthDiff = GetInfo().GetMaxWidthDiff( (sal_uLong)pPos );
// check, if information is stored under other key
if ( !nMaxWidthDiff && pPos == pCurrent->GetFirstPortion() )
nMaxWidthDiff = GetInfo().GetMaxWidthDiff( (sal_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<sal_uInt16>(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 sal_uInt16 nRealHeight = GetLineHeight();
const sal_uInt16 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 );
}