/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   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 .
 */

#include <cmdid.h>
#include <svx/svdmodel.hxx>
#include <editeng/ulspitem.hxx>
#include <editeng/paperinf.hxx>
#include <editeng/frmdiritem.hxx>
#include <sfx2/bindings.hxx>
#include <sfx2/dispatch.hxx>
#include <sfx2/printer.hxx>
#include <fmtfsize.hxx>
#include <fmthdft.hxx>
#include <fmtcntnt.hxx>
#include <ftninfo.hxx>
#include <fesh.hxx>
#include <ndole.hxx>
#include <mdiexp.hxx>
#include <doc.hxx>
#include <IDocumentUndoRedo.hxx>
#include <IDocumentFieldsAccess.hxx>
#include <DocumentContentOperationsManager.hxx>
#include <IDocumentState.hxx>
#include <IDocumentLayoutAccess.hxx>
#include <docary.hxx>
#include <rootfrm.hxx>
#include <frmtool.hxx>
#include <poolfmt.hxx>
#include <docsh.hxx>
#include <ftnidx.hxx>
#include <fmtftn.hxx>
#include <txtftn.hxx>
#include <fntcache.hxx>
#include <viewopt.hxx>
#include <fldbas.hxx>
#include <swwait.hxx>
#include <GetMetricVal.hxx>
#include <statstr.hrc>
#include <hints.hxx>
#include <SwUndoPageDesc.hxx>
#include <pagedeschint.hxx>
#include <tgrditem.hxx>
#include <unotools/configmgr.hxx>
#include <unotools/syslocale.hxx>

using namespace com::sun::star;

static void lcl_DefaultPageFormat( sal_uInt16 nPoolFormatId,
                                SwFrameFormat &rFormat1,
                                SwFrameFormat &rFormat2,
                                SwFrameFormat &rFormat3,
                                SwFrameFormat &rFormat4)
{
    // --> #i41075# Printer on demand
    // This function does not require a printer anymore.
    // The default page size is obtained from the application
    //locale

    SwFormatFrameSize aFrameSize( ATT_FIX_SIZE );
    const Size aPhysSize = SvxPaperInfo::GetDefaultPaperSize();
    aFrameSize.SetSize( aPhysSize );

    // Prepare for default margins.
    // Margins have a default minimum size.
    // If the printer forces a larger margins, that's ok too.
    // 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 == nPoolFormatId )
    {
        nMinRight = nMinTop = nMinBottom = GetMetricVal( CM_1 );
        nMinLeft = nMinRight * 2;
    }
    else if (!utl::ConfigManager::IsAvoidConfig() && MEASURE_METRIC == SvtSysLocale().GetLocaleData().getMeasurementSystemEnum() )
    {
        nMinTop = nMinBottom = nMinLeft = nMinRight = 1134; // 2 centimetres
    }
    else
    {
        nMinTop = nMinBottom = 1440;    // as in MS Word: 1 Inch
        nMinLeft = nMinRight = 1800;    //                1,25 Inch
    }

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

    rFormat1.SetFormatAttr( aFrameSize );
    rFormat1.SetFormatAttr( aLR );
    rFormat1.SetFormatAttr( aUL );

    rFormat2.SetFormatAttr( aFrameSize );
    rFormat2.SetFormatAttr( aLR );
    rFormat2.SetFormatAttr( aUL );

    rFormat3.SetFormatAttr( aFrameSize );
    rFormat3.SetFormatAttr( aLR );
    rFormat3.SetFormatAttr( aUL );

    rFormat4.SetFormatAttr( aFrameSize );
    rFormat4.SetFormatAttr( aLR );
    rFormat4.SetFormatAttr( aUL );
}

static void lcl_DescSetAttr( const SwFrameFormat &rSource, SwFrameFormat &rDest,
                         const bool bPage = true )
{
    // We should actually use ItemSet's Intersect here, but that doesn't work
    // correctly if we have different WhichRanges.

    // Take over the attributes which are of interest.
    sal_uInt16 const 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 )
        {
            // #i45539#
            // 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 SwFormat::GetItemState and not really in state SfxItemState::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 (SfxItemState::SET == rSource.GetItemState(nId, false, &pItem))
                {
                    rDest.SetFormatAttr(*pItem);
                }
                else
                {
                    rDest.ResetFormatAttr(nId);
                }
            }
        }
    }

    // Transmit pool and help IDs too
    rDest.SetPoolFormatId( rSource.GetPoolFormatId() );
    rDest.SetPoolHelpId( rSource.GetPoolHelpId() );
    rDest.SetPoolHlpFileId( rSource.GetPoolHlpFileId() );
}

namespace
{
    SwFrameFormat& getFrameFormat(SwPageDesc &rDesc, bool bLeft, bool bFirst)
    {
        if (bFirst)
        {
            if (bLeft)
                return rDesc.GetFirstLeft();
            return rDesc.GetFirstMaster();
        }
        return rDesc.GetLeft();
    }

    const SwFrameFormat& getConstFrameFormat(const SwPageDesc &rDesc, bool bLeft, bool bFirst)
    {
        return getFrameFormat(const_cast<SwPageDesc&>(rDesc), bLeft, bFirst);
    }
}

void SwDoc::CopyMasterHeader(const SwPageDesc &rChged, const SwFormatHeader &rHead, SwPageDesc &rDesc, bool bLeft, bool bFirst)
{
    assert(bLeft || bFirst);
    SwFrameFormat& rDescFrameFormat = getFrameFormat(rDesc, bLeft, bFirst);
    if (bFirst && bLeft)
    {
        // special case: always shared with something
        rDescFrameFormat.SetFormatAttr( rChged.IsFirstShared()
                ? rDesc.GetLeft().GetHeader()
                : rDesc.GetFirstMaster().GetHeader());
    }
    else if ((bFirst ? rChged.IsFirstShared() : rChged.IsHeaderShared())
         || !rHead.IsActive())
    {
        // Left or first shares the header with the Master.
        rDescFrameFormat.SetFormatAttr( rDesc.GetMaster().GetHeader() );
    }
    else if ( rHead.IsActive() )
    {   // Left or first gets its own header if the Format doesn't already have one.
        // If it already has one and it points to the same Section as the
        // Right one, it needs to get an own Header.
        // The content is evidently copied.
        const SwFormatHeader &rFormatHead = rDescFrameFormat.GetHeader();
        if ( !rFormatHead.IsActive() )
        {
            SwFormatHeader aHead( getIDocumentLayoutAccess().MakeLayoutFormat( RND_STD_HEADERL, nullptr ) );
            rDescFrameFormat.SetFormatAttr( aHead );
            // take over additional attributes (margins, borders ...)
            ::lcl_DescSetAttr( *rHead.GetHeaderFormat(), *aHead.GetHeaderFormat(), false);
        }
        else
        {
            const SwFrameFormat *pRight = rHead.GetHeaderFormat();
            const SwFormatContent &aRCnt = pRight->GetContent();
            const SwFormatContent &aCnt = rFormatHead.GetHeaderFormat()->GetContent();

            if (!aCnt.GetContentIdx())
            {
                const SwFrameFormat& rChgedFrameFormat = getConstFrameFormat(rChged, bLeft, bFirst);
                rDescFrameFormat.SetFormatAttr( rChgedFrameFormat.GetHeader() );
            }
            else if ((*aRCnt.GetContentIdx() == *aCnt.GetContentIdx()) ||
                // The ContentIdx is _always_ different when called from
                // SwDocStyleSheet::SetItemSet, because it deep-copies the
                // PageDesc.  So check if it was previously shared.
                 ((bFirst) ? rDesc.IsFirstShared() : rDesc.IsHeaderShared()))
            {
                SwFrameFormat *pFormat = new SwFrameFormat( GetAttrPool(),
                        (bFirst) ? "First header" : "Left header",
                                                GetDfltFrameFormat() );
                ::lcl_DescSetAttr( *pRight, *pFormat, false );
                // The section which the right header attribute is pointing
                // is copied, and the Index to the StartNode is set to
                // the left or first header attribute.
                SwNodeIndex aTmp( GetNodes().GetEndOfAutotext() );
                SwStartNode* pSttNd = SwNodes::MakeEmptySection( aTmp, SwHeaderStartNode );
                SwNodeRange aRange( aRCnt.GetContentIdx()->GetNode(), 0,
                            *aRCnt.GetContentIdx()->GetNode().EndOfSectionNode() );
                aTmp = *pSttNd->EndOfSectionNode();
                GetNodes()._Copy( aRange, aTmp, false );
                aTmp = *pSttNd;
                GetDocumentContentOperationsManager().CopyFlyInFlyImpl(aRange, 0, aTmp);

                pFormat->SetFormatAttr( SwFormatContent( pSttNd ) );
                rDescFrameFormat.SetFormatAttr( SwFormatHeader( pFormat ) );
            }
            else
                ::lcl_DescSetAttr( *pRight,
                               *const_cast<SwFrameFormat*>(rFormatHead.GetHeaderFormat()), false );
        }
    }
}

void SwDoc::CopyMasterFooter(const SwPageDesc &rChged, const SwFormatFooter &rFoot, SwPageDesc &rDesc, bool bLeft, bool bFirst)
{
    assert(bLeft || bFirst);
    SwFrameFormat& rDescFrameFormat = getFrameFormat(rDesc, bLeft, bFirst);
    if (bFirst && bLeft)
    {
        // special case: always shared with something
        rDescFrameFormat.SetFormatAttr( rChged.IsFirstShared()
                ? rDesc.GetLeft().GetFooter()
                : rDesc.GetFirstMaster().GetFooter());
    }
    else if ((bFirst ? rChged.IsFirstShared() : rChged.IsFooterShared())
        || !rFoot.IsActive())
    {
        // Left or first shares the Header with the Master.
        rDescFrameFormat.SetFormatAttr( rDesc.GetMaster().GetFooter() );
    }
    else if ( rFoot.IsActive() )
    {   // Left or first gets its own Footer if the Format does not already have one.
        // If the Format already has a Footer and it points to the same section as the Right one,
        // it needs to get an own one.
        // The content is evidently copied.
        const SwFormatFooter &rFormatFoot = rDescFrameFormat.GetFooter();
        if ( !rFormatFoot.IsActive() )
        {
            SwFormatFooter aFoot( getIDocumentLayoutAccess().MakeLayoutFormat( RND_STD_FOOTER, nullptr ) );
            rDescFrameFormat.SetFormatAttr( aFoot );
            // Take over additional attributes (margins, borders ...).
            ::lcl_DescSetAttr( *rFoot.GetFooterFormat(), *aFoot.GetFooterFormat(), false);
        }
        else
        {
            const SwFrameFormat *pRight = rFoot.GetFooterFormat();
            const SwFormatContent &aRCnt = pRight->GetContent();
            const SwFormatContent &aLCnt = rFormatFoot.GetFooterFormat()->GetContent();
            if( !aLCnt.GetContentIdx() )
            {
                const SwFrameFormat& rChgedFrameFormat = getConstFrameFormat(rChged, bLeft, bFirst);
                rDescFrameFormat.SetFormatAttr( rChgedFrameFormat.GetFooter() );
            }
            else if ((*aRCnt.GetContentIdx() == *aLCnt.GetContentIdx()) ||
                // The ContentIdx is _always_ different when called from
                // SwDocStyleSheet::SetItemSet, because it deep-copies the
                // PageDesc.  So check if it was previously shared.
                 ((bFirst) ? rDesc.IsFirstShared() : rDesc.IsFooterShared()))
            {
                SwFrameFormat *pFormat = new SwFrameFormat( GetAttrPool(),
                        (bFirst) ? "First footer" : "Left footer",
                                                GetDfltFrameFormat() );
                ::lcl_DescSetAttr( *pRight, *pFormat, false );
                // The section to which the right footer attribute is pointing
                // is copied, and the Index to the StartNode is set to
                // the left footer attribute.
                SwNodeIndex aTmp( GetNodes().GetEndOfAutotext() );
                SwStartNode* pSttNd = SwNodes::MakeEmptySection( aTmp, SwFooterStartNode );
                SwNodeRange aRange( aRCnt.GetContentIdx()->GetNode(), 0,
                            *aRCnt.GetContentIdx()->GetNode().EndOfSectionNode() );
                aTmp = *pSttNd->EndOfSectionNode();
                GetNodes()._Copy( aRange, aTmp, false );
                aTmp = *pSttNd;
                GetDocumentContentOperationsManager().CopyFlyInFlyImpl(aRange, 0, aTmp);

                pFormat->SetFormatAttr( SwFormatContent( pSttNd ) );
                rDescFrameFormat.SetFormatAttr( SwFormatFooter( pFormat ) );
            }
            else
                ::lcl_DescSetAttr( *pRight,
                               *const_cast<SwFrameFormat*>(rFormatFoot.GetFooterFormat()), false );
        }
    }
}

void SwDoc::ChgPageDesc( size_t i, const SwPageDesc &rChged )
{
    OSL_ENSURE(i < m_PageDescs.size(), "PageDescs is out of range.");

    SwPageDesc& rDesc = *m_PageDescs[i];
    SwRootFrame* pTmpRoot = getIDocumentLayoutAccess().GetCurrentLayout();

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

    // Mirror at first if needed.
    if ( rChged.GetUseOn() == nsUseOnPage::PD_MIRROR )
        const_cast<SwPageDesc&>(rChged).Mirror();
    else
    {
        // Or else transfer values from Master to Left
        ::lcl_DescSetAttr(rChged.GetMaster(),
                   const_cast<SwPageDesc&>(rChged).GetLeft());
    }
    ::lcl_DescSetAttr(rChged.GetMaster(),
                   const_cast<SwPageDesc&>(rChged).GetFirstMaster());
    ::lcl_DescSetAttr(rChged.GetLeft(),
                   const_cast<SwPageDesc&>(rChged).GetFirstLeft());

    // Take over NumType.
    if( rChged.GetNumType().GetNumberingType() != rDesc.GetNumType().GetNumberingType() )
    {
        rDesc.SetNumType( rChged.GetNumType() );
        // Notify page number fields that NumFormat has changed
        getIDocumentFieldsAccess().GetSysFieldType( RES_PAGENUMBERFLD )->UpdateFields();
        getIDocumentFieldsAccess().GetSysFieldType( RES_REFPAGEGETFLD )->UpdateFields();

        // If the numbering scheme has changed we could have QuoVadis/ErgoSum texts
        // that refer to a changed page, so we invalidate foot notes.
        SwFootnoteIdxs& rFootnoteIdxs = GetFootnoteIdxs();
        for( SwFootnoteIdxs::size_type nPos = 0; nPos < rFootnoteIdxs.size(); ++nPos )
        {
            SwTextFootnote *pTextFootnote = rFootnoteIdxs[ nPos ];
            const SwFormatFootnote &rFootnote = pTextFootnote->GetFootnote();
            pTextFootnote->SetNumber(rFootnote.GetNumber(), rFootnote.GetNumStr());
        }
    }

    // Take over orientation
    rDesc.SetLandscape( rChged.GetLandscape() );

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

    // Synch header.
    const SwFormatHeader &rHead = rChged.GetMaster().GetHeader();
    if (undoGuard.UndoWasEnabled())
    {
        // #i46909# no undo if header or footer changed
        // Did something change in the nodes?
        const SwFormatHeader &rOldHead = rDesc.GetMaster().GetHeader();
        bHeaderFooterChanged |=
            ( rHead.IsActive() != rOldHead.IsActive() ||
              rChged.IsHeaderShared() != rDesc.IsHeaderShared() ||
              rChged.IsFirstShared() != rDesc.IsFirstShared() );
    }
    rDesc.GetMaster().SetFormatAttr( rHead );
    CopyMasterHeader(rChged, rHead, rDesc, true, false); // Copy left header
    CopyMasterHeader(rChged, rHead, rDesc, false, true); // Copy first master
    CopyMasterHeader(rChged, rHead, rDesc, true, true);  // Copy first left
    rDesc.ChgHeaderShare( rChged.IsHeaderShared() );

    // Synch Footer.
    const SwFormatFooter &rFoot = rChged.GetMaster().GetFooter();
    if (undoGuard.UndoWasEnabled())
    {
        // #i46909# no undo if header or footer changed
        // Did something change in the Nodes?
        const SwFormatFooter &rOldFoot = rDesc.GetMaster().GetFooter();
        bHeaderFooterChanged |=
            ( rFoot.IsActive() != rOldFoot.IsActive() ||
              rChged.IsFooterShared() != rDesc.IsFooterShared() );
    }
    rDesc.GetMaster().SetFormatAttr( rFoot );
    CopyMasterFooter(rChged, rFoot, rDesc, true, false); // Copy left footer
    CopyMasterFooter(rChged, rFoot, rDesc, false, true); // Copy first master
    CopyMasterFooter(rChged, rFoot, rDesc, true, true);  // Copy first left
    rDesc.ChgFooterShare( rChged.IsFooterShared() );
    // there is just one first shared flag for both header and footer?
    rDesc.ChgFirstShare( rChged.IsFirstShared() );

    if ( rDesc.GetName() != rChged.GetName() )
        rDesc.SetName( rChged.GetName() );

    // A RegisterChange is triggered, if necessary
    rDesc.SetRegisterFormatColl( rChged.GetRegisterFormatColl() );

    // If UseOn or the Follow change, the paragraphs need to know about it.
    bool bUseOn  = false;
    bool bFollow = false;
    if (rDesc.GetUseOn() != rChged.GetUseOn())
    {
        rDesc.SetUseOn( rChged.GetUseOn() );
        bUseOn = true;
    }
    if (rDesc.GetFollow() != rChged.GetFollow())
    {
        if (rChged.GetFollow() == &rChged)
        {
            if (rDesc.GetFollow() != &rDesc)
            {
                rDesc.SetFollow( &rDesc );
                bFollow = true;
            }
        }
        else
        {
            rDesc.SetFollow( rChged.m_pFollow );
            bFollow = true;
        }
    }

    if ( (bUseOn || bFollow) && pTmpRoot)
        // Inform layout!
    {
        for( auto aLayout : GetAllLayouts() )
            aLayout->AllCheckPageDescs();
    }

    // Take over the page attributes.
    ::lcl_DescSetAttr( rChged.GetMaster(), rDesc.GetMaster() );
    ::lcl_DescSetAttr( rChged.GetLeft(), rDesc.GetLeft() );
    ::lcl_DescSetAttr( rChged.GetFirstMaster(), rDesc.GetFirstMaster() );
    ::lcl_DescSetAttr( rChged.GetFirstLeft(), rDesc.GetFirstLeft() );

    // If the FootnoteInfo changes, the pages are triggered.
    if( !(rDesc.GetFootnoteInfo() == rChged.GetFootnoteInfo()) )
    {
        rDesc.SetFootnoteInfo( rChged.GetFootnoteInfo() );
        sw::PageFootnoteHint aHint;
        rDesc.GetMaster().CallSwClientNotify(aHint);
        rDesc.GetLeft().CallSwClientNotify(aHint);
        rDesc.GetFirstMaster().CallSwClientNotify(aHint);
        rDesc.GetFirstLeft().CallSwClientNotify(aHint);
    }
    getIDocumentState().SetModified();

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

    SfxBindings* pBindings =
        ( GetDocShell() && GetDocShell()->GetDispatcher() ) ? GetDocShell()->GetDispatcher()->GetBindings() : nullptr;
    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 );
    }

    //h/f of first-left page must not be unique but same as first master or left
    assert((rDesc.IsFirstShared())
        ? rDesc.GetFirstLeft().GetHeader().GetHeaderFormat() == rDesc.GetLeft().GetHeader().GetHeaderFormat()
        : rDesc.GetFirstLeft().GetHeader().GetHeaderFormat() == rDesc.GetFirstMaster().GetHeader().GetHeaderFormat());
    assert((rDesc.IsFirstShared())
        ? rDesc.GetFirstLeft().GetFooter().GetFooterFormat() == rDesc.GetLeft().GetFooter().GetFooterFormat()
        : rDesc.GetFirstLeft().GetFooter().GetFooterFormat() == rDesc.GetFirstMaster().GetFooter().GetFooterFormat());
}

/// All descriptors whose Follow point to the to-be-deleted have to be adapted.
// #i7983#
void SwDoc::PreDelPageDesc(SwPageDesc * pDel)
{
    if (nullptr == pDel)
        return;

    // mba: test iteration as clients are removed while iteration
    SwPageDescHint aHint(m_PageDescs[0].get());
    pDel->CallSwClientNotify( aHint );

    bool bHasLayout = getIDocumentLayoutAccess().HasLayout();
    if ( mpFootnoteInfo->DependsOn( pDel ) )
    {
        mpFootnoteInfo->ChgPageDesc(m_PageDescs[0].get());
        if ( bHasLayout )
        {
            for( auto aLayout : GetAllLayouts() )
                aLayout->CheckFootnotePageDescs(false);
        }
    }
    else if ( mpEndNoteInfo->DependsOn( pDel ) )
    {
        mpEndNoteInfo->ChgPageDesc(m_PageDescs[0].get());
        if ( bHasLayout )
        {
            for( auto aLayout : GetAllLayouts() )
                aLayout->CheckFootnotePageDescs(true);
        }
    }

    for (SwPageDescs::size_type j = 0; j < m_PageDescs.size(); ++j)
    {
        if (m_PageDescs[j]->GetFollow() == pDel)
        {
            m_PageDescs[j]->SetFollow(nullptr);
            if( bHasLayout )
            {
                for( auto aLayout : GetAllLayouts() )
                    aLayout->AllCheckPageDescs();
            }
        }
    }
}

void SwDoc::BroadcastStyleOperation(const OUString& rName, SfxStyleFamily eFamily,
                                    sal_uInt16 nOp)
{
    if (mpDocShell)
    {
        SfxStyleSheetBasePool * pPool = mpDocShell->GetStyleSheetPool();

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

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

void SwDoc::DelPageDesc( size_t i, bool bBroadcast )
{
    OSL_ENSURE(i < m_PageDescs.size(), "PageDescs is out of range.");
    OSL_ENSURE( i != 0, "You cannot delete the default Pagedesc.");
    if ( i == 0 )
        return;

    SwPageDesc &rDel = *m_PageDescs[i];

    if (bBroadcast)
        BroadcastStyleOperation(rDel.GetName(), SFX_STYLE_FAMILY_PAGE,
                                SfxStyleSheetHintId::ERASED);

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

    PreDelPageDesc(&rDel); // #i7983#

    m_PageDescs.erase(m_PageDescs.begin() + i);
    getIDocumentState().SetModified();
}

SwPageDesc* SwDoc::MakePageDesc(const OUString &rName, const SwPageDesc *pCpy,
                            bool bRegardLanguage, bool bBroadcast)
{
    SwPageDesc *pNew;
    if( pCpy )
    {
        pNew = new SwPageDesc( *pCpy );
        pNew->SetName( rName );
        if( rName != pCpy->GetName() )
        {
            pNew->SetPoolFormatId( USHRT_MAX );
            pNew->SetPoolHelpId( USHRT_MAX );
            pNew->SetPoolHlpFileId( UCHAR_MAX );
        }
    }
    else
    {
        pNew = new SwPageDesc( rName, GetDfltFrameFormat(), this );
        // Set the default page format.
        lcl_DefaultPageFormat( USHRT_MAX, pNew->GetMaster(), pNew->GetLeft(), pNew->GetFirstMaster(), pNew->GetFirstLeft() );

        SvxFrameDirection aFrameDirection = bRegardLanguage ?
            GetDefaultFrameDirection(GetAppLanguage())
            : FRMDIR_HORI_LEFT_TOP;

        pNew->GetMaster().SetFormatAttr( SvxFrameDirectionItem(aFrameDirection, RES_FRAMEDIR) );
        pNew->GetLeft().SetFormatAttr( SvxFrameDirectionItem(aFrameDirection, RES_FRAMEDIR) );
        pNew->GetFirstMaster().SetFormatAttr( SvxFrameDirectionItem(aFrameDirection, RES_FRAMEDIR) );
        pNew->GetFirstLeft().SetFormatAttr( SvxFrameDirectionItem(aFrameDirection, RES_FRAMEDIR) );
    }
    m_PageDescs.push_back(std::unique_ptr<SwPageDesc>(pNew));

    if (bBroadcast)
        BroadcastStyleOperation(rName, SFX_STYLE_FAMILY_PAGE,
                                SfxStyleSheetHintId::CREATED);

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

    getIDocumentState().SetModified();
    return pNew;
}

// We collect the GlobalNames of the servers at runtime, who don't want to be notified
// about printer changes. Thereby saving loading a lot of objects (luckily all foreign
// objects are mapped to one ID).
// Initialisation and deinitialisation can be found in init.cxx
extern std::vector<SvGlobalName*> *pGlobalOLEExcludeList;

void SwDoc::PrtOLENotify( bool bAll )
{
    SwFEShell *pShell = nullptr;
    {
        SwViewShell *pSh = getIDocumentLayoutAccess().GetCurrentViewShell();
        if ( pSh )
        {
            for(SwViewShell& rShell : pSh->GetRingContainer())
            {
                if(dynamic_cast<const SwFEShell*>( &rShell) !=  nullptr)
                {
                    pShell = static_cast<SwFEShell*>(&rShell);
                    break;
                }
            }
        }
    }
    if ( !pShell )
    {
        // This doesn't make sense without a Shell and thus without a client, because
        // the communication about size changes is implemented by these components.
        // Because we don't have a Shell we remember this unfortunate situation
        // in the document,
        // which is made up for later on when creating the first Shell.
        mbOLEPrtNotifyPending = true;
        if ( bAll )
            mbAllOLENotify = true;
    }
    else
    {
        if ( mbAllOLENotify )
            bAll = true;

        mbOLEPrtNotifyPending = mbAllOLENotify = false;

        SwOLENodes *pNodes = SwContentNode::CreateOLENodesArray( *GetDfltGrfFormatColl(), !bAll );
        if ( pNodes )
        {
            ::StartProgress( STR_STATSTR_SWGPRTOLENOTIFY,
                             0, pNodes->size(), GetDocShell());
            getIDocumentLayoutAccess().GetCurrentLayout()->StartAllAction();

            for( SwOLENodes::size_type i = 0; i < pNodes->size(); ++i )
            {
                ::SetProgressState( i, GetDocShell() );

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

                // At first load the Infos and see if it's not already in the exclude list.
                SvGlobalName aName;

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

                bool bFound = false;
                for ( std::vector<SvGlobalName*>::size_type j = 0;
                      j < pGlobalOLEExcludeList->size() && !bFound;
                      ++j )
                {
                    bFound = *(*pGlobalOLEExcludeList)[j] == aName;
                }
                if ( bFound )
                    continue;

                // We don't know it, so the object has to be loaded.
                // If it doesn't want to be informed
                if ( xObj.is() )
                {
                        pGlobalOLEExcludeList->push_back( new SvGlobalName( aName ) );
                }
            }
            delete pNodes;
            getIDocumentLayoutAccess().GetCurrentLayout()->EndAllAction();
            ::EndProgress( GetDocShell() );
        }
    }
}

IMPL_LINK_NOARG_TYPED( SwDoc, DoUpdateModifiedOLE, Idle *, void )
{
    SwFEShell* pSh = static_cast<SwFEShell*>(GetEditShell());
    if( pSh )
    {
        mbOLEPrtNotifyPending = mbAllOLENotify = false;

        SwOLENodes *pNodes = SwContentNode::CreateOLENodesArray( *GetDfltGrfFormatColl(), true );
        if( pNodes )
        {
            ::StartProgress( STR_STATSTR_SWGPRTOLENOTIFY,
                             0, pNodes->size(), GetDocShell());
            getIDocumentLayoutAccess().GetCurrentLayout()->StartAllAction();
            SwMsgPoolItem aMsgHint( RES_UPDATE_ATTR );

            for( SwOLENodes::size_type i = 0; i < pNodes->size(); ++i )
            {
                ::SetProgressState( i, GetDocShell() );

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

                // We don't know it, so the object has to be loaded.
                // If it doesn't want to be informed
                if( pOLENd->GetOLEObj().GetOleRef().is() ) // Broken?
                {
                    pOLENd->ModifyNotification( &aMsgHint, &aMsgHint );
                }
            }
            getIDocumentLayoutAccess().GetCurrentLayout()->EndAllAction();
            ::EndProgress( GetDocShell() );
            delete pNodes;
        }
    }
}

struct CompareSwPageDescName {
    explicit CompareSwPageDescName(const OUString &rName) : mName(rName) {}
    bool operator () (const std::unique_ptr<SwPageDesc>& other) const { return other->GetName() == mName; }
    const OUString &mName;
};

template <class UnaryPredicate>
static SwPageDesc* lcl_FindPageDesc( SwPageDescs *pPageDescs,
                                     size_t *pPos, UnaryPredicate pred )
{
    SwPageDescs::iterator it = std::find_if(
        pPageDescs->begin(), pPageDescs->end(), pred);
    SwPageDesc* res = nullptr;
    if( it != pPageDescs->end() )
    {
        res = it->get();
        if( pPos )
            *pPos = std::distance( pPageDescs->begin(), it );
    }
    else if( pPos )
        *pPos = SIZE_MAX;
    return res;
}

SwPageDesc* SwDoc::FindPageDesc( const OUString & rName, size_t* pPos ) const
{
    return lcl_FindPageDesc<CompareSwPageDescName>(
        const_cast <SwPageDescs *>( &m_PageDescs ), pPos,
        CompareSwPageDescName(rName) );
}

struct CompareSwPageDescToPtr {
    explicit CompareSwPageDescToPtr(const SwPageDesc* ptr) : mPtr(ptr) {}
    bool operator () (const std::unique_ptr<SwPageDesc>& other) const { return other.get() == mPtr; }
    const SwPageDesc *mPtr;
};

bool SwDoc::ContainsPageDesc( const SwPageDesc *pDesc, size_t* pPos )
{
    if (pDesc == nullptr)
        return false;
    SwPageDesc *res = lcl_FindPageDesc<CompareSwPageDescToPtr>(
        &m_PageDescs, pPos,
        CompareSwPageDescToPtr(pDesc) );
    return res != nullptr;
}

void SwDoc::DelPageDesc( const OUString & rName, bool bBroadcast )
{
    size_t nI;

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

void SwDoc::ChgPageDesc( const OUString & rName, const SwPageDesc & rDesc)
{
    size_t 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::CheckDefaultPageFormat()
{
    for ( size_t i = 0; i < GetPageDescCnt(); ++i )
    {
        SwPageDesc& rDesc = GetPageDesc( i );

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

        const SwFormatFrameSize& rMasterSize  = rMaster.GetFrameSize();
        const SwFormatFrameSize& rLeftSize    = rLeft.GetFrameSize();

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

        if ( bSetSize )
            lcl_DefaultPageFormat( rDesc.GetPoolFormatId(), rDesc.GetMaster(), rDesc.GetLeft(), rDesc.GetFirstMaster(), rDesc.GetFirstLeft() );
    }
}

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

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

    for ( size_t i = 0; i < GetPageDescCnt(); ++i )
    {
        SwPageDesc& rDesc = GetPageDesc( i );

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

        SwTextGridItem aGrid(static_cast<const SwTextGridItem&>(rMaster.GetFormatAttr(RES_TEXTGRID)));
        aGrid.SwitchPaperMode( bSquaredPageMode );
        rMaster.SetFormatAttr(aGrid);
        rLeft.SetFormatAttr(aGrid);
    }
}

bool SwDoc::IsSquaredPageMode() const
{
    const SwTextGridItem& rGrid =
                        static_cast<const SwTextGridItem&>(GetDefault( RES_TEXTGRID ));
    return rGrid.IsSquaredMode();
}

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