/**************************************************************
 * 
 * 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 <cmdid.h>
#include <hintids.hxx>
#include <vcl/virdev.hxx>
#include <svx/svdmodel.hxx>
#include <editeng/ulspitem.hxx>
#include <editeng/lrspitem.hxx>
#include <editeng/paperinf.hxx>
#include "editeng/frmdiritem.hxx"
#include <tools/urlobj.hxx>
#include <sfx2/docfile.hxx>
#include <sfx2/printer.hxx>
#include <sfx2/bindings.hxx>
#include <sfx2/dispatch.hxx>
#include <unotools/localedatawrapper.hxx>
#include <com/sun/star/document/PrinterIndependentLayout.hpp>
#include <fmtfsize.hxx>
#include <fmthdft.hxx>
#include <fmtcntnt.hxx>
#include <fmtpdsc.hxx>
#include <ftninfo.hxx>
#include <fesh.hxx>
#include <ndole.hxx>
#include <mdiexp.hxx>
#include <doc.hxx>
#include <IDocumentUndoRedo.hxx>
#include <docary.hxx>
#include <pagefrm.hxx>	//Fuer DelPageDesc
#include <rootfrm.hxx>	//Fuer DelPageDesc
#include <ndtxt.hxx>
#include <frmtool.hxx>
#include <pagedesc.hxx>
#include <poolfmt.hxx>
#include <docsh.hxx>
#include <ndindex.hxx>
#include <ftnidx.hxx>
#include <fmtftn.hxx>
#include <txtftn.hxx>
#include <fntcache.hxx>
#include <viewsh.hxx>
#include <viewopt.hxx>
#include <fldbas.hxx>
#include <swwait.hxx>
#include <GetMetricVal.hxx>
#include <unotools/syslocale.hxx>
#include <statstr.hrc>
#include <switerator.hxx>
#include <hints.hxx>
#include <SwUndoPageDesc.hxx>
#include <pagedeschint.hxx>
#include <tgrditem.hxx>
#include <drawdoc.hxx>

using namespace com::sun::star;

static void lcl_DefaultPageFmt( sal_uInt16 nPoolFmtId,
                                SwFrmFmt &rFmt1,
                                SwFrmFmt &rFmt2 )
{
    // --> FME 2005-01-21 #i41075# Printer on demand
    // This function does not require a printer anymore.
    // The default page size is obtained from the application
    //locale
    // <--

    SwFmtFrmSize aFrmSize( ATT_FIX_SIZE );
    const Size aPhysSize = SvxPaperInfo::GetDefaultPaperSize();
    aFrmSize.SetSize( aPhysSize );

    //Auf Default-Raender vorbereiten.
    //Raender haben eine defaultmaessige Mindestgroesse.
    //wenn der Drucker einen groesseren Rand vorgibt, so
    //ist mir dass auch recht.
    // MIB 06/25/2002, #99397#: The HTML page desc had A4 as page size
    // always. This has been changed to take the page size from the printer.
    // Unfortunately, the margins of the HTML page desc are smaller than
    // the margins used here in general, so one extra case is required.
    // In the long term, this needs to be changed to always keep the
    // margins from the page desc.
    sal_Int32 nMinTop, nMinBottom, nMinLeft, nMinRight;
    if( RES_POOLPAGE_HTML == nPoolFmtId )
    {
        nMinRight = nMinTop = nMinBottom = GetMetricVal( CM_1 );
        nMinLeft = nMinRight * 2;
    }
    else if( MEASURE_METRIC == SvtSysLocale().GetLocaleData().getMeasurementSystemEnum() )
    {
        nMinTop = nMinBottom = nMinLeft = nMinRight = 1134; //2 Zentimeter
    }
    else
    {
        nMinTop = nMinBottom = 1440;    //al la WW: 1Inch
        nMinLeft = nMinRight = 1800;    //          1,25 Inch
    }

    //Raender einstellen.
    SvxLRSpaceItem aLR( RES_LR_SPACE );
    SvxULSpaceItem aUL( RES_UL_SPACE );

    aUL.SetUpper( (sal_uInt16)nMinTop );
    aUL.SetLower( (sal_uInt16)nMinBottom );
    aLR.SetRight( nMinRight );
    aLR.SetLeft( nMinLeft );

    rFmt1.SetFmtAttr( aFrmSize );
    rFmt1.SetFmtAttr( aLR );
    rFmt1.SetFmtAttr( aUL );

    rFmt2.SetFmtAttr( aFrmSize );
    rFmt2.SetFmtAttr( aLR );
    rFmt2.SetFmtAttr( aUL );
}

/*************************************************************************
|*
|*	SwDoc::ChgPageDesc()
|*
|*	Ersterstellung		MA 25. Jan. 93
|*	Letzte Aenderung	MA 01. Mar. 95
|*
|*************************************************************************/

void lcl_DescSetAttr( const SwFrmFmt &rSource, SwFrmFmt &rDest,
						 const sal_Bool bPage = sal_True )
{
/////////////// !!!!!!!!!!!!!!!!
//JP 03.03.99:
// eigentlich sollte hier das Intersect von ItemSet benutzt werden, aber das
// funktioniert nicht richtig, wenn man unterschiedliche WhichRanges hat.
/////////////// !!!!!!!!!!!!!!!!
    //Die interressanten Attribute uebernehmen.
    sal_uInt16 __READONLY_DATA aIdArr[] = {
        RES_FRM_SIZE,                   RES_UL_SPACE,                   // [83..86
        RES_BACKGROUND,                 RES_SHADOW,                     // [99..101
        RES_COL,                        RES_COL,                        // [103
        RES_TEXTGRID,                   RES_TEXTGRID,                   // [109
        RES_FRAMEDIR,                   RES_FRAMEDIR,                   // [114
        RES_HEADER_FOOTER_EAT_SPACING,  RES_HEADER_FOOTER_EAT_SPACING,  // [115
        RES_UNKNOWNATR_CONTAINER,       RES_UNKNOWNATR_CONTAINER,       // [143

        //UUUU take over DrawingLayer FillStyles
        XATTR_FILL_FIRST,               XATTR_FILL_LAST,                // [1014

        0};

	const SfxPoolItem* pItem;
	for( sal_uInt16 n = 0; aIdArr[ n ]; n += 2 )
	{
		for( sal_uInt16 nId = aIdArr[ n ]; nId <= aIdArr[ n+1]; ++nId )
		{
            // bPage == true:
            // All in aIdArr except from RES_HEADER_FOOTER_EAT_SPACING
            // bPage == false:
            // All in aIdArr except from RES_COL and RES_PAPER_BIN:
            bool bExecuteId(true);

            if(bPage)
            {
                // When Page
                switch(nId)
                {
                    // All in aIdArr except from RES_HEADER_FOOTER_EAT_SPACING
                    case RES_HEADER_FOOTER_EAT_SPACING:
                    //UUUU take out SvxBrushItem; it's the result of the fallback
                    // at SwFmt::GetItemState and not really in state SFX_ITEM_SET
                    case RES_BACKGROUND:
                        bExecuteId = false;
                        break;
                    default:
                        break;
                }
            }
            else
            {
                // When not Page
                switch(nId)
                {
                    // When not Page: All in aIdArr except from RES_COL and RES_PAPER_BIN:
                    case RES_COL:
                    case RES_PAPER_BIN:
                        bExecuteId = false;
                        break;
                    default:
                        break;
                }
            }

            if(bExecuteId)
            {
                if(SFX_ITEM_SET == rSource.GetItemState(nId,sal_False,&pItem))
                {
                    rDest.SetFmtAttr(*pItem);
                }
                else
                {
                    rDest.ResetFmtAttr(nId);
                }
            }
        }
    }

	// auch Pool-, Hilfe-Id's uebertragen
	rDest.SetPoolFmtId( rSource.GetPoolFmtId() );
	rDest.SetPoolHelpId( rSource.GetPoolHelpId() );
	rDest.SetPoolHlpFileId( rSource.GetPoolHlpFileId() );
}


void SwDoc::ChgPageDesc( sal_uInt16 i, const SwPageDesc &rChged )
{
	ASSERT( i < aPageDescs.Count(), "PageDescs ueberindiziert." );

	SwPageDesc *pDesc = aPageDescs[i];
	SwRootFrm* pTmpRoot = GetCurrentLayout();//swmod 080219

    if (GetIDocumentUndoRedo().DoesUndo())
    {
        SwUndo *const pUndo(new SwUndoPageDesc(*pDesc, rChged, this));
        GetIDocumentUndoRedo().AppendUndo(pUndo);
    }
    ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());

	//Als erstes wird ggf. gespiegelt.
	if ( rChged.GetUseOn() == nsUseOnPage::PD_MIRROR )
		((SwPageDesc&)rChged).Mirror();
	else
		//sonst Werte aus Master nach Left uebertragen.
		::lcl_DescSetAttr( ((SwPageDesc&)rChged).GetMaster(),
					   ((SwPageDesc&)rChged).GetLeft() );

    //NumType uebernehmen.
	if( rChged.GetNumType().GetNumberingType() != pDesc->GetNumType().GetNumberingType() )
	{
		pDesc->SetNumType( rChged.GetNumType() );
		// JP 30.03.99: Bug 64121 - den Seitennummernfeldern bescheid sagen,
		//		das sich das Num-Format geaendert hat
		GetSysFldType( RES_PAGENUMBERFLD )->UpdateFlds();
		GetSysFldType( RES_REFPAGEGETFLD )->UpdateFlds();

		// Wenn sich die Numerierungsart geaendert hat, koennte es QuoVadis/
		// ErgoSum-Texte geben, die sich auf eine geaenderte Seite beziehen,
		// deshalb werden die Fussnoten invalidiert
		SwFtnIdxs& rFtnIdxs = GetFtnIdxs();
		for( sal_uInt16 nPos = 0; nPos < rFtnIdxs.Count(); ++nPos )
		{
			SwTxtFtn *pTxtFtn = rFtnIdxs[ nPos ];
			const SwFmtFtn &rFtn = pTxtFtn->GetFtn();
			pTxtFtn->SetNumber( rFtn.GetNumber(), &rFtn.GetNumStr());
		}
	}

	//Orientierung uebernehmen
	pDesc->SetLandscape( rChged.GetLandscape() );

    // #i46909# no undo if header or footer changed
    bool bHeaderFooterChanged = false;

	//Header abgleichen.
	const SwFmtHeader &rHead = rChged.GetMaster().GetHeader();
    if (undoGuard.UndoWasEnabled())
    {
        // #i46909# no undo if header or footer changed
		// hat sich an den Nodes etwas veraendert ?
		const SwFmtHeader &rOldHead = pDesc->GetMaster().GetHeader();
        bHeaderFooterChanged |=
            ( rHead.IsActive() != rOldHead.IsActive() ||
              rChged.IsHeaderShared() != pDesc->IsHeaderShared() );
	}
    pDesc->GetMaster().SetFmtAttr( rHead );
	if ( rChged.IsHeaderShared() || !rHead.IsActive() )
	{
		//Left teilt sich den Header mit dem Master.
        pDesc->GetLeft().SetFmtAttr( pDesc->GetMaster().GetHeader() );
	}
	else if ( rHead.IsActive() )
	{	//Left bekommt einen eigenen Header verpasst wenn das Format nicht
		//bereits einen hat.
		//Wenn er bereits einen hat und dieser auf die gleiche Section
		//wie der Rechte zeigt, so muss er einen eigenen bekommen. Der
		//Inhalt wird sinnigerweise kopiert.
		const SwFmtHeader &rLeftHead = pDesc->GetLeft().GetHeader();
		if ( !rLeftHead.IsActive() )
		{
            SwFmtHeader aHead( MakeLayoutFmt( RND_STD_HEADERL, 0 ) );
            pDesc->GetLeft().SetFmtAttr( aHead );
			//Weitere Attribute (Raender, Umrandung...) uebernehmen.
			::lcl_DescSetAttr( *rHead.GetHeaderFmt(), *aHead.GetHeaderFmt(), sal_False);
		}
		else
		{
			const SwFrmFmt *pRight = rHead.GetHeaderFmt();
			const SwFmtCntnt &aRCnt = pRight->GetCntnt();
			const SwFmtCntnt &aLCnt = rLeftHead.GetHeaderFmt()->GetCntnt();
			if( !aLCnt.GetCntntIdx() )
                pDesc->GetLeft().SetFmtAttr( rChged.GetLeft().GetHeader() );
            else if( (*aRCnt.GetCntntIdx()) == (*aLCnt.GetCntntIdx()) )
			{
				SwFrmFmt *pFmt = new SwFrmFmt( GetAttrPool(), "Header",
												GetDfltFrmFmt() );
				::lcl_DescSetAttr( *pRight, *pFmt, sal_False );
				//Der Bereich auf den das rechte Kopfattribut zeigt wird
				//kopiert und der Index auf den StartNode in das linke
				//Kopfattribut gehaengt.
				SwNodeIndex aTmp( GetNodes().GetEndOfAutotext() );
				SwStartNode* pSttNd = GetNodes().MakeEmptySection( aTmp, SwHeaderStartNode );
				SwNodeRange aRange( aRCnt.GetCntntIdx()->GetNode(), 0,
							*aRCnt.GetCntntIdx()->GetNode().EndOfSectionNode() );
				aTmp = *pSttNd->EndOfSectionNode();
				GetNodes()._Copy( aRange, aTmp, sal_False );

                pFmt->SetFmtAttr( SwFmtCntnt( pSttNd ) );
                pDesc->GetLeft().SetFmtAttr( SwFmtHeader( pFmt ) );
			}
			else
				::lcl_DescSetAttr( *pRight,
							   *(SwFrmFmt*)rLeftHead.GetHeaderFmt(), sal_False );

		}
	}
	pDesc->ChgHeaderShare( rChged.IsHeaderShared() );

	//Footer abgleichen.
	const SwFmtFooter &rFoot = rChged.GetMaster().GetFooter();
    if (undoGuard.UndoWasEnabled())
    {
        // #i46909# no undo if header or footer changed
		// hat sich an den Nodes etwas veraendert ?
        const SwFmtFooter &rOldFoot = pDesc->GetMaster().GetFooter();
        bHeaderFooterChanged |=
            ( rFoot.IsActive() != rOldFoot.IsActive() ||
              rChged.IsFooterShared() != pDesc->IsFooterShared() );
    }
    pDesc->GetMaster().SetFmtAttr( rFoot );
	if ( rChged.IsFooterShared() || !rFoot.IsActive() )
		//Left teilt sich den Header mit dem Master.
        pDesc->GetLeft().SetFmtAttr( pDesc->GetMaster().GetFooter() );
	else if ( rFoot.IsActive() )
	{	//Left bekommt einen eigenen Footer verpasst wenn das Format nicht
		//bereits einen hat.
		//Wenn er bereits einen hat und dieser auf die gleiche Section
		//wie der Rechte zeigt, so muss er einen eigenen bekommen. Der
		//Inhalt wird sinnigerweise kopiert.
		const SwFmtFooter &rLeftFoot = pDesc->GetLeft().GetFooter();
		if ( !rLeftFoot.IsActive() )
		{
            SwFmtFooter aFoot( MakeLayoutFmt( RND_STD_FOOTER, 0 ) );
            pDesc->GetLeft().SetFmtAttr( aFoot );
			//Weitere Attribute (Raender, Umrandung...) uebernehmen.
			::lcl_DescSetAttr( *rFoot.GetFooterFmt(), *aFoot.GetFooterFmt(), sal_False);
		}
		else
		{
			const SwFrmFmt *pRight = rFoot.GetFooterFmt();
			const SwFmtCntnt &aRCnt = pRight->GetCntnt();
			const SwFmtCntnt &aLCnt = rLeftFoot.GetFooterFmt()->GetCntnt();
			if( !aLCnt.GetCntntIdx() )
                pDesc->GetLeft().SetFmtAttr( rChged.GetLeft().GetFooter() );
			else if( (*aRCnt.GetCntntIdx()) == (*aLCnt.GetCntntIdx()) )
			{
				SwFrmFmt *pFmt = new SwFrmFmt( GetAttrPool(), "Footer",
												GetDfltFrmFmt() );
				::lcl_DescSetAttr( *pRight, *pFmt, sal_False );
				//Der Bereich auf den das rechte Kopfattribut zeigt wird
				//kopiert und der Index auf den StartNode in das linke
				//Kopfattribut gehaengt.
				SwNodeIndex aTmp( GetNodes().GetEndOfAutotext() );
				SwStartNode* pSttNd = GetNodes().MakeEmptySection( aTmp, SwFooterStartNode );
				SwNodeRange aRange( aRCnt.GetCntntIdx()->GetNode(), 0,
							*aRCnt.GetCntntIdx()->GetNode().EndOfSectionNode() );
				aTmp = *pSttNd->EndOfSectionNode();
				GetNodes()._Copy( aRange, aTmp, sal_False );

                pFmt->SetFmtAttr( SwFmtCntnt( pSttNd ) );
                pDesc->GetLeft().SetFmtAttr( SwFmtFooter( pFmt ) );
			}
			else
				::lcl_DescSetAttr( *pRight,
							   *(SwFrmFmt*)rLeftFoot.GetFooterFmt(), sal_False );
		}
	}
	pDesc->ChgFooterShare( rChged.IsFooterShared() );

	if ( pDesc->GetName() != rChged.GetName() )
		pDesc->SetName( rChged.GetName() );

	// Dadurch wird ein RegisterChange ausgeloest, wenn notwendig
	pDesc->SetRegisterFmtColl( rChged.GetRegisterFmtColl() );

	//Wenn sich das UseOn oder der Follow aendern muessen die
	//Absaetze das erfahren.
	sal_Bool bUseOn  = sal_False;
	sal_Bool bFollow = sal_False;
	if ( pDesc->GetUseOn() != rChged.GetUseOn() )
	{   pDesc->SetUseOn( rChged.GetUseOn() );
		bUseOn = sal_True;
	}
	if ( pDesc->GetFollow() != rChged.GetFollow() )
	{	if ( rChged.GetFollow() == &rChged )
		{	if ( pDesc->GetFollow() != pDesc )
			{	pDesc->SetFollow( pDesc );
				bFollow = sal_True;
			}
		}
		else
		{	pDesc->SetFollow( rChged.pFollow );
			bFollow = sal_True;
		}
	}

	if ( (bUseOn || bFollow) && pTmpRoot)
		//Layot benachrichtigen!
	{				
		std::set<SwRootFrm*> aAllLayouts = GetAllLayouts();
		std::for_each( aAllLayouts.begin(), aAllLayouts.end(),std::mem_fun(&SwRootFrm::AllCheckPageDescs));//swmod 080304
	}

	//Jetzt noch die Seiten-Attribute uebernehmen.
	::lcl_DescSetAttr( rChged.GetMaster(), pDesc->GetMaster() );
	::lcl_DescSetAttr( rChged.GetLeft(), pDesc->GetLeft() );

	//Wenn sich FussnotenInfo veraendert, so werden die Seiten
	//angetriggert.
	if( !(pDesc->GetFtnInfo() == rChged.GetFtnInfo()) )
	{
		pDesc->SetFtnInfo( rChged.GetFtnInfo() );
		SwMsgPoolItem  aInfo( RES_PAGEDESC_FTNINFO );
		{
            pDesc->GetMaster().ModifyBroadcast( &aInfo, 0, TYPE(SwFrm) );
		}
		{
            pDesc->GetLeft().ModifyBroadcast( &aInfo, 0, TYPE(SwFrm) );
		}
	}
	SetModified();

    // #i46909# no undo if header or footer changed
    if( bHeaderFooterChanged )
    {
        GetIDocumentUndoRedo().DelAllUndoObj();
    }

    SfxBindings* pBindings = 
        ( GetDocShell() && GetDocShell()->GetDispatcher() ) ? GetDocShell()->GetDispatcher()->GetBindings() : 0;
    if ( pBindings )
    {
        pBindings->Invalidate( SID_ATTR_PAGE_COLUMN );
        pBindings->Invalidate( SID_ATTR_PAGE );
        pBindings->Invalidate( SID_ATTR_PAGE_SIZE );
        pBindings->Invalidate( SID_ATTR_PAGE_ULSPACE );
        pBindings->Invalidate( SID_ATTR_PAGE_LRSPACE );
    }

}

/*************************************************************************
|*
|*	SwDoc::DelPageDesc()
|*
|* 	Beschreibung		Alle Descriptoren, deren Follow auf den zu loeschenden
|*		zeigen muessen angepasst werden.
|*	Ersterstellung		MA 25. Jan. 93
|*	Letzte Aenderung	JP 04.09.95
|*
|*************************************************************************/

// #i7983#
void SwDoc::PreDelPageDesc(SwPageDesc * pDel)
{
    if (0 == pDel)
        return;

    // mba: test iteration as clients are removed while iteration
    SwPageDescHint aHint( aPageDescs[0] );
    pDel->CallSwClientNotify( aHint );
 
    bool bHasLayout = HasLayout();
    if ( pFtnInfo->DependsOn( pDel ) )
    {
        pFtnInfo->ChgPageDesc( aPageDescs[0] );
        if ( bHasLayout )
        {
            std::set<SwRootFrm*> aAllLayouts = GetAllLayouts();
            std::for_each( aAllLayouts.begin(), aAllLayouts.end(),std::bind2nd(std::mem_fun(&SwRootFrm::CheckFtnPageDescs), false));
        }
    }
    else if ( pEndNoteInfo->DependsOn( pDel ) )
    {
        pEndNoteInfo->ChgPageDesc( aPageDescs[0] );
        if ( bHasLayout )
        {
            std::set<SwRootFrm*> aAllLayouts = GetAllLayouts();
            std::for_each( aAllLayouts.begin(), aAllLayouts.end(),std::bind2nd(std::mem_fun(&SwRootFrm::CheckFtnPageDescs), true));
        }
    }    

    for ( sal_uInt16 j = 0; j < aPageDescs.Count(); ++j )
    {
        if ( aPageDescs[j]->GetFollow() == pDel )
        {
            aPageDescs[j]->SetFollow( 0 );
            if( bHasLayout )
            {
                std::set<SwRootFrm*> aAllLayouts = GetAllLayouts();
                std::for_each( aAllLayouts.begin(), aAllLayouts.end(),std::mem_fun(&SwRootFrm::AllCheckPageDescs));//swmod 080228
            }
        }
    }
}

// #116530#
void SwDoc::BroadcastStyleOperation(String rName, SfxStyleFamily eFamily,
                                    sal_uInt16 nOp)
{
    if (pDocShell)
    {
        SfxStyleSheetBasePool * pPool = pDocShell->GetStyleSheetPool();

        if (pPool)
        {
            pPool->SetSearchMask(eFamily, SFXSTYLEBIT_ALL );
            SfxStyleSheetBase * pBase = pPool->Find(rName);

            if (pBase != NULL)
                pPool->Broadcast(SfxStyleSheetHint( nOp, *pBase ));
        }
    }
}

void SwDoc::DelPageDesc( sal_uInt16 i, sal_Bool bBroadcast )
{
	ASSERT( i < aPageDescs.Count(), "PageDescs ueberindiziert." );
	ASSERT( i != 0, "Default Pagedesc loeschen is nicht." );
	if ( i == 0 )
		return;

	SwPageDesc *pDel = aPageDescs[i];

    // -> #116530#
    if (bBroadcast)
        BroadcastStyleOperation(pDel->GetName(), SFX_STYLE_FAMILY_PAGE,
                                SFX_STYLESHEET_ERASED);
    // <- #116530#

    if (GetIDocumentUndoRedo().DoesUndo())
    {
        SwUndo *const pUndo(new SwUndoPageDescDelete(*pDel, this));
        GetIDocumentUndoRedo().AppendUndo(pUndo);
    }

    PreDelPageDesc(pDel); // #i7983#

	aPageDescs.Remove( i );
	delete pDel;
	SetModified();
}



/*************************************************************************
|*
|*	SwDoc::MakePageDesc()
|*
|*	Ersterstellung		MA 25. Jan. 93
|*	Letzte Aenderung	MA 20. Aug. 93
|*
|*************************************************************************/

sal_uInt16 SwDoc::MakePageDesc( const String &rName, const SwPageDesc *pCpy,
                            sal_Bool bRegardLanguage, sal_Bool bBroadcast) // #116530#
{
	SwPageDesc *pNew;
	if( pCpy )
	{
		pNew = new SwPageDesc( *pCpy );
		pNew->SetName( rName );
		if( rName != pCpy->GetName() )
		{
			pNew->SetPoolFmtId( USHRT_MAX );
			pNew->SetPoolHelpId( USHRT_MAX );
			pNew->SetPoolHlpFileId( UCHAR_MAX );
		}
	}
	else
	{
		pNew = new SwPageDesc( rName, GetDfltFrmFmt(), this );
		//Default-Seitenformat einstellen.
        lcl_DefaultPageFmt( USHRT_MAX, pNew->GetMaster(), pNew->GetLeft() );
		
		SvxFrameDirection aFrameDirection = bRegardLanguage ?
            GetDefaultFrameDirection(GetAppLanguage())
            : FRMDIR_HORI_LEFT_TOP;

        pNew->GetMaster().SetFmtAttr( SvxFrameDirectionItem(aFrameDirection, RES_FRAMEDIR) );
        pNew->GetLeft().SetFmtAttr( SvxFrameDirectionItem(aFrameDirection, RES_FRAMEDIR) );
	}
	aPageDescs.Insert( pNew, aPageDescs.Count() );

    // -> #116530#
    if (bBroadcast)
        BroadcastStyleOperation(rName, SFX_STYLE_FAMILY_PAGE,
                                SFX_STYLESHEET_CREATED);
    // <- #116530#

    if (GetIDocumentUndoRedo().DoesUndo())
    {
        // #116530#
        GetIDocumentUndoRedo().AppendUndo(new SwUndoPageDescCreate(pNew, this));
    }

	SetModified();
	return (aPageDescs.Count()-1);
}

SwPageDesc* SwDoc::FindPageDescByName( const String& rName, sal_uInt16* pPos ) const
{
	SwPageDesc* pRet = 0;
	if( pPos ) *pPos = USHRT_MAX;

	for( sal_uInt16 n = 0, nEnd = aPageDescs.Count(); n < nEnd; ++n )
		if( aPageDescs[ n ]->GetName() == rName )
		{
			pRet = aPageDescs[ n ];
			if( pPos )
				*pPos = n;
			break;
		}
	return pRet;
}

/******************************************************************************
 *  Methode     :   void SwDoc::PrtDataChanged()
 *	Beschreibung:
 *	Erstellt	:	OK 27.10.94 10:20
 *	Aenderung	:	MA 26. Mar. 98
 ******************************************************************************/

void SwDoc::PrtDataChanged()
{
//!!!!!!!! Bei Aenderungen hier bitte ggf. InJobSetup im Sw3io mitpflegen

    // --> FME 2005-01-21 #i41075#
    ASSERT( get(IDocumentSettingAccess::USE_VIRTUAL_DEVICE) ||
            0 != getPrinter( sal_False ), "PrtDataChanged will be called recursive!" )
    // <--
	SwRootFrm* pTmpRoot = GetCurrentLayout();//swmod 080219
	SwWait *pWait = 0;
	sal_Bool bEndAction = sal_False;

	if( GetDocShell() )
		GetDocShell()->UpdateFontList();

	sal_Bool bDraw = sal_True;
	if ( pTmpRoot )
	{
		ViewShell *pSh = GetCurrentViewShell();
        if( !pSh->GetViewOptions()->getBrowseMode() ||
            pSh->GetViewOptions()->IsPrtFormat() )
		{
			if ( GetDocShell() )
				pWait = new SwWait( *GetDocShell(), true );

			pTmpRoot->StartAllAction();
			bEndAction = sal_True;

			bDraw = sal_False;
			if( pDrawModel )
            {
                pDrawModel->SetAddExtLeading( get(IDocumentSettingAccess::ADD_EXT_LEADING) );
                pDrawModel->SetRefDevice( getReferenceDevice( false ) );
            }

			pFntCache->Flush();
			
			std::set<SwRootFrm*> aAllLayouts = GetAllLayouts();
			std::for_each( aAllLayouts.begin(), aAllLayouts.end(),std::bind2nd(std::mem_fun(&SwRootFrm::InvalidateAllCntnt), INV_SIZE));//swmod 080304

			if ( pSh )
			{
				do
                {
                    pSh->InitPrt( pPrt );
					pSh = (ViewShell*)pSh->GetNext();
                }
                while ( pSh != GetCurrentViewShell() );
			}

		}
	}	//swmod 080218
    if ( bDraw && pDrawModel )
    {
        const sal_Bool bTmpAddExtLeading = get(IDocumentSettingAccess::ADD_EXT_LEADING);
        if ( bTmpAddExtLeading != pDrawModel->IsAddExtLeading() )
            pDrawModel->SetAddExtLeading( bTmpAddExtLeading );

        OutputDevice* pOutDev = getReferenceDevice( false );
        if ( pOutDev != pDrawModel->GetRefDevice() )
            pDrawModel->SetRefDevice( pOutDev );
    }

	PrtOLENotify( sal_True );

	if ( bEndAction )
		pTmpRoot->EndAllAction();	//swmod 080218
	delete pWait;
}

//Zur Laufzeit sammeln wir die GlobalNames der Server, die keine
//Benachrichtigung zu Druckerwechseln wuenschen. Dadurch sparen wir
//das Laden vieler Objekte (gluecklicherweise werden obendrein alle
//Fremdobjekte unter einer ID abgebuildet). Init und DeInit vom Array
//ist in init.cxx zu finden.
extern SvPtrarr *pGlobalOLEExcludeList;

void SwDoc::PrtOLENotify( sal_Bool bAll )
{
	SwFEShell *pShell = 0;
	if ( GetCurrentViewShell() )
	{
		ViewShell *pSh = GetCurrentViewShell();
		if ( !pSh->ISA(SwFEShell) )
			do
			{	pSh = (ViewShell*)pSh->GetNext();
			} while ( !pSh->ISA(SwFEShell) &&
					  pSh != GetCurrentViewShell() );

		if ( pSh->ISA(SwFEShell) )
			pShell = (SwFEShell*)pSh;
	}	//swmod 071107//swmod 071225
	if ( !pShell )
	{
		//Das hat ohne Shell und damit ohne Client keinen Sinn, weil nur darueber
		//die Kommunikation bezueglich der Groessenaenderung implementiert ist.
		//Da wir keine Shell haben, merken wir uns diesen unguenstigen
		//Zustand am Dokument, dies wird dann beim Erzeugen der ersten Shell
		//nachgeholt.
		mbOLEPrtNotifyPending = sal_True;
		if ( bAll )
			mbAllOLENotify = sal_True;
	}
	else
	{
		if ( mbAllOLENotify )
			bAll = sal_True;

		mbOLEPrtNotifyPending = mbAllOLENotify = sal_False;

		SwOLENodes *pNodes = SwCntntNode::CreateOLENodesArray( *GetDfltGrfFmtColl(), !bAll );
		if ( pNodes )
		{
			::StartProgress( STR_STATSTR_SWGPRTOLENOTIFY,
							 0, pNodes->Count(), GetDocShell());
			GetCurrentLayout()->StartAllAction();	//swmod 080218

			for( sal_uInt16 i = 0; i < pNodes->Count(); ++i )
			{
				::SetProgressState( i, GetDocShell() );

				SwOLENode* pOLENd = (*pNodes)[i];
				pOLENd->SetOLESizeInvalid( sal_False );

				//Ersteinmal die Infos laden und festellen ob das Teil nicht
				//schon in der Exclude-Liste steht
				SvGlobalName aName;

                svt::EmbeddedObjectRef& xObj = pOLENd->GetOLEObj().GetObject();
                if ( xObj.is() )
                    aName = SvGlobalName( xObj->getClassID() );
                else  //Noch nicht geladen
				{
                        // TODO/LATER: retrieve ClassID of an unloaded object
                        // aName = ????
                }

				sal_Bool bFound = sal_False;
				for ( sal_uInt16 j = 0;
					  j < pGlobalOLEExcludeList->Count() && !bFound;
					  ++j )
				{
					bFound = *(SvGlobalName*)(*pGlobalOLEExcludeList)[j] ==
									aName;
				}
				if ( bFound )
					continue;

				//Kennen wir nicht, also muss das Objekt geladen werden.
				//Wenn es keine Benachrichtigung wuenscht
                if ( xObj.is() )
				{
                    //TODO/LATER: needs MiscStatus for ResizeOnPrinterChange
                    /*
					if ( SVOBJ_MISCSTATUS_RESIZEONPRINTERCHANGE & xRef->GetMiscStatus())
					{
						if ( pOLENd->GetFrm() )
						{
                            xObj->OnDocumentPrinterChanged( pPrt );
                            pShell->CalcAndSetScale( xObj );//Client erzeugen lassen.
						}
						else
							pOLENd->SetOLESizeInvalid( sal_True );
                    }
                    else */
						pGlobalOLEExcludeList->Insert(
                                new SvGlobalName( aName ),
								pGlobalOLEExcludeList->Count() );
				}
			}
			delete pNodes;
			GetCurrentLayout()->EndAllAction();	//swmod 080218
			::EndProgress( GetDocShell() );
		}
	}
}

IMPL_LINK( SwDoc, DoUpdateModifiedOLE, Timer *, )
{
	SwFEShell* pSh = (SwFEShell*)GetEditShell();
	if( pSh )
	{
		mbOLEPrtNotifyPending = mbAllOLENotify = sal_False;

		SwOLENodes *pNodes = SwCntntNode::CreateOLENodesArray( *GetDfltGrfFmtColl(), true );
		if( pNodes )
		{
			::StartProgress( STR_STATSTR_SWGPRTOLENOTIFY,
							 0, pNodes->Count(), GetDocShell());
			GetCurrentLayout()->StartAllAction();	//swmod 080218
			SwMsgPoolItem aMsgHint( RES_UPDATE_ATTR );

			for( sal_uInt16 i = 0; i < pNodes->Count(); ++i )
			{
				::SetProgressState( i, GetDocShell() );

				SwOLENode* pOLENd = (*pNodes)[i];
				pOLENd->SetOLESizeInvalid( sal_False );

				//Kennen wir nicht, also muss das Objekt geladen werden.
				//Wenn es keine Benachrichtigung wuenscht
                if( pOLENd->GetOLEObj().GetOleRef().is() ) //Kaputt?
				{
                    //TODO/LATER: needs MiscStatus for ResizeOnPrinterChange
                    /*
					if( SVOBJ_MISCSTATUS_RESIZEONPRINTERCHANGE &
							xRef->GetMiscStatus() )
					{
						if( pOLENd->GetFrm() )
						{
							xRef->OnDocumentPrinterChanged( pPrt );
							pSh->CalcAndSetScale( xRef );//Client erzeugen lassen.
						}
						else
							pOLENd->SetOLESizeInvalid( sal_True );
                    }*/
					// repaint it
					pOLENd->ModifyNotification( &aMsgHint, &aMsgHint );
				}
			}
			GetCurrentLayout()->EndAllAction();	//swmod 080218
			::EndProgress( GetDocShell() );
            delete pNodes;
		}
	}
	return 0;
}

sal_Bool SwDoc::FindPageDesc( const String & rName, sal_uInt16 * pFound)
{
    sal_Bool bResult = sal_False;
    sal_uInt16 nI;
    for (nI = 0; nI < aPageDescs.Count(); nI++)
    {
        if (aPageDescs[nI]->GetName() == rName)
        {
            *pFound = nI;
            bResult = sal_True;
            break;
        }
    }

    return bResult;
}

SwPageDesc * SwDoc::GetPageDesc( const String & rName )
{
    SwPageDesc * aResult = NULL;

    sal_uInt16 nI;

    if (FindPageDesc(rName, &nI))
        aResult = aPageDescs[nI];

    return aResult;
}

void SwDoc::DelPageDesc( const String & rName, sal_Bool bBroadcast ) // #116530#
{
    sal_uInt16 nI;

    if (FindPageDesc(rName, &nI))
        DelPageDesc(nI, bBroadcast); // #116530#
}

void SwDoc::ChgPageDesc( const String & rName, const SwPageDesc & rDesc)
{
    sal_uInt16 nI;

    if (FindPageDesc(rName, &nI))
        ChgPageDesc(nI, rDesc);
}

/*
 * The HTML import cannot resist changing the page descriptions, I don't
 * know why. This function is meant to check the page descriptors for invalid
 * values.
 */
void SwDoc::CheckDefaultPageFmt()
{
    for ( sal_uInt16 i = 0; i < GetPageDescCnt(); ++i )
    {
        SwPageDesc& rDesc = _GetPageDesc( i );

        SwFrmFmt& rMaster = rDesc.GetMaster();
        SwFrmFmt& rLeft   = rDesc.GetLeft();

        const SwFmtFrmSize& rMasterSize  = rMaster.GetFrmSize();
        const SwFmtFrmSize& rLeftSize    = rLeft.GetFrmSize();

        const bool bSetSize = LONG_MAX == rMasterSize.GetWidth() ||
                              LONG_MAX == rMasterSize.GetHeight() ||
                              LONG_MAX == rLeftSize.GetWidth() ||
                              LONG_MAX == rLeftSize.GetHeight();

        if ( bSetSize )
            lcl_DefaultPageFmt( rDesc.GetPoolFmtId(), rDesc.GetMaster(), rDesc.GetLeft() );
    }
}

void SwDoc::SetDefaultPageMode(bool bSquaredPageMode)
{
    if( !bSquaredPageMode == !IsSquaredPageMode() )
		return;

	const SwTextGridItem& rGrid =
					(const SwTextGridItem&)GetDefault( RES_TEXTGRID );
	SwTextGridItem aNewGrid = rGrid;
	aNewGrid.SetSquaredMode(bSquaredPageMode);
	aNewGrid.Init();
	SetDefault(aNewGrid);

    for ( sal_uInt16 i = 0; i < GetPageDescCnt(); ++i )
    {
        SwPageDesc& rDesc = _GetPageDesc( i );

        SwFrmFmt& rMaster = rDesc.GetMaster();
		SwFrmFmt& rLeft = rDesc.GetLeft();

        SwTextGridItem aGrid((SwTextGridItem&)rMaster.GetFmtAttr(RES_TEXTGRID));
        aGrid.SwitchPaperMode( bSquaredPageMode );
        rMaster.SetFmtAttr(aGrid);
        rLeft.SetFmtAttr(aGrid);
    }
}

sal_Bool SwDoc::IsSquaredPageMode() const
{
	const SwTextGridItem& rGrid =
						(const SwTextGridItem&)GetDefault( RES_TEXTGRID );
	return rGrid.IsSquaredMode();
}