/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/



#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 <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 );
    }
    // #i107143#
    // pass placeholder property to created <SwFldPortion> instance.
    SwFldPortion* pClone = new SwFldPortion( rExpand, pNewFnt, bPlaceHolder );
    pClone->SetNextOffset( nNextOffset );
    pClone->m_bNoLength = this->m_bNoLength;
    return pClone;
}

void SwFldPortion::TakeNextOffset( const SwFldPortion* pFld )
{
    OSL_ENSURE( 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 );
}

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();
        // #i98418#
        const sal_uInt8 nFldDir = ( IsNumberPortion() || IsFtnNumPortion() ) ?
                             rSI.GetDefaultDir() :
                             rSI.DirType( IsFollow() ? rInf.GetIdx() - 1 : rInf.GetIdx() );

        bool bPerformUBA = UBIDI_LTR != nFldDir ? true : i18n::ScriptType::COMPLEX == nScript;
        if (bPerformUBA)
        {
            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;
                //If we decided that this range was RTL after all and the
                //previous range was complex but clipped to the start of this
                //range, then extend it to be complex over the additional RTL
                //range
                if (nScript == i18n::ScriptType::COMPLEX)
                    nNextScriptChg = nNextDirChg;
            }
        }

        // #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() )
        {
            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:
                // #i59759# Erase additional control
                // characters from field string, otherwise we get stuck in
                // a loop.
                case CHAR_ZWSP :
                case CHAR_ZWNBSP :
                // #i111750#
                // - Erasing further control characters from field string in
                // to avoid loop.
                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 );

    OSL_ENSURE( 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() );
}

/*************************************************************************
 *                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
 *************************************************************************/

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 ),
          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 );

    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() );
        OSL_ENSURE( Height() && nAscent, "NumberPortions without Height | Ascent" );

        long nDiff( 0 );

        if ( !mbLabelAlignmentPosAndSpaceModeActive )
        {
            if ( !rInf.GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) &&
                 // #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 const sDoubleSpace[] = "  ";
                aInf.X( aInf.X() + nSpaceOffs );

                // #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
 *************************************************************************/

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

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 );
//    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 );
//    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 );

    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() &&
                      // #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 )
{
    OSL_ENSURE( 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
{
    OSL_ENSURE( 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;
        OSL_ENSURE( 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;
    }

    OSL_ENSURE( 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() )
        {
            OSL_ENSURE( 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 );
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
