/**************************************************************
 * 
 * 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 "viewsh.hxx"
#include "doc.hxx"
#include "pagefrm.hxx"
#include "rootfrm.hxx"
#include "ndtxt.hxx"
#include "txtatr.hxx"
#include <SwPortionHandler.hxx>
#include <txtftn.hxx>
#include <flyfrm.hxx>
#include <fmtftn.hxx>
#include <ftninfo.hxx>
#include <charfmt.hxx>
#include <dflyobj.hxx>
#include <rowfrm.hxx>
#include <editeng/brshitem.hxx>
#include <editeng/charrotateitem.hxx>
#include <breakit.hxx>
#ifndef _COM_SUN_STAR_I18N_SCRIPTTYPE_HDL_
#include <com/sun/star/i18n/ScriptType.hdl>
#endif
#include <tabfrm.hxx>
// OD 2004-05-24 #i28701#
#include <sortedobjs.hxx>

#include "txtcfg.hxx"
#include "swfont.hxx"	// new SwFont
#include "porftn.hxx"
#include "porfly.hxx"
#include "porlay.hxx"
#include "txtfrm.hxx"
#include "itrform2.hxx"
#include "ftnfrm.hxx"	// FindQuoVadisFrm(),
#include "pagedesc.hxx"
#include "redlnitr.hxx" // SwRedlnItr
#include "sectfrm.hxx"	// SwSectionFrm
#include "layouter.hxx" // Endnote-Collection
#include "frmtool.hxx"
#include "ndindex.hxx"

using namespace ::com::sun::star;

/*************************************************************************
 *								_IsFtnNumFrm()
 *************************************************************************/

sal_Bool SwTxtFrm::_IsFtnNumFrm() const
{
	const SwFtnFrm* pFtn = FindFtnFrm()->GetMaster();
	while( pFtn && !pFtn->ContainsCntnt() )
		pFtn = pFtn->GetMaster();
	return !pFtn;
}

/*************************************************************************
 *								FindFtn()
 *************************************************************************/

// Sucht innerhalb einer Master-Follow-Kette den richtigen TxtFrm zum SwTxtFtn

SwTxtFrm *SwTxtFrm::FindFtnRef( const SwTxtFtn *pFtn )
{
	SwTxtFrm *pFrm = this;
	const sal_Bool bFwd = *pFtn->GetStart() >= GetOfst();
	while( pFrm )
	{
		if( SwFtnBossFrm::FindFtn( pFrm, pFtn ) )
			return pFrm;
		pFrm = bFwd ? pFrm->GetFollow() :
					  pFrm->IsFollow() ? pFrm->FindMaster() : 0;
	}
	return pFrm;
}

/*************************************************************************
 *								CalcFtnFlag()
 *************************************************************************/

#ifndef DBG_UTIL
void SwTxtFrm::CalcFtnFlag()
#else
void SwTxtFrm::CalcFtnFlag( xub_StrLen nStop )//Fuer den Test von SplitFrm
#endif
{
	bFtn = sal_False;

	const SwpHints *pHints = GetTxtNode()->GetpSwpHints();
	if( !pHints )
		return;

    const sal_uInt16 nSize = pHints->Count();

#ifndef DBG_UTIL
	const xub_StrLen nEnd = GetFollow() ? GetFollow()->GetOfst() : STRING_LEN;
#else
	const xub_StrLen nEnd = nStop != STRING_LEN ? nStop
						: GetFollow() ? GetFollow()->GetOfst() : STRING_LEN;
#endif

    for ( sal_uInt16 i = 0; i < nSize; ++i )
    {
		const SwTxtAttr *pHt = (*pHints)[i];
		if ( pHt->Which() == RES_TXTATR_FTN )
		{
			const xub_StrLen nIdx = *pHt->GetStart();
			if ( nEnd < nIdx )
				break;
			if( GetOfst() <= nIdx )
			{
				bFtn = sal_True;
				break;
			}
		}
	}
}

/*************************************************************************
 *								CalcPrepFtnAdjust()
 *************************************************************************/

sal_Bool SwTxtFrm::CalcPrepFtnAdjust()
{
	ASSERT( HasFtn(), "Wer ruft mich da?" );
	SwFtnBossFrm *pBoss = FindFtnBossFrm( sal_True );
	const SwFtnFrm *pFtn = pBoss->FindFirstFtn( this );
	if( pFtn && FTNPOS_CHAPTER != GetNode()->GetDoc()->GetFtnInfo().ePos &&
		( !pBoss->GetUpper()->IsSctFrm() ||
		!((SwSectionFrm*)pBoss->GetUpper())->IsFtnAtEnd() ) )
	{
		const SwFtnContFrm *pCont = pBoss->FindFtnCont();
		sal_Bool bReArrange = sal_True;

        SWRECTFN( this )
        if ( pCont && (*fnRect->fnYDiff)( (pCont->Frm().*fnRect->fnGetTop)(),
                                          (Frm().*fnRect->fnGetBottom)() ) > 0 )
		{
            pBoss->RearrangeFtns( (Frm().*fnRect->fnGetBottom)(), sal_False,
								  pFtn->GetAttr() );
			ValidateBodyFrm();
			ValidateFrm();
			pFtn = pBoss->FindFirstFtn( this );
		}
		else
			bReArrange = sal_False;
		if( !pCont || !pFtn || bReArrange != (pFtn->FindFtnBossFrm() == pBoss) )
		{
			SwTxtFormatInfo aInf( this );
			SwTxtFormatter aLine( this, &aInf );
			aLine.TruncLines();
			SetPara( 0 );		//Wird ggf. geloescht!
			ResetPreps();
			return sal_False;
		}
	}
	return sal_True;
}


/*************************************************************************
 *                      lcl_GetFtnLower()
 *
 * Local helper function. Checks if nLower should be taken as the boundary
 * for the footnote.
 *************************************************************************/

SwTwips lcl_GetFtnLower( const SwTxtFrm* pFrm, SwTwips nLower )
{
    // nLower is an absolute value. It denotes the bottom of the line
    // containing the footnote.
    SWRECTFN( pFrm )

    ASSERT( !pFrm->IsVertical() || !pFrm->IsSwapped(),
            "lcl_GetFtnLower with swapped frame" );

    SwTwips nAdd;
    SwTwips nRet = nLower;

    //
    // Check if text is inside a table.
    //
    if ( pFrm->IsInTab() )
    {
        //
        // If pFrm is inside a table, we have to check if
        // a) The table is not allowed to split or
        // b) The table row is not allowed to split
        //
        // Inside a table, there are no footnotes,
        // see SwFrm::FindFtnBossFrm. So we don't have to check
        // the case that pFrm is inside a (footnote collecting) section
        // within the table.
        //
        const SwFrm* pRow = pFrm;
        while( !pRow->IsRowFrm() || !pRow->GetUpper()->IsTabFrm() )
            pRow = pRow->GetUpper();
        const SwTabFrm* pTabFrm = (SwTabFrm*)pRow->GetUpper();

        ASSERT( pTabFrm && pRow &&
                pRow->GetUpper()->IsTabFrm(), "Upper of row should be tab" )

        const sal_Bool bDontSplit = !pTabFrm->IsFollow() &&
                                !pTabFrm->IsLayoutSplitAllowed();

        SwTwips nMin = 0;
        if ( bDontSplit )
            nMin = (pTabFrm->Frm().*fnRect->fnGetBottom)();
        else if ( !((SwRowFrm*)pRow)->IsRowSplitAllowed() )
            nMin = (pRow->Frm().*fnRect->fnGetBottom)();

        if ( nMin && (*fnRect->fnYDiff)( nMin, nLower ) > 0 )
            nRet = nMin;

        nAdd = (pRow->GetUpper()->*fnRect->fnGetBottomMargin)();
    }
    else
        nAdd = (pFrm->*fnRect->fnGetBottomMargin)();

    if( nAdd > 0 )
    {
        if ( bVert )
            nRet -= nAdd;
        else
            nRet += nAdd;
    }

    // #i10770#: If there are fly frames anchored at previous paragraphs,
    // the deadline should consider their lower borders.
    const SwFrm* pStartFrm = pFrm->GetUpper()->GetLower();
    ASSERT( pStartFrm, "Upper has no lower" )
    SwTwips nFlyLower = bVert ? LONG_MAX : 0;
    while ( pStartFrm != pFrm )
    {
        ASSERT( pStartFrm, "Frame chain is broken" )
        if ( pStartFrm->GetDrawObjs() )
        {
            const SwSortedObjs &rObjs = *pStartFrm->GetDrawObjs();
            for ( sal_uInt16 i = 0; i < rObjs.Count(); ++i )
            {
                SwAnchoredObject* pAnchoredObj = rObjs[i];
                SwRect aRect( pAnchoredObj->GetObjRect() );

                if ( !pAnchoredObj->ISA(SwFlyFrm) ||
                     static_cast<SwFlyFrm*>(pAnchoredObj)->IsValid() )
                {
                    const SwTwips nBottom = (aRect.*fnRect->fnGetBottom)();
                    if ( (*fnRect->fnYDiff)( nBottom, nFlyLower ) > 0 )
                        nFlyLower = nBottom;
                }
            }
        }

        pStartFrm = pStartFrm->GetNext();
    }

    if ( bVert )
        nRet = Min( nRet, nFlyLower );
    else
        nRet = Max( nRet, nFlyLower );

    return nRet;
}


/*************************************************************************
 *						SwTxtFrm::GetFtnLine()
 *************************************************************************/

SwTwips SwTxtFrm::GetFtnLine( const SwTxtFtn *pFtn ) const
{
    ASSERT( ! IsVertical() || ! IsSwapped(),
            "SwTxtFrm::GetFtnLine with swapped frame" )

	SwTxtFrm *pThis = (SwTxtFrm*)this;

	if( !HasPara() )
	{
        // #109071# GetFormatted() does not work here, because most probably
        // the frame is currently locked. We return the previous value.
        return pThis->mnFtnLine > 0 ?
               pThis->mnFtnLine :
               IsVertical() ? Frm().Left() : Frm().Bottom();
	}

    SWAP_IF_NOT_SWAPPED( this )

	SwTxtInfo aInf( pThis );
	SwTxtIter aLine( pThis, &aInf );
	const xub_StrLen nPos = *pFtn->GetStart();
	aLine.CharToLine( nPos );

    SwTwips nRet = aLine.Y() + SwTwips(aLine.GetLineHeight());
    if( IsVertical() )
        nRet = SwitchHorizontalToVertical( nRet );

    UNDO_SWAP( this )

    nRet = lcl_GetFtnLower( pThis, nRet );

    pThis->mnFtnLine = nRet;
    return nRet;
}

/*************************************************************************
 *						SwTxtFrm::GetFtnRstHeight()
 *************************************************************************/

// Ermittelt die max. erreichbare Hoehe des TxtFrm im Ftn-Bereich.
// Sie wird eingeschraenkt durch den unteren Rand der Zeile mit
// der Ftn-Referenz.

SwTwips SwTxtFrm::_GetFtnFrmHeight() const
{
	ASSERT( !IsFollow() && IsInFtn(), "SwTxtFrm::SetFtnLine: moon walk" );

	const SwFtnFrm *pFtnFrm = FindFtnFrm();
	const SwTxtFrm *pRef = (const SwTxtFrm *)pFtnFrm->GetRef();
	const SwFtnBossFrm *pBoss = FindFtnBossFrm();
	if( pBoss != pRef->FindFtnBossFrm( !pFtnFrm->GetAttr()->
										GetFtn().IsEndNote() ) )
		return 0;

    SWAP_IF_SWAPPED( this )

	SwTwips nHeight = pRef->IsInFtnConnect() ?
                            1 : pRef->GetFtnLine( pFtnFrm->GetAttr() );
	if( nHeight )
	{
		// So komisch es aussehen mag: Die erste Ftn auf der Seite darf sich
		// nicht mit der Ftn-Referenz beruehren, wenn wir im Ftn-Bereich Text
		// eingeben.
		const SwFrm *pCont = pFtnFrm->GetUpper();
		//Hoehe innerhalb des Cont, die ich mir 'eh noch genehmigen darf.
        SWRECTFN( pCont )
        SwTwips nTmp = (*fnRect->fnYDiff)( (pCont->*fnRect->fnGetPrtBottom)(),
                                           (Frm().*fnRect->fnGetTop)() );

#ifdef DBG_UTIL
		if( nTmp < 0 )
		{
			sal_Bool bInvalidPos = sal_False;
            const SwLayoutFrm* pTmp = GetUpper();
			while( !bInvalidPos && pTmp )
			{
                bInvalidPos = !pTmp->GetValidPosFlag() ||
                               !pTmp->Lower()->GetValidPosFlag();
				if( pTmp == pCont )
					break;
				pTmp = pTmp->GetUpper();
			}
			ASSERT( bInvalidPos, "Hanging below FtnCont" );
		}
#endif

        if ( (*fnRect->fnYDiff)( (pCont->Frm().*fnRect->fnGetTop)(), nHeight) > 0 )
		{
			//Wachstumspotential den Containers.
			if ( !pRef->IsInFtnConnect() )
			{
				SwSaveFtnHeight aSave( (SwFtnBossFrm*)pBoss, nHeight  );
                nHeight = ((SwFtnContFrm*)pCont)->Grow( LONG_MAX, sal_True );
			}
			else
                nHeight = ((SwFtnContFrm*)pCont)->Grow( LONG_MAX, sal_True );

			nHeight += nTmp;
			if( nHeight < 0 )
				nHeight = 0;
		}
		else
		{   // The container has to shrink
            nTmp += (*fnRect->fnYDiff)( (pCont->Frm().*fnRect->fnGetTop)(), nHeight);
			if( nTmp > 0 )
				nHeight = nTmp;
			else
				nHeight = 0;
		}
	}

    UNDO_SWAP( this )

	return nHeight;
}

/*************************************************************************
 *						SwTxtFrm::FindQuoVadisFrm()
 *************************************************************************/

SwTxtFrm *SwTxtFrm::FindQuoVadisFrm()
{
	// Erstmal feststellen, ob wir in einem FtnFrm stehen:
	if( GetIndPrev() || !IsInFtn() )
		return 0;

	// Zum Vorgaenger-FtnFrm
	SwFtnFrm *pFtnFrm = FindFtnFrm()->GetMaster();
	if( !pFtnFrm )
		return 0;

	// Nun den letzten Cntnt:
	const SwCntntFrm *pCnt = pFtnFrm->ContainsCntnt();
	if( !pCnt )
		return NULL;
	const SwCntntFrm *pLast;
	do
	{	pLast = pCnt;
		pCnt = pCnt->GetNextCntntFrm();
	} while( pCnt && pFtnFrm->IsAnLower( pCnt ) );
	return (SwTxtFrm*)pLast;
}

/*************************************************************************
 *						SwTxtFrm::RemoveFtn()
 *************************************************************************/

void SwTxtFrm::RemoveFtn( const xub_StrLen nStart, const xub_StrLen nLen )
{
	if ( !IsFtnAllowed() )
		return;

	SwpHints *pHints = GetTxtNode()->GetpSwpHints();
	if( !pHints )
		return;

	sal_Bool bRollBack = nLen != STRING_LEN;
	sal_uInt16 nSize = pHints->Count();
	xub_StrLen nEnd;
	SwTxtFrm* pSource;
	if( bRollBack )
	{
		nEnd = nStart + nLen;
		pSource = GetFollow();
		if( !pSource )
			return;
	}
	else
	{
		nEnd = STRING_LEN;
		pSource = this;
	}

	if( nSize )
	{
		SwPageFrm* pUpdate = NULL;
		sal_Bool bRemove = sal_False;
		SwFtnBossFrm *pFtnBoss = 0;
		SwFtnBossFrm *pEndBoss = 0;
		sal_Bool bFtnEndDoc
			= FTNPOS_CHAPTER == GetNode()->GetDoc()->GetFtnInfo().ePos;
        for ( sal_uInt16 i = nSize; i; )
        {
			SwTxtAttr *pHt = pHints->GetTextHint(--i);
			if ( RES_TXTATR_FTN != pHt->Which() )
				continue;

			const xub_StrLen nIdx = *pHt->GetStart();
			if( nStart > nIdx )
				break;

			if( nEnd >= nIdx )
			{
				SwTxtFtn *pFtn = (SwTxtFtn*)pHt;
				sal_Bool bEndn = pFtn->GetFtn().IsEndNote();

				if( bEndn )
				{
					if( !pEndBoss )
						pEndBoss = pSource->FindFtnBossFrm();
				}
				else
				{
					if( !pFtnBoss )
					{
						pFtnBoss = pSource->FindFtnBossFrm( sal_True );
						if( pFtnBoss->GetUpper()->IsSctFrm() )
						{
							SwSectionFrm* pSect = (SwSectionFrm*)
												  pFtnBoss->GetUpper();
							if( pSect->IsFtnAtEnd() )
								bFtnEndDoc = sal_False;
						}
					}
				}

				// Wir loeschen nicht, sondern wollen die Ftn verschieben.
				// Drei Faelle koennen auftreten:
				// 1) Es gibt weder Follow noch PrevFollow
				//	  -> RemoveFtn()  (vielleicht sogar ein ASSERT wert)
				// 2) nStart > GetOfst, ich habe einen Follow
				//	  -> Ftn wandert in den Follow
				// 3) nStart < GetOfst, ich bin ein Follow
				//	  -> Ftn wandert in den PrevFollow
				// beide muessen auf einer Seite/in einer Spalte stehen.

				SwFtnFrm *pFtnFrm = bEndn ? pEndBoss->FindFtn( pSource, pFtn ) :
											pFtnBoss->FindFtn( pSource, pFtn );

				if( pFtnFrm )
				{
					const sal_Bool bEndDoc = bEndn ? sal_True : bFtnEndDoc;
					if( bRollBack )
					{
						while ( pFtnFrm )
						{
							pFtnFrm->SetRef( this );
							pFtnFrm = pFtnFrm->GetFollow();
							SetFtn( sal_True );
						}
					}
					else if( GetFollow() )
					{
						SwCntntFrm *pDest = GetFollow();
						while( pDest->GetFollow() && ((SwTxtFrm*)pDest->
							   GetFollow())->GetOfst() <= nIdx )
							pDest = pDest->GetFollow();
						ASSERT( !pDest->FindFtnBossFrm( !bEndn )->FindFtn(
							pDest,pFtn),"SwTxtFrm::RemoveFtn: footnote exists");

						//Nicht ummelden sondern immer Moven.
                        // OD 08.11.2002 #104840# - use <SwlayoutFrm::IsBefore(::)>
                        if ( bEndDoc ||
                             !pFtnFrm->FindFtnBossFrm()->IsBefore( pDest->FindFtnBossFrm( !bEndn ) )
                           )
						{
							SwPageFrm* pTmp = pFtnFrm->FindPageFrm();
							if( pUpdate && pUpdate != pTmp )
								pUpdate->UpdateFtnNum();
							pUpdate = pTmp;
							while ( pFtnFrm )
							{
								pFtnFrm->SetRef( pDest );
								pFtnFrm = pFtnFrm->GetFollow();
							}
						}
						else
						{
							if( bEndn )
								pEndBoss->MoveFtns( this, pDest, pFtn );
							else
								pFtnBoss->MoveFtns( this, pDest, pFtn );
							bRemove = sal_True;
						}
						((SwTxtFrm*)pDest)->SetFtn( sal_True );

						ASSERT( pDest->FindFtnBossFrm( !bEndn )->FindFtn( pDest,
						   pFtn),"SwTxtFrm::RemoveFtn: footnote ChgRef failed");
					}
					else
					{
                        if( !bEndDoc || ( bEndn && pEndBoss->IsInSct() &&
                            !SwLayouter::Collecting( GetNode()->GetDoc(),
                            pEndBoss->FindSctFrm(), NULL ) ) )
						{
							if( bEndn )
								pEndBoss->RemoveFtn( this, pFtn );
							else
								pFtnBoss->RemoveFtn( this, pFtn );
							bRemove = bRemove || !bEndDoc;
							ASSERT( bEndn ? !pEndBoss->FindFtn( this, pFtn ) :
									!pFtnBoss->FindFtn( this, pFtn ),
							"SwTxtFrm::RemoveFtn: can't get off that footnote" );
						}
					}
				}
			}
		}
		if( pUpdate )
			pUpdate->UpdateFtnNum();
		// Wir bringen die Oszillation zum stehen:
		if( bRemove && !bFtnEndDoc && HasPara() )
		{
			ValidateBodyFrm();
			ValidateFrm();
		}
	}
	// Folgendes Problem: Aus dem FindBreak heraus wird das RemoveFtn aufgerufen,
	// weil die letzte Zeile an den Follow abgegeben werden soll. Der Offset
	// des Follows ist aber veraltet, er wird demnaechst gesetzt. CalcFntFlag ist
	// auf einen richtigen Follow-Offset angewiesen. Deshalb wird hier kurzfristig
	// der Follow-Offset manipuliert.
	xub_StrLen nOldOfst = STRING_LEN;
	if( HasFollow() && nStart > GetOfst() )
	{
		nOldOfst = GetFollow()->GetOfst();
		GetFollow()->ManipOfst( nStart + ( bRollBack ? nLen : 0 ) );
	}
	pSource->CalcFtnFlag();
	if( nOldOfst < STRING_LEN )
		GetFollow()->ManipOfst( nOldOfst );
}

/*************************************************************************
 *						SwTxtFormatter::ConnectFtn()
 *************************************************************************/
// sal_False, wenn irgendetwas schief gegangen ist.
// Es gibt eigentlich nur zwei Moeglichkeiten:
// a) Die Ftn ist bereits vorhanden
// => dann wird sie gemoved, wenn ein anderer pSrcFrm gefunden wurde
// b) Die Ftn ist nicht vorhanden
// => dann wird sie fuer uns angelegt.
// Ob die Ftn schliesslich auf unserer Spalte/Seite landet oder nicht,
// spielt in diesem Zusammenhang keine Rolle.
// Optimierungen bei Endnoten.
// Noch ein Problem: wenn die Deadline im Ftn-Bereich liegt, muss die
// Ftn verschoben werden.

void SwTxtFrm::ConnectFtn( SwTxtFtn *pFtn, const SwTwips nDeadLine )
{
    ASSERT( !IsVertical() || !IsSwapped(),
            "SwTxtFrm::ConnectFtn with swapped frame" );

	bFtn = sal_True;
	bInFtnConnect = sal_True;	//Bloss zuruecksetzen!
	sal_Bool bEnd = pFtn->GetFtn().IsEndNote();

    //
    // We want to store this value, because it is needed as a fallback
    // in GetFtnLine(), if there is no paragraph information available
    //
    mnFtnLine = nDeadLine;

	// Wir brauchen immer einen Boss (Spalte/Seite)
	SwSectionFrm *pSect;
	SwCntntFrm *pCntnt = this;
	if( bEnd && IsInSct() )
	{
		pSect = FindSctFrm();
		if( pSect->IsEndnAtEnd() )
			pCntnt = pSect->FindLastCntnt( FINDMODE_ENDNOTE );
		if( !pCntnt )
			pCntnt = this;
	}

	SwFtnBossFrm *pBoss = pCntnt->FindFtnBossFrm( !bEnd );

#if OSL_DEBUG_LEVEL > 1
	SwTwips nRstHeight = GetRstHeight();
#endif

	pSect = pBoss->FindSctFrm();
	sal_Bool bDocEnd = bEnd ? !( pSect && pSect->IsEndnAtEnd() ) :
				   ( !( pSect && pSect->IsFtnAtEnd() ) &&
		  			 FTNPOS_CHAPTER == GetNode()->GetDoc()->GetFtnInfo().ePos );
	//Ftn kann beim Follow angemeldet sein.
	SwCntntFrm *pSrcFrm = FindFtnRef( pFtn );

	if( bDocEnd )
	{
		if( pSect && pSrcFrm )
		{
			SwFtnFrm *pFtnFrm = pBoss->FindFtn( pSrcFrm, pFtn );
			if( pFtnFrm && pFtnFrm->IsInSct() )
			{
				pBoss->RemoveFtn( pSrcFrm, pFtn );
				pSrcFrm = 0;
			}
		}
	}
	else if( bEnd && pSect )
	{
		SwFtnFrm *pFtnFrm = pSrcFrm ? pBoss->FindFtn( pSrcFrm, pFtn ) : NULL;
		if( pFtnFrm && !pFtnFrm->GetUpper() )
			pFtnFrm = NULL;
		SwDoc *pDoc = GetNode()->GetDoc();
		if( SwLayouter::Collecting( pDoc, pSect, pFtnFrm ) )
		{
			if( !pSrcFrm )
			{
				SwFtnFrm *pNew = new SwFtnFrm(pDoc->GetDfltFrmFmt(),this,this,pFtn);
			 	SwNodeIndex aIdx( *pFtn->GetStartNode(), 1 );
			 	::_InsertCnt( pNew, pDoc, aIdx.GetIndex() );
                GetNode()->getIDocumentLayoutAccess()->GetLayouter()->CollectEndnote( pNew );
			}
			else if( pSrcFrm != this )
				pBoss->ChangeFtnRef( pSrcFrm, pFtn, this );
			bInFtnConnect = sal_False;
			return;
		}
		else if( pSrcFrm )
		{
			SwFtnBossFrm *pFtnBoss = pFtnFrm->FindFtnBossFrm();
			if( !pFtnBoss->IsInSct() ||
				pFtnBoss->ImplFindSctFrm()->GetSection()!=pSect->GetSection() )
			{
				pBoss->RemoveFtn( pSrcFrm, pFtn );
				pSrcFrm = 0;
			}
		}
	}

	if( bDocEnd || bEnd )
	{
		if( !pSrcFrm )
			pBoss->AppendFtn( this, pFtn );
		else if( pSrcFrm != this )
			pBoss->ChangeFtnRef( pSrcFrm, pFtn, this );
		bInFtnConnect = sal_False;
		return;
	}

	SwSaveFtnHeight aHeight( pBoss, nDeadLine );

	if( !pSrcFrm )		// Es wurde ueberhaupt keine Ftn gefunden.
		pBoss->AppendFtn( this, pFtn );
	else
	{
		SwFtnFrm *pFtnFrm = pBoss->FindFtn( pSrcFrm, pFtn );
		SwFtnBossFrm *pFtnBoss = pFtnFrm->FindFtnBossFrm();

		sal_Bool bBrutal = sal_False;

		if( pFtnBoss == pBoss )	// Ref und Ftn sind auf der selben Seite/Spalte.
		{
			SwFrm *pCont = pFtnFrm->GetUpper();

            SWRECTFN ( pCont )
            long nDiff = (*fnRect->fnYDiff)( (pCont->Frm().*fnRect->fnGetTop)(),
                                             nDeadLine );

            if( nDiff >= 0 )
			{
				//Wenn die Fussnote bei einem Follow angemeldet ist, so ist
				//es jetzt an der Zeit sie umzumelden.
				if ( pSrcFrm != this )
					pBoss->ChangeFtnRef( pSrcFrm, pFtn, this );
				//Es steht Platz zur Verfuegung, also kann die Fussnote evtl.
				//wachsen.
                if ( pFtnFrm->GetFollow() && nDiff > 0 )
				{
                    SwTwips nHeight = (pCont->Frm().*fnRect->fnGetHeight)();
					pBoss->RearrangeFtns( nDeadLine, sal_False, pFtn );
					ValidateBodyFrm();
					ValidateFrm();
					ViewShell *pSh = getRootFrm()->GetCurrShell();
                    if ( pSh && nHeight == (pCont->Frm().*fnRect->fnGetHeight)() )
						//Damit uns nix durch die Lappen geht.
						pSh->InvalidateWindows( pCont->Frm() );
				}
				bInFtnConnect = sal_False;
				return;
			}
			else
				bBrutal = sal_True;
		}
		else
		{
			// Ref und Ftn sind nicht auf einer Seite, Move-Versuch ist noetig.
            SwFrm* pTmp = this;
			while( pTmp->GetNext() && pSrcFrm != pTmp )
				pTmp = pTmp->GetNext();
			if( pSrcFrm == pTmp )
				bBrutal = sal_True;
			else
			{   // Wenn unser Boss in einem spaltigen Bereich sitzt, es aber auf
				// der Seite schon einen FtnContainer gibt, hilft nur die brutale
				// Methode
				if( pSect && pSect->FindFtnBossFrm( !bEnd )->FindFtnCont() )
					bBrutal = sal_True;
                // OD 08.11.2002 #104840# - use <SwLayoutFrm::IsBefore(..)>
                else if ( !pFtnFrm->GetPrev() ||
                          pFtnBoss->IsBefore( pBoss )
                        )
				{
					SwFtnBossFrm *pSrcBoss = pSrcFrm->FindFtnBossFrm( !bEnd );
					pSrcBoss->MoveFtns( pSrcFrm, this, pFtn );
				}
				else
					pBoss->ChangeFtnRef( pSrcFrm, pFtn, this );
			}
		}

		// Die brutale Loesung: Fussnote entfernen und appenden.
		// Es muss SetFtnDeadLine() gerufen werden, weil nach
		// RemoveFtn die nMaxFtnHeight evtl. besser auf unsere Wuensche
		// eingestellt werden kann.
		if( bBrutal )
		{
			pBoss->RemoveFtn( pSrcFrm, pFtn, sal_False );
			SwSaveFtnHeight *pHeight = bEnd ? NULL :
				new SwSaveFtnHeight( pBoss, nDeadLine );
			pBoss->AppendFtn( this, pFtn );
			delete pHeight;
		}
	}

	// In spaltigen Bereichen, die noch nicht bis zum Seitenrand gehen,
	// ist kein RearrangeFtns sinnvoll, da der Fussnotencontainer noch
	// nicht kalkuliert worden ist.
    if( !pSect || !pSect->Growable() )
	{
		// Umgebung validieren, um Oszillationen zu verhindern.
		SwSaveFtnHeight aNochmal( pBoss, nDeadLine );
		ValidateBodyFrm();
		pBoss->RearrangeFtns( nDeadLine, sal_True );
		ValidateFrm();
	}
	else if( pSect->IsFtnAtEnd() )
	{
		ValidateBodyFrm();
		ValidateFrm();
	}

#if OSL_DEBUG_LEVEL > 1
	// pFtnFrm kann sich durch Calc veraendert haben ...
	SwFtnFrm *pFtnFrm = pBoss->FindFtn( this, pFtn );
	if( pFtnFrm && pBoss != pFtnFrm->FindFtnBossFrm( !bEnd ) )
	{
		int bla = 5;
        (void)bla;
	}
	nRstHeight = GetRstHeight();
#endif
	bInFtnConnect = sal_False;
	return;
}



/*************************************************************************
 *						SwTxtFormatter::NewFtnPortion()
 *************************************************************************/

// Die Portion fuer die Ftn-Referenz im Text
SwFtnPortion *SwTxtFormatter::NewFtnPortion( SwTxtFormatInfo &rInf,
											 SwTxtAttr *pHint )
{
    ASSERT( ! pFrm->IsVertical() || pFrm->IsSwapped(),
            "NewFtnPortion with unswapped frame" );

	if( !pFrm->IsFtnAllowed() )
		return 0;

	SwTxtFtn  *pFtn = (SwTxtFtn*)pHint;
	SwFmtFtn& rFtn = (SwFmtFtn&)pFtn->GetFtn();
	SwDoc *pDoc = pFrm->GetNode()->GetDoc();

	if( rInf.IsTest() )
		return new SwFtnPortion( rFtn.GetViewNumStr( *pDoc ), pFrm, pFtn );

    SWAP_IF_SWAPPED( pFrm )

	KSHORT nReal;
	{
		KSHORT nOldReal = pCurr->GetRealHeight();
		KSHORT nOldAscent = pCurr->GetAscent();
		KSHORT nOldHeight = pCurr->Height();
		((SwTxtFormatter*)this)->CalcRealHeight();
		nReal = pCurr->GetRealHeight();
		if( nReal < nOldReal )
			nReal = nOldReal;
		pCurr->SetRealHeight( nOldReal );
		pCurr->Height( nOldHeight );
		pCurr->SetAscent( nOldAscent );
	}

	SwTwips nLower = Y() + nReal;

    const bool bVertical = pFrm->IsVertical();
    if( bVertical )
        nLower = pFrm->SwitchHorizontalToVertical( nLower );

    nLower = lcl_GetFtnLower( pFrm, nLower );

    //6995: Wir frischen nur auf. Das Connect tut fuer diesen Fall nix
    //Brauchbares, sondern wuerde stattdessen fuer diesen Fall meist die
    //Ftn wegwerfen und neu erzeugen.

	if( !rInf.IsQuick() )
		pFrm->ConnectFtn( pFtn, nLower );

	SwTxtFrm *pScrFrm = pFrm->FindFtnRef( pFtn );
	SwFtnBossFrm *pBoss = pFrm->FindFtnBossFrm( !rFtn.IsEndNote() );
	SwFtnFrm *pFtnFrm = NULL;
	if( pScrFrm )
        pFtnFrm = pBoss->FindFtn( pScrFrm, pFtn );

    // Wir erkundigen uns, ob durch unser Append irgendeine
	// Fussnote noch auf der Seite/Spalte steht. Wenn nicht verschwindet
	// auch unsere Zeile. Dies fuehrt zu folgendem erwuenschten
	// Verhalten: Ftn1 pass noch auf die Seite/Spalte, Ftn2 nicht mehr.
	// Also bleibt die Ftn2-Referenz auf der Seite/Spalte stehen. Die
	// Fussnote selbst folgt aber erst auf der naechsten Seite/Spalte.
	// Ausnahme: Wenn keine weitere Zeile auf diese Seite/Spalte passt,
	// so sollte die Ftn2-Referenz auch auf die naechste wandern.
	if( !rFtn.IsEndNote() )
	{
		SwSectionFrm *pSct = pBoss->FindSctFrm();
		sal_Bool bAtSctEnd = pSct && pSct->IsFtnAtEnd();
		if( FTNPOS_CHAPTER != pDoc->GetFtnInfo().ePos || bAtSctEnd )
		{
			SwFrm* pFtnCont = pBoss->FindFtnCont();
			// Wenn der Boss in einem Bereich liegt, kann es sich nur um eine
			// Spalte dieses Bereichs handeln. Wenn dies nicht die erste Spalte
			// ist, duerfen wir ausweichen
			if( !pFrm->IsInTab() && ( GetLineNr() > 1 || pFrm->GetPrev() ||
				( !bAtSctEnd && pFrm->GetIndPrev() ) ||
				( pSct && pBoss->GetPrev() ) ) )
			{
				if( !pFtnCont )
				{
					rInf.SetStop( sal_True );
                    UNDO_SWAP( pFrm )
					return 0;
				}
				else
				{
					// Es darf keine Fussnotencontainer in spaltigen Bereichen und
					// gleichzeitig auf der Seite/Seitenspalte geben
					if( pSct && !bAtSctEnd ) // liegt unser Container in einem (spaltigen) Bereich?
					{
						SwFtnBossFrm* pTmp = pBoss->FindSctFrm()->FindFtnBossFrm( sal_True );
						SwFtnContFrm* pFtnC = pTmp->FindFtnCont();
						if( pFtnC )
						{
                            SwFtnFrm* pTmpFrm = (SwFtnFrm*)pFtnC->Lower();
                            if( pTmpFrm && *pTmpFrm < pFtn )
							{
								rInf.SetStop( sal_True );
                                UNDO_SWAP( pFrm )
								return 0;
							}
						}
					}
					// Ist dies die letzte passende Zeile?
                    SwTwips nTmpBot = Y() + nReal * 2;

                    if( bVertical )
                        nTmpBot = pFrm->SwitchHorizontalToVertical( nTmpBot );

                    SWRECTFN( pFtnCont )

                    const long nDiff = (*fnRect->fnYDiff)(
                                            (pFtnCont->Frm().*fnRect->fnGetTop)(),
                                             nTmpBot );

                    if( pScrFrm && nDiff < 0 )
					{
						if( pFtnFrm )
						{
							SwFtnBossFrm *pFtnBoss = pFtnFrm->FindFtnBossFrm();
							if( pFtnBoss != pBoss )
							{
								// Wir sind in der letzte Zeile und die Fussnote
								// ist auf eine andere Seite gewandert, dann wollen
								// wir mit ...
								rInf.SetStop( sal_True );
                                UNDO_SWAP( pFrm )
								return 0;
							}
						}
					}
				}
			}
		}
	}
	// Endlich: FtnPortion anlegen und raus hier...
	SwFtnPortion *pRet = new SwFtnPortion( rFtn.GetViewNumStr( *pDoc ),
											pFrm, pFtn, nReal );
	rInf.SetFtnInside( sal_True );

    UNDO_SWAP( pFrm )

	return pRet;
 }

/*************************************************************************
 *						SwTxtFormatter::NewFtnNumPortion()
 *************************************************************************/

// Die Portion fuer die Ftn-Nummerierung im Ftn-Bereich

SwNumberPortion *SwTxtFormatter::NewFtnNumPortion( SwTxtFormatInfo &rInf ) const
{
	ASSERT( pFrm->IsInFtn() && !pFrm->GetIndPrev() && !rInf.IsFtnDone(),
			"This is the wrong place for a ftnnumber" );
	if( rInf.GetTxtStart() != nStart ||
		rInf.GetTxtStart() != rInf.GetIdx() )
		return 0;

    const SwFtnFrm* pFtnFrm = pFrm->FindFtnFrm();
    const SwTxtFtn* pFtn = pFtnFrm->GetAttr();

	// Aha, wir sind also im Fussnotenbereich
	SwFmtFtn& rFtn = (SwFmtFtn&)pFtn->GetFtn();

	SwDoc *pDoc = pFrm->GetNode()->GetDoc();
	XubString aFtnTxt( rFtn.GetViewNumStr( *pDoc, sal_True ));

    const SwEndNoteInfo* pInfo;
	if( rFtn.IsEndNote() )
		pInfo = &pDoc->GetEndNoteInfo();
	else
		pInfo = &pDoc->GetFtnInfo();
	const SwAttrSet& rSet = pInfo->GetCharFmt(*pDoc)->GetAttrSet();

	const SwAttrSet* pParSet = &rInf.GetCharAttr();
    const IDocumentSettingAccess* pIDSA = pFrm->GetTxtNode()->getIDocumentSettingAccess();
    SwFont *pNumFnt = new SwFont( pParSet, pIDSA );

    // --> FME 2005-02-17 #i37142#
    // Underline style of paragraph font should not be considered
    // Overline style of paragraph font should not be considered
    // Weight style of paragraph font should not be considered
    // Posture style of paragraph font should not be considered
    // See also #i18463# and SwTxtFormatter::NewNumberPortion()
    pNumFnt->SetUnderline( UNDERLINE_NONE );
    pNumFnt->SetOverline( UNDERLINE_NONE );
    pNumFnt->SetItalic( ITALIC_NONE, SW_LATIN );
    pNumFnt->SetItalic( ITALIC_NONE, SW_CJK );
    pNumFnt->SetItalic( ITALIC_NONE, SW_CTL );
    pNumFnt->SetWeight( WEIGHT_NORMAL, SW_LATIN );
    pNumFnt->SetWeight( WEIGHT_NORMAL, SW_CJK );
    pNumFnt->SetWeight( WEIGHT_NORMAL, SW_CTL );
    // <--

    pNumFnt->SetDiffFnt(&rSet, pIDSA );
    pNumFnt->SetVertical( pNumFnt->GetOrientation(), pFrm->IsVertical() );

    SwFtnNumPortion* pNewPor = new SwFtnNumPortion( aFtnTxt, pNumFnt );
    pNewPor->SetLeft( !pFrm->IsRightToLeft() );
	return pNewPor;
}

/*************************************************************************
 *					SwTxtFormatter::NewErgoSumPortion()
 *************************************************************************/

XubString lcl_GetPageNumber( const SwPageFrm* pPage )
{
	ASSERT( pPage, "GetPageNumber: Homeless TxtFrm" );
	MSHORT nVirtNum = pPage->GetVirtPageNum();
	const SvxNumberType& rNum = pPage->GetPageDesc()->GetNumType();
	return rNum.GetNumStr( nVirtNum );
}

SwErgoSumPortion *SwTxtFormatter::NewErgoSumPortion( SwTxtFormatInfo &rInf ) const
{
	// Wir koennen nicht davon ausgehen, dass wir ein Follow sind
	// 7983: GetIdx() nicht nStart
	if( !pFrm->IsInFtn()  || pFrm->GetPrev() ||
		rInf.IsErgoDone() || rInf.GetIdx() != pFrm->GetOfst() ||
		pFrm->ImplFindFtnFrm()->GetAttr()->GetFtn().IsEndNote() )
		return 0;

	// Aha, wir sind also im Fussnotenbereich
	const SwFtnInfo &rFtnInfo = pFrm->GetNode()->GetDoc()->GetFtnInfo();
	SwTxtFrm *pQuoFrm = pFrm->FindQuoVadisFrm();
	if( !pQuoFrm )
		return 0;
	const SwPageFrm* pPage = pFrm->FindPageFrm();
	const SwPageFrm* pQuoPage = pQuoFrm->FindPageFrm();
	if( pPage == pQuoFrm->FindPageFrm() )
		return 0; // Wenn der QuoVadis auf der selben (spaltigen) Seite steht
	const XubString aPage = lcl_GetPageNumber( pPage );
	SwParaPortion *pPara = pQuoFrm->GetPara();
	if( pPara )
		pPara->SetErgoSumNum( aPage );
	if( !rFtnInfo.aErgoSum.Len() )
		return 0;
	SwErgoSumPortion *pErgo = new SwErgoSumPortion( rFtnInfo.aErgoSum,
								lcl_GetPageNumber( pQuoPage ) );
	return pErgo;
}

/*************************************************************************
 *					SwTxtFormatter::FormatQuoVadis()
 *************************************************************************/

xub_StrLen SwTxtFormatter::FormatQuoVadis( const xub_StrLen nOffset )
{
    ASSERT( ! pFrm->IsVertical() || ! pFrm->IsSwapped(),
            "SwTxtFormatter::FormatQuoVadis with swapped frame" );

	if( !pFrm->IsInFtn() || pFrm->ImplFindFtnFrm()->GetAttr()->GetFtn().IsEndNote() )
		return nOffset;

	const SwFrm* pErgoFrm = pFrm->FindFtnFrm()->GetFollow();
	if( !pErgoFrm && pFrm->HasFollow() )
		pErgoFrm = pFrm->GetFollow();
	if( !pErgoFrm )
		return nOffset;

	if( pErgoFrm == pFrm->GetNext() )
	{
		SwFrm *pCol = pFrm->FindColFrm();
		while( pCol && !pCol->GetNext() )
			pCol = pCol->GetUpper()->FindColFrm();
		if( pCol )
			return nOffset;
	}
	else
	{
		const SwPageFrm* pPage = pFrm->FindPageFrm();
		const SwPageFrm* pErgoPage = pErgoFrm->FindPageFrm();
        if( pPage == pErgoPage )
			return nOffset; // Wenn der ErgoSum auf der selben Seite steht
	}

	SwTxtFormatInfo &rInf = GetInfo();
	const SwFtnInfo &rFtnInfo = pFrm->GetNode()->GetDoc()->GetFtnInfo();
	if( !rFtnInfo.aQuoVadis.Len() )
		return nOffset;

	// Ein Wort zu QuoVadis/ErgoSum:
	// Fuer diese Texte wird der am Absatz eingestellte Font verwendet.
	// Wir initialisieren uns also:
//	ResetFont();
	FeedInf( rInf );
	SeekStartAndChg( rInf, sal_True );
	if( GetRedln() && pCurr->HasRedline() )
		GetRedln()->Seek( *pFnt, nOffset, 0 );

	// Ein fieser Sonderfall: Flyfrms reichen in die Zeile und stehen
	// natuerlich da, wo wir unseren Quovadis Text reinsetzen wollen.
	// Erst mal sehen, ob es so schlimm ist:
	SwLinePortion *pPor = pCurr->GetFirstPortion();
	KSHORT nLastLeft = 0;
	while( pPor )
	{
		if ( pPor->IsFlyPortion() )
			nLastLeft = ( (SwFlyPortion*) pPor)->Fix() +
						( (SwFlyPortion*) pPor)->Width();
		pPor = pPor->GetPortion();
	}
	// Das alte Spiel: wir wollen, dass die Zeile an einer bestimmten
	// Stelle umbricht, also beeinflussen wir die Width.
	// nLastLeft ist jetzt quasi der rechte Rand.
	const KSHORT nOldRealWidth = rInf.RealWidth();
	rInf.RealWidth( nOldRealWidth - nLastLeft );

	XubString aErgo = lcl_GetPageNumber( pErgoFrm->FindPageFrm() );
	SwQuoVadisPortion *pQuo = new SwQuoVadisPortion(rFtnInfo.aQuoVadis, aErgo );
	pQuo->SetAscent( rInf.GetAscent()  );
	pQuo->Height( rInf.GetTxtHeight() );
	pQuo->Format( rInf );
    sal_uInt16 nQuoWidth = pQuo->Width();
    SwLinePortion* pCurrPor = pQuo;

    while ( rInf.GetRest() )
    {
        SwLinePortion* pFollow = rInf.GetRest();
	    rInf.SetRest( 0 );
        pCurrPor->Move( rInf );

        ASSERT( pFollow->IsQuoVadisPortion(),
                "Quo Vadis, rest of QuoVadisPortion" )

        // format the rest and append it to the other QuoVadis parts
		pFollow->Format( rInf );
        nQuoWidth = nQuoWidth + pFollow->Width();

        pCurrPor->Append( pFollow );
        pCurrPor = pFollow;
    }

    nLastLeft = nOldRealWidth - nQuoWidth;
    Right( Right() - nQuoWidth );

    SWAP_IF_NOT_SWAPPED( pFrm )

	const xub_StrLen nRet = FormatLine( nStart );

    UNDO_SWAP( pFrm )

	Right( rInf.Left() + nOldRealWidth - 1 );

	nLastLeft = nOldRealWidth - pCurr->Width();
	FeedInf( rInf );

	// Es kann durchaus sein, dass am Ende eine Marginportion steht,
	// die beim erneuten Aufspannen nur Aerger bereiten wuerde.
	pPor = pCurr->FindLastPortion();
	SwGluePortion *pGlue = pPor->IsMarginPortion() ?
		(SwMarginPortion*) pPor : 0;
	if( pGlue )
	{
		pGlue->Height( 0 );
		pGlue->Width( 0 );
		pGlue->SetLen( 0 );
		pGlue->SetAscent( 0 );
		pGlue->SetPortion( NULL );
		pGlue->SetFixWidth(0);
	}

	// Luxus: Wir sorgen durch das Aufspannen von Glues dafuer,
	// dass der QuoVadis-Text rechts erscheint:
    nLastLeft = nLastLeft - nQuoWidth;
	if( nLastLeft )
	{
		if( nLastLeft > pQuo->GetAscent() ) // Mindestabstand
		{
			switch( GetAdjust() )
			{
				case SVX_ADJUST_BLOCK:
				{
					if( !pCurr->GetLen() ||
						CH_BREAK != GetInfo().GetChar(nStart+pCurr->GetLen()-1))
						nLastLeft = pQuo->GetAscent();
					nQuoWidth = nQuoWidth + nLastLeft;
					break;
				}
				case SVX_ADJUST_RIGHT:
				{
					nLastLeft = pQuo->GetAscent();
					nQuoWidth = nQuoWidth + nLastLeft;
					break;
				}
				case SVX_ADJUST_CENTER:
				{
					nQuoWidth = nQuoWidth + pQuo->GetAscent();
					long nDiff = nLastLeft - nQuoWidth;
					if( nDiff < 0 )
					{
						nLastLeft = pQuo->GetAscent();
                        nQuoWidth = (sal_uInt16)(-nDiff + nLastLeft);
					}
					else
					{
						nQuoWidth = 0;
                        nLastLeft = sal_uInt16(( pQuo->GetAscent() + nDiff ) / 2);
					}
					break;
				}
				default:
					nQuoWidth = nQuoWidth + nLastLeft;
			}
		}
		else
			nQuoWidth = nQuoWidth + nLastLeft;
		if( nLastLeft )
		{
			pGlue = new SwGluePortion(0);
			pGlue->Width( nLastLeft );
			pPor->Append( pGlue );
			pPor = pPor->GetPortion();
		}
	}

	// Jetzt aber: die QuoVadis-Portion wird angedockt:
    pCurrPor = pQuo;
    while ( pCurrPor )
    {
        // pPor->Append deletes the pPortoin pointer of pPor. Therefore
        // we have to keep a pointer to the next portion
        pQuo = (SwQuoVadisPortion*)pCurrPor->GetPortion();
        pPor->Append( pCurrPor );
        pPor = pPor->GetPortion();
        pCurrPor = pQuo;
    }

	pCurr->Width( pCurr->Width() + KSHORT( nQuoWidth ) );

	// Und noch einmal adjustieren wegen des Adjustment und nicht zu Letzt
	// wegen folgendem Sonderfall: In der Zeile hat der DummUser durchgaengig
	// einen kleineren Font eingestellt als der vom QuoVadis-Text ...
	CalcAdjustLine( pCurr );

#if OSL_DEBUG_LEVEL > 1
	if( OPTDBG( rInf ) )
	{
//		  aDbstream << "FormatQuoVadis:" << endl;
//		  pCurr->DebugPortions( aDbstream, rInf.GetTxt(), nStart );
	}
#endif

	// Uff...
	return nRet;
}


/*************************************************************************
 *					SwTxtFormatter::MakeDummyLine()
 *************************************************************************/

// MakeDummyLine() erzeugt eine Line, die bis zum unteren Seitenrand
// reicht. DummyLines bzw. DummyPortions sorgen dafuer, dass Oszillationen
// zum stehen kommen, weil Rueckflussmoeglichkeiten genommen werden.
// Sie werden bei absatzgebundenen Frames in Fussnoten und bei Ftn-
// Oszillationen verwendet.

void SwTxtFormatter::MakeDummyLine()
{
	KSHORT nRstHeight = GetFrmRstHeight();
	if( pCurr && nRstHeight > pCurr->Height() )
	{
		SwLineLayout *pLay = new SwLineLayout;
		nRstHeight = nRstHeight - pCurr->Height();
		pLay->Height( nRstHeight );
		pLay->SetAscent( nRstHeight );
		Insert( pLay );
		Next();
	}
}

/*************************************************************************
 *                      class SwFtnSave
  *************************************************************************/
class SwFtnSave
{
    SwTxtSizeInfo *pInf;
    SwFont       *pFnt;
    SwFont       *pOld;
public:
    SwFtnSave( const SwTxtSizeInfo &rInf,
               const SwTxtFtn *pTxtFtn,
               const bool bApplyGivenScriptType,
               const sal_uInt8 nGivenScriptType );
   ~SwFtnSave();
};

/*************************************************************************
 *                     SwFtnSave::SwFtnSave()
 *************************************************************************/

SwFtnSave::SwFtnSave( const SwTxtSizeInfo &rInf,
                      const SwTxtFtn* pTxtFtn,
                      const bool bApplyGivenScriptType,
                      const sal_uInt8 nGivenScriptType )
    : pInf( &((SwTxtSizeInfo&)rInf) )
    , pFnt( 0 )
    , pOld( 0 )
{
	if( pTxtFtn && rInf.GetTxtFrm() )
	{
		pFnt = ((SwTxtSizeInfo&)rInf).GetFont();
	  	pOld = new SwFont( *pFnt );
		pOld->GetTox() = pFnt->GetTox();
		pFnt->GetTox() = 0;
		SwFmtFtn& rFtn = (SwFmtFtn&)pTxtFtn->GetFtn();
		const SwDoc *pDoc = rInf.GetTxtFrm()->GetNode()->GetDoc();

        // --> OD 2009-01-29 #i98418#
        if ( bApplyGivenScriptType )
        {
            pFnt->SetActual( nGivenScriptType );
        }
        else
        {
            // examine text and set script
            String aTmpStr( rFtn.GetViewNumStr( *pDoc ) );
            pFnt->SetActual( SwScriptInfo::WhichFont( 0, &aTmpStr, 0 ) );
        }
        // <--

        const SwEndNoteInfo* pInfo;
		if( rFtn.IsEndNote() )
			pInfo = &pDoc->GetEndNoteInfo();
		else
			pInfo = &pDoc->GetFtnInfo();
		const SwAttrSet& rSet =	pInfo->GetAnchorCharFmt((SwDoc&)*pDoc)->GetAttrSet();
        pFnt->SetDiffFnt( &rSet, rInf.GetTxtFrm()->GetNode()->getIDocumentSettingAccess() );

        // we reduce footnote size, if we are inside a double line portion
        if ( ! pOld->GetEscapement() && 50 == pOld->GetPropr() )
        {
            Size aSize = pFnt->GetSize( pFnt->GetActual() );
            pFnt->SetSize( Size( (long)aSize.Width() / 2,
                                 (long)aSize.Height() / 2 ),
                           pFnt->GetActual() );
        }

        // set the correct rotation at the footnote font
        const SfxPoolItem* pItem;
        if( SFX_ITEM_SET == rSet.GetItemState( RES_CHRATR_ROTATE,
			sal_True, &pItem ))
            pFnt->SetVertical( ((SvxCharRotateItem*)pItem)->GetValue(),
                                rInf.GetTxtFrm()->IsVertical() );

        pFnt->ChgPhysFnt( pInf->GetVsh(), *pInf->GetOut() );

		if( SFX_ITEM_SET == rSet.GetItemState( RES_CHRATR_BACKGROUND,
			sal_True, &pItem ))
			pFnt->SetBackColor( new Color( ((SvxBrushItem*)pItem)->GetColor() ) );
	}
	else
		pFnt = NULL;
}

/*************************************************************************
 *					   SwFtnSave::~SwFtnSave()
 *************************************************************************/

SwFtnSave::~SwFtnSave()
{
	if( pFnt )
	{
		// SwFont zurueckstellen
		*pFnt = *pOld;
		pFnt->GetTox() = pOld->GetTox();
        pFnt->ChgPhysFnt( pInf->GetVsh(), *pInf->GetOut() );
		delete pOld;
	}
}

/*************************************************************************
 *						SwFtnPortion::SwFtnPortion()
 *************************************************************************/

SwFtnPortion::SwFtnPortion( const XubString &rExpand, SwTxtFrm *pFrame,
                            SwTxtFtn *pFootn, KSHORT nReal )
        : SwFldPortion( rExpand, 0 )
        , pFrm(pFrame)
        , pFtn(pFootn)
        , nOrigHeight( nReal )
        // --> OD 2009-01-29 #i98418#
        , mbPreferredScriptTypeSet( false )
        , mnPreferredScriptType( SW_LATIN )
        // <--
{
	SetLen(1);
	SetWhichPor( POR_FTN );
}

/*************************************************************************
 *						SwFtnPortion::GetExpTxt()
 *************************************************************************/

sal_Bool SwFtnPortion::GetExpTxt( const SwTxtSizeInfo &, XubString &rTxt ) const
{
	rTxt = aExpand;
	return sal_True;
}

/*************************************************************************
 *				   virtual SwFtnPortion::Format()
 *************************************************************************/

sal_Bool SwFtnPortion::Format( SwTxtFormatInfo &rInf )
{
    // --> OD 2009-01-29 #i98418#
//    SwFtnSave aFtnSave( rInf, pFtn );
    SwFtnSave aFtnSave( rInf, pFtn, mbPreferredScriptTypeSet, mnPreferredScriptType );
    // <--
    // the idx is manipulated in SwExpandPortion::Format
    // this flag indicates, that a footnote is allowed to trigger
    // an underflow during SwTxtGuess::Guess
    rInf.SetFakeLineStart( rInf.GetIdx() > rInf.GetLineStart() );
    sal_Bool bFull = SwFldPortion::Format( rInf );
    rInf.SetFakeLineStart( sal_False );
	SetAscent( rInf.GetAscent() );
    Height( rInf.GetTxtHeight() );
	rInf.SetFtnDone( !bFull );
	if( !bFull )
		rInf.SetParaFtn();
	return bFull;
}

/*************************************************************************
 *				 virtual SwFtnPortion::Paint()
 *************************************************************************/

void SwFtnPortion::Paint( const SwTxtPaintInfo &rInf ) const
{
    // --> OD 2009-01-29 #i98418#
//    SwFtnSave aFtnSave( rInf, pFtn );
    SwFtnSave aFtnSave( rInf, pFtn, mbPreferredScriptTypeSet, mnPreferredScriptType );
    // <--
	rInf.DrawViewOpt( *this, POR_FTN );
	SwExpandPortion::Paint( rInf );
}

/*************************************************************************
 *				 virtual SwFtnPortion::GetTxtSize()
 *************************************************************************/

SwPosSize SwFtnPortion::GetTxtSize( const SwTxtSizeInfo &rInfo ) const
{
    // --> OD 2009-01-29 #i98418#
//    SwFtnSave aFtnSave( rInfo, pFtn );
    SwFtnSave aFtnSave( rInfo, pFtn, mbPreferredScriptTypeSet, mnPreferredScriptType );
    // <--
	return SwExpandPortion::GetTxtSize( rInfo );
}

// --> OD 2009-01-29 #i98418#
void SwFtnPortion::SetPreferredScriptType( sal_uInt8 nPreferredScriptType )
{
    mbPreferredScriptTypeSet = true;
    mnPreferredScriptType = nPreferredScriptType;
}
// <--

/*************************************************************************
 *						class SwQuoVadisPortion
 *************************************************************************/

SwFldPortion *SwQuoVadisPortion::Clone( const XubString &rExpand ) const
{ return new SwQuoVadisPortion( rExpand, aErgo ); }

SwQuoVadisPortion::SwQuoVadisPortion( const XubString &rExp, const XubString& rStr )
	: SwFldPortion( rExp ), aErgo(rStr)
{
	SetLen(0);
	SetWhichPor( POR_QUOVADIS );
}

/*************************************************************************
 *				   virtual SwQuoVadisPortion::Format()
 *************************************************************************/

sal_Bool SwQuoVadisPortion::Format( SwTxtFormatInfo &rInf )
{
	// erster Versuch, vielleicht passt der Text
	CheckScript( rInf );
	sal_Bool bFull = SwFldPortion::Format( rInf );
	SetLen( 0 );

	if( bFull )
	{
		// zweiter Versuch, wir kuerzen den String:
		aExpand = XubString( "...", RTL_TEXTENCODING_MS_1252 );
		bFull = SwFldPortion::Format( rInf );
		SetLen( 0 );
		if( bFull  )
			// dritter Versuch, es langt: jetzt wird gestaucht:
            Width( sal_uInt16(rInf.Width() - rInf.X()) );

		// 8317: keine mehrzeiligen Felder bei QuoVadis und ErgoSum
		if( rInf.GetRest() )
		{
			delete rInf.GetRest();
			rInf.SetRest( 0 );
		}
	}
	return bFull;
}

/*************************************************************************
 *				 virtual SwQuoVadisPortion::GetExpTxt()
 *************************************************************************/

sal_Bool SwQuoVadisPortion::GetExpTxt( const SwTxtSizeInfo &, XubString &rTxt ) const
{
	rTxt = aExpand;
    // if this QuoVadisPortion has a follow, the follow is responsible for
    // the ergo text.
    if ( ! HasFollow() )
        rTxt += aErgo;
	return sal_True;
}

/*************************************************************************
 *              virtual SwQuoVadisPortion::HandlePortion()
 *************************************************************************/

void SwQuoVadisPortion::HandlePortion( SwPortionHandler& rPH ) const
{
    String aString( aExpand );
    aString += aErgo;
    rPH.Special( GetLen(), aString, GetWhichPor() );
}

/*************************************************************************
 *				 virtual SwQuoVadisPortion::Paint()
 *************************************************************************/

void SwQuoVadisPortion::Paint( const SwTxtPaintInfo &rInf ) const
{
	// Wir wollen _immer_ per DrawStretchText ausgeben,
	// weil nErgo schnell mal wechseln kann.
	if( PrtWidth() )
	{
		rInf.DrawViewOpt( *this, POR_QUOVADIS );
        SwTxtSlot aDiffTxt( &rInf, this, true, false );
        SwFontSave aSave( rInf, pFnt );
		rInf.DrawText( *this, rInf.GetLen(), sal_True );
	}
}

/*************************************************************************
 *						class SwErgoSumPortion
 *************************************************************************/

SwFldPortion *SwErgoSumPortion::Clone( const XubString &rExpand ) const
{
	UniString aTmp; // = UniString::CreateFromInt32( 0 );
	return new SwErgoSumPortion( rExpand, aTmp );
}

SwErgoSumPortion::SwErgoSumPortion( const XubString &rExp, const XubString& rStr )
	: SwFldPortion( rExp )
{
	SetLen(0);
	aExpand += rStr;

	// 7773: sinnvolle Massnahme: ein Blank Abstand zum Text
	aExpand += ' ';
	SetWhichPor( POR_ERGOSUM );
}

xub_StrLen SwErgoSumPortion::GetCrsrOfst( const KSHORT ) const
{
	return 0;
}

/*************************************************************************
 *				   virtual SwErgoSumPortion::Format()
 *************************************************************************/

sal_Bool SwErgoSumPortion::Format( SwTxtFormatInfo &rInf )
{
	sal_Bool bFull = SwFldPortion::Format( rInf );
	SetLen( 0 );
    rInf.SetErgoDone( sal_True );

	// 8317: keine mehrzeiligen Felder bei QuoVadis und ErgoSum
    if( bFull && rInf.GetRest() )
    {
        delete rInf.GetRest();
        rInf.SetRest( 0 );
    }

    // We return false in order to get some text into the current line,
    // even if it's full (better than looping)
    return sal_False;
}


/*************************************************************************
 *						SwParaPortion::SetErgoSumNum()
 *************************************************************************/

void SwParaPortion::SetErgoSumNum( const XubString& rErgo )
{
	SwLineLayout *pLay = this;
	while( pLay->GetNext() )
	{
		DBG_LOOP;
		pLay = pLay->GetNext();
	}
	SwLinePortion	  *pPor = pLay;
	SwQuoVadisPortion *pQuo = 0;
	while( pPor && !pQuo )
	{
		if ( pPor->IsQuoVadisPortion() )
			pQuo = (SwQuoVadisPortion*)pPor;
		pPor = pPor->GetPortion();
	}
	if( pQuo )
		pQuo->SetNumber( rErgo );
}

/*************************************************************************
 *						SwParaPortion::UpdateQuoVadis()
 *
 * Wird im SwTxtFrm::Prepare() gerufen
 *************************************************************************/

sal_Bool SwParaPortion::UpdateQuoVadis( const XubString &rQuo )
{
	SwLineLayout *pLay = this;
	while( pLay->GetNext() )
	{
		DBG_LOOP;
		pLay = pLay->GetNext();
	}
	SwLinePortion	  *pPor = pLay;
	SwQuoVadisPortion *pQuo = 0;
	while( pPor && !pQuo )
	{
		if ( pPor->IsQuoVadisPortion() )
			pQuo = (SwQuoVadisPortion*)pPor;
		pPor = pPor->GetPortion();
	}

	if( !pQuo )
		return sal_False;

	return pQuo->GetQuoTxt() == rQuo;
}