summaryrefslogtreecommitdiff
path: root/sw/source/core/text/pormulti.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sw/source/core/text/pormulti.cxx')
-rw-r--r--sw/source/core/text/pormulti.cxx2415
1 files changed, 2415 insertions, 0 deletions
diff --git a/sw/source/core/text/pormulti.cxx b/sw/source/core/text/pormulti.cxx
new file mode 100644
index 000000000000..f9f86db667b9
--- /dev/null
+++ b/sw/source/core/text/pormulti.cxx
@@ -0,0 +1,2415 @@
+/*************************************************************************
+ *
+ * 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"
+
+
+#include <hintids.hxx>
+
+#include <com/sun/star/i18n/ScriptType.hdl>
+#include <editeng/twolinesitem.hxx>
+#include <editeng/charrotateitem.hxx>
+#include <vcl/outdev.hxx>
+#include <fmtfld.hxx>
+#include <fldbas.hxx> // SwField
+#include <txatbase.hxx>
+#include <fmtruby.hxx> // SwFmtRuby
+#include <txtatr.hxx> // SwTxtRuby
+#include <charfmt.hxx>
+#include <txtinet.hxx>
+#include <fchrfmt.hxx>
+#include <layfrm.hxx> // GetUpper()
+#include <SwPortionHandler.hxx>
+#include <pormulti.hxx> // SwMultiPortion
+#include <inftxt.hxx> // SwTxtSizeInfo
+#include <itrpaint.hxx> // SwTxtPainter
+#include <viewopt.hxx> // SwViewOptions
+#include <itrform2.hxx> // SwTxtFormatter
+#include <porfld.hxx> // SwFldPortion
+#include <porglue.hxx>
+#include <breakit.hxx>
+#include <pagefrm.hxx>
+#include <rowfrm.hxx>
+#include <pagedesc.hxx> // SwPageDesc
+#include <tgrditem.hxx>
+#include <swtable.hxx>
+#include <fmtfsize.hxx>
+
+using namespace ::com::sun::star;
+extern sal_Bool IsUnderlineBreak( const SwLinePortion& rPor, const SwFont& rFnt );
+
+/*-----------------10.10.00 15:23-------------------
+ * class SwMultiPortion
+ *
+ * A SwMultiPortion is not a simple portion,
+ * it's a container, which contains almost a SwLineLayoutPortion.
+ * This SwLineLayout could be followed by other textportions via pPortion
+ * and by another SwLineLayout via pNext to realize a doubleline portion.
+ * --------------------------------------------------*/
+
+SwMultiPortion::~SwMultiPortion()
+{
+ delete pFldRest;
+}
+
+void SwMultiPortion::Paint( const SwTxtPaintInfo & ) const
+{
+ ASSERT( FALSE,
+ "Don't try SwMultiPortion::Paint, try SwTxtPainter::PaintMultiPortion" );
+}
+
+/*-----------------13.10.00 16:21-------------------
+ * Summarize the internal lines to calculate the (external) size.
+ * The internal line has to calculate first.
+ * --------------------------------------------------*/
+
+void SwMultiPortion::CalcSize( SwTxtFormatter& rLine, SwTxtFormatInfo &rInf )
+{
+ Width( 0 );
+ Height( 0 );
+ SetAscent( 0 );
+ SetFlyInCntnt( sal_False );
+ SwLineLayout *pLay = &GetRoot();
+ do
+ {
+ pLay->CalcLine( rLine, rInf );
+ if( rLine.IsFlyInCntBase() )
+ SetFlyInCntnt( sal_True );
+ if( IsRuby() && ( OnTop() == ( pLay == &GetRoot() ) ) )
+ {
+ // An empty phonetic line don't need an ascent or a height.
+ if( !pLay->Width() )
+ {
+ pLay->SetAscent( 0 );
+ pLay->Height( 0 );
+ }
+ if( OnTop() )
+ SetAscent( GetAscent() + pLay->Height() );
+ }
+ else
+ SetAscent( GetAscent() + pLay->GetAscent() );
+ Height( Height() + pLay->Height() );
+ if( Width() < pLay->Width() )
+ Width( pLay->Width() );
+ pLay = pLay->GetNext();
+ } while ( pLay );
+ if( HasBrackets() )
+ {
+ KSHORT nTmp = ((SwDoubleLinePortion*)this)->GetBrackets()->nHeight;
+ if( nTmp > Height() )
+ {
+ KSHORT nAdd = ( nTmp - Height() ) / 2;
+ GetRoot().SetAscent( GetRoot().GetAscent() + nAdd );
+ GetRoot().Height( GetRoot().Height() + nAdd );
+ Height( nTmp );
+ }
+ nTmp = ((SwDoubleLinePortion*)this)->GetBrackets()->nAscent;
+ if( nTmp > GetAscent() )
+ SetAscent( nTmp );
+ }
+}
+
+long SwMultiPortion::CalcSpacing( long , const SwTxtSizeInfo & ) const
+{
+ return 0;
+}
+
+sal_Bool SwMultiPortion::ChgSpaceAdd( SwLineLayout*, long ) const
+{
+ return sal_False;
+}
+
+/*************************************************************************
+ * virtual SwMultiPortion::HandlePortion()
+ *************************************************************************/
+
+void SwMultiPortion::HandlePortion( SwPortionHandler& rPH ) const
+{
+ rPH.Text( GetLen(), GetWhichPor() );
+}
+
+/*-----------------01.11.00 14:21-------------------
+ * SwMultiPortion::ActualizeTabulator()
+ * sets the tabulator-flag, if there's any tabulator-portion inside.
+ * --------------------------------------------------*/
+
+void SwMultiPortion::ActualizeTabulator()
+{
+ SwLinePortion* pPor = GetRoot().GetFirstPortion();
+ // First line
+ for( bTab1 = bTab2 = sal_False; pPor; pPor = pPor->GetPortion() )
+ if( pPor->InTabGrp() )
+ SetTab1( sal_True );
+ if( GetRoot().GetNext() )
+ {
+ // Second line
+ pPor = GetRoot().GetNext()->GetFirstPortion();
+ do
+ {
+ if( pPor->InTabGrp() )
+ SetTab2( sal_True );
+ pPor = pPor->GetPortion();
+ } while ( pPor );
+ }
+}
+
+/*-----------------16.02.01 12:07-------------------
+ * SwRotatedPortion::SwRotatedPortion(..)
+ * --------------------------------------------------*/
+
+SwRotatedPortion::SwRotatedPortion( const SwMultiCreator& rCreate,
+ xub_StrLen nEnd, sal_Bool bRTL ) : SwMultiPortion( nEnd )
+{
+ const SvxCharRotateItem* pRot = (SvxCharRotateItem*)rCreate.pItem;
+ if( !pRot )
+ {
+ const SwTxtAttr& rAttr = *rCreate.pAttr;
+ const SfxPoolItem *const pItem =
+ CharFmt::GetItem(rAttr, RES_CHRATR_ROTATE);
+ if ( pItem )
+ {
+ pRot = static_cast<const SvxCharRotateItem*>(pItem);
+ }
+ }
+ if( pRot )
+ {
+ sal_uInt8 nDir;
+ if ( bRTL )
+ nDir = pRot->IsBottomToTop() ? 3 : 1;
+ else
+ nDir = pRot->IsBottomToTop() ? 1 : 3;
+
+ SetDirection( nDir );
+ }
+}
+
+/*---------------------------------------------------
+ * SwBidiPortion::SwBidiPortion(..)
+ * --------------------------------------------------*/
+
+SwBidiPortion::SwBidiPortion( xub_StrLen nEnd, BYTE nLv )
+ : SwMultiPortion( nEnd ), nLevel( nLv )
+{
+ SetBidi();
+
+ if ( nLevel % 2 )
+ SetDirection( DIR_RIGHT2LEFT );
+ else
+ SetDirection( DIR_LEFT2RIGHT );
+}
+
+
+long SwBidiPortion::CalcSpacing( long nSpaceAdd, const SwTxtSizeInfo& rInf ) const
+{
+ return HasTabulator() ? 0 : GetSpaceCnt(rInf) * nSpaceAdd / SPACING_PRECISION_FACTOR;
+}
+
+sal_Bool SwBidiPortion::ChgSpaceAdd( SwLineLayout* pCurr, long nSpaceAdd ) const
+{
+ sal_Bool bRet = sal_False;
+ if( !HasTabulator() && nSpaceAdd > 0 && !pCurr->IsSpaceAdd() )
+ {
+ pCurr->CreateSpaceAdd();
+ pCurr->SetLLSpaceAdd( nSpaceAdd, 0 );
+ bRet = sal_True;
+ }
+
+ return bRet;
+}
+
+xub_StrLen SwBidiPortion::GetSpaceCnt( const SwTxtSizeInfo &rInf ) const
+{
+ // Calculate number of blanks for justified alignment
+ SwLinePortion* pPor = GetRoot().GetFirstPortion();
+ xub_StrLen nTmpStart = rInf.GetIdx();
+ xub_StrLen nNull = 0;
+ xub_StrLen nBlanks;
+
+ for( nBlanks = 0; pPor; pPor = pPor->GetPortion() )
+ {
+ if( pPor->InTxtGrp() )
+ nBlanks = nBlanks + ((SwTxtPortion*)pPor)->GetSpaceCnt( rInf, nNull );
+ else if ( pPor->IsMultiPortion() &&
+ ((SwMultiPortion*)pPor)->IsBidi() )
+ nBlanks = nBlanks + ((SwBidiPortion*)pPor)->GetSpaceCnt( rInf );
+
+ ((SwTxtSizeInfo &)rInf).SetIdx( rInf.GetIdx() + pPor->GetLen() );
+ }
+ ((SwTxtSizeInfo &)rInf).SetIdx( nTmpStart );
+ return nBlanks;
+}
+
+/*-----------------01.11.00 14:22-------------------
+ * SwDoubleLinePortion::SwDoubleLinePortion(..)
+ * This constructor is for the continuation of a doubleline portion
+ * in the next line.
+ * It takes the same brackets and if the original has no content except
+ * brackets, these will be deleted.
+ * --------------------------------------------------*/
+
+SwDoubleLinePortion::SwDoubleLinePortion( SwDoubleLinePortion& rDouble,
+ xub_StrLen nEnd ) :
+ SwMultiPortion( nEnd ),
+ pBracket( 0 )
+{
+ SetDirection( rDouble.GetDirection() );
+ SetDouble();
+ if( rDouble.GetBrackets() )
+ {
+ SetBrackets( rDouble );
+ // An empty multiportion needs no brackets.
+ // Notice: GetLen() might be zero, if the multiportion contains
+ // the second part of a field and the width might be zero, if
+ // it contains a note only. In this cases the brackets are okay.
+ // But if the length and the width are both zero, the portion
+ // is really empty.
+ if( rDouble.Width() == rDouble.BracketWidth() )
+ rDouble.ClearBrackets();
+ }
+}
+
+/*-----------------01.11.00 14:22-------------------
+ * SwDoubleLinePortion::SwDoubleLinePortion(..)
+ * This constructor uses the textattribut to get the right brackets.
+ * The textattribut could be a 2-line-attribute or a character- or
+ * internetstyle, which contains the 2-line-attribute.
+ * --------------------------------------------------*/
+
+SwDoubleLinePortion::SwDoubleLinePortion( const SwMultiCreator& rCreate,
+ xub_StrLen nEnd ) : SwMultiPortion( nEnd ), pBracket( new SwBracket() )
+{
+ SetDouble();
+ const SvxTwoLinesItem* pTwo = (SvxTwoLinesItem*)rCreate.pItem;
+ if( pTwo )
+ pBracket->nStart = 0;
+ else
+ {
+ const SwTxtAttr& rAttr = *rCreate.pAttr;
+ pBracket->nStart = *rAttr.GetStart();
+
+ const SfxPoolItem * const pItem =
+ CharFmt::GetItem( rAttr, RES_CHRATR_TWO_LINES );
+ if ( pItem )
+ {
+ pTwo = static_cast<const SvxTwoLinesItem*>(pItem);
+ }
+ }
+ if( pTwo )
+ {
+ pBracket->cPre = pTwo->GetStartBracket();
+ pBracket->cPost = pTwo->GetEndBracket();
+ }
+ else
+ {
+ pBracket->cPre = 0;
+ pBracket->cPost = 0;
+ }
+ BYTE nTmp = SW_SCRIPTS;
+ if( pBracket->cPre > 255 )
+ {
+ String aTxt( pBracket->cPre );
+ nTmp = SwScriptInfo::WhichFont( 0, &aTxt, 0 );
+ }
+ pBracket->nPreScript = nTmp;
+ nTmp = SW_SCRIPTS;
+ if( pBracket->cPost > 255 )
+ {
+ String aTxt( pBracket->cPost );
+ nTmp = SwScriptInfo::WhichFont( 0, &aTxt, 0 );
+ }
+ pBracket->nPostScript = nTmp;
+
+ if( !pBracket->cPre && !pBracket->cPost )
+ {
+ delete pBracket;
+ pBracket = 0;
+ }
+
+ // double line portions have the same direction as the frame directions
+ if ( rCreate.nLevel % 2 )
+ SetDirection( DIR_RIGHT2LEFT );
+ else
+ SetDirection( DIR_LEFT2RIGHT );
+}
+
+
+/*-----------------25.10.00 09:51-------------------
+ * SwMultiPortion::PaintBracket paints the wished bracket,
+ * if the multiportion has surrounding brackets.
+ * The X-position of the SwTxtPaintInfo will be modified:
+ * the open bracket sets position behind itself,
+ * the close bracket in front of itself.
+ * --------------------------------------------------*/
+
+void SwDoubleLinePortion::PaintBracket( SwTxtPaintInfo &rInf,
+ long nSpaceAdd,
+ sal_Bool bOpen ) const
+{
+ sal_Unicode cCh = bOpen ? pBracket->cPre : pBracket->cPost;
+ if( !cCh )
+ return;
+ KSHORT nChWidth = bOpen ? PreWidth() : PostWidth();
+ if( !nChWidth )
+ return;
+ if( !bOpen )
+ rInf.X( rInf.X() + Width() - PostWidth() +
+ ( nSpaceAdd > 0 ? CalcSpacing( nSpaceAdd, rInf ) : 0 ) );
+
+ SwBlankPortion aBlank( cCh, sal_True );
+ aBlank.SetAscent( pBracket->nAscent );
+ aBlank.Width( nChWidth );
+ aBlank.Height( pBracket->nHeight );
+ {
+ SwFont* pTmpFnt = new SwFont( *rInf.GetFont() );
+ BYTE nAct = bOpen ? pBracket->nPreScript : pBracket->nPostScript;
+ if( SW_SCRIPTS > nAct )
+ pTmpFnt->SetActual( nAct );
+ pTmpFnt->SetProportion( 100 );
+ SwFontSave aSave( rInf, pTmpFnt );
+ aBlank.Paint( rInf );
+ delete pTmpFnt;
+ }
+ if( bOpen )
+ rInf.X( rInf.X() + PreWidth() );
+}
+
+/*-----------------25.10.00 16:26-------------------
+ * SwDoubleLinePortion::SetBrackets creates the bracket-structur
+ * and fills it, if not both characters are 0x00.
+ * --------------------------------------------------*/
+
+void SwDoubleLinePortion::SetBrackets( const SwDoubleLinePortion& rDouble )
+{
+ if( rDouble.pBracket )
+ {
+ pBracket = new SwBracket;
+ pBracket->cPre = rDouble.pBracket->cPre;
+ pBracket->cPost = rDouble.pBracket->cPost;
+ pBracket->nPreScript = rDouble.pBracket->nPreScript;
+ pBracket->nPostScript = rDouble.pBracket->nPostScript;
+ pBracket->nStart = rDouble.pBracket->nStart;
+ }
+}
+
+/*-----------------25.10.00 16:29-------------------
+ * SwDoubleLinePortion::FormatBrackets
+ * calculates the size of the brackets => pBracket,
+ * reduces the nMaxWidth-parameter ( minus bracket-width )
+ * and moves the rInf-x-position behind the opening bracket.
+ * --------------------------------------------------*/
+
+void SwDoubleLinePortion::FormatBrackets( SwTxtFormatInfo &rInf, SwTwips& nMaxWidth )
+{
+ nMaxWidth -= rInf.X();
+ SwFont* pTmpFnt = new SwFont( *rInf.GetFont() );
+ pTmpFnt->SetProportion( 100 );
+ pBracket->nAscent = 0;
+ pBracket->nHeight = 0;
+ if( pBracket->cPre )
+ {
+ String aStr( pBracket->cPre );
+ BYTE nActualScr = pTmpFnt->GetActual();
+ if( SW_SCRIPTS > pBracket->nPreScript )
+ pTmpFnt->SetActual( pBracket->nPreScript );
+ SwFontSave aSave( rInf, pTmpFnt );
+ SwPosSize aSize = rInf.GetTxtSize( aStr );
+ pBracket->nAscent = rInf.GetAscent();
+ pBracket->nHeight = aSize.Height();
+ pTmpFnt->SetActual( nActualScr );
+ if( nMaxWidth > aSize.Width() )
+ {
+ pBracket->nPreWidth = aSize.Width();
+ nMaxWidth -= aSize.Width();
+ rInf.X( rInf.X() + aSize.Width() );
+ }
+ else
+ {
+ pBracket->nPreWidth = 0;
+ nMaxWidth = 0;
+ }
+ }
+ else
+ pBracket->nPreWidth = 0;
+ if( pBracket->cPost )
+ {
+ String aStr( pBracket->cPost );
+ if( SW_SCRIPTS > pBracket->nPostScript )
+ pTmpFnt->SetActual( pBracket->nPostScript );
+ SwFontSave aSave( rInf, pTmpFnt );
+ SwPosSize aSize = rInf.GetTxtSize( aStr );
+ KSHORT nTmpAsc = rInf.GetAscent();
+ if( nTmpAsc > pBracket->nAscent )
+ {
+ pBracket->nHeight += nTmpAsc - pBracket->nAscent;
+ pBracket->nAscent = nTmpAsc;
+ }
+ if( aSize.Height() > pBracket->nHeight )
+ pBracket->nHeight = aSize.Height();
+ if( nMaxWidth > aSize.Width() )
+ {
+ pBracket->nPostWidth = aSize.Width();
+ nMaxWidth -= aSize.Width();
+ }
+ else
+ {
+ pBracket->nPostWidth = 0;
+ nMaxWidth = 0;
+ }
+ }
+ else
+ pBracket->nPostWidth = 0;
+ nMaxWidth += rInf.X();
+}
+
+/*-----------------26.10.00 10:36-------------------
+ * SwDoubleLinePortion::CalcBlanks
+ * calculates the number of blanks in each line and
+ * the difference of the width of the two lines.
+ * These results are used from the text adjustment.
+ * --------------------------------------------------*/
+
+void SwDoubleLinePortion::CalcBlanks( SwTxtFormatInfo &rInf )
+{
+ SwLinePortion* pPor = GetRoot().GetFirstPortion();
+ xub_StrLen nNull = 0;
+ xub_StrLen nStart = rInf.GetIdx();
+ SetTab1( sal_False );
+ SetTab2( sal_False );
+ for( nBlank1 = 0; pPor; pPor = pPor->GetPortion() )
+ {
+ if( pPor->InTxtGrp() )
+ nBlank1 = nBlank1 + ((SwTxtPortion*)pPor)->GetSpaceCnt( rInf, nNull );
+ rInf.SetIdx( rInf.GetIdx() + pPor->GetLen() );
+ if( pPor->InTabGrp() )
+ SetTab1( sal_True );
+ }
+ nLineDiff = GetRoot().Width();
+ if( GetRoot().GetNext() )
+ {
+ pPor = GetRoot().GetNext()->GetFirstPortion();
+ nLineDiff -= GetRoot().GetNext()->Width();
+ }
+ for( nBlank2 = 0; pPor; pPor = pPor->GetPortion() )
+ {
+ if( pPor->InTxtGrp() )
+ nBlank2 = nBlank2 + ((SwTxtPortion*)pPor)->GetSpaceCnt( rInf, nNull );
+ rInf.SetIdx( rInf.GetIdx() + pPor->GetLen() );
+ if( pPor->InTabGrp() )
+ SetTab2( sal_True );
+ }
+ rInf.SetIdx( nStart );
+}
+
+long SwDoubleLinePortion::CalcSpacing( long nSpaceAdd, const SwTxtSizeInfo & ) const
+{
+ return HasTabulator() ? 0 : GetSpaceCnt() * nSpaceAdd / SPACING_PRECISION_FACTOR;
+}
+
+/*-----------------01.11.00 14:29-------------------
+ * SwDoubleLinePortion::ChangeSpaceAdd(..)
+ * merges the spaces for text adjustment from the inner and outer part.
+ * Inside the doubleline portion the wider line has no spaceadd-array, the
+ * smaller line has such an array to reach width of the wider line.
+ * If the surrounding line has text adjustment and the doubleline portion
+ * contains no tabulator, it is necessary to create/manipulate the inner
+ * space arrays.
+ * --------------------------------------------------*/
+
+sal_Bool SwDoubleLinePortion::ChgSpaceAdd( SwLineLayout* pCurr,
+ long nSpaceAdd ) const
+{
+ sal_Bool bRet = sal_False;
+ if( !HasTabulator() && nSpaceAdd > 0 )
+ {
+ if( !pCurr->IsSpaceAdd() )
+ {
+ // The wider line gets the spaceadd from the surrounding line direct
+ pCurr->CreateSpaceAdd();
+ pCurr->SetLLSpaceAdd( nSpaceAdd, 0 );
+ bRet = sal_True;
+ }
+ else
+ {
+ xub_StrLen nMyBlank = GetSmallerSpaceCnt();
+ xub_StrLen nOther = GetSpaceCnt();
+ SwTwips nMultiSpace = pCurr->GetLLSpaceAdd( 0 ) * nMyBlank + nOther * nSpaceAdd;
+
+ if( nMyBlank )
+ nMultiSpace /= nMyBlank;
+
+ if( nMultiSpace < KSHRT_MAX * SPACING_PRECISION_FACTOR )
+ {
+// pCurr->SetLLSpaceAdd( nMultiSpace, 0 );
+ // --> FME 2006-07-11 #i65711# SetLLSpaceAdd replaces the first value,
+ // instead we want to insert a new first value:
+ std::vector<long>* pVec = pCurr->GetpLLSpaceAdd();
+ pVec->insert( pVec->begin(), nMultiSpace );
+ // <--
+ bRet = sal_True;
+ }
+ }
+ }
+ return bRet;
+}
+/*-----------------01.11.00 14:29-------------------
+ * SwDoubleLinePortion::ResetSpaceAdd(..)
+ * cancels the manipulation from SwDoubleLinePortion::ChangeSpaceAdd(..)
+ * --------------------------------------------------*/
+
+void SwDoubleLinePortion::ResetSpaceAdd( SwLineLayout* pCurr )
+{
+ pCurr->RemoveFirstLLSpaceAdd();;
+ if( !pCurr->GetLLSpaceAddCount() )
+ pCurr->FinishSpaceAdd();
+}
+
+SwDoubleLinePortion::~SwDoubleLinePortion()
+{
+ delete pBracket;
+}
+
+/*-----------------13.11.00 14:50-------------------
+ * SwRubyPortion::SwRubyPortion(..)
+ * constructs a ruby portion, i.e. an additional text is displayed
+ * beside the main text, e.g. phonetic characters.
+ * --------------------------------------------------*/
+
+
+SwRubyPortion::SwRubyPortion( const SwRubyPortion& rRuby, xub_StrLen nEnd ) :
+ SwMultiPortion( nEnd ),
+ nRubyOffset( rRuby.GetRubyOffset() ),
+ nAdjustment( rRuby.GetAdjustment() )
+{
+ SetDirection( rRuby.GetDirection() ),
+ SetTop( rRuby.OnTop() );
+ SetRuby();
+}
+
+/*-----------------13.11.00 14:50-------------------
+ * SwRubyPortion::SwRubyPortion(..)
+ * constructs a ruby portion, i.e. an additional text is displayed
+ * beside the main text, e.g. phonetic characters.
+ * --------------------------------------------------*/
+
+SwRubyPortion::SwRubyPortion( const SwMultiCreator& rCreate, const SwFont& rFnt,
+ const IDocumentSettingAccess& rIDocumentSettingAccess,
+ xub_StrLen nEnd, xub_StrLen nOffs,
+ const sal_Bool* pForceRubyPos )
+ : SwMultiPortion( nEnd )
+{
+ SetRuby();
+ ASSERT( SW_MC_RUBY == rCreate.nId, "Ruby expected" );
+ ASSERT( RES_TXTATR_CJK_RUBY == rCreate.pAttr->Which(), "Wrong attribute" );
+ const SwFmtRuby& rRuby = rCreate.pAttr->GetRuby();
+ nAdjustment = rRuby.GetAdjustment();
+ nRubyOffset = nOffs;
+
+ // in grid mode we force the ruby text to the upper or lower line
+ if ( pForceRubyPos )
+ SetTop( *pForceRubyPos );
+ else
+ SetTop( ! rRuby.GetPosition() );
+
+ const SwCharFmt* pFmt = ((SwTxtRuby*)rCreate.pAttr)->GetCharFmt();
+ SwFont *pRubyFont;
+ if( pFmt )
+ {
+ const SwAttrSet& rSet = pFmt->GetAttrSet();
+ pRubyFont = new SwFont( rFnt );
+ pRubyFont->SetDiffFnt( &rSet, &rIDocumentSettingAccess );
+
+ // we do not allow a vertical font for the ruby text
+ pRubyFont->SetVertical( rFnt.GetOrientation() );
+ }
+ else
+ pRubyFont = NULL;
+
+ String aStr( rRuby.GetText(), nOffs, STRING_LEN );
+ SwFldPortion *pFld = new SwFldPortion( aStr, pRubyFont );
+ pFld->SetNextOffset( nOffs );
+ pFld->SetFollow( sal_True );
+
+ if( OnTop() )
+ GetRoot().SetPortion( pFld );
+ else
+ {
+ GetRoot().SetNext( new SwLineLayout() );
+ GetRoot().GetNext()->SetPortion( pFld );
+ }
+
+ // ruby portions have the same direction as the frame directions
+ if ( rCreate.nLevel % 2 )
+ {
+ // switch right and left ruby adjustment in rtl environment
+ if ( 0 == nAdjustment )
+ nAdjustment = 2;
+ else if ( 2 == nAdjustment )
+ nAdjustment = 0;
+
+ SetDirection( DIR_RIGHT2LEFT );
+ }
+ else
+ SetDirection( DIR_LEFT2RIGHT );
+}
+
+/*-----------------13.11.00 14:56-------------------
+ * SwRubyPortion::_Adjust(..)
+ * In ruby portion there are different alignments for
+ * the ruby text and the main text.
+ * Left, right, centered and two possibilities of block adjustment
+ * The block adjustment is realized by spacing between the characteres,
+ * either with a half space or no space in front of the first letter and
+ * a half space at the end of the last letter.
+ * Notice: the smaller line will be manipulated, normally it's the ruby line,
+ * but it could be the main text, too.
+ * If there is a tabulator in smaller line, no adjustment is possible.
+ * --------------------------------------------------*/
+
+void SwRubyPortion::_Adjust( SwTxtFormatInfo &rInf )
+{
+ SwTwips nLineDiff = GetRoot().Width() - GetRoot().GetNext()->Width();
+ xub_StrLen nOldIdx = rInf.GetIdx();
+ if( !nLineDiff )
+ return;
+ SwLineLayout *pCurr;
+ if( nLineDiff < 0 )
+ { // The first line has to be adjusted.
+ if( GetTab1() )
+ return;
+ pCurr = &GetRoot();
+ nLineDiff = -nLineDiff;
+ }
+ else
+ { // The second line has to be adjusted.
+ if( GetTab2() )
+ return;
+ pCurr = GetRoot().GetNext();
+ rInf.SetIdx( nOldIdx + GetRoot().GetLen() );
+ }
+ KSHORT nLeft = 0; // the space in front of the first letter
+ KSHORT nRight = 0; // the space at the end of the last letter
+ USHORT nSub = 0;
+ switch ( nAdjustment )
+ {
+ case 1: nRight = static_cast<USHORT>(nLineDiff / 2); // no break
+ case 2: nLeft = static_cast<USHORT>(nLineDiff - nRight); break;
+ case 3: nSub = 1; // no break
+ case 4:
+ {
+ xub_StrLen nCharCnt = 0;
+ SwLinePortion *pPor;
+ for( pPor = pCurr->GetFirstPortion(); pPor; pPor = pPor->GetPortion() )
+ {
+ if( pPor->InTxtGrp() )
+ ((SwTxtPortion*)pPor)->GetSpaceCnt( rInf, nCharCnt );
+ rInf.SetIdx( rInf.GetIdx() + pPor->GetLen() );
+ }
+ if( nCharCnt > nSub )
+ {
+ SwTwips nCalc = nLineDiff / ( nCharCnt - nSub );
+ short nTmp;
+ if( nCalc < SHRT_MAX )
+ nTmp = -short(nCalc);
+ else
+ nTmp = SHRT_MIN;
+
+ pCurr->CreateSpaceAdd( SPACING_PRECISION_FACTOR * nTmp );
+ nLineDiff -= nCalc * ( nCharCnt - 1 );
+ }
+ if( nLineDiff > 1 )
+ {
+ nRight = static_cast<USHORT>(nLineDiff / 2);
+ nLeft = static_cast<USHORT>(nLineDiff - nRight);
+ }
+ break;
+ }
+ default: ASSERT( sal_False, "New ruby adjustment" );
+ }
+ if( nLeft || nRight )
+ {
+ if( !pCurr->GetPortion() )
+ pCurr->SetPortion( new SwTxtPortion( *pCurr ) );
+ SwMarginPortion *pMarg = new SwMarginPortion( 0 );
+ if( nLeft )
+ {
+ pMarg->AddPrtWidth( nLeft );
+ pMarg->SetPortion( pCurr->GetPortion() );
+ pCurr->SetPortion( pMarg );
+ }
+ if( nRight )
+ {
+ pMarg = new SwMarginPortion( 0 );
+ pMarg->AddPrtWidth( nRight );
+ pCurr->FindLastPortion()->Append( pMarg );
+ }
+ }
+
+ pCurr->Width( Width() );
+ rInf.SetIdx( nOldIdx );
+}
+
+/*-----------------08.11.00 14:14-------------------
+ * CalcRubyOffset()
+ * has to change the nRubyOffset, if there's a fieldportion
+ * in the phonetic line.
+ * The nRubyOffset is the position in the rubystring, where the
+ * next SwRubyPortion has start the displaying of the phonetics.
+ * --------------------------------------------------*/
+
+void SwRubyPortion::CalcRubyOffset()
+{
+ const SwLineLayout *pCurr = &GetRoot();
+ if( !OnTop() )
+ {
+ pCurr = pCurr->GetNext();
+ if( !pCurr )
+ return;
+ }
+ const SwLinePortion *pPor = pCurr->GetFirstPortion();
+ const SwFldPortion *pFld = NULL;
+ while( pPor )
+ {
+ if( pPor->InFldGrp() )
+ pFld = (SwFldPortion*)pPor;
+ pPor = pPor->GetPortion();
+ }
+ if( pFld )
+ {
+ if( pFld->HasFollow() )
+ nRubyOffset = pFld->GetNextOffset();
+ else
+ nRubyOffset = STRING_LEN;
+ }
+}
+
+/*-----------------13.10.00 16:22-------------------
+ * SwTxtSizeInfo::GetMultiCreator(..)
+ * If we (e.g. the position rPos) are inside a two-line-attribute or
+ * a ruby-attribute, the attribute will be returned in a SwMultiCreator-struct,
+ * otherwise the function returns zero.
+ * The rPos parameter is set to the end of the multiportion,
+ * normally this is the end of the attribute,
+ * but sometimes it is the start of another attribute, which finished or
+ * interrupts the first attribute.
+ * E.g. a ruby portion interrupts a 2-line-attribute, a 2-line-attribute
+ * with different brackets interrupts another 2-line-attribute.
+ * --------------------------------------------------*/
+
+/*-----------------13.11.00 15:38-------------------
+ * lcl_Has2Lines(..)
+ * is a little help function for GetMultiCreator(..)
+ * It extracts the 2-line-format from a 2-line-attribute or a character style.
+ * The rValue is set to TRUE, if the 2-line-attribute's value is set and
+ * no 2-line-format reference is passed. If there is a 2-line-format reference,
+ * then the rValue is set only, if the 2-line-attribute's value is set _and_
+ * the 2-line-formats has the same brackets.
+ * --------------------------------------------------*/
+
+sal_Bool lcl_Has2Lines( const SwTxtAttr& rAttr, const SvxTwoLinesItem* &rpRef,
+ sal_Bool &rValue )
+{
+ const SfxPoolItem* pItem = CharFmt::GetItem( rAttr, RES_CHRATR_TWO_LINES );
+ if( pItem )
+ {
+ rValue = ((SvxTwoLinesItem*)pItem)->GetValue();
+ if( !rpRef )
+ rpRef = (SvxTwoLinesItem*)pItem;
+ else if( ((SvxTwoLinesItem*)pItem)->GetEndBracket() !=
+ rpRef->GetEndBracket() ||
+ ((SvxTwoLinesItem*)pItem)->GetStartBracket() !=
+ rpRef->GetStartBracket() )
+ rValue = sal_False;
+ return sal_True;
+ }
+ return sal_False;
+}
+
+/*-----------------16.02.01 16:39-------------------
+ * lcl_HasRotation(..)
+ * is a little help function for GetMultiCreator(..)
+ * It extracts the charrotation from a charrotate-attribute or a character style.
+ * The rValue is set to TRUE, if the charrotate-attribute's value is set and
+ * no charrotate-format reference is passed.
+ * If there is a charrotate-format reference, then the rValue is set only,
+ * if the charrotate-attribute's value is set _and_ identical
+ * to the charrotate-format's value.
+ * --------------------------------------------------*/
+
+sal_Bool lcl_HasRotation( const SwTxtAttr& rAttr,
+ const SvxCharRotateItem* &rpRef, sal_Bool &rValue )
+{
+ const SfxPoolItem* pItem = CharFmt::GetItem( rAttr, RES_CHRATR_ROTATE );
+ if ( pItem )
+ {
+ rValue = 0 != ((SvxCharRotateItem*)pItem)->GetValue();
+ if( !rpRef )
+ rpRef = (SvxCharRotateItem*)pItem;
+ else if( ((SvxCharRotateItem*)pItem)->GetValue() !=
+ rpRef->GetValue() )
+ rValue = sal_False;
+ return sal_True;
+ }
+
+ return sal_False;
+}
+
+SwMultiCreator* SwTxtSizeInfo::GetMultiCreator( xub_StrLen &rPos,
+ SwMultiPortion* pMulti ) const
+{
+ SwScriptInfo& rSI = ((SwParaPortion*)GetParaPortion())->GetScriptInfo();
+
+ // get the last embedding level
+ BYTE nCurrLevel;
+ if ( pMulti )
+ {
+ ASSERT( pMulti->IsBidi(), "Nested MultiPortion is not BidiPortion" )
+ // level associated with bidi-portion;
+ nCurrLevel = ((SwBidiPortion*)pMulti)->GetLevel();
+ }
+ else
+ // no nested bidi portion required
+ nCurrLevel = GetTxtFrm()->IsRightToLeft() ? 1 : 0;
+
+ // check if there is a field at rPos:
+ BYTE nNextLevel = nCurrLevel;
+ sal_Bool bFldBidi = sal_False;
+
+ if ( CH_TXTATR_BREAKWORD == GetChar( rPos ) )
+ {
+ bFldBidi = sal_True;
+/*
+ // examining the script of the field text should be sufficient
+ // for 99% of all cases
+ XubString aTxt = GetTxtFrm()->GetTxtNode()->GetExpandTxt( rPos, 1 );
+
+ if ( pBreakIt->GetBreakIter().is() && aTxt.Len() )
+ {
+ sal_Bool bFldDir = ( i18n::ScriptType::COMPLEX ==
+ pBreakIt->GetRealScriptOfText( aTxt, 0 ) );
+ sal_Bool bCurrDir = ( 0 != ( nCurrLevel % 2 ) );
+ if ( bFldDir != bCurrDir )
+ {
+ nNextLevel = nCurrLevel + 1;
+ bFldBidi = sal_True;
+ }
+ }*/
+ }
+ else
+ nNextLevel = rSI.DirType( rPos );
+
+ if ( GetTxt().Len() != rPos && nNextLevel > nCurrLevel )
+ {
+ rPos = bFldBidi ? rPos + 1 : rSI.NextDirChg( rPos, &nCurrLevel );
+ if ( STRING_LEN == rPos )
+ return NULL;
+ SwMultiCreator *pRet = new SwMultiCreator;
+ pRet->pItem = NULL;
+ pRet->pAttr = NULL;
+ pRet->nId = SW_MC_BIDI;
+ pRet->nLevel = nCurrLevel + 1;
+ return pRet;
+ }
+
+ // a bidi portion can only contain other bidi portions
+ if ( pMulti )
+ return NULL;
+
+ const SvxCharRotateItem* pRotate = NULL;
+ const SfxPoolItem* pRotItem;
+ if( SFX_ITEM_SET == pFrm->GetTxtNode()->GetSwAttrSet().
+ GetItemState( RES_CHRATR_ROTATE, TRUE, &pRotItem ) &&
+ ((SvxCharRotateItem*)pRotItem)->GetValue() )
+ pRotate = (SvxCharRotateItem*)pRotItem;
+ else
+ pRotItem = NULL;
+ const SvxTwoLinesItem* p2Lines = NULL;
+ const SfxPoolItem* pItem;
+ if( SFX_ITEM_SET == pFrm->GetTxtNode()->GetSwAttrSet().
+ GetItemState( RES_CHRATR_TWO_LINES, TRUE, &pItem ) &&
+ ((SvxTwoLinesItem*)pItem)->GetValue() )
+ p2Lines = (SvxTwoLinesItem*)pItem;
+ else
+ pItem = NULL;
+
+ const SwpHints *pHints = pFrm->GetTxtNode()->GetpSwpHints();
+ if( !pHints && !p2Lines && !pRotate )
+ return NULL;
+ const SwTxtAttr *pRuby = NULL;
+ sal_Bool bTwo = sal_False;
+ sal_Bool bRot = sal_False;
+ USHORT n2Lines = USHRT_MAX;
+ USHORT nRotate = USHRT_MAX;
+ USHORT nCount = pHints ? pHints->Count() : 0;
+ USHORT i;
+ for( i = 0; i < nCount; ++i )
+ {
+ const SwTxtAttr *pTmp = (*pHints)[i];
+ xub_StrLen nStart = *pTmp->GetStart();
+ if( rPos < nStart )
+ break;
+ if( *pTmp->GetAnyEnd() > rPos )
+ {
+ if( RES_TXTATR_CJK_RUBY == pTmp->Which() )
+ pRuby = pTmp;
+ else
+ {
+ const SvxCharRotateItem* pRoTmp = NULL;
+ if( lcl_HasRotation( *pTmp, pRoTmp, bRot ) )
+ {
+ nRotate = bRot ? i : nCount;
+ pRotate = pRoTmp;
+ }
+ const SvxTwoLinesItem* p2Tmp = NULL;
+ if( lcl_Has2Lines( *pTmp, p2Tmp, bTwo ) )
+ {
+ n2Lines = bTwo ? i : nCount;
+ p2Lines = p2Tmp;
+ }
+ }
+ }
+ }
+ if( pRuby )
+ { // The winner is ... a ruby attribute and so
+ // the end of the multiportion is the end of the ruby attribute.
+ rPos = *pRuby->GetEnd();
+ SwMultiCreator *pRet = new SwMultiCreator;
+ pRet->pItem = NULL;
+ pRet->pAttr = pRuby;
+ pRet->nId = SW_MC_RUBY;
+ pRet->nLevel = GetTxtFrm()->IsRightToLeft() ? 1 : 0;
+ return pRet;
+ }
+ if( n2Lines < nCount || ( pItem && pItem == p2Lines &&
+ rPos < GetTxt().Len() ) )
+ { // The winner is a 2-line-attribute,
+ // the end of the multiportion depends on the following attributes...
+ SwMultiCreator *pRet = new SwMultiCreator;
+
+ // We note the endpositions of the 2-line attributes in aEnd as stack
+ SvXub_StrLens aEnd;
+
+ // The bOn flag signs the state of the last 2-line attribute in the
+ // aEnd-stack, it is compatible with the winner-attribute or
+ // it interrupts the other attribute.
+ sal_Bool bOn = sal_True;
+
+ if( n2Lines < nCount )
+ {
+ pRet->pItem = NULL;
+ pRet->pAttr = (*pHints)[n2Lines];
+ aEnd.Insert( *pRet->pAttr->GetEnd(), 0 );
+ if( pItem )
+ {
+ aEnd[ 0 ] = GetTxt().Len();
+ bOn = ((SvxTwoLinesItem*)pItem)->GetEndBracket() ==
+ p2Lines->GetEndBracket() &&
+ ((SvxTwoLinesItem*)pItem)->GetStartBracket() ==
+ p2Lines->GetStartBracket();
+ }
+ }
+ else
+ {
+ pRet->pItem = pItem;
+ pRet->pAttr = NULL;
+ aEnd.Insert( GetTxt().Len(), 0 );
+ }
+ pRet->nId = SW_MC_DOUBLE;
+ pRet->nLevel = GetTxtFrm()->IsRightToLeft() ? 1 : 0;
+
+ // n2Lines is the index of the last 2-line-attribute, which contains
+ // the actual position.
+ i = 0;
+ // At this moment we know that at position rPos the "winner"-attribute
+ // causes a 2-line-portion. The end of the attribute is the end of the
+ // portion, if there's no interrupting attribute.
+ // There are two kinds of interruptors:
+ // - ruby attributes stops the 2-line-attribute, the end of the
+ // multiline is the start of the ruby attribute
+ // - 2-line-attributes with value "Off" or with different brackets,
+ // these attributes may interrupt the winner, but they could be
+ // neutralized by another 2-line-attribute starting at the same
+ // position with the same brackets as the winner-attribute.
+
+ // In the following loop rPos is the critical position and it will be
+ // evaluated, if at rPos starts a interrupting or a maintaining
+ // continuity attribute.
+ while( i < nCount )
+ {
+ const SwTxtAttr *pTmp = (*pHints)[i++];
+ if( *pTmp->GetAnyEnd() <= rPos )
+ continue;
+ if( rPos < *pTmp->GetStart() )
+ {
+ // If bOn is FALSE and the next attribute starts later than rPos
+ // the winner attribute is interrupted at rPos.
+ // If the start of the next atribute is behind the end of
+ // the last attribute on the aEnd-stack, this is the endposition
+ // on the stack is the end of the 2-line portion.
+ if( !bOn || aEnd[ aEnd.Count()-1 ] < *pTmp->GetStart() )
+ break;
+ // At this moment, bOn is TRUE and the next attribute starts
+ // behind rPos, so we could move rPos to the next startpoint
+ rPos = *pTmp->GetStart();
+ // We clean up the aEnd-stack, endpositions equal to rPos are
+ // superfluous.
+ while( aEnd.Count() && aEnd[ aEnd.Count()-1 ] <= rPos )
+ {
+ bOn = !bOn;
+ aEnd.Remove( aEnd.Count()-1, 1 );
+ }
+ // If the endstack is empty, we simulate an attribute with
+ // state TRUE and endposition rPos
+ if( !aEnd.Count() )
+ {
+ aEnd.Insert( rPos, 0 );
+ bOn = sal_True;
+ }
+ }
+ // A ruby attribute stops the 2-line immediately
+ if( RES_TXTATR_CJK_RUBY == pTmp->Which() )
+ return pRet;
+ if( lcl_Has2Lines( *pTmp, p2Lines, bTwo ) )
+ { // We have an interesting attribute..
+ if( bTwo == bOn )
+ { // .. with the same state, so the last attribute could
+ // be continued.
+ if( aEnd[ aEnd.Count()-1 ] < *pTmp->GetEnd() )
+ aEnd[ aEnd.Count()-1 ] = *pTmp->GetEnd();
+ }
+ else
+ { // .. with a different state.
+ bOn = bTwo;
+ // If this is smaller than the last on the stack, we put
+ // it on the stack. If it has the same endposition, the last
+ // could be removed.
+ if( aEnd[ aEnd.Count()-1 ] > *pTmp->GetEnd() )
+ aEnd.Insert( *pTmp->GetEnd(), aEnd.Count() );
+ else if( aEnd.Count() > 1 )
+ aEnd.Remove( aEnd.Count()-1, 1 );
+ else
+ aEnd[ aEnd.Count()-1 ] = *pTmp->GetEnd();
+ }
+ }
+ }
+ if( bOn && aEnd.Count() )
+ rPos = aEnd[ aEnd.Count()-1 ];
+ return pRet;
+ }
+ if( nRotate < nCount || ( pRotItem && pRotItem == pRotate &&
+ rPos < GetTxt().Len() ) )
+ { // The winner is a rotate-attribute,
+ // the end of the multiportion depends on the following attributes...
+ SwMultiCreator *pRet = new SwMultiCreator;
+ pRet->nId = SW_MC_ROTATE;
+
+ // We note the endpositions of the 2-line attributes in aEnd as stack
+ SvXub_StrLens aEnd;
+
+ // The bOn flag signs the state of the last 2-line attribute in the
+ // aEnd-stack, which could interrupts the winning rotation attribute.
+ sal_Bool bOn = pItem ? sal_True : sal_False;
+ aEnd.Insert( GetTxt().Len(), 0 );
+ // n2Lines is the index of the last 2-line-attribute, which contains
+ // the actual position.
+ i = 0;
+ xub_StrLen n2Start = rPos;
+ while( i < nCount )
+ {
+ const SwTxtAttr *pTmp = (*pHints)[i++];
+ if( *pTmp->GetAnyEnd() <= n2Start )
+ continue;
+ if( n2Start < *pTmp->GetStart() )
+ {
+ if( bOn || aEnd[ aEnd.Count()-1 ] < *pTmp->GetStart() )
+ break;
+ n2Start = *pTmp->GetStart();
+ while( aEnd.Count() && aEnd[ aEnd.Count()-1 ] <= n2Start )
+ {
+ bOn = !bOn;
+ aEnd.Remove( aEnd.Count()-1, 1 );
+ }
+ if( !aEnd.Count() )
+ {
+ aEnd.Insert( n2Start, 0 );
+ bOn = sal_False;
+ }
+ }
+ // A ruby attribute stops immediately
+ if( RES_TXTATR_CJK_RUBY == pTmp->Which() )
+ {
+ bOn = sal_True;
+ break;
+ }
+ p2Lines = NULL;
+ if( lcl_Has2Lines( *pTmp, p2Lines, bTwo ) )
+ {
+ if( bTwo == bOn )
+ {
+ if( aEnd[ aEnd.Count()-1 ] < *pTmp->GetEnd() )
+ aEnd[ aEnd.Count()-1 ] = *pTmp->GetEnd();
+ }
+ else
+ {
+ bOn = bTwo;
+ if( aEnd[ aEnd.Count()-1 ] > *pTmp->GetEnd() )
+ aEnd.Insert( *pTmp->GetEnd(), aEnd.Count() );
+ else if( aEnd.Count() > 1 )
+ aEnd.Remove( aEnd.Count()-1, 1 );
+ else
+ aEnd[ aEnd.Count()-1 ] = *pTmp->GetEnd();
+ }
+ }
+ }
+ if( !bOn && aEnd.Count() )
+ n2Start = aEnd[ aEnd.Count()-1 ];
+
+ if( aEnd.Count() )
+ aEnd.Remove( 0, aEnd.Count() );
+
+ bOn = sal_True;
+ if( nRotate < nCount )
+ {
+ pRet->pItem = NULL;
+ pRet->pAttr = (*pHints)[nRotate];
+ aEnd.Insert( *pRet->pAttr->GetEnd(), 0 );
+ if( pRotItem )
+ {
+ aEnd[ 0 ] = GetTxt().Len();
+ bOn = ((SvxCharRotateItem*)pRotItem)->GetValue() ==
+ pRotate->GetValue();
+ }
+ }
+ else
+ {
+ pRet->pItem = pRotItem;
+ pRet->pAttr = NULL;
+ aEnd.Insert( GetTxt().Len(), 0 );
+ }
+ i = 0;
+ while( i < nCount )
+ {
+ const SwTxtAttr *pTmp = (*pHints)[i++];
+ if( *pTmp->GetAnyEnd() <= rPos )
+ continue;
+ if( rPos < *pTmp->GetStart() )
+ {
+ if( !bOn || aEnd[ aEnd.Count()-1 ] < *pTmp->GetStart() )
+ break;
+ rPos = *pTmp->GetStart();
+ while( aEnd.Count() && aEnd[ aEnd.Count()-1 ] <= rPos )
+ {
+ bOn = !bOn;
+ aEnd.Remove( aEnd.Count()-1, 1 );
+ }
+ if( !aEnd.Count() )
+ {
+ aEnd.Insert( rPos, 0 );
+ bOn = sal_True;
+ }
+ }
+ if( RES_TXTATR_CJK_RUBY == pTmp->Which() )
+ {
+ bOn = sal_False;
+ break;
+ }
+ if( lcl_HasRotation( *pTmp, pRotate, bTwo ) )
+ {
+ if( bTwo == bOn )
+ {
+ if( aEnd[ aEnd.Count()-1 ] < *pTmp->GetEnd() )
+ aEnd[ aEnd.Count()-1 ] = *pTmp->GetEnd();
+ }
+ else
+ {
+ bOn = bTwo;
+ if( aEnd[ aEnd.Count()-1 ] > *pTmp->GetEnd() )
+ aEnd.Insert( *pTmp->GetEnd(), aEnd.Count() );
+ else if( aEnd.Count() > 1 )
+ aEnd.Remove( aEnd.Count()-1, 1 );
+ else
+ aEnd[ aEnd.Count()-1 ] = *pTmp->GetEnd();
+ }
+ }
+ }
+ if( bOn && aEnd.Count() )
+ rPos = aEnd[ aEnd.Count()-1 ];
+ if( rPos > n2Start )
+ rPos = n2Start;
+ return pRet;
+ }
+ return NULL;
+}
+
+/*-----------------01.11.00 14:52-------------------
+ * SwSpaceManipulator
+ * is a little helper class to manage the spaceadd-arrays of the text adjustment
+ * during a PaintMultiPortion.
+ * The constructor prepares the array for the first line of multiportion,
+ * the SecondLine-function restores the values for the first line and prepares
+ * the second line.
+ * The destructor restores the values of the last manipulation.
+ * --------------------------------------------------*/
+
+class SwSpaceManipulator
+{
+ SwTxtPaintInfo& rInfo;
+ SwMultiPortion& rMulti;
+ std::vector<long>* pOldSpaceAdd;
+ MSHORT nOldSpIdx;
+ long nSpaceAdd;
+ sal_Bool bSpaceChg : 1;
+ sal_uInt8 nOldDir : 2;
+public:
+ SwSpaceManipulator( SwTxtPaintInfo& rInf, SwMultiPortion& rMult );
+ ~SwSpaceManipulator();
+ void SecondLine();
+ inline long GetSpaceAdd() const { return nSpaceAdd; }
+};
+
+SwSpaceManipulator::SwSpaceManipulator( SwTxtPaintInfo& rInf,
+ SwMultiPortion& rMult ) :
+ rInfo( rInf ), rMulti( rMult )
+{
+ pOldSpaceAdd = rInfo.GetpSpaceAdd();
+ nOldSpIdx = rInfo.GetSpaceIdx();
+ nOldDir = rInfo.GetDirection();
+ rInfo.SetDirection( rMulti.GetDirection() );
+ bSpaceChg = sal_False;
+
+ if( rMulti.IsDouble() )
+ {
+ nSpaceAdd = ( pOldSpaceAdd && !rMulti.HasTabulator() ) ?
+ rInfo.GetSpaceAdd() : 0;
+ if( rMulti.GetRoot().IsSpaceAdd() )
+ {
+ rInfo.SetpSpaceAdd( rMulti.GetRoot().GetpLLSpaceAdd() );
+ rInfo.ResetSpaceIdx();
+ bSpaceChg = rMulti.ChgSpaceAdd( &rMulti.GetRoot(), nSpaceAdd );
+ }
+ else if( rMulti.HasTabulator() )
+ rInfo.SetpSpaceAdd( NULL );
+ }
+ else if ( ! rMulti.IsBidi() )
+ {
+ rInfo.SetpSpaceAdd( rMulti.GetRoot().GetpLLSpaceAdd() );
+ rInfo.ResetSpaceIdx();
+ }
+}
+
+void SwSpaceManipulator::SecondLine()
+{
+ if( bSpaceChg )
+ {
+ rInfo.RemoveFirstSpaceAdd();
+ bSpaceChg = sal_False;
+ }
+ SwLineLayout *pLay = rMulti.GetRoot().GetNext();
+ if( pLay->IsSpaceAdd() )
+ {
+ rInfo.SetpSpaceAdd( pLay->GetpLLSpaceAdd() );
+ rInfo.ResetSpaceIdx();
+ bSpaceChg = rMulti.ChgSpaceAdd( pLay, nSpaceAdd );
+ }
+ else
+ {
+ rInfo.SetpSpaceAdd( (!rMulti.IsDouble() || rMulti.HasTabulator() ) ?
+ 0 : pOldSpaceAdd );
+ rInfo.SetSpaceIdx( nOldSpIdx);
+ }
+}
+
+SwSpaceManipulator::~SwSpaceManipulator()
+{
+ if( bSpaceChg )
+ {
+ rInfo.RemoveFirstSpaceAdd();
+ bSpaceChg = sal_False;
+ }
+ rInfo.SetpSpaceAdd( pOldSpaceAdd );
+ rInfo.SetSpaceIdx( nOldSpIdx);
+ rInfo.SetDirection( nOldDir );
+}
+
+/*-----------------13.10.00 16:24-------------------
+ * SwTxtPainter::PaintMultiPortion manages the paint for a SwMultiPortion.
+ * External, for the calling function, it seems to be a normal Paint-function,
+ * internal it is like a SwTxtFrm::Paint with multiple DrawTextLines
+ * --------------------------------------------------*/
+
+void SwTxtPainter::PaintMultiPortion( const SwRect &rPaint,
+ SwMultiPortion& rMulti, const SwMultiPortion* pEnvPor )
+{
+ GETGRID( pFrm->FindPageFrm() )
+ const sal_Bool bHasGrid = pGrid && GetInfo().SnapToGrid();
+ USHORT nGridWidth = 0;
+ USHORT nRubyHeight = 0;
+ sal_Bool bRubyTop = sal_False;
+
+ if ( bHasGrid )
+ {
+ nGridWidth = pGrid->GetBaseHeight();
+ nRubyHeight = pGrid->GetRubyHeight();
+ bRubyTop = ! pGrid->GetRubyTextBelow();
+ }
+
+ // do not allow grid mode for first line in ruby portion
+ const sal_Bool bRubyInGrid = bHasGrid && rMulti.IsRuby();
+
+ const USHORT nOldHeight = rMulti.Height();
+ const sal_Bool bOldGridModeAllowed = GetInfo().SnapToGrid();
+
+ if ( bRubyInGrid )
+ {
+ GetInfo().SetSnapToGrid( ! bRubyTop );
+ rMulti.Height( pCurr->Height() );
+ }
+
+ SwLayoutModeModifier aLayoutModeModifier( *GetInfo().GetOut() );
+ BYTE nEnvDir = 0;
+ BYTE nThisDir = 0;
+ BYTE nFrmDir = 0;
+ if ( rMulti.IsBidi() )
+ {
+ // these values are needed for the calculation of the x coordinate
+ // and the layout mode
+ ASSERT( ! pEnvPor || pEnvPor->IsBidi(),
+ "Oh no, I expected a BidiPortion" )
+ nFrmDir = GetInfo().GetTxtFrm()->IsRightToLeft() ? 1 : 0;
+ nEnvDir = pEnvPor ? ((SwBidiPortion*)pEnvPor)->GetLevel() % 2 : nFrmDir;
+ nThisDir = ((SwBidiPortion&)rMulti).GetLevel() % 2;
+ }
+
+#if OSL_DEBUG_LEVEL > 1
+ // only paint first level bidi portions
+ if( rMulti.Width() > 1 && ! pEnvPor )
+ GetInfo().DrawViewOpt( rMulti, POR_FLD );
+#endif
+
+ if ( bRubyInGrid )
+ rMulti.Height( nOldHeight );
+
+ // do we have to repaint a post it portion?
+ if( GetInfo().OnWin() && rMulti.GetPortion() &&
+ ! rMulti.GetPortion()->Width() )
+ rMulti.GetPortion()->PrePaint( GetInfo(), &rMulti );
+
+ // old values must be saved and restored at the end
+ xub_StrLen nOldLen = GetInfo().GetLen();
+ KSHORT nOldX = KSHORT(GetInfo().X());
+ long nOldY = GetInfo().Y();
+ xub_StrLen nOldIdx = GetInfo().GetIdx();
+
+ SwSpaceManipulator aManip( GetInfo(), rMulti );
+
+ SwFontSave *pFontSave;
+ SwFont* pTmpFnt;
+
+ if( rMulti.IsDouble() )
+ {
+ pTmpFnt = new SwFont( *GetInfo().GetFont() );
+ if( rMulti.IsDouble() )
+ {
+ SetPropFont( 50 );
+ pTmpFnt->SetProportion( GetPropFont() );
+ }
+ pFontSave = new SwFontSave( GetInfo(), pTmpFnt, this );
+ }
+ else
+ {
+ pFontSave = NULL;
+ pTmpFnt = NULL;
+ }
+
+ if( rMulti.HasBrackets() )
+ {
+ xub_StrLen nTmpOldIdx = GetInfo().GetIdx();
+ GetInfo().SetIdx(((SwDoubleLinePortion&)rMulti).GetBrackets()->nStart);
+ SeekAndChg( GetInfo() );
+ ((SwDoubleLinePortion&)rMulti).PaintBracket( GetInfo(), 0, sal_True );
+ GetInfo().SetIdx( nTmpOldIdx );
+ }
+
+ KSHORT nTmpX = KSHORT(GetInfo().X());
+
+ SwLineLayout* pLay = &rMulti.GetRoot();// the first line of the multiportion
+ SwLinePortion* pPor = pLay->GetFirstPortion();//first portion of these line
+ SwTwips nOfst = 0;
+
+ // GetInfo().Y() is the baseline from the surrounding line. We must switch
+ // this temporary to the baseline of the inner lines of the multiportion.
+ if( rMulti.HasRotation() )
+ {
+ if( rMulti.IsRevers() )
+ {
+ GetInfo().Y( nOldY - rMulti.GetAscent() );
+ nOfst = nTmpX + rMulti.Width();
+ }
+ else
+ {
+ GetInfo().Y( nOldY - rMulti.GetAscent() + rMulti.Height() );
+ nOfst = nTmpX;
+ }
+ }
+ else if ( rMulti.IsBidi() )
+ {
+ // does the current bidi portion has the same direction
+ // as its environment?
+ if ( nEnvDir != nThisDir )
+ {
+ // different directions, we have to adjust the x coordinate
+ SwTwips nMultiWidth = rMulti.Width() +
+ rMulti.CalcSpacing( GetInfo().GetSpaceAdd(), GetInfo() );
+
+ if ( nFrmDir == nThisDir )
+ GetInfo().X( GetInfo().X() - nMultiWidth );
+ else
+ GetInfo().X( GetInfo().X() + nMultiWidth );
+ }
+
+ nOfst = nOldY - rMulti.GetAscent();
+
+ // set layout mode
+ aLayoutModeModifier.Modify( nThisDir );
+ }
+ else
+ nOfst = nOldY - rMulti.GetAscent();
+
+ sal_Bool bRest = pLay->IsRest();
+ sal_Bool bFirst = sal_True;
+
+ ASSERT( 0 == GetInfo().GetUnderFnt() || rMulti.IsBidi(),
+ " Only BiDi portions are allowed to use the common underlining font" )
+
+ do
+ {
+ if ( bHasGrid )
+ {
+ if( rMulti.HasRotation() )
+ {
+ const USHORT nAdjustment = ( pLay->Height() - pPor->Height() ) / 2 +
+ pPor->GetAscent();
+ if( rMulti.IsRevers() )
+ GetInfo().X( nOfst - nAdjustment );
+ else
+ GetInfo().X( nOfst + nAdjustment );
+ }
+ else
+ {
+ // special treatment for ruby portions in grid mode
+ SwTwips nAdjustment = 0;
+ if ( rMulti.IsRuby() )
+ {
+ if ( bRubyTop != ( pLay == &rMulti.GetRoot() ) )
+ // adjust base text
+ nAdjustment = ( pCurr->Height() - nRubyHeight - pPor->Height() ) / 2;
+ else if ( bRubyTop )
+ // adjust upper ruby text
+ nAdjustment = nRubyHeight - pPor->Height();
+ // else adjust lower ruby text
+ }
+
+ GetInfo().Y( nOfst + nAdjustment + pPor->GetAscent() );
+ }
+ }
+ else if( rMulti.HasRotation() )
+ {
+ if( rMulti.IsRevers() )
+ GetInfo().X( nOfst - AdjustBaseLine( *pLay, pPor, 0, 0, sal_True ) );
+ else
+ GetInfo().X( nOfst + AdjustBaseLine( *pLay, pPor ) );
+ }
+ else
+ GetInfo().Y( nOfst + AdjustBaseLine( *pLay, pPor ) );
+
+ sal_Bool bSeeked = sal_True;
+ GetInfo().SetLen( pPor->GetLen() );
+
+ if( bRest && pPor->InFldGrp() && !pPor->GetLen() )
+ {
+ if( ((SwFldPortion*)pPor)->HasFont() )
+ bSeeked = sal_False;
+ else
+ SeekAndChgBefore( GetInfo() );
+ }
+ else if( pPor->InTxtGrp() || pPor->InFldGrp() || pPor->InTabGrp() )
+ SeekAndChg( GetInfo() );
+ else if ( !bFirst && pPor->IsBreakPortion() && GetInfo().GetOpt().IsParagraph() )
+ {
+ if( GetRedln() )
+ SeekAndChg( GetInfo() );
+ else
+ SeekAndChgBefore( GetInfo() );
+ }
+ else
+ bSeeked = sal_False;
+
+ SwLinePortion *pNext = pPor->GetPortion();
+ if(GetInfo().OnWin() && pNext && !pNext->Width() )
+ {
+ if ( !bSeeked )
+ SeekAndChg( GetInfo() );
+ pNext->PrePaint( GetInfo(), pPor );
+ }
+
+ CheckSpecialUnderline( pPor );
+ SwUnderlineFont* pUnderLineFnt = GetInfo().GetUnderFnt();
+ if ( pUnderLineFnt )
+ {
+ if ( rMulti.IsDouble() )
+ pUnderLineFnt->GetFont().SetProportion( 50 );
+ pUnderLineFnt->SetPos( GetInfo().GetPos() );
+ }
+
+ if ( rMulti.IsBidi() )
+ {
+ // we do not allow any rotation inside a bidi portion
+ SwFont* pTmpFont = GetInfo().GetFont();
+ pTmpFont->SetVertical( 0, GetInfo().GetTxtFrm()->IsVertical() );
+ }
+
+ if( pPor->IsMultiPortion() && ((SwMultiPortion*)pPor)->IsBidi() )
+ {
+ // but we do allow nested bidi portions
+ ASSERT( rMulti.IsBidi(), "Only nesting of bidi portions is allowed" )
+ PaintMultiPortion( rPaint, (SwMultiPortion&)*pPor, &rMulti );
+ }
+ else
+ pPor->Paint( GetInfo() );
+
+ if( GetFnt()->IsURL() && pPor->InTxtGrp() )
+ GetInfo().NotifyURL( *pPor );
+
+ bFirst &= !pPor->GetLen();
+ if( pNext || !pPor->IsMarginPortion() )
+ pPor->Move( GetInfo() );
+
+ pPor = pNext;
+
+ // If there's no portion left, we go to the next line
+ if( !pPor && pLay->GetNext() )
+ {
+ pLay = pLay->GetNext();
+ pPor = pLay->GetFirstPortion();
+ bRest = pLay->IsRest();
+ aManip.SecondLine();
+
+ // delete underline font
+ delete GetInfo().GetUnderFnt();
+ GetInfo().SetUnderFnt( 0 );
+
+ if( rMulti.HasRotation() )
+ {
+ if( rMulti.IsRevers() )
+ {
+ nOfst += pLay->Height();
+ GetInfo().Y( nOldY - rMulti.GetAscent() );
+ }
+ else
+ {
+ nOfst -= pLay->Height();
+ GetInfo().Y( nOldY - rMulti.GetAscent() + rMulti.Height() );
+ }
+ }
+ else if ( bHasGrid && rMulti.IsRuby() )
+ {
+ GetInfo().X( nTmpX );
+ if ( bRubyTop )
+ {
+ nOfst += nRubyHeight;
+ GetInfo().SetSnapToGrid( sal_True );
+ }
+ else
+ {
+ nOfst += pCurr->Height() - nRubyHeight;
+ GetInfo().SetSnapToGrid( sal_False );
+ }
+ } else
+ {
+ GetInfo().X( nTmpX );
+ // We switch to the baseline of the next inner line
+ nOfst += rMulti.GetRoot().Height();
+ }
+ }
+ } while( pPor );
+
+ if ( bRubyInGrid )
+ GetInfo().SetSnapToGrid( bOldGridModeAllowed );
+
+ // delete underline font
+ if ( ! rMulti.IsBidi() )
+ {
+ delete GetInfo().GetUnderFnt();
+ GetInfo().SetUnderFnt( 0 );
+ }
+
+ GetInfo().SetIdx( nOldIdx );
+ GetInfo().Y( nOldY );
+
+ if( rMulti.HasBrackets() )
+ {
+ xub_StrLen nTmpOldIdx = GetInfo().GetIdx();
+ GetInfo().SetIdx(((SwDoubleLinePortion&)rMulti).GetBrackets()->nStart);
+ SeekAndChg( GetInfo() );
+ GetInfo().X( nOldX );
+ ((SwDoubleLinePortion&)rMulti).PaintBracket( GetInfo(),
+ aManip.GetSpaceAdd(), sal_False );
+ GetInfo().SetIdx( nTmpOldIdx );
+ }
+ // Restore the saved values
+ GetInfo().X( nOldX );
+ GetInfo().SetLen( nOldLen );
+ delete pFontSave;
+ delete pTmpFnt;
+ SetPropFont( 0 );
+}
+
+sal_Bool lcl_ExtractFieldFollow( SwLineLayout* pLine, SwLinePortion* &rpFld )
+{
+ SwLinePortion* pLast = pLine;
+ rpFld = pLine->GetPortion();
+ while( rpFld && !rpFld->InFldGrp() )
+ {
+ pLast = rpFld;
+ rpFld = rpFld->GetPortion();
+ }
+ sal_Bool bRet = rpFld != 0;
+ if( bRet )
+ {
+ if( ((SwFldPortion*)rpFld)->IsFollow() )
+ {
+ rpFld->Truncate();
+ pLast->SetPortion( NULL );
+ }
+ else
+ rpFld = NULL;
+ }
+ pLine->Truncate();
+ return bRet;
+}
+
+/*----------------------------------------------------
+ * lcl_TruncateMultiPortion
+ * If a multi portion completely has to go to the
+ * next line, this function is called to trunctate
+ * the rest of the remaining multi portion
+ * --------------------------------------------------*/
+
+void lcl_TruncateMultiPortion( SwMultiPortion& rMulti, SwTxtFormatInfo& rInf,
+ xub_StrLen nStartIdx )
+{
+ rMulti.GetRoot().Truncate();
+ rMulti.GetRoot().SetLen(0);
+ rMulti.GetRoot().Width(0);
+// rMulti.CalcSize( *this, aInf );
+ if ( rMulti.GetRoot().GetNext() )
+ {
+ rMulti.GetRoot().GetNext()->Truncate();
+ rMulti.GetRoot().GetNext()->SetLen( 0 );
+ rMulti.GetRoot().GetNext()->Width( 0 );
+ }
+ rMulti.Width( 0 );
+ rMulti.SetLen(0);
+ rInf.SetIdx( nStartIdx );
+}
+
+/*-----------------------------------------------------------------------------
+ * SwTxtFormatter::BuildMultiPortion
+ * manages the formatting of a SwMultiPortion. External, for the calling
+ * function, it seems to be a normal Format-function, internal it is like a
+ * SwTxtFrm::_Format with multiple BuildPortions
+ *---------------------------------------------------------------------------*/
+
+BOOL SwTxtFormatter::BuildMultiPortion( SwTxtFormatInfo &rInf,
+ SwMultiPortion& rMulti )
+{
+ SwTwips nMaxWidth = rInf.Width();
+ KSHORT nOldX = 0;
+
+ if( rMulti.HasBrackets() )
+ {
+ xub_StrLen nOldIdx = rInf.GetIdx();
+ rInf.SetIdx( ((SwDoubleLinePortion&)rMulti).GetBrackets()->nStart );
+ SeekAndChg( rInf );
+ nOldX = KSHORT(GetInfo().X());
+ ((SwDoubleLinePortion&)rMulti).FormatBrackets( rInf, nMaxWidth );
+ rInf.SetIdx( nOldIdx );
+ }
+
+ SeekAndChg( rInf );
+ SwFontSave *pFontSave;
+ if( rMulti.IsDouble() )
+ {
+ SwFont* pTmpFnt = new SwFont( *rInf.GetFont() );
+ if( rMulti.IsDouble() )
+ {
+ SetPropFont( 50 );
+ pTmpFnt->SetProportion( GetPropFont() );
+ }
+ pFontSave = new SwFontSave( rInf, pTmpFnt, this );
+ }
+ else
+ pFontSave = NULL;
+
+ SwLayoutModeModifier aLayoutModeModifier( *GetInfo().GetOut() );
+ if ( rMulti.IsBidi() )
+ {
+ // set layout mode
+ aLayoutModeModifier.Modify( ! rInf.GetTxtFrm()->IsRightToLeft() );
+ }
+
+ SwTwips nTmpX = 0;
+
+ if( rMulti.HasRotation() )
+ {
+ // For nMaxWidth we take the height of the body frame.
+ // #i25067#: If the current frame is inside a table, we restrict
+ // nMaxWidth to the current frame height, unless the frame size
+ // attribute is set to variable size:
+
+ // We set nTmpX (which is used for portion calculating) to the
+ // current Y value
+ const SwPageFrm* pPage = pFrm->FindPageFrm();
+ ASSERT( pPage, "No page in frame!");
+ const SwLayoutFrm* pUpperFrm = pPage;
+
+ if ( pFrm->IsInTab() )
+ {
+ pUpperFrm = pFrm->GetUpper();
+ while ( pUpperFrm && !pUpperFrm->IsCellFrm() )
+ pUpperFrm = pUpperFrm->GetUpper();
+ ASSERT( pUpperFrm, "pFrm is in table but does not have an upper cell frame" )
+ const SwTableLine* pLine = ((SwRowFrm*)pUpperFrm->GetUpper())->GetTabLine();
+ const SwFmtFrmSize& rFrmFmtSize = pLine->GetFrmFmt()->GetFrmSize();
+ if ( ATT_VAR_SIZE == rFrmFmtSize.GetHeightSizeType() )
+ pUpperFrm = pPage;
+ }
+ if ( pUpperFrm == pPage && !pFrm->IsInFtn() )
+ pUpperFrm = pPage->FindBodyCont();
+
+ nMaxWidth = pUpperFrm ?
+ ( rInf.GetTxtFrm()->IsVertical() ?
+ pUpperFrm->Prt().Width() :
+ pUpperFrm->Prt().Height() ) :
+ USHRT_MAX;
+ }
+ else
+ nTmpX = rInf.X();
+
+ SwMultiPortion* pOldMulti = pMulti;
+
+ pMulti = &rMulti;
+ SwLineLayout *pOldCurr = pCurr;
+ xub_StrLen nOldStart = GetStart();
+ SwTwips nMinWidth = nTmpX + 1;
+ SwTwips nActWidth = nMaxWidth;
+ const xub_StrLen nStartIdx = rInf.GetIdx();
+ xub_StrLen nMultiLen = rMulti.GetLen();
+
+ SwLinePortion *pFirstRest;
+ SwLinePortion *pSecondRest;
+ if( rMulti.IsFormatted() )
+ {
+ if( !lcl_ExtractFieldFollow( &rMulti.GetRoot(), pFirstRest )
+ && rMulti.IsDouble() && rMulti.GetRoot().GetNext() )
+ lcl_ExtractFieldFollow( rMulti.GetRoot().GetNext(), pFirstRest );
+ if( !rMulti.IsDouble() && rMulti.GetRoot().GetNext() )
+ lcl_ExtractFieldFollow( rMulti.GetRoot().GetNext(), pSecondRest );
+ else
+ pSecondRest = NULL;
+ }
+ else
+ {
+ pFirstRest = rMulti.GetRoot().GetPortion();
+ pSecondRest = rMulti.GetRoot().GetNext() ?
+ rMulti.GetRoot().GetNext()->GetPortion() : NULL;
+ if( pFirstRest )
+ rMulti.GetRoot().SetPortion( NULL );
+ if( pSecondRest )
+ rMulti.GetRoot().GetNext()->SetPortion( NULL );
+ rMulti.SetFormatted();
+ nMultiLen = nMultiLen - rInf.GetIdx();
+ }
+
+ // save some values
+ const XubString* pOldTxt = &(rInf.GetTxt());
+ const SwTwips nOldPaintOfst = rInf.GetPaintOfst();
+
+ XubString aMultiStr( rInf.GetTxt(), 0, nMultiLen + rInf.GetIdx() );
+ rInf.SetTxt( aMultiStr );
+ SwTxtFormatInfo aInf( rInf, rMulti.GetRoot(), nActWidth );
+ // Do we allow break cuts? The FirstMulti-Flag is evaluated during
+ // line break determination.
+ sal_Bool bFirstMulti = rInf.GetIdx() != rInf.GetLineStart();
+
+ SwLinePortion *pNextFirst = NULL;
+ SwLinePortion *pNextSecond = NULL;
+ BOOL bRet = FALSE;
+
+ GETGRID( pFrm->FindPageFrm() )
+ const sal_Bool bHasGrid = pGrid && GRID_LINES_CHARS == pGrid->GetGridType();
+
+ USHORT nGridWidth = 0;
+ USHORT nRubyHeight = 0;
+ sal_Bool bRubyTop = sal_False;
+
+ if ( bHasGrid )
+ {
+ nGridWidth = pGrid->GetBaseHeight();
+ nRubyHeight = pGrid->GetRubyHeight();
+ bRubyTop = ! pGrid->GetRubyTextBelow();
+ }
+
+ do
+ {
+ pCurr = &rMulti.GetRoot();
+ nStart = nStartIdx;
+ bRet = FALSE;
+ FormatReset( aInf );
+ aInf.X( nTmpX );
+ aInf.Width( KSHORT(nActWidth) );
+ aInf.RealWidth( KSHORT(nActWidth) );
+ aInf.SetFirstMulti( bFirstMulti );
+ aInf.SetNumDone( rInf.IsNumDone() );
+ aInf.SetFtnDone( rInf.IsFtnDone() );
+
+ if( pFirstRest )
+ {
+ ASSERT( pFirstRest->InFldGrp(), "BuildMulti: Fieldrest expected");
+ SwFldPortion *pFld =
+ ((SwFldPortion*)pFirstRest)->Clone(
+ ((SwFldPortion*)pFirstRest)->GetExp() );
+ pFld->SetFollow( sal_True );
+ aInf.SetRest( pFld );
+ }
+ aInf.SetRuby( rMulti.IsRuby() && rMulti.OnTop() );
+
+ // in grid mode we temporarily have to disable the grid for the ruby line
+ const sal_Bool bOldGridModeAllowed = GetInfo().SnapToGrid();
+ if ( bHasGrid && aInf.IsRuby() && bRubyTop )
+ aInf.SetSnapToGrid( sal_False );
+
+ // If there's no more rubytext, then buildportion is forbidden
+ if( pFirstRest || !aInf.IsRuby() )
+ BuildPortions( aInf );
+
+ aInf.SetSnapToGrid( bOldGridModeAllowed );
+
+ rMulti.CalcSize( *this, aInf );
+ pCurr->SetRealHeight( pCurr->Height() );
+
+ if( rMulti.IsBidi() )
+ {
+ pNextFirst = aInf.GetRest();
+ break;
+ }
+
+ if( rMulti.HasRotation() && !rMulti.IsDouble() )
+ break;
+ // second line has to be formatted
+ else if( pCurr->GetLen()<nMultiLen || rMulti.IsRuby() || aInf.GetRest())
+ {
+ xub_StrLen nFirstLen = pCurr->GetLen();
+ delete pCurr->GetNext();
+ pCurr->SetNext( new SwLineLayout() );
+ pCurr = pCurr->GetNext();
+ nStart = aInf.GetIdx();
+ aInf.X( nTmpX );
+ SwTxtFormatInfo aTmp( aInf, *pCurr, nActWidth );
+ if( rMulti.IsRuby() )
+ {
+ aTmp.SetRuby( !rMulti.OnTop() );
+ pNextFirst = aInf.GetRest();
+ if( pSecondRest )
+ {
+ ASSERT( pSecondRest->InFldGrp(), "Fieldrest expected");
+ SwFldPortion *pFld = ((SwFldPortion*)pSecondRest)->Clone(
+ ((SwFldPortion*)pSecondRest)->GetExp() );
+ pFld->SetFollow( sal_True );
+ aTmp.SetRest( pFld );
+ }
+ if( !rMulti.OnTop() && nFirstLen < nMultiLen )
+ bRet = sal_True;
+ }
+ else
+ aTmp.SetRest( aInf.GetRest() );
+ aInf.SetRest( NULL );
+
+ // in grid mode we temporarily have to disable the grid for the ruby line
+ if ( bHasGrid && aTmp.IsRuby() && ! bRubyTop )
+ aTmp.SetSnapToGrid( sal_False );
+
+ BuildPortions( aTmp );
+
+ aTmp.SetSnapToGrid( bOldGridModeAllowed );
+
+ rMulti.CalcSize( *this, aInf );
+ rMulti.GetRoot().SetRealHeight( rMulti.GetRoot().Height() );
+ pCurr->SetRealHeight( pCurr->Height() );
+ if( rMulti.IsRuby() )
+ {
+ pNextSecond = aTmp.GetRest();
+ if( pNextFirst )
+ bRet = sal_True;
+ }
+ else
+ pNextFirst = aTmp.GetRest();
+ if( ( !aTmp.IsRuby() && nFirstLen + pCurr->GetLen() < nMultiLen )
+ || aTmp.GetRest() )
+ // our guess for width of multiportion was too small,
+ // text did not fit into multiportion
+ bRet = sal_True;
+ }
+ if( rMulti.IsRuby() )
+ break;
+ if( bRet )
+ {
+ // our guess for multiportion width was too small,
+ // we set min to act
+ nMinWidth = nActWidth;
+ nActWidth = ( 3 * nMaxWidth + nMinWidth + 3 ) / 4;
+ if ( nActWidth == nMaxWidth && rInf.GetLineStart() == rInf.GetIdx() )
+ // we have too less space, we must allow break cuts
+ // ( the first multi flag is considered during TxtPortion::_Format() )
+ bFirstMulti = sal_False;
+ if( nActWidth <= nMinWidth )
+ break;
+ }
+ else
+ {
+ // For Solaris, this optimisation can causes trouble:
+ // Setting this to the portion width ( = rMulti.Width() )
+ // can make GetTextBreak inside SwTxtGuess::Guess return to small
+ // values. Therefore we add some extra twips.
+ if( nActWidth > nTmpX + rMulti.Width() + 6 )
+ nActWidth = nTmpX + rMulti.Width() + 6;
+ nMaxWidth = nActWidth;
+ nActWidth = ( 3 * nMaxWidth + nMinWidth + 3 ) / 4;
+ if( nActWidth >= nMaxWidth )
+ break;
+ // we do not allow break cuts during formatting
+ bFirstMulti = sal_True;
+ }
+ delete pNextFirst;
+ pNextFirst = NULL;
+ } while ( TRUE );
+
+ pMulti = pOldMulti;
+
+ pCurr = pOldCurr;
+ nStart = nOldStart;
+ SetPropFont( 0 );
+
+ rMulti.SetLen( rMulti.GetRoot().GetLen() + ( rMulti.GetRoot().GetNext() ?
+ rMulti.GetRoot().GetNext()->GetLen() : 0 ) );
+
+ if( rMulti.IsDouble() )
+ {
+ ((SwDoubleLinePortion&)rMulti).CalcBlanks( rInf );
+ if( ((SwDoubleLinePortion&)rMulti).GetLineDiff() )
+ {
+ SwLineLayout* pLine = &rMulti.GetRoot();
+ if( ((SwDoubleLinePortion&)rMulti).GetLineDiff() > 0 )
+ {
+ rInf.SetIdx( nStartIdx + pLine->GetLen() );
+ pLine = pLine->GetNext();
+ }
+ if( pLine )
+ {
+ GetInfo().SetMulti( sal_True );
+ CalcNewBlock( pLine, NULL, rMulti.Width() );
+ GetInfo().SetMulti( sal_False );
+ }
+ rInf.SetIdx( nStartIdx );
+ }
+ if( ((SwDoubleLinePortion&)rMulti).GetBrackets() )
+ {
+ rMulti.Width( rMulti.Width() +
+ ((SwDoubleLinePortion&)rMulti).BracketWidth() );
+ GetInfo().X( nOldX );
+ }
+ }
+ else
+ {
+ rMulti.ActualizeTabulator();
+ if( rMulti.IsRuby() )
+ {
+ ((SwRubyPortion&)rMulti).Adjust( rInf );
+ ((SwRubyPortion&)rMulti).CalcRubyOffset();
+ }
+ }
+ if( rMulti.HasRotation() )
+ {
+ SwTwips nH = rMulti.Width();
+ SwTwips nAsc = rMulti.GetAscent() + ( nH - rMulti.Height() )/2;
+ if( nAsc > nH )
+ nAsc = nH;
+ else if( nAsc < 0 )
+ nAsc = 0;
+ rMulti.Width( rMulti.Height() );
+ rMulti.Height( KSHORT(nH) );
+ rMulti.SetAscent( KSHORT(nAsc) );
+ bRet = ( rInf.GetPos().X() + rMulti.Width() > rInf.Width() ) &&
+ nStartIdx != rInf.GetLineStart();
+ }
+ else if ( rMulti.IsBidi() )
+ {
+ bRet = rMulti.GetLen() < nMultiLen || pNextFirst;
+ }
+
+ // line break has to be performed!
+ if( bRet )
+ {
+ ASSERT( !pNextFirst || pNextFirst->InFldGrp(),
+ "BuildMultiPortion: Surprising restportion, field expected" );
+ SwMultiPortion *pTmp;
+ if( rMulti.IsDouble() )
+ pTmp = new SwDoubleLinePortion( ((SwDoubleLinePortion&)rMulti),
+ nMultiLen + rInf.GetIdx() );
+ else if( rMulti.IsRuby() )
+ {
+ ASSERT( !pNextSecond || pNextSecond->InFldGrp(),
+ "BuildMultiPortion: Surprising restportion, field expected" );
+
+ if ( rInf.GetIdx() == rInf.GetLineStart() )
+ {
+ // the ruby portion has to be split in two portions
+ pTmp = new SwRubyPortion( ((SwRubyPortion&)rMulti),
+ nMultiLen + rInf.GetIdx() );
+
+ if( pNextSecond )
+ {
+ pTmp->GetRoot().SetNext( new SwLineLayout() );
+ pTmp->GetRoot().GetNext()->SetPortion( pNextSecond );
+ }
+ pTmp->SetFollowFld();
+ }
+ else
+ {
+ // we try to keep our ruby portion together
+ lcl_TruncateMultiPortion( rMulti, rInf, nStartIdx );
+ pTmp = 0;
+ }
+ }
+ else if( rMulti.HasRotation() )
+ {
+ // we try to keep our rotated portion together
+ lcl_TruncateMultiPortion( rMulti, rInf, nStartIdx );
+ pTmp = new SwRotatedPortion( nMultiLen + rInf.GetIdx(),
+ rMulti.GetDirection() );
+ }
+ // during a recursion of BuildMultiPortions we may not build
+ // a new SwBidiPortion, this would cause a memory leak
+ else if( rMulti.IsBidi() && ! pMulti )
+ {
+ if ( ! rMulti.GetLen() )
+ lcl_TruncateMultiPortion( rMulti, rInf, nStartIdx );
+
+ // If there is a HolePortion at the end of the bidi portion,
+ // it has to be moved behind the bidi portion. Otherwise
+ // the visual cursor travelling gets into trouble.
+ SwLineLayout& aRoot = rMulti.GetRoot();
+ SwLinePortion* pPor = aRoot.GetFirstPortion();
+ while ( pPor )
+ {
+ if ( pPor->GetPortion() && pPor->GetPortion()->IsHolePortion() )
+ {
+ SwLinePortion* pHolePor = pPor->GetPortion();
+ pPor->SetPortion( NULL );
+ aRoot.SetLen( aRoot.GetLen() - pHolePor->GetLen() );
+ rMulti.SetLen( rMulti.GetLen() - pHolePor->GetLen() );
+ rMulti.SetPortion( pHolePor );
+ break;
+ }
+ pPor = pPor->GetPortion();
+ }
+
+ pTmp = new SwBidiPortion( nMultiLen + rInf.GetIdx(),
+ ((SwBidiPortion&)rMulti).GetLevel() );
+ }
+ else
+ pTmp = NULL;
+
+ if ( ! rMulti.GetLen() && rInf.GetLast() )
+ {
+ SeekAndChgBefore( rInf );
+ rInf.GetLast()->FormatEOL( rInf );
+ }
+
+ if( pNextFirst && pTmp )
+ {
+ pTmp->SetFollowFld();
+ pTmp->GetRoot().SetPortion( pNextFirst );
+ }
+ else
+ // A follow field portion is still waiting. If nobody wants it,
+ // we delete it.
+ delete pNextFirst;
+
+ rInf.SetRest( pTmp );
+ }
+
+ rInf.SetTxt( *pOldTxt );
+ rInf.SetPaintOfst( nOldPaintOfst );
+ rInf.SetStop( aInf.IsStop() );
+ rInf.SetNumDone( sal_True );
+ rInf.SetFtnDone( sal_True );
+ SeekAndChg( rInf );
+ delete pFirstRest;
+ delete pSecondRest;
+ delete pFontSave;
+ return bRet;
+}
+
+/*-----------------08.11.00 09:29-------------------
+ * SwTxtFormatter::MakeRestPortion(..)
+ * When a fieldportion at the end of line breaks and needs a following
+ * fieldportion in the next line, then the "restportion" of the formatinfo
+ * has to be set. Normally this happens during the formatting of the first
+ * part of the fieldportion.
+ * But sometimes the formatting starts at the line with the following part,
+ * exspecally when the following part is on the next page.
+ * In this case the MakeRestPortion-function has to create the following part.
+ * The first parameter is the line that contains possibly a first part
+ * of a field. When the function finds such field part, it creates the right
+ * restportion. This may be a multiportion, e.g. if the field is surrounded by
+ * a doubleline- or ruby-portion.
+ * The second parameter is the start index of the line.
+ * --------------------------------------------------*/
+
+SwLinePortion* SwTxtFormatter::MakeRestPortion( const SwLineLayout* pLine,
+ xub_StrLen nPosition )
+{
+ if( !nPosition )
+ return NULL;
+ xub_StrLen nMultiPos = nPosition - pLine->GetLen();
+ const SwMultiPortion *pTmpMulti = NULL;
+ const SwMultiPortion *pHelpMulti = NULL;
+ const SwLinePortion* pPor = pLine->GetFirstPortion();
+ SwFldPortion *pFld = NULL;
+ while( pPor )
+ {
+ if( pPor->GetLen() )
+ {
+ if( !pHelpMulti )
+ {
+ nMultiPos = nMultiPos + pPor->GetLen();
+ pTmpMulti = NULL;
+ }
+ }
+ if( pPor->InFldGrp() )
+ {
+ if( !pHelpMulti )
+ pTmpMulti = NULL;
+ pFld = (SwFldPortion*)pPor;
+ }
+ else if( pPor->IsMultiPortion() )
+ {
+ ASSERT( !pHelpMulti || pHelpMulti->IsBidi(),
+ "Nested multiportions are forbidden." );
+
+ pFld = NULL;
+ pTmpMulti = (SwMultiPortion*)pPor;
+ }
+ pPor = pPor->GetPortion();
+ // If the last portion is a multi-portion, we enter it
+ // and look for a field portion inside.
+ // If we are already in a multiportion, we could change to the
+ // next line
+ if( !pPor && pTmpMulti )
+ {
+ if( pHelpMulti )
+ { // We're already inside the multiportion, let's take the second
+ // line, if we are in a double line portion
+ if( !pHelpMulti->IsRuby() )
+ pPor = pHelpMulti->GetRoot().GetNext();
+ pTmpMulti = NULL;
+ }
+ else
+ { // Now we enter a multiportion, in a ruby portion we take the
+ // main line, not the phonetic line, in a doublelineportion we
+ // starts with the first line.
+ pHelpMulti = pTmpMulti;
+ nMultiPos = nMultiPos - pHelpMulti->GetLen();
+ if( pHelpMulti->IsRuby() && pHelpMulti->OnTop() )
+ pPor = pHelpMulti->GetRoot().GetNext();
+ else
+ pPor = pHelpMulti->GetRoot().GetFirstPortion();
+ }
+ }
+ }
+ if( pFld && !pFld->HasFollow() )
+ pFld = NULL;
+
+ SwLinePortion *pRest = NULL;
+ if( pFld )
+ {
+ const SwTxtAttr *pHint = GetAttr( nPosition - 1 );
+ if( pHint && pHint->Which() == RES_TXTATR_FIELD )
+ {
+ pRest = NewFldPortion( GetInfo(), pHint );
+ if( pRest->InFldGrp() )
+ ((SwFldPortion*)pRest)->TakeNextOffset( pFld );
+ else
+ {
+ delete pRest;
+ pRest = NULL;
+ }
+ }
+ }
+ if( !pHelpMulti )
+ return pRest;
+
+ nPosition = nMultiPos + pHelpMulti->GetLen();
+ SwMultiCreator* pCreate = GetInfo().GetMultiCreator( nMultiPos, 0 );
+
+ if ( !pCreate )
+ {
+ ASSERT( !pHelpMulti->GetLen(), "Multiportion without attribut?" );
+ if ( nMultiPos )
+ --nMultiPos;
+ pCreate = GetInfo().GetMultiCreator( --nMultiPos, 0 );
+ }
+
+ if( pRest || nMultiPos > nPosition || ( pHelpMulti->IsRuby() &&
+ ((SwRubyPortion*)pHelpMulti)->GetRubyOffset() < STRING_LEN ) )
+ {
+ SwMultiPortion* pTmp;
+ if( pHelpMulti->IsDouble() )
+ pTmp = new SwDoubleLinePortion( *pCreate, nMultiPos );
+ else if( pHelpMulti->IsBidi() )
+ pTmp = new SwBidiPortion( nMultiPos, pCreate->nLevel );
+ else if( pHelpMulti->IsRuby() )
+ {
+ sal_Bool bRubyTop;
+ sal_Bool* pRubyPos = 0;
+
+ if ( GetInfo().SnapToGrid() )
+ {
+ GETGRID( pFrm->FindPageFrm() )
+ if ( pGrid )
+ {
+ bRubyTop = ! pGrid->GetRubyTextBelow();
+ pRubyPos = &bRubyTop;
+ }
+ }
+
+ pTmp = new SwRubyPortion( *pCreate, *GetInfo().GetFont(),
+ *pFrm->GetTxtNode()->getIDocumentSettingAccess(),
+ nMultiPos, ((SwRubyPortion*)pHelpMulti)->GetRubyOffset(),
+ pRubyPos );
+ }
+ else if( pHelpMulti->HasRotation() )
+ pTmp = new SwRotatedPortion( nMultiPos, pHelpMulti->GetDirection() );
+ else
+ {
+ delete pCreate;
+ return pRest;
+ }
+ delete pCreate;
+ pTmp->SetFollowFld();
+ if( pRest )
+ {
+ SwLineLayout *pLay = &pTmp->GetRoot();
+ if( pTmp->IsRuby() && pTmp->OnTop() )
+ {
+ pLay->SetNext( new SwLineLayout() );
+ pLay = pLay->GetNext();
+ }
+ pLay->SetPortion( pRest );
+ }
+ return pTmp;
+ }
+ return pRest;
+}
+
+
+
+/*-----------------23.10.00 10:47-------------------
+ * SwTxtCursorSave notes the start and current line of a SwTxtCursor,
+ * sets them to the values for GetCrsrOfst inside a multiportion
+ * and restores them in the destructor.
+ * --------------------------------------------------*/
+
+SwTxtCursorSave::SwTxtCursorSave( SwTxtCursor* pTxtCursor,
+ SwMultiPortion* pMulti,
+ SwTwips nY,
+ USHORT& nX,
+ xub_StrLen nCurrStart,
+ long nSpaceAdd )
+{
+ pTxtCrsr = pTxtCursor;
+ nStart = pTxtCursor->nStart;
+ pTxtCursor->nStart = nCurrStart;
+ pCurr = pTxtCursor->pCurr;
+ pTxtCursor->pCurr = &pMulti->GetRoot();
+ while( pTxtCursor->Y() + pTxtCursor->GetLineHeight() < nY &&
+ pTxtCursor->Next() )
+ ; // nothing
+ nWidth = pTxtCursor->pCurr->Width();
+ nOldProp = pTxtCursor->GetPropFont();
+
+ if ( pMulti->IsDouble() || pMulti->IsBidi() )
+ {
+ bSpaceChg = pMulti->ChgSpaceAdd( pTxtCursor->pCurr, nSpaceAdd );
+
+ USHORT nSpaceCnt;
+ if ( pMulti->IsDouble() )
+ {
+ pTxtCursor->SetPropFont( 50 );
+ nSpaceCnt = ((SwDoubleLinePortion*)pMulti)->GetSpaceCnt();
+ }
+ else
+ {
+ const xub_StrLen nOldIdx = pTxtCursor->GetInfo().GetIdx();
+ pTxtCursor->GetInfo().SetIdx ( nCurrStart );
+ nSpaceCnt = ((SwBidiPortion*)pMulti)->GetSpaceCnt(pTxtCursor->GetInfo());
+ pTxtCursor->GetInfo().SetIdx ( nOldIdx );
+ }
+
+ if( nSpaceAdd > 0 && !pMulti->HasTabulator() )
+ pTxtCursor->pCurr->Width( static_cast<USHORT>(nWidth + nSpaceAdd * nSpaceCnt / SPACING_PRECISION_FACTOR ) );
+
+ // For a BidiPortion we have to calculate the offset from the
+ // end of the portion
+ if ( nX && pMulti->IsBidi() )
+ nX = pTxtCursor->pCurr->Width() - nX;
+ }
+ else
+ bSpaceChg = sal_False;
+}
+
+SwTxtCursorSave::~SwTxtCursorSave()
+{
+ if( bSpaceChg )
+ SwDoubleLinePortion::ResetSpaceAdd( pTxtCrsr->pCurr );
+ pTxtCrsr->pCurr->Width( KSHORT(nWidth) );
+ pTxtCrsr->pCurr = pCurr;
+ pTxtCrsr->nStart = nStart;
+ pTxtCrsr->SetPropFont( nOldProp );
+}
+