Files
libreoffice/sw/source/core/text/pormulti.cxx
Jens-Heiner Rechtien 7827c6daf1 INTEGRATION: CWS writercorehandoff (1.82.48); FILE MERGED
2006/07/27 14:47:54 fme 1.82.48.5: RESYNC: (1.83-1.84); FILE MERGED
2005/09/13 14:34:15 tra 1.82.48.4: RESYNC: (1.82-1.83); FILE MERGED
2005/07/05 08:12:59 tra 1.82.48.3: #i50348#make SwDoc interface based
2005/06/24 11:43:38 fme 1.82.48.2: #i50348# Make SwDoc accessible via interfaces
2005/06/07 14:14:24 fme 1.82.48.1: #i50348# General cleanup - removed unused header files, functions, members, declarations etc.
2006-08-14 15:41:38 +00:00

2530 lines
84 KiB
C++

/*************************************************************************
*
* OpenOffice.org - a multi-platform office productivity suite
*
* $RCSfile: pormulti.cxx,v $
*
* $Revision: 1.85 $
*
* last change: $Author: hr $ $Date: 2006-08-14 16:41:38 $
*
* The Contents of this file are made available subject to
* the terms of GNU Lesser General Public License Version 2.1.
*
*
* GNU Lesser General Public License Version 2.1
* =============================================
* Copyright 2005 by Sun Microsystems, Inc.
* 901 San Antonio Road, Palo Alto, CA 94303, USA
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
************************************************************************/
#pragma hdrstop
#ifndef _HINTIDS_HXX
#include <hintids.hxx>
#endif
#ifndef _COM_SUN_STAR_I18N_SCRIPTTYPE_HDL_
#include <com/sun/star/i18n/ScriptType.hdl>
#endif
#ifndef _SVX_TWOLINESITEM_HXX
#include <svx/twolinesitem.hxx>
#endif
#ifndef _SVX_CHARROTATEITEM_HXX
#include <svx/charrotateitem.hxx>
#endif
#ifndef _SV_OUTDEV_HXX //autogen
#include <vcl/outdev.hxx>
#endif
#ifndef _FMTFLD_HXX
#include <fmtfld.hxx>
#endif
#ifndef _FLDBAS_HXX
#include <fldbas.hxx> // SwField
#endif
#ifndef _TXATBASE_HXX //autogen
#include <txatbase.hxx>
#endif
#ifndef _FMTRUBY_HXX
#include <fmtruby.hxx> // SwFmtRuby
#endif
#ifndef _TXTATR_HXX
#include <txtatr.hxx> // SwTxtRuby
#endif
#ifndef _CHARFMT_HXX // SwCharFmt
#include <charfmt.hxx>
#endif
#ifndef _TXTINET_HXX // SwTxtINetFmt
#include <txtinet.hxx>
#endif
#ifndef _FCHRFMT_HXX //autogen
#include <fchrfmt.hxx>
#endif
#ifndef _LAYFRM_HXX
#include <layfrm.hxx> // GetUpper()
#endif
#ifndef _SW_PORTIONHANDLER_HXX
#include <SwPortionHandler.hxx>
#endif
#ifndef _PORMULTI_HXX
#include <pormulti.hxx> // SwMultiPortion
#endif
#ifndef _INFTXT_HXX
#include <inftxt.hxx> // SwTxtSizeInfo
#endif
#ifndef _ITRPAINT_HXX
#include <itrpaint.hxx> // SwTxtPainter
#endif
#ifndef _VIEWOPT_HXX
#include <viewopt.hxx> // SwViewOptions
#endif
#ifndef _ITRFORM2_HXX
#include <itrform2.hxx> // SwTxtFormatter
#endif
#ifndef _PORFLD_HXX
#include <porfld.hxx> // SwFldPortion
#endif
#ifndef _PORGLUE_HXX
#include <porglue.hxx>
#endif
#ifndef _BREAKIT_HXX
#include <breakit.hxx>
#endif
#ifndef _PAGEFRM_HXX
#include <pagefrm.hxx>
#endif
#ifndef _ROWFRM_HXX
#include <rowfrm.hxx>
#endif
#ifndef _PAGEDESC_HXX
#include <pagedesc.hxx> // SwPageDesc
#endif
#ifndef SW_TGRDITEM_HXX
#include <tgrditem.hxx>
#endif
#ifndef _SWTABLE_HXX
#include <swtable.hxx>
#endif
#ifndef _FMTFSIZE_HXX //autogen
#include <fmtfsize.hxx>
#endif
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 &rInf ) 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 nSpaceAdd, const SwTxtSizeInfo &rInf ) const
{
return 0;
}
sal_Bool SwMultiPortion::ChgSpaceAdd( SwLineLayout* pCurr, long nSpaceAdd ) 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;
if( RES_CHRATR_ROTATE == rAttr.Which() )
pRot = &rAttr.GetCharRotate();
else
{
SwCharFmt* pFmt = NULL;
if( RES_TXTATR_INETFMT == rAttr.Which() )
pFmt = ((SwTxtINetFmt&)rAttr).GetCharFmt();
else if( RES_TXTATR_CHARFMT == rAttr.Which() )
pFmt = rAttr.GetCharFmt().GetCharFmt();
if ( pFmt )
{
const SfxPoolItem* pItem;
if( SFX_ITEM_SET == pFmt->GetAttrSet().
GetItemState( RES_CHRATR_ROTATE, TRUE, &pItem ) )
pRot = (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() * 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;
}
/*-----------------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();
if( RES_CHRATR_TWO_LINES == rAttr.Which() )
pTwo = &rAttr.Get2Lines();
else
{
SwCharFmt* pFmt = NULL;
if( RES_TXTATR_INETFMT == rAttr.Which() )
pFmt = ((SwTxtINetFmt&)rAttr).GetCharFmt();
else if( RES_TXTATR_CHARFMT == rAttr.Which() )
pFmt = rAttr.GetCharFmt().GetCharFmt();
if ( pFmt )
{
const SfxPoolItem* pItem;
if( SFX_ITEM_SET == pFmt->GetAttrSet().
GetItemState( RES_CHRATR_TWO_LINES, TRUE, &pItem ) )
pTwo = (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 += ((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 += ((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 &rInf ) 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 )
{
if( RES_CHRATR_TWO_LINES == rAttr.Which() )
{
rValue = rAttr.Get2Lines().GetValue();
if( !rpRef )
rpRef = &rAttr.Get2Lines();
else if( rAttr.Get2Lines().GetEndBracket() != rpRef->GetEndBracket() ||
rAttr.Get2Lines().GetStartBracket() != rpRef->GetStartBracket() )
rValue = sal_False;
return sal_True;
}
SwCharFmt* pFmt = NULL;
if( RES_TXTATR_INETFMT == rAttr.Which() )
pFmt = ((SwTxtINetFmt&)rAttr).GetCharFmt();
else if( RES_TXTATR_CHARFMT == rAttr.Which() )
pFmt = rAttr.GetCharFmt().GetCharFmt();
if ( pFmt )
{
const SfxPoolItem* pItem;
if( SFX_ITEM_SET == pFmt->GetAttrSet().
GetItemState( RES_CHRATR_TWO_LINES, TRUE, &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 )
{
if( RES_CHRATR_ROTATE == rAttr.Which() )
{
rValue = 0 != rAttr.GetCharRotate().GetValue();
if( !rpRef )
rpRef = &rAttr.GetCharRotate();
else if( rAttr.GetCharRotate().GetValue() != rpRef->GetValue() )
rValue = sal_False;
return sal_True;
}
SwCharFmt* pFmt = NULL;
if( RES_TXTATR_INETFMT == rAttr.Which() )
pFmt = ((SwTxtINetFmt&)rAttr).GetCharFmt();
else if( RES_TXTATR_CHARFMT == rAttr.Which() )
pFmt = rAttr.GetCharFmt().GetCharFmt();
if ( pFmt )
{
const SfxPoolItem* pItem;
if( SFX_ITEM_SET == pFmt->GetAttrSet().
GetItemState( RES_CHRATR_ROTATE, TRUE, &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->xBreak.is() && aTxt.Len() )
{
sal_Bool bFldDir = ( ::com::sun::star::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, nThisDir, nFrmDir;
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 nOldIdx = GetInfo().GetIdx();
GetInfo().SetIdx(((SwDoubleLinePortion&)rMulti).GetBrackets()->nStart);
SeekAndChg( GetInfo() );
((SwDoubleLinePortion&)rMulti).PaintBracket( GetInfo(), 0, sal_True );
GetInfo().SetIdx( nOldIdx );
}
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* pFnt = GetInfo().GetFont();
pFnt->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 nOldIdx = GetInfo().GetIdx();
GetInfo().SetIdx(((SwDoubleLinePortion&)rMulti).GetBrackets()->nStart);
SeekAndChg( GetInfo() );
GetInfo().X( nOldX );
((SwDoubleLinePortion&)rMulti).PaintBracket( GetInfo(),
aManip.GetSpaceAdd(), sal_False );
GetInfo().SetIdx( nOldIdx );
}
// 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;
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 -= 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() )
{
// Calculate number of blanks for justified alignment
SwLinePortion* pPor = rMulti.GetRoot().GetFirstPortion();
xub_StrLen nStart = rInf.GetIdx();
xub_StrLen nNull = 0;
xub_StrLen nBlanks;
for( nBlanks = 0; pPor; pPor = pPor->GetPortion() )
{
if( pPor->InTxtGrp() )
nBlanks += ((SwTxtPortion*)pPor)->GetSpaceCnt( rInf, nNull );
else if ( pPor->IsMultiPortion() &&
((SwMultiPortion*)pPor)->IsBidi() )
nBlanks += ((SwBidiPortion*)pPor)->GetSpaceCnt();
rInf.SetIdx( rInf.GetIdx() + pPor->GetLen() );
}
rInf.SetIdx( nStart );
((SwBidiPortion&)rMulti).SetSpaceCnt( nBlanks );
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 nPos )
{
if( !nPos )
return NULL;
xub_StrLen nMultiPos = nPos - pLine->GetLen();
const SwMultiPortion *pTmpMulti = NULL;
const SwMultiPortion *pMulti = NULL;
const SwLinePortion* pPor = pLine->GetFirstPortion();
SwFldPortion *pFld = NULL;
while( pPor )
{
if( pPor->GetLen() )
{
if( !pMulti )
{
nMultiPos += pPor->GetLen();
pTmpMulti = NULL;
}
}
if( pPor->InFldGrp() )
{
if( !pMulti )
pTmpMulti = NULL;
pFld = (SwFldPortion*)pPor;
}
else if( pPor->IsMultiPortion() )
{
ASSERT( !pMulti || pMulti->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( pMulti )
{ // We're already inside the multiportion, let's take the second
// line, if we are in a double line portion
if( !pMulti->IsRuby() )
pPor = pMulti->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.
pMulti = pTmpMulti;
nMultiPos -= pMulti->GetLen();
if( pMulti->IsRuby() && pMulti->OnTop() )
pPor = pMulti->GetRoot().GetNext();
else
pPor = pMulti->GetRoot().GetFirstPortion();
}
}
}
if( pFld && !pFld->HasFollow() )
pFld = NULL;
SwLinePortion *pRest = NULL;
if( pFld )
{
const SwTxtAttr *pHint = GetAttr( nPos - 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( !pMulti )
return pRest;
nPos = nMultiPos + pMulti->GetLen();
SwMultiCreator* pCreate = GetInfo().GetMultiCreator( nMultiPos, 0 );
if ( !pCreate )
{
ASSERT( !pMulti->GetLen(), "Multiportion without attribut?" );
if ( nMultiPos )
--nMultiPos;
pCreate = GetInfo().GetMultiCreator( --nMultiPos, 0 );
}
if( pRest || nMultiPos > nPos || ( pMulti->IsRuby() &&
((SwRubyPortion*)pMulti)->GetRubyOffset() < STRING_LEN ) )
{
SwMultiPortion* pTmp;
if( pMulti->IsDouble() )
pTmp = new SwDoubleLinePortion( *pCreate, nMultiPos );
else if( pMulti->IsBidi() )
pTmp = new SwBidiPortion( nMultiPos, pCreate->nLevel );
else if( pMulti->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*)pMulti)->GetRubyOffset(),
pRubyPos );
}
else if( pMulti->HasRotation() )
pTmp = new SwRotatedPortion( nMultiPos, pMulti->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
nSpaceCnt = ((SwBidiPortion*)pMulti)->GetSpaceCnt();
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 );
}