/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sw.hxx"
#include <hintids.hxx>
#include <sfx2/printer.hxx>
#include <editeng/lspcitem.hxx>
#include <editeng/adjitem.hxx>
#include <editeng/escpitem.hxx>
#include <editeng/lrspitem.hxx>
#include <editeng/pgrditem.hxx>
#include <vcl/window.hxx>
#include <vcl/svapp.hxx>
#include <viewsh.hxx>	// ViewShell
#include <viewopt.hxx>
#include <ndtxt.hxx>	// SwTxtNode
#include <pagefrm.hxx>  // SwPageFrm
#include <paratr.hxx>
#include <SwPortionHandler.hxx>
#include <txtcfg.hxx>
#include <porrst.hxx>
#include <inftxt.hxx>
#include <txtpaint.hxx>	// ClipVout
#include <swfntcch.hxx>	// SwFontAccess
#include <tgrditem.hxx>
#include <pagedesc.hxx> // SwPageDesc
#include <frmatr.hxx>
#include <redlnitr.hxx>	// SwRedlineItr
#include <porfly.hxx> 	// SwFlyPortion
#include <atrhndl.hxx>
#include "rootfrm.hxx"

#include <IDocumentRedlineAccess.hxx>
#include <IDocumentSettingAccess.hxx>
#include <IDocumentDeviceAccess.hxx>

/*************************************************************************
 *                      class SwTmpEndPortion
 *************************************************************************/

SwTmpEndPortion::SwTmpEndPortion( const SwLinePortion &rPortion )
{
	Height( rPortion.Height() );
	SetAscent( rPortion.GetAscent() );
	SetWhichPor( POR_TMPEND );
}

/*************************************************************************
 *				   virtual SwTmpEndPortion::Paint()
 *************************************************************************/

void SwTmpEndPortion::Paint( const SwTxtPaintInfo &rInf ) const
{
	if( rInf.OnWin() && rInf.GetOpt().IsParagraph() )
	{
		SwDefFontSave aSave( rInf );
		const XubString aTmp( CH_PAR );
		rInf.DrawText( aTmp, *this );
	}
}

/*************************************************************************
 *						class SwBreakPortion
 *************************************************************************/
SwBreakPortion::SwBreakPortion( const SwLinePortion &rPortion )
    : SwLinePortion( rPortion )
{
	nLineLength = 1;
	SetWhichPor( POR_BRK );
}

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

KSHORT SwBreakPortion::GetViewWidth( const SwTxtSizeInfo & ) const
{ return 0; }

SwLinePortion *SwBreakPortion::Compress()
{ return (GetPortion() && GetPortion()->InTxtGrp() ? 0 : this); }

void SwBreakPortion::Paint( const SwTxtPaintInfo &rInf ) const
{
    if( rInf.OnWin() && rInf.GetOpt().IsLineBreak() )
        rInf.DrawLineBreak( *this );
}

/*************************************************************************
 *				   virtual SwBreakPortion::Format()
 *************************************************************************/

sal_Bool SwBreakPortion::Format( SwTxtFormatInfo &rInf )
{
    const SwLinePortion *pRoot = rInf.GetRoot();
	Width( 0 );
	Height( pRoot->Height() );
	SetAscent( pRoot->GetAscent() );
	if ( rInf.GetIdx()+1 == rInf.GetTxt().Len() )
		rInf.SetNewLine( sal_True );
	return sal_True;
}

/*************************************************************************
 *              virtual SwBreakPortion::HandlePortion()
 *************************************************************************/

void SwBreakPortion::HandlePortion( SwPortionHandler& rPH ) const
{
    rPH.Text( GetLen(), GetWhichPor() );
}


SwKernPortion::SwKernPortion( SwLinePortion &rPortion, short nKrn,
                              sal_Bool bBG, sal_Bool bGK ) :
    nKern( nKrn ), bBackground( bBG ), bGridKern( bGK )
{
	Height( rPortion.Height() );
	SetAscent( rPortion.GetAscent() );
	nLineLength = 0;
	SetWhichPor( POR_KERN );
	if( nKern > 0 )
		Width( nKern );
 	rPortion.Insert( this );
}

SwKernPortion::SwKernPortion( const SwLinePortion& rPortion ) :
    nKern( 0 ), bBackground( sal_False ), bGridKern( sal_True )
{
	Height( rPortion.Height() );
	SetAscent( rPortion.GetAscent() );

	nLineLength = 0;
	SetWhichPor( POR_KERN );
}

void SwKernPortion::Paint( const SwTxtPaintInfo &rInf ) const
{
	if( Width() )
	{
        // bBackground is set for Kerning Portions between two fields
        if ( bBackground )
            rInf.DrawViewOpt( *this, POR_FLD );

		rInf.DrawBackBrush( *this );

        // do we have to repaint a post it portion?
        if( rInf.OnWin() && pPortion && !pPortion->Width() )
            pPortion->PrePaint( rInf, this );

        if( rInf.GetFont()->IsPaintBlank() )
		{
            static sal_Char __READONLY_DATA sDoubleSpace[] = "  ";
			XubString aTxtDouble( sDoubleSpace, RTL_TEXTENCODING_MS_1252 );
            // --> FME 2006-07-12 #b6439097#
            SwRect aClipRect;
            rInf.CalcRect( *this, &aClipRect, 0 );
            SwSaveClip aClip( (OutputDevice*)rInf.GetOut() );
            aClip.ChgClip( aClipRect, 0 );
            // <--
            rInf.DrawText( aTxtDouble, *this, 0, 2, sal_True );
		}
	}
}

void SwKernPortion::FormatEOL( SwTxtFormatInfo &rInf )
{
    if ( bGridKern )
        return;

    if( rInf.GetLast() == this )
		rInf.SetLast( FindPrevPortion( rInf.GetRoot() ) );
	if( nKern < 0 )
		Width( -nKern );
	else
		Width( 0 );
	rInf.GetLast()->FormatEOL( rInf );
}

SwArrowPortion::SwArrowPortion( const SwLinePortion &rPortion ) :
	bLeft( sal_True )
{
	Height( rPortion.Height() );
	SetAscent( rPortion.GetAscent() );
	nLineLength = 0;
	SetWhichPor( POR_ARROW );
}

SwArrowPortion::SwArrowPortion( const SwTxtPaintInfo &rInf )
	: bLeft( sal_False )
{
    Height( (sal_uInt16)(rInf.GetTxtFrm()->Prt().Height()) );
	aPos.X() = rInf.GetTxtFrm()->Frm().Left() +
			   rInf.GetTxtFrm()->Prt().Right();
	aPos.Y() = rInf.GetTxtFrm()->Frm().Top() +
			   rInf.GetTxtFrm()->Prt().Bottom();
    SetWhichPor( POR_ARROW );
}

void SwArrowPortion::Paint( const SwTxtPaintInfo &rInf ) const
{
	((SwArrowPortion*)this)->aPos = rInf.GetPos();
}

SwLinePortion *SwArrowPortion::Compress() { return this; }

SwTwips SwTxtFrm::EmptyHeight() const
{
    ASSERT( ! IsVertical() || ! IsSwapped(),"SwTxtFrm::EmptyHeight with swapped frame" );

    SwFont *pFnt;
	const SwTxtNode& rTxtNode = *GetTxtNode();
    const IDocumentSettingAccess* pIDSA = rTxtNode.getIDocumentSettingAccess();
	ViewShell *pSh = getRootFrm()->GetCurrShell();
	if ( rTxtNode.HasSwAttrSet() )
	{
		const SwAttrSet *pAttrSet = &( rTxtNode.GetSwAttrSet() );
        pFnt = new SwFont( pAttrSet, pIDSA );
	}
	else
	{
		SwFontAccess aFontAccess( &rTxtNode.GetAnyFmtColl(), pSh);
        pFnt = new SwFont( *aFontAccess.Get()->GetFont() );
		pFnt->ChkMagic( pSh, pFnt->GetActual() );
	}

    if ( IsVertical() )
        pFnt->SetVertical( 2700 );

    OutputDevice* pOut = pSh ? pSh->GetOut() : 0;
    if ( !pOut || !pSh->GetViewOptions()->getBrowseMode() ||
		 pSh->GetViewOptions()->IsPrtFormat() )
	{
        pOut = rTxtNode.getIDocumentDeviceAccess()->getReferenceDevice(true);
	}

    const IDocumentRedlineAccess* pIDRA = rTxtNode.getIDocumentRedlineAccess();
    if( IDocumentRedlineAccess::IsShowChanges( pIDRA->GetRedlineMode() ) )
	{
        MSHORT nRedlPos = pIDRA->GetRedlinePos( rTxtNode, USHRT_MAX );
        if( MSHRT_MAX != nRedlPos )
        {
            SwAttrHandler aAttrHandler;
            aAttrHandler.Init(  GetTxtNode()->GetSwAttrSet(),
                               *GetTxtNode()->getIDocumentSettingAccess(), NULL );
            SwRedlineItr aRedln( rTxtNode, *pFnt, aAttrHandler,
                                 nRedlPos, sal_True );
        }
    }

	SwTwips nRet;
	if( !pOut )
        nRet = IsVertical() ?
               Prt().SSize().Width() + 1 :
               Prt().SSize().Height() + 1;
	else
	{
		pFnt->SetFntChg( sal_True );
        pFnt->ChgPhysFnt( pSh, *pOut );
        nRet = pFnt->GetHeight( pSh, *pOut );
	}
	delete pFnt;
	return nRet;
}

/*************************************************************************
 *						SwTxtFrm::FormatEmpty()
 *************************************************************************/

sal_Bool SwTxtFrm::FormatEmpty()
{
    ASSERT( ! IsVertical() || ! IsSwapped(),"SwTxtFrm::FormatEmpty with swapped frame" );

	if ( HasFollow() || GetTxtNode()->GetpSwpHints() ||
		0 != GetTxtNode()->GetNumRule() ||
        GetTxtNode()->HasHiddenCharAttribute( true ) ||
		 IsInFtn() || ( HasPara() && GetPara()->IsPrepMustFit() ) )
		return sal_False;
	const SwAttrSet& aSet = GetTxtNode()->GetSwAttrSet();
    const SvxAdjust nAdjust = aSet.GetAdjust().GetAdjust();
    if( ( ( ! IsRightToLeft() && ( SVX_ADJUST_LEFT != nAdjust ) ) ||
          (   IsRightToLeft() && ( SVX_ADJUST_RIGHT != nAdjust ) ) ) ||
          aSet.GetRegister().GetValue() )
        return sal_False;
	const SvxLineSpacingItem &rSpacing = aSet.GetLineSpacing();
	if( SVX_LINE_SPACE_MIN == rSpacing.GetLineSpaceRule() ||
		SVX_LINE_SPACE_FIX == rSpacing.GetLineSpaceRule() ||
		aSet.GetLRSpace().IsAutoFirst() )
		return sal_False;
	else
	{
        SwTxtFly aTxtFly( this );
		SwRect aRect;
		sal_Bool bFirstFlyCheck = 0 != Prt().Height();
		if ( bFirstFlyCheck &&
			 aTxtFly.IsOn() && aTxtFly.IsAnyObj( aRect ) )
			return sal_False;
		else
		{
            SwTwips nHeight = EmptyHeight();

            if ( GetTxtNode()->GetSwAttrSet().GetParaGrid().GetValue() &&
                 IsInDocBody() )
            {
                GETGRID( FindPageFrm() )
                if ( pGrid )
                    nHeight = pGrid->GetBaseHeight() + pGrid->GetRubyHeight();
            }

            SWRECTFN( this )
            const SwTwips nChg = nHeight - (Prt().*fnRect->fnGetHeight)();

            if( !nChg )
				SetUndersized( sal_False );
			AdjustFrm( nChg );

			if( HasBlinkPor() )
			{
				ClearPara();
				ResetBlinkPor();
			}
			SetCacheIdx( MSHRT_MAX );
			if( !IsEmpty() )
			{
				SetEmpty( sal_True );
				SetCompletePaint();
			}
			if( !bFirstFlyCheck &&
				 aTxtFly.IsOn() && aTxtFly.IsAnyObj( aRect ) )
				 return sal_False;

            // --> OD 2004-11-17 #i35635# - call method <HideAndShowObjects()>
            // to assure that objects anchored at the empty paragraph are
            // correctly visible resp. invisible.
            HideAndShowObjects();
            // <--
			return sal_True;
		}
	}
}

sal_Bool SwTxtFrm::FillRegister( SwTwips& rRegStart, KSHORT& rRegDiff )
{
	const SwFrm *pFrm = this;
	rRegDiff = 0;
	while( !( ( FRM_BODY | FRM_FLY )
		   & pFrm->GetType() ) && pFrm->GetUpper() )
		pFrm = pFrm->GetUpper();
	if( ( FRM_BODY| FRM_FLY ) & pFrm->GetType() )
	{
        SWRECTFN( pFrm )
        rRegStart = (pFrm->*fnRect->fnGetPrtTop)();
		pFrm = pFrm->FindPageFrm();
		if( pFrm->IsPageFrm() )
		{
			SwPageDesc* pDesc = ((SwPageFrm*)pFrm)->FindPageDesc();
			if( pDesc )
			{
				rRegDiff = pDesc->GetRegHeight();
				if( !rRegDiff )
				{
					const SwTxtFmtColl *pFmt = pDesc->GetRegisterFmtColl();
					if( pFmt )
					{
						const SvxLineSpacingItem &rSpace = pFmt->GetLineSpacing();
						if( SVX_LINE_SPACE_FIX == rSpace.GetLineSpaceRule() )
						{
							rRegDiff = rSpace.GetLineHeight();
							pDesc->SetRegHeight( rRegDiff );
							pDesc->SetRegAscent( ( 4 * rRegDiff ) / 5 );
						}
						else
						{
							ViewShell *pSh = getRootFrm()->GetCurrShell();
							SwFontAccess aFontAccess( pFmt, pSh );
							SwFont aFnt( *aFontAccess.Get()->GetFont() );

							OutputDevice *pOut = 0;
                            if( !pSh || !pSh->GetViewOptions()->getBrowseMode() ||
								pSh->GetViewOptions()->IsPrtFormat() )
                                pOut = GetTxtNode()->getIDocumentDeviceAccess()->getReferenceDevice( true );

                            if( pSh && !pOut )
								pOut = pSh->GetWin();

							if( !pOut )
								pOut = GetpApp()->GetDefaultDevice();

							MapMode aOldMap( pOut->GetMapMode() );
							pOut->SetMapMode( MapMode( MAP_TWIP ) );

                            aFnt.ChgFnt( pSh, *pOut );
                            rRegDiff = aFnt.GetHeight( pSh, *pOut );
							KSHORT nNettoHeight = rRegDiff;

							switch( rSpace.GetLineSpaceRule() )
							{
								case SVX_LINE_SPACE_AUTO:
								break;
								case SVX_LINE_SPACE_MIN:
								{
									if( rRegDiff < KSHORT( rSpace.GetLineHeight() ) )
										rRegDiff = rSpace.GetLineHeight();
									break;
								}
								default: ASSERT(
								sal_False, ": unknown LineSpaceRule" );
							}
							switch( rSpace.GetInterLineSpaceRule() )
							{
								case SVX_INTER_LINE_SPACE_OFF:
								break;
								case SVX_INTER_LINE_SPACE_PROP:
								{
									long nTmp = rSpace.GetPropLineSpace();
									if( nTmp < 50 )
										nTmp = nTmp ? 50 : 100;
									nTmp *= rRegDiff;
									nTmp /= 100;
									if( !nTmp )
										++nTmp;
									rRegDiff = (KSHORT)nTmp;
									nNettoHeight = rRegDiff;
									break;
								}
								case SVX_INTER_LINE_SPACE_FIX:
								{
									rRegDiff = rRegDiff + rSpace.GetInterLineSpace();
									nNettoHeight = rRegDiff;
									break;
								}
								default: ASSERT( sal_False, ": unknown InterLineSpaceRule" );
							}
							pDesc->SetRegHeight( rRegDiff );
							pDesc->SetRegAscent( rRegDiff - nNettoHeight +
                                                 aFnt.GetAscent( pSh, *pOut ) );
							pOut->SetMapMode( aOldMap );
						}
					}
				}
                const long nTmpDiff = pDesc->GetRegAscent() - rRegDiff;
                if ( bVert )
                    rRegStart -= nTmpDiff;
                else
                    rRegStart += nTmpDiff;
			}
		}
	}
	return ( 0 != rRegDiff );
}

/*************************************************************************
 *              virtual SwHiddenTextPortion::Paint()
 *************************************************************************/

void SwHiddenTextPortion::Paint( const SwTxtPaintInfo & rInf) const
{
    (void)rInf;
#if OSL_DEBUG_LEVEL > 1
    OutputDevice* pOut = (OutputDevice*)rInf.GetOut();
    Color aCol( SwViewOption::GetFieldShadingsColor() );
    Color aOldColor( pOut->GetFillColor() );
    pOut->SetFillColor( aCol );
    Point aPos( rInf.GetPos() );
    aPos.Y() -= 150;
    aPos.X() -= 25;
    SwRect aRect( aPos, Size( 100, 200 ) );
    ((OutputDevice*)pOut)->DrawRect( aRect.SVRect() );
    pOut->SetFillColor( aOldColor );
#endif
}

/*************************************************************************
 *              virtual SwHiddenTextPortion::Format()
 *************************************************************************/

sal_Bool SwHiddenTextPortion::Format( SwTxtFormatInfo &rInf )
{
    Width( 0 );
    rInf.GetTxtFrm()->HideFootnotes( rInf.GetIdx(), rInf.GetIdx() + GetLen() );

    return sal_False;
};

/*************************************************************************
 *              virtual SwControlCharPortion::Paint()
 *************************************************************************/

void SwControlCharPortion::Paint( const SwTxtPaintInfo &rInf ) const
{
    if ( Width() )  // is only set during prepaint mode
    {
        rInf.DrawViewOpt( *this, POR_CONTROLCHAR );

        if ( !rInf.GetOpt().IsPagePreview() &&
             !rInf.GetOpt().IsReadonly() &&
              SwViewOption::IsFieldShadings() &&
              CHAR_ZWNBSP != mcChar )
        {
            SwFont aTmpFont( *rInf.GetFont() );
            aTmpFont.SetEscapement( CHAR_ZWSP == mcChar ? DFLT_ESC_AUTO_SUB : -25 );
            const sal_uInt16 nProp = 40;
            aTmpFont.SetProportion( nProp );  // a smaller font
            SwFontSave aFontSave( rInf, &aTmpFont );

            String aOutString;

            switch ( mcChar )
            {
                case CHAR_ZWSP :
                    aOutString = '/'; break;
//                case CHAR_LRM :
//                    rTxt = sal_Unicode(0x2514); break;
//                case CHAR_RLM :
//                    rTxt = sal_Unicode(0x2518); break;
            }

            if ( !mnHalfCharWidth )
                mnHalfCharWidth = rInf.GetTxtSize( aOutString ).Width() / 2;

            Point aOldPos = rInf.GetPos();
            Point aNewPos( aOldPos );
            aNewPos.X() = aNewPos.X() + ( Width() / 2 ) - mnHalfCharWidth;
            const_cast< SwTxtPaintInfo& >( rInf ).SetPos( aNewPos );

            rInf.DrawText( aOutString, *this );

            const_cast< SwTxtPaintInfo& >( rInf ).SetPos( aOldPos );
        }
    }
}

/*************************************************************************
 *              virtual SwControlCharPortion::Format()
 *************************************************************************/

sal_Bool SwControlCharPortion::Format( SwTxtFormatInfo &rInf )
{
    const SwLinePortion* pRoot = rInf.GetRoot();
    Width( 0 );
    Height( pRoot->Height() );
    SetAscent( pRoot->GetAscent() );

    return sal_False;
}

/*************************************************************************
 *              virtual SwControlCharPortion::GetViewWidth()
 *************************************************************************/

KSHORT SwControlCharPortion::GetViewWidth( const SwTxtSizeInfo& rInf ) const
{
    if( !mnViewWidth )
        mnViewWidth = rInf.GetTxtSize( ' ' ).Width();

    return mnViewWidth;
}
