blob: 4e3ea47f54a2422ecce72556539cebaa0aef424d [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sw.hxx"
#include <hintids.hxx>
#include <com/sun/star/i18n/ScriptType.hdl>
#include <vcl/graph.hxx>
#include <editeng/brshitem.hxx>
#include <vcl/metric.hxx>
#include <vcl/outdev.hxx>
#include <viewopt.hxx> // SwViewOptions
#include <txtcfg.hxx>
#include <SwPortionHandler.hxx>
#include <porlay.hxx>
#include <porfld.hxx>
#include <inftxt.hxx>
#include <blink.hxx> // pBlink
#include <frmtool.hxx> // DrawGraphic
#include <viewsh.hxx>
#include <docsh.hxx>
#include <doc.hxx>
#include "rootfrm.hxx"
#include <breakit.hxx>
#include <porrst.hxx>
#include <porftn.hxx> // SwFtnPortion
#include <accessibilityoptions.hxx>
#include <editeng/lrspitem.hxx>
#include <unicode/ubidi.h>
using namespace ::com::sun::star;
/*************************************************************************
* class SwFldPortion
*************************************************************************/
SwLinePortion *SwFldPortion::Compress()
{ return (GetLen() || aExpand.Len() || SwLinePortion::Compress()) ? this : 0; }
SwFldPortion *SwFldPortion::Clone( const XubString &rExpand ) const
{
SwFont *pNewFnt;
if( 0 != ( pNewFnt = pFnt ) )
{
pNewFnt = new SwFont( *pFnt );
}
SwFldPortion* pClone = new SwFldPortion( rExpand, pNewFnt, bPlaceHolder );
pClone->SetNextOffset( nNextOffset );
pClone->m_bNoLength = this->m_bNoLength;
return pClone;
}
void SwFldPortion::TakeNextOffset( const SwFldPortion* pFld )
{
ASSERT( pFld, "TakeNextOffset: Missing Source" );
nNextOffset = pFld->GetNextOffset();
aExpand.Erase( 0, nNextOffset );
bFollow = sal_True;
}
SwFldPortion::SwFldPortion( const XubString &rExpand, SwFont *pFont, sal_Bool bPlaceHold )
: aExpand(rExpand), pFnt(pFont), nNextOffset(0), nNextScriptChg(STRING_LEN), nViewWidth(0),
bFollow( sal_False ), bHasFollow( sal_False ), bPlaceHolder( bPlaceHold )
, m_bNoLength( sal_False )
{
SetWhichPor( POR_FLD );
m_nAttrFldType = 0;
}
SwFldPortion::SwFldPortion( const SwFldPortion& rFld )
: SwExpandPortion( rFld ),
aExpand( rFld.GetExp() ),
nNextOffset( rFld.GetNextOffset() ),
nNextScriptChg( rFld.GetNextScriptChg() ),
bFollow( rFld.IsFollow() ),
bLeft( rFld.IsLeft() ),
bHide( rFld.IsHide() ),
bCenter( rFld.IsCenter() ),
bHasFollow( rFld.HasFollow() ),
bPlaceHolder( rFld.bPlaceHolder )
, m_bNoLength( rFld.m_bNoLength )
{
if ( rFld.HasFont() )
pFnt = new SwFont( *rFld.GetFont() );
else
pFnt = 0;
SetWhichPor( POR_FLD );
}
SwFldPortion::~SwFldPortion()
{
delete pFnt;
if( pBlink )
pBlink->Delete( this );
}
/*************************************************************************
* virtual SwFldPortion::GetViewWidth()
*************************************************************************/
KSHORT SwFldPortion::GetViewWidth( const SwTxtSizeInfo &rInf ) const
{
// Wir stehen zwar im const, aber nViewWidth sollte erst im letzten
// Moment errechnet werden:
SwFldPortion* pThis = (SwFldPortion*)this;
if( !Width() && rInf.OnWin() && !rInf.GetOpt().IsPagePreview() &&
!rInf.GetOpt().IsReadonly() && SwViewOption::IsFieldShadings() )
{
if( !nViewWidth )
pThis->nViewWidth = rInf.GetTxtSize( ' ' ).Width();
}
else
pThis->nViewWidth = 0;
return nViewWidth;
}
/*************************************************************************
* virtual SwFldPortion::Format()
*************************************************************************/
// 8653: in keinem Fall nur SetLen(0);
/*************************************************************************
* Hilfsklasse SwFldSlot
**************************************************************************/
class SwFldSlot
{
const XubString *pOldTxt;
XubString aTxt;
xub_StrLen nIdx;
xub_StrLen nLen;
sal_Bool bOn;
SwTxtFormatInfo *pInf;
public:
SwFldSlot( const SwTxtFormatInfo* pNew, const SwFldPortion *pPor );
~SwFldSlot();
};
SwFldSlot::SwFldSlot( const SwTxtFormatInfo* pNew, const SwFldPortion *pPor )
{
bOn = pPor->GetExpTxt( *pNew, aTxt );
// Der Text wird ausgetauscht...
if( bOn )
{
pInf = (SwTxtFormatInfo*)pNew;
nIdx = pInf->GetIdx();
nLen = pInf->GetLen();
pOldTxt = &(pInf->GetTxt());
pInf->SetLen( aTxt.Len() );
if( pPor->IsFollow() )
{
pInf->SetFakeLineStart( nIdx > pInf->GetLineStart() );
pInf->SetIdx( 0 );
}
else
{
XubString aTmp( aTxt );
aTxt = *pOldTxt;
aTxt.Erase( nIdx, 1 );
aTxt.Insert( aTmp, nIdx );
}
pInf->SetTxt( aTxt );
}
}
SwFldSlot::~SwFldSlot()
{
if( bOn )
{
pInf->SetTxt( *pOldTxt );
pInf->SetIdx( nIdx );
pInf->SetLen( nLen );
pInf->SetFakeLineStart( sal_False );
}
}
void SwFldPortion::CheckScript( const SwTxtSizeInfo &rInf )
{
String aTxt;
if( GetExpTxt( rInf, aTxt ) && aTxt.Len() && pBreakIt->GetBreakIter().is() )
{
sal_uInt8 nActual = pFnt ? pFnt->GetActual() : rInf.GetFont()->GetActual();
sal_uInt16 nScript;
{
nScript = pBreakIt->GetBreakIter()->getScriptType( aTxt, 0 );
xub_StrLen nChg = 0;
if( i18n::ScriptType::WEAK == nScript )
{
nChg =(xub_StrLen)pBreakIt->GetBreakIter()->endOfScript(aTxt,0,nScript);
if( nChg < aTxt.Len() )
nScript = pBreakIt->GetBreakIter()->getScriptType( aTxt, nChg );
}
//
// nNextScriptChg will be evaluated during SwFldPortion::Format()
//
if ( nChg < aTxt.Len() )
nNextScriptChg = (xub_StrLen)pBreakIt->GetBreakIter()->endOfScript( aTxt, nChg, nScript );
else
nNextScriptChg = aTxt.Len();
}
sal_uInt8 nTmp;
switch ( nScript ) {
case i18n::ScriptType::LATIN : nTmp = SW_LATIN; break;
case i18n::ScriptType::ASIAN : nTmp = SW_CJK; break;
case i18n::ScriptType::COMPLEX : nTmp = SW_CTL; break;
default: nTmp = nActual;
}
// #i16354# Change script type for RTL text to CTL.
const SwScriptInfo& rSI = rInf.GetParaPortion()->GetScriptInfo();
// --> OD 2009-01-29 #i98418#
// const sal_uInt8 nFldDir = IsNumberPortion() ?
const sal_uInt8 nFldDir = ( IsNumberPortion() || IsFtnNumPortion() ) ?
rSI.GetDefaultDir() :
rSI.DirType( IsFollow() ? rInf.GetIdx() - 1 : rInf.GetIdx() );
// <--
if ( UBIDI_RTL == nFldDir )
{
UErrorCode nError = U_ZERO_ERROR;
UBiDi* pBidi = ubidi_openSized( aTxt.Len(), 0, &nError );
ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(aTxt.GetBuffer()), aTxt.Len(), nFldDir, NULL, &nError );
int32_t nEnd;
UBiDiLevel nCurrDir;
ubidi_getLogicalRun( pBidi, 0, &nEnd, &nCurrDir );
ubidi_close( pBidi );
const xub_StrLen nNextDirChg = (xub_StrLen)nEnd;
nNextScriptChg = Min( nNextScriptChg, nNextDirChg );
// #i89825# change the script type also to CTL
// if there is no strong LTR char in the LTR run (numbers)
if ( nCurrDir != UBIDI_RTL )
{
nCurrDir = UBIDI_RTL;
for ( xub_StrLen nCharIdx = 0; nCharIdx < nEnd; ++nCharIdx )
{
UCharDirection nCharDir = u_charDirection ( aTxt.GetChar ( nCharIdx ));
if ( nCharDir == U_LEFT_TO_RIGHT ||
nCharDir == U_LEFT_TO_RIGHT_EMBEDDING ||
nCharDir == U_LEFT_TO_RIGHT_OVERRIDE )
{
nCurrDir = UBIDI_LTR;
break;
}
}
}
if ( nCurrDir == UBIDI_RTL )
nTmp = SW_CTL;
}
// --> OD 2009-01-29 #i98418#
// keep determined script type for footnote portions as preferred script type.
// For footnote portions a font can not be created directly - see footnote
// portion format method.
// if( !IsFtnPortion() && nTmp != nActual )
if ( IsFtnPortion() )
{
dynamic_cast<SwFtnPortion*>(this)->SetPreferredScriptType( nTmp );
}
else if ( nTmp != nActual )
{
if( !pFnt )
pFnt = new SwFont( *rInf.GetFont() );
pFnt->SetActual( nTmp );
}
// <--
}
}
sal_Bool SwFldPortion::Format( SwTxtFormatInfo &rInf )
{
// Scope wegen aDiffTxt::DTOR!
xub_StrLen nRest;
sal_Bool bFull;
sal_Bool bEOL = sal_False;
long nTxtRest = rInf.GetTxt().Len() - rInf.GetIdx();
{
SwFldSlot aDiffTxt( &rInf, this );
SwLayoutModeModifier aLayoutModeModifier( *rInf.GetOut() );
aLayoutModeModifier.SetAuto();
// Field portion has to be split in several parts if
// 1. There are script/direction changes inside the field
// 2. There are portion breaks (tab, break) inside the field:
const xub_StrLen nOldFullLen = rInf.GetLen();
xub_StrLen nFullLen = rInf.ScanPortionEnd( rInf.GetIdx(), rInf.GetIdx() + nOldFullLen ) - rInf.GetIdx();
if ( nNextScriptChg < nFullLen )
{
nFullLen = nNextScriptChg;
rInf.SetHookChar( 0 );
}
rInf.SetLen( nFullLen );
if ( STRING_LEN != rInf.GetUnderScorePos() &&
rInf.GetUnderScorePos() > rInf.GetIdx() )
rInf.SetUnderScorePos( rInf.GetIdx() );
if( pFnt )
pFnt->GoMagic( rInf.GetVsh(), pFnt->GetActual() );
SwFontSave aSave( rInf, pFnt );
// 8674: Laenge muss 0 sein, bei bFull nach Format ist die Laenge
// gesetzt und wird in nRest uebertragen. Ansonsten bleibt die
// Laenge erhalten und wuerde auch in nRest einfliessen!
SetLen(0);
const MSHORT nFollow = IsFollow() ? 0 : 1;
// So komisch es aussieht, die Abfrage auf GetLen() muss wegen der
// ExpandPortions _hinter_ aDiffTxt (vgl. SoftHyphs)
// sal_False returnen wegen SetFull ...
if( !nFullLen )
{
// nicht Init(), weil wir Hoehe und Ascent brauchen
Width(0);
bFull = rInf.Width() <= rInf.GetPos().X();
}
else
{
xub_StrLen nOldLineStart = rInf.GetLineStart();
if( IsFollow() )
rInf.SetLineStart( 0 );
rInf.SetNotEOL( nFullLen == nOldFullLen && nTxtRest > nFollow );
// the height depending on the fields font is set,
// this is required for SwTxtGuess::Guess
Height( rInf.GetTxtHeight() );
// If a kerning portion is inserted after our field portion,
// the ascent and height must be known
SetAscent( rInf.GetAscent() );
bFull = SwTxtPortion::Format( rInf );
rInf.SetNotEOL( sal_False );
rInf.SetLineStart( nOldLineStart );
}
xub_StrLen nTmpLen = GetLen();
bEOL = !nTmpLen && nFollow && bFull;
nRest = nOldFullLen - nTmpLen;
// Das Zeichen wird in der ersten Portion gehalten.
// Unbedingt nach Format!
SetLen( (m_bNoLength) ? 0 : nFollow );
if( nRest )
{
// aExpand ist noch nicht gekuerzt worden, der neue Ofst
// ergibt sich durch nRest.
xub_StrLen nNextOfst = aExpand.Len() - nRest;
if ( IsQuoVadisPortion() )
nNextOfst = nNextOfst + ((SwQuoVadisPortion*)this)->GetContTxt().Len();
XubString aNew( aExpand, nNextOfst, STRING_LEN );
aExpand.Erase( nNextOfst, STRING_LEN );
// These characters should not be contained in the follow
// field portion. They are handled via the HookChar mechanism.
switch( aNew.GetChar( 0 ))
{
case CH_BREAK : bFull = sal_True;
// kein break;
case ' ' :
case CH_TAB :
case CHAR_HARDHYPHEN: // non-breaking hyphen
case CHAR_SOFTHYPHEN:
case CHAR_HARDBLANK:
case CHAR_ZWSP :
case CHAR_ZWNBSP :
case CH_TXTATR_BREAKWORD:
case CH_TXTATR_INWORD:
{
aNew.Erase( 0, 1 );
++nNextOfst;
break;
}
default: ;
}
// Even if there is no more text left for a follow field,
// we have to build a follow field portion (without font),
// otherwise the HookChar mechanism would not work.
SwFldPortion *pFld = Clone( aNew );
if( aNew.Len() && !pFld->GetFont() )
{
SwFont *pNewFnt = new SwFont( *rInf.GetFont() );
pFld->SetFont( pNewFnt );
}
pFld->SetFollow( sal_True );
SetHasFollow( sal_True );
// In nNextOffset steht bei einem neuangelegten Feld zunaechst
// der Offset, an dem es selbst im Originalstring beginnt.
// Wenn beim Formatieren ein FollowFeld angelegt wird, wird
// der Offset dieses FollowFelds in nNextOffset festgehalten.
nNextOffset = nNextOffset + nNextOfst;
pFld->SetNextOffset( nNextOffset );
rInf.SetRest( pFld );
}
}
if( bEOL && rInf.GetLast() && !rInf.GetUnderFlow() )
rInf.GetLast()->FormatEOL( rInf );
return bFull;
}
/*************************************************************************
* virtual SwFldPortion::Paint()
*************************************************************************/
void SwFldPortion::Paint( const SwTxtPaintInfo &rInf ) const
{
SwFontSave aSave( rInf, pFnt );
ASSERT( GetLen() <= 1, "SwFldPortion::Paint: rest-portion polution?" );
if( Width() && ( !bPlaceHolder || rInf.GetOpt().IsShowPlaceHolderFields() ) )
{
// Dies ist eine freizuegige Auslegung der Hintergrundbelegung ...
rInf.DrawViewOpt( *this, POR_FLD );
SwExpandPortion::Paint( rInf );
}
}
/*************************************************************************
* virtual SwFldPortion::GetExpTxt()
*************************************************************************/
sal_Bool SwFldPortion::GetExpTxt( const SwTxtSizeInfo &rInf, XubString &rTxt ) const
{
rTxt = aExpand;
if( !rTxt.Len() && rInf.OnWin() &&
!rInf.GetOpt().IsPagePreview() && !rInf.GetOpt().IsReadonly() &&
SwViewOption::IsFieldShadings() &&
!HasFollow() )
rTxt = ' ';
return sal_True;
}
/*************************************************************************
* virtual SwFldPortion::HandlePortion()
*************************************************************************/
void SwFldPortion::HandlePortion( SwPortionHandler& rPH ) const
{
rPH.Special( GetLen(), aExpand, GetWhichPor() );
if( GetWhichPor() == POR_FLD )
{
rPH.SetAttrFieldType(m_nAttrFldType);
}
}
/*************************************************************************
* virtual SwFldPortion::GetTxtSize()
*************************************************************************/
SwPosSize SwFldPortion::GetTxtSize( const SwTxtSizeInfo &rInf ) const
{
SwFontSave aSave( rInf, pFnt );
SwPosSize aSize( SwExpandPortion::GetTxtSize( rInf ) );
return aSize;
}
/*************************************************************************
* class SwHiddenPortion
*************************************************************************/
SwFldPortion *SwHiddenPortion::Clone(const XubString &rExpand ) const
{
SwFont *pNewFnt;
if( 0 != ( pNewFnt = pFnt ) )
pNewFnt = new SwFont( *pFnt );
return new SwHiddenPortion( rExpand, pNewFnt );
}
/*************************************************************************
* virtual SwHiddenPortion::Paint()
*************************************************************************/
void SwHiddenPortion::Paint( const SwTxtPaintInfo &rInf ) const
{
if( Width() )
{
SwFontSave aSave( rInf, pFnt );
rInf.DrawViewOpt( *this, POR_HIDDEN );
SwExpandPortion::Paint( rInf );
}
}
/*************************************************************************
* virtual SwHiddenPortion::GetExpTxt()
*************************************************************************/
sal_Bool SwHiddenPortion::GetExpTxt( const SwTxtSizeInfo &rInf, XubString &rTxt ) const
{
// Nicht auf IsHidden() abfragen !
return SwFldPortion::GetExpTxt( rInf, rTxt );
}
/*************************************************************************
* class SwNumberPortion
*************************************************************************/
// --> OD 2008-01-23 #newlistlevelattrs#
SwNumberPortion::SwNumberPortion( const XubString &rExpand,
SwFont *pFont,
const sal_Bool bLft,
const sal_Bool bCntr,
const KSHORT nMinDst,
const bool bLabelAlignmentPosAndSpaceModeActive )
: SwFldPortion( rExpand, pFont ),
nFixWidth(0),
nMinDist( nMinDst ),
// --> OD 2008-01-23 #newlistlevelattrs#
mbLabelAlignmentPosAndSpaceModeActive( bLabelAlignmentPosAndSpaceModeActive )
// <--
{
SetWhichPor( POR_NUMBER );
SetLeft( bLft );
SetHide( sal_False );
SetCenter( bCntr );
}
xub_StrLen SwNumberPortion::GetCrsrOfst( const MSHORT ) const
{
return 0;
}
SwFldPortion *SwNumberPortion::Clone( const XubString &rExpand ) const
{
SwFont *pNewFnt;
if( 0 != ( pNewFnt = pFnt ) )
pNewFnt = new SwFont( *pFnt );
// --> OD 2008-01-23 #newlistlevelattrs#
return new SwNumberPortion( rExpand, pNewFnt, IsLeft(), IsCenter(),
nMinDist, mbLabelAlignmentPosAndSpaceModeActive );
// <--
}
/*************************************************************************
* virtual SwNumberPortion::Format()
*************************************************************************/
// 5010: Wir sind in der Lage, mehrzeilige NumFelder anzulegen!
// 3689: Fies ist, wenn man in der Dialogbox soviel Davor-Text
// eingibt, bis die Zeile ueberlaeuft.
// Man muss die Fly-Ausweichmanoever beachten!
sal_Bool SwNumberPortion::Format( SwTxtFormatInfo &rInf )
{
SetHide( sal_False );
const sal_Bool bFull = SwFldPortion::Format( rInf );
SetLen( 0 );
// a numbering portion can be contained in a rotated portion!!!
nFixWidth = rInf.IsMulti() ? Height() : Width();
rInf.SetNumDone( !rInf.GetRest() );
if( rInf.IsNumDone() )
{
// SetAscent( rInf.GetAscent() );
ASSERT( Height() && nAscent, "NumberPortions without Height | Ascent" );
long nDiff( 0 );
// --> OD 2008-01-23 #newlistlevelattrs#
if ( !mbLabelAlignmentPosAndSpaceModeActive )
{
if ( !rInf.GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) &&
// --> FME 2004-08-13 #i32902#
!IsFtnNumPortion() )
// <--
{
nDiff = rInf.Left()
+ rInf.GetTxtFrm()->GetTxtNode()->
GetSwAttrSet().GetLRSpace().GetTxtFirstLineOfst()
- rInf.First()
+ rInf.ForcedLeftMargin();
}
else
{
nDiff = rInf.Left() - rInf.First() + rInf.ForcedLeftMargin();
}
}
// <--
// Ein Vorschlag von Juergen und Volkmar:
// Der Textteil hinter der Numerierung sollte immer
// mindestens beim linken Rand beginnen.
if( nDiff < 0 )
nDiff = 0;
else if ( nDiff > rInf.X() )
nDiff -= rInf.X();
else
nDiff = 0;
if( nDiff < nFixWidth + nMinDist )
nDiff = nFixWidth + nMinDist;
// 2739: Numerierung weicht Fly aus, kein nDiff in der zweiten Runde
// fieser Sonderfall: FlyFrm liegt in dem Bereich,
// den wir uns gerade unter den Nagel reissen wollen.
// Die NumberPortion wird als verborgen markiert.
const sal_Bool bFly = rInf.GetFly() ||
( rInf.GetLast() && rInf.GetLast()->IsFlyPortion() );
if( nDiff > rInf.Width() )
{
nDiff = rInf.Width();
if ( bFly )
SetHide( sal_True );
}
// A numbering portion can be inside a SwRotatedPortion. Then the
// Height has to be changed
if ( rInf.IsMulti() )
{
if ( Height() < nDiff )
Height( KSHORT( nDiff ) );
}
else if( Width() < nDiff )
Width( KSHORT(nDiff) );
}
return bFull;
}
void SwNumberPortion::FormatEOL( SwTxtFormatInfo& )
{
/* Ein FormatEOL deutet daraufhin, dass der folgende Text
* nicht mit auf die Zeile passte. Damit die Numerierung mitwandert,
* wird diese NumberPortion verborgen.
*/
// This caused trouble with flys anchored as characters.
// If one of these is numbered but does not fit to the line,
// it calls this function, causing a loop because both the number
// portion and the fly portion go to the next line
// SetHide( sal_True );
}
/*************************************************************************
* virtual SwNumberPortion::Paint()
*************************************************************************/
void SwNumberPortion::Paint( const SwTxtPaintInfo &rInf ) const
{
/* Eine verborgene NumberPortion wird nicht angezeigt, es sei denn, es gibt
* Textportions in dieser Zeile oder es gibt ueberhaupt nur eine einzige Zeile.
*/
if ( IsHide() && rInf.GetParaPortion() && rInf.GetParaPortion()->GetNext() )
{
SwLinePortion *pTmp = GetPortion();
while ( pTmp && !pTmp->InTxtGrp() )
pTmp = pTmp->GetPortion();
if ( !pTmp )
return;
}
// calculate the width of the number portion, including follows
const KSHORT nOldWidth = Width();
sal_uInt16 nSumWidth = 0;
sal_uInt16 nOffset = 0;
const SwLinePortion* pTmp = this;
while ( pTmp && pTmp->InNumberGrp() )
{
nSumWidth = nSumWidth + pTmp->Width();
if ( ((SwNumberPortion*)pTmp)->HasFollow() )
pTmp = pTmp->GetPortion();
else
{
nOffset = pTmp->Width() - ((SwNumberPortion*)pTmp)->nFixWidth;
break;
}
}
// The master portion takes care for painting the background of the
// follow field portions
if ( ! IsFollow() )
{
SwLinePortion *pThis = (SwLinePortion*)this;
pThis->Width( nSumWidth );
rInf.DrawViewOpt( *this, POR_NUMBER );
pThis->Width( nOldWidth );
}
if( aExpand.Len() )
{
const SwFont *pTmpFnt = rInf.GetFont();
sal_Bool bPaintSpace = ( UNDERLINE_NONE != pTmpFnt->GetUnderline() ||
UNDERLINE_NONE != pTmpFnt->GetOverline() ||
STRIKEOUT_NONE != pTmpFnt->GetStrikeout() ) &&
!pTmpFnt->IsWordLineMode();
if( bPaintSpace && pFnt )
bPaintSpace = ( UNDERLINE_NONE != pFnt->GetUnderline() ||
UNDERLINE_NONE != pFnt->GetOverline() ||
STRIKEOUT_NONE != pFnt->GetStrikeout() ) &&
!pFnt->IsWordLineMode();
SwFontSave aSave( rInf, pFnt );
if( nFixWidth == Width() && ! HasFollow() )
SwExpandPortion::Paint( rInf );
else
{
// logisches const: Width wird wieder zurueckgesetzt
SwLinePortion *pThis = (SwLinePortion*)this;
bPaintSpace = bPaintSpace && nFixWidth < nOldWidth;
KSHORT nSpaceOffs = nFixWidth;
pThis->Width( nFixWidth );
if( ( IsLeft() && ! rInf.GetTxtFrm()->IsRightToLeft() ) ||
( ! IsLeft() && ! IsCenter() && rInf.GetTxtFrm()->IsRightToLeft() ) )
SwExpandPortion::Paint( rInf );
else
{
SwTxtPaintInfo aInf( rInf );
if( nOffset < nMinDist )
nOffset = 0;
else
{
if( IsCenter() )
{
/* #110778# a / 2 * 2 == a is not a tautology */
KSHORT nTmpOffset = nOffset;
nOffset /= 2;
if( nOffset < nMinDist )
nOffset = nTmpOffset - nMinDist;
}
else
nOffset = nOffset - nMinDist;
}
aInf.X( aInf.X() + nOffset );
SwExpandPortion::Paint( aInf );
if( bPaintSpace )
nSpaceOffs = nSpaceOffs + nOffset;
}
if( bPaintSpace && nOldWidth > nSpaceOffs )
{
SwTxtPaintInfo aInf( rInf );
static sal_Char __READONLY_DATA sDoubleSpace[] = " ";
aInf.X( aInf.X() + nSpaceOffs );
// --> FME 2005-08-12 #i53199# Adjust position of underline:
if ( rInf.GetUnderFnt() )
{
const Point aNewPos( aInf.GetPos().X(), rInf.GetUnderFnt()->GetPos().Y() );
rInf.GetUnderFnt()->SetPos( aNewPos );
}
// <--
pThis->Width( nOldWidth - nSpaceOffs + 12 );
{
SwTxtSlot aDiffTxt( &aInf, this, true, false, sDoubleSpace );
aInf.DrawText( *this, aInf.GetLen(), sal_True );
}
}
pThis->Width( nOldWidth );
}
}
}
/*************************************************************************
* class SwBulletPortion
*************************************************************************/
// --> OD 2008-01-23 #newlistlevelattrs#
SwBulletPortion::SwBulletPortion( const xub_Unicode cBullet,
const XubString& rBulletFollowedBy,
SwFont *pFont,
const sal_Bool bLft,
const sal_Bool bCntr,
const KSHORT nMinDst,
const bool bLabelAlignmentPosAndSpaceModeActive )
: SwNumberPortion( XubString( rBulletFollowedBy ).Insert( cBullet, 0 ) ,
pFont, bLft, bCntr, nMinDst,
bLabelAlignmentPosAndSpaceModeActive )
// <--
{
SetWhichPor( POR_BULLET );
}
/*************************************************************************
* class SwGrfNumPortion
*************************************************************************/
#define GRFNUM_SECURE 10
// --> OD 2008-01-23 #newlistlevelattrs#
SwGrfNumPortion::SwGrfNumPortion(
SwFrm*,
const XubString& rGraphicFollowedBy,
const SvxBrushItem* pGrfBrush,
const SwFmtVertOrient* pGrfOrient, const Size& rGrfSize,
const sal_Bool bLft, const sal_Bool bCntr, const KSHORT nMinDst,
const bool bLabelAlignmentPosAndSpaceModeActive ) :
SwNumberPortion( rGraphicFollowedBy, NULL, bLft, bCntr, nMinDst,
bLabelAlignmentPosAndSpaceModeActive ),
// <--
pBrush( new SvxBrushItem(RES_BACKGROUND) ), nId( 0 )
{
SetWhichPor( POR_GRFNUM );
SetAnimated( sal_False );
bReplace = sal_False;
if( pGrfBrush )
{
*pBrush = *pGrfBrush;
const Graphic* pGraph = pGrfBrush->GetGraphic();
if( pGraph )
SetAnimated( pGraph->IsAnimated() );
else
bReplace = sal_True;
}
if( pGrfOrient )
{
nYPos = pGrfOrient->GetPos();
eOrient = pGrfOrient->GetVertOrient();
}
else
{
nYPos = 0;
eOrient = text::VertOrientation::TOP;
}
Width( static_cast<sal_uInt16>(rGrfSize.Width() + 2 * GRFNUM_SECURE) );
nFixWidth = Width();
nGrfHeight = rGrfSize.Height() + 2 * GRFNUM_SECURE;
Height( KSHORT(nGrfHeight) );
bNoPaint = sal_False;
}
SwGrfNumPortion::~SwGrfNumPortion()
{
if ( IsAnimated() )
( (Graphic*) pBrush->GetGraphic() )->StopAnimation( 0, nId );
delete pBrush;
}
void SwGrfNumPortion::StopAnimation( OutputDevice* pOut )
{
if ( IsAnimated() )
( (Graphic*) pBrush->GetGraphic() )->StopAnimation( pOut, nId );
}
sal_Bool SwGrfNumPortion::Format( SwTxtFormatInfo &rInf )
{
SetHide( sal_False );
// --> OD 2008-01-29 #newlistlevelattrs#
// Width( nFixWidth );
KSHORT nFollowedByWidth( 0 );
if ( mbLabelAlignmentPosAndSpaceModeActive )
{
SwFldPortion::Format( rInf );
nFollowedByWidth = Width();
SetLen( 0 );
}
Width( nFixWidth + nFollowedByWidth );
// <--
const sal_Bool bFull = rInf.Width() < rInf.X() + Width();
const sal_Bool bFly = rInf.GetFly() ||
( rInf.GetLast() && rInf.GetLast()->IsFlyPortion() );
SetAscent( static_cast<sal_uInt16>(GetRelPos() > 0 ? GetRelPos() : 0) );
if( GetAscent() > Height() )
Height( GetAscent() );
if( bFull )
{
Width( rInf.Width() - (KSHORT)rInf.X() );
if( bFly )
{
SetLen( 0 );
SetNoPaint( sal_True );
rInf.SetNumDone( sal_False );
return sal_True;
}
}
rInf.SetNumDone( sal_True );
// --> OD 2008-01-23 #newlistlevelattrs#
// long nDiff = rInf.Left() - rInf.First() + rInf.ForcedLeftMargin();
long nDiff = mbLabelAlignmentPosAndSpaceModeActive
? 0
: rInf.Left() - rInf.First() + rInf.ForcedLeftMargin();
// <--
// Ein Vorschlag von Juergen und Volkmar:
// Der Textteil hinter der Numerierung sollte immer
// mindestens beim linken Rand beginnen.
if( nDiff < 0 )
nDiff = 0;
else if ( nDiff > rInf.X() )
nDiff -= rInf.X();
if( nDiff < nFixWidth + nMinDist )
nDiff = nFixWidth + nMinDist;
// 2739: Numerierung weicht Fly aus, kein nDiff in der zweiten Runde
// fieser Sonderfall: FlyFrm liegt in dem Bereich,
// den wir uns gerade unter den Nagel reissen wollen.
// Die NumberPortion wird als verborgen markiert.
if( nDiff > rInf.Width() )
{
nDiff = rInf.Width();
if( bFly )
SetHide( sal_True );
}
if( Width() < nDiff )
Width( KSHORT(nDiff) );
return bFull;
}
void SwGrfNumPortion::Paint( const SwTxtPaintInfo &rInf ) const
{
if( DontPaint() )
return;
/* Eine verborgene NumberPortion wird nicht angezeigt, es sei denn, es gibt
* Textportions in dieser Zeile oder es gibt ueberhaupt nur eine einzige Zeile.
*/
if ( IsHide() && rInf.GetParaPortion() && rInf.GetParaPortion()->GetNext() )
{
SwLinePortion *pTmp = GetPortion();
while ( pTmp && !pTmp->InTxtGrp() )
pTmp = pTmp->GetPortion();
if ( !pTmp )
return;
}
Point aPos( rInf.X() + GRFNUM_SECURE, rInf.Y() - GetRelPos() + GRFNUM_SECURE );
long nTmpWidth = Max( (long)0, (long)(nFixWidth - 2 * GRFNUM_SECURE) );
Size aSize( nTmpWidth, GetGrfHeight() - 2 * GRFNUM_SECURE );
// --> OD 2008-02-05 #newlistlevelattrs#
const sal_Bool bTmpLeft = mbLabelAlignmentPosAndSpaceModeActive ||
( IsLeft() && ! rInf.GetTxtFrm()->IsRightToLeft() ) ||
( ! IsLeft() && ! IsCenter() && rInf.GetTxtFrm()->IsRightToLeft() );
// <--
if( nFixWidth < Width() && !bTmpLeft )
{
KSHORT nOffset = Width() - nFixWidth;
if( nOffset < nMinDist )
nOffset = 0;
else
{
if( IsCenter() )
{
nOffset /= 2;
if( nOffset < nMinDist )
nOffset = Width() - nFixWidth - nMinDist;
}
else
nOffset = nOffset - nMinDist;
}
aPos.X() += nOffset;
}
if( bReplace )
{
KSHORT nTmpH = GetPortion() ? GetPortion()->GetAscent() : 120;
aSize = Size( nTmpH, nTmpH );
aPos.Y() = rInf.Y() - nTmpH;
}
SwRect aTmp( aPos, aSize );
sal_Bool bDraw = sal_True;
if ( IsAnimated() )
{
bDraw = !rInf.GetOpt().IsGraphic();
if( !nId )
{
SetId( long( rInf.GetTxtFrm() ) );
rInf.GetTxtFrm()->SetAnimation();
}
if( aTmp.IsOver( rInf.GetPaintRect() ) && !bDraw )
{
rInf.NoteAnimation();
const ViewShell* pViewShell = rInf.GetVsh();
// virtual device, not pdf export
if( OUTDEV_VIRDEV == rInf.GetOut()->GetOutDevType() &&
pViewShell && pViewShell->GetWin() )
{
( (Graphic*) pBrush->GetGraphic() )->StopAnimation(0,nId);
rInf.GetTxtFrm()->getRootFrm()->GetCurrShell()->InvalidateWindows( aTmp );
}
else if ( pViewShell &&
!pViewShell->GetAccessibilityOptions()->IsStopAnimatedGraphics() &&
!pViewShell->IsPreView() &&
// --> FME 2004-06-21 #i9684# Stop animation during printing/pdf export.
pViewShell->GetWin() )
// <--
{
( (Graphic*) pBrush->GetGraphic() )->StartAnimation(
(OutputDevice*)rInf.GetOut(), aPos, aSize, nId );
}
// pdf export, printing, preview, stop animations...
else
bDraw = sal_True;
}
if( bDraw )
( (Graphic*) pBrush->GetGraphic() )->StopAnimation( 0, nId );
}
SwRect aRepaint( rInf.GetPaintRect() );
const SwTxtFrm& rFrm = *rInf.GetTxtFrm();
if( rFrm.IsVertical() )
{
rFrm.SwitchHorizontalToVertical( aTmp );
rFrm.SwitchHorizontalToVertical( aRepaint );
}
if( rFrm.IsRightToLeft() )
{
rFrm.SwitchLTRtoRTL( aTmp );
rFrm.SwitchLTRtoRTL( aRepaint );
}
if( bDraw && aTmp.HasArea() )
DrawGraphic( pBrush, (OutputDevice*)rInf.GetOut(),
aTmp, aRepaint, bReplace ? GRFNUM_REPLACE : GRFNUM_YES );
}
void SwGrfNumPortion::SetBase( long nLnAscent, long nLnDescent,
long nFlyAsc, long nFlyDesc )
{
if ( GetOrient() != text::VertOrientation::NONE )
{
SetRelPos( 0 );
if ( GetOrient() == text::VertOrientation::CENTER )
SetRelPos( GetGrfHeight() / 2 );
else if ( GetOrient() == text::VertOrientation::TOP )
SetRelPos( GetGrfHeight() - GRFNUM_SECURE );
else if ( GetOrient() == text::VertOrientation::BOTTOM )
;
else if ( GetOrient() == text::VertOrientation::CHAR_CENTER )
SetRelPos( ( GetGrfHeight() + nLnAscent - nLnDescent ) / 2 );
else if ( GetOrient() == text::VertOrientation::CHAR_TOP )
SetRelPos( nLnAscent );
else if ( GetOrient() == text::VertOrientation::CHAR_BOTTOM )
SetRelPos( GetGrfHeight() - nLnDescent );
else
{
if( GetGrfHeight() >= nFlyAsc + nFlyDesc )
{
// wenn ich genauso gross bin wie die Zeile, brauche ich mich
// nicht an der Zeile nicht weiter ausrichten, ich lasse
// dann auch den max. Ascent der Zeile unveraendert
SetRelPos( nFlyAsc );
}
else if ( GetOrient() == text::VertOrientation::LINE_CENTER )
SetRelPos( ( GetGrfHeight() + nFlyAsc - nFlyDesc ) / 2 );
else if ( GetOrient() == text::VertOrientation::LINE_TOP )
SetRelPos( nFlyAsc );
else if ( GetOrient() == text::VertOrientation::LINE_BOTTOM )
SetRelPos( GetGrfHeight() - nFlyDesc );
}
}
}
void SwTxtFrm::StopAnimation( OutputDevice* pOut )
{
ASSERT( HasAnimation(), "SwTxtFrm::StopAnimation: Which Animation?" );
if( HasPara() )
{
SwLineLayout *pLine = GetPara();
while( pLine )
{
SwLinePortion *pPor = pLine->GetPortion();
while( pPor )
{
if( pPor->IsGrfNumPortion() )
((SwGrfNumPortion*)pPor)->StopAnimation( pOut );
// Die Numerierungsportion sitzt immer vor dem ersten Zeichen,
// deshalb koennen wir abbrechen, sobald wir eine Portion mit
// einer Laenge > 0 erreicht haben.
pPor = pPor->GetLen() ? 0 : pPor->GetPortion();
}
pLine = pLine->GetLen() ? 0 : pLine->GetNext();
}
}
}
/*************************************************************************
* SwCombinedPortion::SwCombinedPortion(..)
* initializes the script array and clears the width array
*************************************************************************/
SwCombinedPortion::SwCombinedPortion( const XubString &rTxt )
: SwFldPortion( rTxt )
{
SetLen(1);
SetWhichPor( POR_COMBINED );
if( aExpand.Len() > 6 )
aExpand.Erase( 6 );
// Initialization of the scripttype array,
// the arrays of width and position are filled by the format function
if( pBreakIt->GetBreakIter().is() )
{
sal_uInt8 nScr = SW_SCRIPTS;
for( sal_uInt16 i = 0; i < rTxt.Len(); ++i )
{
sal_uInt16 nScript = pBreakIt->GetBreakIter()->getScriptType( rTxt, i );
switch ( nScript ) {
case i18n::ScriptType::LATIN : nScr = SW_LATIN; break;
case i18n::ScriptType::ASIAN : nScr = SW_CJK; break;
case i18n::ScriptType::COMPLEX : nScr = SW_CTL; break;
}
aScrType[i] = nScr;
}
}
else
{
for( sal_uInt16 i = 0; i < 6; aScrType[i++] = 0 )
; // nothing
}
memset( &aWidth, 0, sizeof(aWidth) );
}
/*************************************************************************
* SwCombinedPortion::Paint(..)
*************************************************************************/
void SwCombinedPortion::Paint( const SwTxtPaintInfo &rInf ) const
{
ASSERT( GetLen() <= 1, "SwFldPortion::Paint: rest-portion polution?" );
if( Width() )
{
rInf.DrawBackBrush( *this );
rInf.DrawViewOpt( *this, POR_FLD );
// do we have to repaint a post it portion?
if( rInf.OnWin() && pPortion && !pPortion->Width() )
pPortion->PrePaint( rInf, this );
sal_uInt16 nCount = aExpand.Len();
if( !nCount )
return;
ASSERT( nCount < 7, "Too much combined characters" );
// the first character of the second row
sal_uInt16 nTop = ( nCount + 1 ) / 2;
SwFont aTmpFont( *rInf.GetFont() );
aTmpFont.SetProportion( nProportion ); // a smaller font
SwFontSave aFontSave( rInf, &aTmpFont );
sal_uInt16 i = 0;
Point aOldPos = rInf.GetPos();
Point aOutPos( aOldPos.X(), aOldPos.Y() - nUpPos );// Y of the first row
while( i < nCount )
{
if( i == nTop ) // change the row
aOutPos.Y() = aOldPos.Y() + nLowPos; // Y of the second row
aOutPos.X() = aOldPos.X() + aPos[i]; // X position
const sal_uInt8 nAct = aScrType[i]; // script type
aTmpFont.SetActual( nAct );
// if there're more than 4 characters to display, we choose fonts
// with 2/3 of the original font width.
if( aWidth[ nAct ] )
{
Size aTmpSz = aTmpFont.GetSize( nAct );
if( aTmpSz.Width() != aWidth[ nAct ] )
{
aTmpSz.Width() = aWidth[ nAct ];
aTmpFont.SetSize( aTmpSz, nAct );
}
}
((SwTxtPaintInfo&)rInf).SetPos( aOutPos );
rInf.DrawText( aExpand, *this, i, 1 );
++i;
}
// rInf is const, so we have to take back our manipulations
((SwTxtPaintInfo&)rInf).SetPos( aOldPos );
}
}
/*************************************************************************
* SwCombinedPortion::Format(..)
*************************************************************************/
sal_Bool SwCombinedPortion::Format( SwTxtFormatInfo &rInf )
{
sal_uInt16 nCount = aExpand.Len();
if( !nCount )
{
Width( 0 );
return sal_False;
}
ASSERT( nCount < 7, "Too much combined characters" );
// If there are leading "weak"-scripttyped characters in this portion,
// they get the actual scripttype.
sal_uInt16 i = 0;
while( i < nCount && SW_SCRIPTS == aScrType[i] )
aScrType[i++] = rInf.GetFont()->GetActual();
if( nCount > 4 )
{
// more than four? Ok, then we need the 2/3 font width
i = 0;
while( i < aExpand.Len() )
{
ASSERT( aScrType[i] < SW_SCRIPTS, "Combined: Script fault" );
if( !aWidth[ aScrType[i] ] )
{
rInf.GetOut()->SetFont( rInf.GetFont()->GetFnt( aScrType[i] ) );
aWidth[ aScrType[i] ] =
static_cast<sal_uInt16>(2 * rInf.GetOut()->GetFontMetric().GetSize().Width() / 3);
}
++i;
}
}
sal_uInt16 nTop = ( nCount + 1 ) / 2; // the first character of the second line
ViewShell *pSh = rInf.GetTxtFrm()->getRootFrm()->GetCurrShell();
SwFont aTmpFont( *rInf.GetFont() );
SwFontSave aFontSave( rInf, &aTmpFont );
nProportion = 55;
// In nMainAscent/Descent we store the ascent and descent
// of the original surrounding font
sal_uInt16 nMaxDescent, nMaxAscent, nMaxWidth;
sal_uInt16 nMainDescent = rInf.GetFont()->GetHeight( pSh, *rInf.GetOut() );
const sal_uInt16 nMainAscent = rInf.GetFont()->GetAscent( pSh, *rInf.GetOut() );
nMainDescent = nMainDescent - nMainAscent;
// we start with a 50% font, but if we notice that the combined portion
// becomes bigger than the surrounding font, we check 45% and maybe 40%.
do
{
nProportion -= 5;
aTmpFont.SetProportion( nProportion );
i = 0;
memset( &aPos, 0, sizeof(aPos) );
nMaxDescent = 0;
nMaxAscent = 0;
nMaxWidth = 0;
nUpPos = nLowPos = 0;
// Now we get the width of all characters.
// The ascent and the width of the first line are stored in the
// ascent member of the portion, the descent in nLowPos.
// The ascent, descent and width of the second line are stored in the
// local nMaxAscent, nMaxDescent and nMaxWidth variables.
while( i < nCount )
{
sal_uInt8 nScrp = aScrType[i];
aTmpFont.SetActual( nScrp );
if( aWidth[ nScrp ] )
{
Size aFontSize( aTmpFont.GetSize( nScrp ) );
aFontSize.Width() = aWidth[ nScrp ];
aTmpFont.SetSize( aFontSize, nScrp );
}
SwDrawTextInfo aDrawInf( pSh, *rInf.GetOut(), 0, aExpand, i, 1 );
Size aSize = aTmpFont._GetTxtSize( aDrawInf );
sal_uInt16 nAsc = aTmpFont.GetAscent( pSh, *rInf.GetOut() );
aPos[ i ] = (sal_uInt16)aSize.Width();
if( i == nTop ) // enter the second line
{
nLowPos = nMaxDescent;
Height( nMaxDescent + nMaxAscent );
Width( nMaxWidth );
SetAscent( nMaxAscent );
nMaxAscent = 0;
nMaxDescent = 0;
nMaxWidth = 0;
}
nMaxWidth = nMaxWidth + aPos[ i++ ];
if( nAsc > nMaxAscent )
nMaxAscent = nAsc;
if( aSize.Height() - nAsc > nMaxDescent )
nMaxDescent = static_cast<sal_uInt16>(aSize.Height() - nAsc);
}
// for one or two characters we double the width of the portion
if( nCount < 3 )
{
nMaxWidth *= 2;
Width( 2*Width() );
if( nCount < 2 )
{
Height( nMaxAscent + nMaxDescent );
nLowPos = nMaxDescent;
}
}
Height( Height() + nMaxDescent + nMaxAscent );
nUpPos = nMaxAscent;
SetAscent( Height() - nMaxDescent - nLowPos );
} while( nProportion > 40 && ( GetAscent() > nMainAscent ||
Height() - GetAscent() > nMainDescent ) );
// if the combined portion is smaller than the surrounding text,
// the portion grows. This looks better, if there's a character background.
if( GetAscent() < nMainAscent )
{
Height( Height() + nMainAscent - GetAscent() );
SetAscent( nMainAscent );
}
if( Height() < nMainAscent + nMainDescent )
Height( nMainAscent + nMainDescent );
// We calculate the x positions of the characters in both lines..
sal_uInt16 nTopDiff = 0;
sal_uInt16 nBotDiff = 0;
if( nMaxWidth > Width() )
{
nTopDiff = ( nMaxWidth - Width() ) / 2;
Width( nMaxWidth );
}
else
nBotDiff = ( Width() - nMaxWidth ) / 2;
switch( nTop)
{
case 3: aPos[1] = aPos[0] + nTopDiff; // no break
case 2: aPos[nTop-1] = Width() - aPos[nTop-1];
}
aPos[0] = 0;
switch( nCount )
{
case 5: aPos[4] = aPos[3] + nBotDiff; // no break
case 3: aPos[nTop] = nBotDiff; break;
case 6: aPos[4] = aPos[3] + nBotDiff; // no break
case 4: aPos[nTop] = 0; // no break
case 2: aPos[nCount-1] = Width() - aPos[nCount-1];
}
// Does the combined portion fit the line?
const sal_Bool bFull = rInf.Width() < rInf.X() + Width();
if( bFull )
{
if( rInf.GetLineStart() == rInf.GetIdx() && (!rInf.GetLast()->InFldGrp()
|| !((SwFldPortion*)rInf.GetLast())->IsFollow() ) )
Width( (sal_uInt16)( rInf.Width() - rInf.X() ) );
else
{
Truncate();
Width( 0 );
SetLen( 0 );
if( rInf.GetLast() )
rInf.GetLast()->FormatEOL( rInf );
}
}
return bFull;
}
/*************************************************************************
* SwCombinedPortion::GetViewWidth(..)
*************************************************************************/
KSHORT SwCombinedPortion::GetViewWidth( const SwTxtSizeInfo &rInf ) const
{
if( !GetLen() ) // for the dummy part at the end of the line, where
return 0; // the combined portion doesn't fit.
return SwFldPortion::GetViewWidth( rInf );
}