/* -*- 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 <memory>
#include <cassert>

#include <svx/svdpage.hxx>

// HACK
#include <sot/storage.hxx>
#include <comphelper/classids.hxx>
#include <svx/svdview.hxx>
#include <string.h>
#include <vcl/svapp.hxx>

#include <tools/diagnose_ex.h>
#include <tools/helpers.hxx>

#include <svx/svdetc.hxx>
#include <svx/svdobj.hxx>
#include <svx/svdogrp.hxx>
#include <svx/svdograf.hxx>
#include <svx/svdoedge.hxx>
#include <svx/svdoole2.hxx>
#include <svx/svditer.hxx>
#include <svx/svdmodel.hxx>
#include <svx/svdlayer.hxx>
#include <svx/svdotext.hxx>
#include <svx/svdpagv.hxx>
#include <svx/svdundo.hxx>
#include <svx/fmglob.hxx>

#include <svx/fmdpage.hxx>

#include <sfx2/objsh.hxx>
#include <sdr/contact/viewcontactofsdrpage.hxx>
#include <svx/sdr/contact/viewobjectcontact.hxx>
#include <svx/sdr/contact/displayinfo.hxx>
#include <algorithm>
#include <svl/hint.hxx>
#include <rtl/strbuf.hxx>
#include <libxml/xmlwriter.h>

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

class SdrObjList::WeakSdrObjectContainerType
    : public ::std::vector<SdrObjectWeakRef>
{
public:
    explicit WeakSdrObjectContainerType (const sal_Int32 nInitialSize)
        : ::std::vector<SdrObjectWeakRef>(nInitialSize) {};
};


static const sal_Int32 InitialObjectContainerCapacity (64);


SdrObjList::SdrObjList(SdrModel* pNewModel, SdrPage* pNewPage):
    maList(),
    mxNavigationOrder(),
    mbIsNavigationOrderDirty(false)
{
    maList.reserve(InitialObjectContainerCapacity);
    pModel=pNewModel;
    pPage=pNewPage;
    pUpList=nullptr;
    bObjOrdNumsDirty=false;
    bRectsDirty=false;
    pOwnerObj=nullptr;
    eListKind=SdrObjListKind::Unknown;
}

SdrObjList::SdrObjList():
    maList(),
    mxNavigationOrder(),
    mbIsNavigationOrderDirty(false)
{
    maList.reserve(InitialObjectContainerCapacity);
    pModel=nullptr;
    pPage=nullptr;
    pUpList=nullptr;
    bObjOrdNumsDirty=false;
    bRectsDirty=false;
    pOwnerObj=nullptr;
    eListKind=SdrObjListKind::Unknown;
}

SdrObjList::~SdrObjList()
{

    // To avoid that the Clear() method will broadcast changes when in destruction
    // which would call virtual method (not allowed in destructor), the model is set
    // to NULL here.
    pModel = nullptr;

    Clear(); // delete contents of container
}

SdrObjList* SdrObjList::Clone() const
{
    SdrObjList* const pObjList = new SdrObjList();
    pObjList->lateInit(*this);
    return pObjList;
}

void SdrObjList::lateInit(const SdrObjList& rSrcList)
{
    // this function is only supposed to be called once, right after construction
    assert(maList.empty());

    eListKind=rSrcList.eListKind;
    CopyObjects(rSrcList);
}

void SdrObjList::CopyObjects(const SdrObjList& rSrcList)
{
    Clear();
    bObjOrdNumsDirty=false;
    bRectsDirty     =false;
    size_t nCloneErrCnt = 0;
    const size_t nCount = rSrcList.GetObjCount();
    for (size_t no=0; no<nCount; ++no) {
        SdrObject* pSO=rSrcList.GetObj(no);

        SdrObject* pDO = pSO->Clone();

        if (pDO!=nullptr) {
            pDO->SetModel(pModel);
            pDO->SetPage(pPage);
            NbcInsertObject(pDO, SAL_MAX_SIZE);
        } else {
            nCloneErrCnt++;
        }
    }

    // and now for the Connectors
    // The new objects would be shown in the rSrcList
    // and then the object connections are made.
    // Similar implementation are setup as the following:
    //    void SdrObjList::CopyObjects(const SdrObjList& rSrcList)
    //    SdrModel* SdrExchangeView::GetMarkedObjModel() const
    //    BOOL SdrExchangeView::Paste(const SdrModel& rMod,...)
    //    void SdrEditView::CopyMarked()
    if (nCloneErrCnt==0) {
        for (size_t no=0; no<nCount; ++no) {
            const SdrObject* pSrcOb=rSrcList.GetObj(no);
            const SdrEdgeObj* pSrcEdge=dynamic_cast<const SdrEdgeObj*>( pSrcOb );
            if (pSrcEdge!=nullptr) {
                SdrObject* pSrcNode1=pSrcEdge->GetConnectedNode(true);
                SdrObject* pSrcNode2=pSrcEdge->GetConnectedNode(false);
                if (pSrcNode1!=nullptr && pSrcNode1->GetObjList()!=pSrcEdge->GetObjList()) pSrcNode1=nullptr; // can't do this
                if (pSrcNode2!=nullptr && pSrcNode2->GetObjList()!=pSrcEdge->GetObjList()) pSrcNode2=nullptr; // across all lists (yet)
                if (pSrcNode1!=nullptr || pSrcNode2!=nullptr) {
                    SdrObject* pEdgeObjTmp=GetObj(no);
                    SdrEdgeObj* pDstEdge=dynamic_cast<SdrEdgeObj*>( pEdgeObjTmp );
                    if (pDstEdge!=nullptr) {
                        if (pSrcNode1!=nullptr) {
                            sal_uIntPtr nDstNode1=pSrcNode1->GetOrdNum();
                            SdrObject* pDstNode1=GetObj(nDstNode1);
                            if (pDstNode1!=nullptr) { // else we get an error!
                                pDstEdge->ConnectToNode(true,pDstNode1);
                            } else {
                                OSL_FAIL("SdrObjList::operator=(): pDstNode1==NULL!");
                            }
                        }
                        if (pSrcNode2!=nullptr) {
                            sal_uIntPtr nDstNode2=pSrcNode2->GetOrdNum();
                            SdrObject* pDstNode2=GetObj(nDstNode2);
                            if (pDstNode2!=nullptr) { // else the node was probably not selected
                                pDstEdge->ConnectToNode(false,pDstNode2);
                            } else {
                                OSL_FAIL("SdrObjList::operator=(): pDstNode2==NULL!");
                            }
                        }
                    } else {
                        OSL_FAIL("SdrObjList::operator=(): pDstEdge==NULL!");
                    }
                }
            }
        }
    } else {
#ifdef DBG_UTIL
        OStringBuffer aStr("SdrObjList::operator=(): Error when cloning ");

        if(nCloneErrCnt == 1)
        {
            aStr.append("a drawing object.");
        }
        else
        {
            aStr.append(static_cast<sal_Int32>(nCloneErrCnt));
            aStr.append(" drawing objects.");
        }

        aStr.append(" Not copying connectors.");

        OSL_FAIL(aStr.getStr());
#endif
    }
}

void SdrObjList::Clear()
{
    bool bObjectsRemoved(false);

    while( ! maList.empty())
    {
        // remove last object from list
        SdrObject* pObj = maList.back();
        RemoveObjectFromContainer(maList.size()-1);

        // flushViewObjectContacts() is done since SdrObject::Free is not guaranteed
        // to delete the object and thus refresh visualisations
        pObj->GetViewContact().flushViewObjectContacts();

        bObjectsRemoved = true;

        // sent remove hint (after removal, see RemoveObject())
        if(pModel)
        {
            SdrHint aHint(SdrHintKind::ObjectRemoved, *pObj, pPage);
            pModel->Broadcast(aHint);
        }

        // delete the object itself
        SdrObject::Free( pObj );
    }

    if(pModel && bObjectsRemoved)
    {
        pModel->SetChanged();
    }
}

SdrPage* SdrObjList::GetPage() const
{
    return pPage;
}

void SdrObjList::SetPage(SdrPage* pNewPage)
{
    if (pPage!=pNewPage) {
        pPage=pNewPage;
        const size_t nCount = GetObjCount();
        for (size_t no=0; no<nCount; ++no) {
            SdrObject* pObj=GetObj(no);
            pObj->SetPage(pPage);
        }
    }
}

SdrModel* SdrObjList::GetModel() const
{
    return pModel;
}

void SdrObjList::SetModel(SdrModel* pNewModel)
{
    if (pModel!=pNewModel) {
        pModel=pNewModel;
        const size_t nCount = GetObjCount();
        for (size_t i=0; i<nCount; ++i) {
            SdrObject* pObj=GetObj(i);
            pObj->SetModel(pModel);
        }
    }
}

void SdrObjList::RecalcObjOrdNums()
{
    const size_t nCount = GetObjCount();
    for (size_t no=0; no<nCount; ++no) {
        SdrObject* pObj=GetObj(no);
        pObj->SetOrdNum(no);
    }
    bObjOrdNumsDirty=false;
}

void SdrObjList::RecalcRects()
{
    aOutRect=tools::Rectangle();
    aSnapRect=aOutRect;
    const size_t nCount = GetObjCount();
    for (size_t i=0; i<nCount; ++i) {
        SdrObject* pObj=GetObj(i);
        if (i==0) {
            aOutRect=pObj->GetCurrentBoundRect();
            aSnapRect=pObj->GetSnapRect();
        } else {
            aOutRect.Union(pObj->GetCurrentBoundRect());
            aSnapRect.Union(pObj->GetSnapRect());
        }
    }
}

void SdrObjList::SetRectsDirty()
{
    bRectsDirty=true;
    if (pUpList!=nullptr) pUpList->SetRectsDirty();
}

void SdrObjList::impChildInserted(SdrObject const & rChild)
{
    sdr::contact::ViewContact* pParent = rChild.GetViewContact().GetParentContact();

    if(pParent)
    {
        pParent->ActionChildInserted(rChild.GetViewContact());
    }
}

void SdrObjList::NbcInsertObject(SdrObject* pObj, size_t nPos)
{
    DBG_ASSERT(pObj!=nullptr,"SdrObjList::NbcInsertObject(NULL)");
    if (pObj==nullptr)
        return;

    DBG_ASSERT(!pObj->IsInserted(),"The object already has the status Inserted.");
    const size_t nCount = GetObjCount();
    if (nPos>nCount) nPos=nCount;
    InsertObjectIntoContainer(*pObj,nPos);

    if (nPos<nCount) bObjOrdNumsDirty=true;
    pObj->SetOrdNum(nPos);
    pObj->SetObjList(this);
    pObj->SetPage(pPage);

    // Inform the parent about change to allow invalidations at
    // evtl. existing parent visualisations
    impChildInserted(*pObj);

    if (!bRectsDirty) {
        aOutRect.Union(pObj->GetCurrentBoundRect());
        aSnapRect.Union(pObj->GetSnapRect());
    }
    pObj->SetInserted(true); // calls the UserCall (among others)
}

void SdrObjList::InsertObject(SdrObject* pObj, size_t nPos)
{
    DBG_ASSERT(pObj!=nullptr,"SdrObjList::InsertObject(NULL)");

    if(pObj)
    {
        // if anchor is used, reset it before grouping
        if(GetOwnerObj())
        {
            const Point& rAnchorPos = pObj->GetAnchorPos();
            if(rAnchorPos.X() || rAnchorPos.Y())
                pObj->NbcSetAnchorPos(Point());
        }

        // do insert to new group
        NbcInsertObject(pObj, nPos);

        // In case the object is inserted into a group and doesn't overlap with
        // the group's other members, it needs an own repaint.
        if(pOwnerObj)
        {
            // only repaint here
            pOwnerObj->ActionChanged();
        }

        if(pModel)
        {
            // TODO: We need a different broadcast here!
            // Repaint from object number ... (heads-up: GroupObj)
            if(pObj->GetPage())
            {
                SdrHint aHint(SdrHintKind::ObjectInserted, *pObj);
                pModel->Broadcast(aHint);
            }

            pModel->SetChanged();
        }
    }
}

SdrObject* SdrObjList::NbcRemoveObject(size_t nObjNum)
{
    if (nObjNum >= maList.size())
    {
        OSL_ASSERT(nObjNum<maList.size());
        return nullptr;
    }

    const size_t nCount = GetObjCount();
    SdrObject* pObj=maList[nObjNum];
    RemoveObjectFromContainer(nObjNum);

    DBG_ASSERT(pObj!=nullptr,"Could not find object to remove.");
    if (pObj!=nullptr) {
        // flushViewObjectContacts() clears the VOC's and those invalidate
        pObj->GetViewContact().flushViewObjectContacts();

        DBG_ASSERT(pObj->IsInserted(),"The object does not have the status Inserted.");
        pObj->SetInserted(false); // calls UserCall, among other
        pObj->SetObjList(nullptr);
        pObj->SetPage(nullptr);
        if (!bObjOrdNumsDirty) { // optimizing for the case that the last object has to be removed
            if (nObjNum+1!=nCount) {
                bObjOrdNumsDirty=true;
            }
        }
        SetRectsDirty();
    }
    return pObj;
}

SdrObject* SdrObjList::RemoveObject(size_t nObjNum)
{
    if (nObjNum >= maList.size())
    {
        OSL_ASSERT(nObjNum<maList.size());
        return nullptr;
    }

    const size_t nCount = GetObjCount();
    SdrObject* pObj=maList[nObjNum];
    RemoveObjectFromContainer(nObjNum);

    DBG_ASSERT(pObj!=nullptr,"Object to remove not found.");
    if(pObj)
    {
        // flushViewObjectContacts() clears the VOC's and those invalidate
        pObj->GetViewContact().flushViewObjectContacts();

        DBG_ASSERT(pObj->IsInserted(),"The object does not have the status Inserted.");
        if (pModel!=nullptr) {
            // TODO: We need a different broadcast here.
            if (pObj->GetPage()!=nullptr) {
                SdrHint aHint(SdrHintKind::ObjectRemoved, *pObj);
                pModel->Broadcast(aHint);
            }
            pModel->SetChanged();
        }
        pObj->SetInserted(false); // calls, among other things, the UserCall
        pObj->SetObjList(nullptr);
        pObj->SetPage(nullptr);
        if (!bObjOrdNumsDirty) { // optimization for the case that the last object is removed
            if (nObjNum+1!=nCount) {
                bObjOrdNumsDirty=true;
            }
        }
        SetRectsDirty();

        if(pOwnerObj && !GetObjCount())
        {
            // empty group created; it needs to be repainted since it's
            // visualization changes
            pOwnerObj->ActionChanged();
        }
    }
    return pObj;
}

SdrObject* SdrObjList::NbcReplaceObject(SdrObject* pNewObj, size_t nObjNum)
{
    if (nObjNum >= maList.size() || pNewObj == nullptr)
    {
        OSL_ASSERT(nObjNum<maList.size());
        OSL_ASSERT(pNewObj!=nullptr);
        return nullptr;
    }

    SdrObject* pObj=maList[nObjNum];
    DBG_ASSERT(pObj!=nullptr,"SdrObjList::ReplaceObject: Could not find object to remove.");
    if (pObj!=nullptr) {
        DBG_ASSERT(pObj->IsInserted(),"SdrObjList::ReplaceObject: the object does not have status Inserted.");
        pObj->SetInserted(false);
        pObj->SetObjList(nullptr);
        pObj->SetPage(nullptr);
        ReplaceObjectInContainer(*pNewObj,nObjNum);

        // flushViewObjectContacts() clears the VOC's and those invalidate
        pObj->GetViewContact().flushViewObjectContacts();

        pNewObj->SetOrdNum(nObjNum);
        pNewObj->SetObjList(this);
        pNewObj->SetPage(pPage);

        // Inform the parent about change to allow invalidations at
        // evtl. existing parent visualisations
        impChildInserted(*pNewObj);

        pNewObj->SetInserted(true);
        SetRectsDirty();
    }
    return pObj;
}

SdrObject* SdrObjList::ReplaceObject(SdrObject* pNewObj, size_t nObjNum)
{
    if (nObjNum >= maList.size())
    {
        OSL_ASSERT(nObjNum<maList.size());
        return nullptr;
    }
    if (pNewObj == nullptr)
    {
        OSL_ASSERT(pNewObj!=nullptr);
        return nullptr;
    }

    SdrObject* pObj=maList[nObjNum];
    DBG_ASSERT(pObj!=nullptr,"SdrObjList::ReplaceObject: Could not find object to remove.");
    if (pObj!=nullptr) {
        DBG_ASSERT(pObj->IsInserted(),"SdrObjList::ReplaceObject: the object does not have status Inserted.");
        if (pModel!=nullptr) {
            // TODO: We need a different broadcast here.
            if (pObj->GetPage()!=nullptr) {
                SdrHint aHint(SdrHintKind::ObjectRemoved, *pObj);
                pModel->Broadcast(aHint);
            }
        }
        pObj->SetInserted(false);
        pObj->SetObjList(nullptr);
        pObj->SetPage(nullptr);
        ReplaceObjectInContainer(*pNewObj,nObjNum);

        // flushViewObjectContacts() clears the VOC's and those invalidate
        pObj->GetViewContact().flushViewObjectContacts();

        pNewObj->SetOrdNum(nObjNum);
        pNewObj->SetObjList(this);
        pNewObj->SetPage(pPage);

        // Inform the parent about change to allow invalidations at
        // evtl. existing parent visualisations
        impChildInserted(*pNewObj);

        pNewObj->SetInserted(true);
        if (pModel!=nullptr) {
            // TODO: We need a different broadcast here.
            if (pNewObj->GetPage()!=nullptr) {
                SdrHint aHint(SdrHintKind::ObjectInserted, *pNewObj);
                pModel->Broadcast(aHint);
            }
            pModel->SetChanged();
        }
        SetRectsDirty();
    }
    return pObj;
}

SdrObject* SdrObjList::SetObjectOrdNum(size_t nOldObjNum, size_t nNewObjNum)
{
    if (nOldObjNum >= maList.size() || nNewObjNum >= maList.size())
    {
        OSL_ASSERT(nOldObjNum<maList.size());
        OSL_ASSERT(nNewObjNum<maList.size());
        return nullptr;
    }

    SdrObject* pObj=maList[nOldObjNum];
    if (nOldObjNum==nNewObjNum) return pObj;
    DBG_ASSERT(pObj!=nullptr,"SdrObjList::SetObjectOrdNum: Object not found.");
    if (pObj!=nullptr) {
        DBG_ASSERT(pObj->IsInserted(),"SdrObjList::SetObjectOrdNum: the object does not have status Inserted.");
        RemoveObjectFromContainer(nOldObjNum);
        InsertObjectIntoContainer(*pObj,nNewObjNum);

        // No need to delete visualisation data since same object
        // gets inserted again. Also a single ActionChanged is enough
        pObj->ActionChanged();

        pObj->SetOrdNum(nNewObjNum);
        bObjOrdNumsDirty=true;
        if (pModel!=nullptr)
        {
            // TODO: We need a different broadcast here.
            if (pObj->GetPage()!=nullptr)
                pModel->Broadcast(SdrHint(SdrHintKind::ObjectChange, *pObj));
            pModel->SetChanged();
        }
    }
    return pObj;
}

const tools::Rectangle& SdrObjList::GetAllObjSnapRect() const
{
    if (bRectsDirty) {
        const_cast<SdrObjList*>(this)->RecalcRects();
        const_cast<SdrObjList*>(this)->bRectsDirty=false;
    }
    return aSnapRect;
}

const tools::Rectangle& SdrObjList::GetAllObjBoundRect() const
{
    // #i106183# for deep group hierarchies like in chart2, the invalidates
    // through the hierarchy are not correct; use a 2nd hint for the needed
    // recalculation. Future versions will have no bool flag at all, but
    // just aOutRect in empty state to represent an invalid state, thus
    // it's a step in the right direction.
    if (bRectsDirty || aOutRect.IsEmpty())
    {
        const_cast<SdrObjList*>(this)->RecalcRects();
        const_cast<SdrObjList*>(this)->bRectsDirty=false;
    }
    return aOutRect;
}

void SdrObjList::NbcReformatAllTextObjects()
{
    size_t nCount=GetObjCount();
    size_t nNum=0;

    while (nNum<nCount)
    {
        SdrObject* pObj = GetObj(nNum);

        pObj->NbcReformatText();
        nCount=GetObjCount();             // ReformatText may delete an object
        nNum++;
    }

}

void SdrObjList::ReformatAllTextObjects()
{
    NbcReformatAllTextObjects();
}

/** steps over all available objects and reformats all
    edge objects that are connected to other objects so that
    they may reposition themselves.
*/
void SdrObjList::ReformatAllEdgeObjects()
{
    // #i120437# go over whole hierarchy, not only over object level null (seen from grouping)
    SdrObjListIter aIter(*this, SdrIterMode::DeepNoGroups);

    while(aIter.IsMore())
    {
        SdrEdgeObj* pSdrEdgeObj = dynamic_cast< SdrEdgeObj* >(aIter.Next());

        if(pSdrEdgeObj)
        {
            pSdrEdgeObj->Reformat();
        }
    }
}

void SdrObjList::BurnInStyleSheetAttributes()
{
    for(size_t a = 0; a < GetObjCount(); ++a)
    {
        GetObj(a)->BurnInStyleSheetAttributes();
    }
}

size_t SdrObjList::GetObjCount() const
{
    return maList.size();
}


SdrObject* SdrObjList::GetObj(size_t nNum) const
{
    if (nNum >= maList.size())
    {
        OSL_ASSERT(nNum<maList.size());
        return nullptr;
    }
    else
        return maList[nNum];
}


bool SdrObjList::IsReadOnly() const
{
    bool bRet = false;
    if (pPage!=nullptr && pPage!=this) bRet=pPage->IsReadOnly();
    return bRet;
}

size_t SdrObjList::CountAllObjects() const
{
    const size_t nCount=GetObjCount();
    size_t nCnt=nCount;
    for (size_t nNum=0; nNum<nCount; nNum++) {
        SdrObjList* pSubOL=GetObj(nNum)->GetSubList();
        if (pSubOL!=nullptr) {
            nCnt+=pSubOL->CountAllObjects();
        }
    }
    return nCnt;
}

void SdrObjList::FlattenGroups()
{
    const size_t nObj = GetObjCount();
    for( size_t i = nObj; i>0; )
        UnGroupObj(--i);
}

void SdrObjList::UnGroupObj( size_t nObjNum )
{
    // if the given object is no group, this method is a noop
    SdrObject* pUngroupObj = GetObj( nObjNum );
    if( pUngroupObj )
    {
        SdrObjList* pSrcLst = pUngroupObj->GetSubList();
        if( dynamic_cast<const SdrObjGroup*>( pUngroupObj) !=  nullptr && pSrcLst )
        {
            SdrObjGroup* pUngroupGroup = static_cast< SdrObjGroup* > (pUngroupObj);

            // ungroup recursively (has to be head recursion,
            // otherwise our indices will get trashed when doing it in
            // the loop)
            pSrcLst->FlattenGroups();

            // the position at which we insert the members of rUngroupGroup
            size_t nInsertPos( pUngroupGroup->GetOrdNum() );

            const size_t nCount = pSrcLst->GetObjCount();
            for( size_t i=0; i<nCount; ++i )
            {
                SdrObject* pObj = pSrcLst->RemoveObject(0);
                InsertObject(pObj, nInsertPos);
                ++nInsertPos;
            }

            RemoveObject(nInsertPos);
        }
    }
#ifdef DBG_UTIL
    else
        OSL_FAIL("SdrObjList::UnGroupObj: object index invalid");
#endif
}


bool SdrObjList::HasObjectNavigationOrder() const
{
    return mxNavigationOrder.get() != nullptr;
}


void SdrObjList::SetObjectNavigationPosition (
    SdrObject& rObject,
    const sal_uInt32 nNewPosition)
{
    // When the navigation order container has not yet been created then
    // create one now.  It is initialized with the z-order taken from
    // maList.
    if (mxNavigationOrder.get() == nullptr)
    {
        mxNavigationOrder.reset(new WeakSdrObjectContainerType(maList.size()));
        ::std::copy(
            maList.begin(),
            maList.end(),
            mxNavigationOrder->begin());
    }
    OSL_ASSERT(mxNavigationOrder.get()!=nullptr);
    OSL_ASSERT( mxNavigationOrder->size() == maList.size());

    SdrObjectWeakRef aReference (&rObject);

    // Look up the object whose navigation position is to be changed.
    WeakSdrObjectContainerType::iterator iObject (::std::find(
        mxNavigationOrder->begin(),
        mxNavigationOrder->end(),
        aReference));
    if (iObject == mxNavigationOrder->end())
    {
        // The given object is not a member of the navigation order.
        return;
    }

    // Move the object to its new position.
    const sal_uInt32 nOldPosition = ::std::distance(mxNavigationOrder->begin(), iObject);
    if (nOldPosition != nNewPosition)
    {
        mxNavigationOrder->erase(iObject);
        sal_uInt32 nInsertPosition (nNewPosition);
        // Adapt insertion position for the just erased object.
        if (nNewPosition >= nOldPosition)
            nInsertPosition -= 1;
        if (nInsertPosition >= mxNavigationOrder->size())
            mxNavigationOrder->push_back(aReference);
        else
            mxNavigationOrder->insert(mxNavigationOrder->begin()+nInsertPosition, aReference);

        mbIsNavigationOrderDirty = true;

        // The navigation order is written out to file so mark the model as modified.
        if (pModel != nullptr)
            pModel->SetChanged();
    }
}


SdrObject* SdrObjList::GetObjectForNavigationPosition (const sal_uInt32 nNavigationPosition) const
{
    if (HasObjectNavigationOrder())
    {
        // There is a user defined navigation order. Make sure the object
        // index is correct and look up the object in mxNavigationOrder.
        if (nNavigationPosition >= mxNavigationOrder->size())
        {
            OSL_ASSERT(nNavigationPosition < mxNavigationOrder->size());
        }
        else
            return (*mxNavigationOrder)[nNavigationPosition].get();
    }
    else
    {
        // There is no user defined navigation order. Use the z-order
        // instead.
        if (nNavigationPosition >= maList.size())
        {
            OSL_ASSERT(nNavigationPosition < maList.size());
        }
        else
            return maList[nNavigationPosition];
    }
    return nullptr;
}


void SdrObjList::ClearObjectNavigationOrder()
{
    mxNavigationOrder.reset();
    mbIsNavigationOrderDirty = true;
}


bool SdrObjList::RecalcNavigationPositions()
{
    if (mbIsNavigationOrderDirty)
    {
        if (mxNavigationOrder.get() != nullptr)
        {
            mbIsNavigationOrderDirty = false;

            WeakSdrObjectContainerType::iterator iObject;
            WeakSdrObjectContainerType::const_iterator iEnd (mxNavigationOrder->end());
            sal_uInt32 nIndex (0);
            for (iObject=mxNavigationOrder->begin(); iObject!=iEnd; ++iObject,++nIndex)
                (*iObject)->SetNavigationPosition(nIndex);
        }
    }

    return mxNavigationOrder.get() != nullptr;
}


void SdrObjList::SetNavigationOrder (const uno::Reference<container::XIndexAccess>& rxOrder)
{
    if (rxOrder.is())
    {
        const sal_Int32 nCount = rxOrder->getCount();
        if ((sal_uInt32)nCount != maList.size())
            return;

        if (mxNavigationOrder.get() == nullptr)
            mxNavigationOrder.reset(new WeakSdrObjectContainerType(nCount));

        for (sal_Int32 nIndex=0; nIndex<nCount; ++nIndex)
        {
            uno::Reference<uno::XInterface> xShape (rxOrder->getByIndex(nIndex), uno::UNO_QUERY);
            SdrObject* pObject = SdrObject::getSdrObjectFromXShape(xShape);
            if (pObject == nullptr)
                break;
            (*mxNavigationOrder)[nIndex] = pObject;
        }

        mbIsNavigationOrderDirty = true;
    }
    else
        ClearObjectNavigationOrder();
}


void SdrObjList::InsertObjectIntoContainer (
    SdrObject& rObject,
    const sal_uInt32 nInsertPosition)
{
    OSL_ASSERT(nInsertPosition<=maList.size());

    // Update the navigation positions.
    if (HasObjectNavigationOrder())
    {
        // The new object does not have a user defined position so append it
        // to the list.
        rObject.SetNavigationPosition(mxNavigationOrder->size());
        mxNavigationOrder->push_back(&rObject);
    }

    // Insert object into object list.  Because the insert() method requires
    // a valid iterator as insertion position, we have to use push_back() to
    // insert at the end of the list.
    if (nInsertPosition >= maList.size())
        maList.push_back(&rObject);
    else
        maList.insert(maList.begin()+nInsertPosition, &rObject);
    bObjOrdNumsDirty=true;
}


void SdrObjList::ReplaceObjectInContainer (
    SdrObject& rNewObject,
    const sal_uInt32 nObjectPosition)
{
    if (nObjectPosition >= maList.size())
    {
        OSL_ASSERT(nObjectPosition<maList.size());
        return;
    }

    // Update the navigation positions.
    if (HasObjectNavigationOrder())
    {
        // A user defined position of the object that is to be replaced is
        // not transferred to the new object so erase the former and append
        // the later object from/to the navigation order.
        OSL_ASSERT(nObjectPosition < maList.size());
        SdrObjectWeakRef aReference (maList[nObjectPosition]);
        WeakSdrObjectContainerType::iterator iObject (::std::find(
            mxNavigationOrder->begin(),
            mxNavigationOrder->end(),
            aReference));
        if (iObject != mxNavigationOrder->end())
            mxNavigationOrder->erase(iObject);

        mxNavigationOrder->push_back(&rNewObject);

        mbIsNavigationOrderDirty = true;
    }

    maList[nObjectPosition] = &rNewObject;
    bObjOrdNumsDirty=true;
}


void SdrObjList::RemoveObjectFromContainer (
    const sal_uInt32 nObjectPosition)
{
    if (nObjectPosition >= maList.size())
    {
        OSL_ASSERT(nObjectPosition<maList.size());
        return;
    }

    // Update the navigation positions.
    if (HasObjectNavigationOrder())
    {
        SdrObjectWeakRef aReference (maList[nObjectPosition]);
        WeakSdrObjectContainerType::iterator iObject (::std::find(
            mxNavigationOrder->begin(),
            mxNavigationOrder->end(),
            aReference));
        if (iObject != mxNavigationOrder->end())
            mxNavigationOrder->erase(iObject);
        mbIsNavigationOrderDirty = true;
    }

    maList.erase(maList.begin()+nObjectPosition);
    bObjOrdNumsDirty=true;
}

void SdrObjList::dumpAsXml(xmlTextWriterPtr pWriter) const
{
    xmlTextWriterStartElement(pWriter, BAD_CAST("SdrObjList"));
    xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
    xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("symbol"), "%s", BAD_CAST(typeid(*this).name()));

    size_t nObjCount = GetObjCount();
    for (size_t i = 0; i < nObjCount; ++i)
    {
        if (const SdrObject* pObject = GetObj(i))
            pObject->dumpAsXml(pWriter);
    }

    xmlTextWriterEndElement(pWriter);
}


void SdrPageGridFrameList::Clear()
{
    sal_uInt16 nCount=GetCount();
    for (sal_uInt16 i=0; i<nCount; i++) {
        delete GetObject(i);
    }
    aList.clear();
}


// PageUser section

void SdrPage::AddPageUser(sdr::PageUser& rNewUser)
{
    maPageUsers.push_back(&rNewUser);
}

void SdrPage::RemovePageUser(sdr::PageUser& rOldUser)
{
    const sdr::PageUserVector::iterator aFindResult = ::std::find(maPageUsers.begin(), maPageUsers.end(), &rOldUser);
    if(aFindResult != maPageUsers.end())
    {
        maPageUsers.erase(aFindResult);
    }
}


// DrawContact section

sdr::contact::ViewContact* SdrPage::CreateObjectSpecificViewContact()
{
    return new sdr::contact::ViewContactOfSdrPage(*this);
}

const sdr::contact::ViewContact& SdrPage::GetViewContact() const
{
    if (!mpViewContact)
        const_cast<SdrPage*>(this)->mpViewContact.reset(
            const_cast<SdrPage*>(this)->CreateObjectSpecificViewContact());

    return *mpViewContact;
}

sdr::contact::ViewContact& SdrPage::GetViewContact()
{
    if (!mpViewContact)
        mpViewContact.reset(CreateObjectSpecificViewContact());

    return *mpViewContact;
}

void SdrPageProperties::ImpRemoveStyleSheet()
{
    if(mpStyleSheet)
    {
        EndListening(*mpStyleSheet);
        maProperties.SetParent(nullptr);
        mpStyleSheet = nullptr;
    }
}

void SdrPageProperties::ImpAddStyleSheet(SfxStyleSheet& rNewStyleSheet)
{
    if(mpStyleSheet != &rNewStyleSheet)
    {
        ImpRemoveStyleSheet();
        mpStyleSheet = &rNewStyleSheet;
        StartListening(rNewStyleSheet);
        maProperties.SetParent(&rNewStyleSheet.GetItemSet());
    }
}

void ImpPageChange(SdrPage& rSdrPage)
{
    rSdrPage.ActionChanged();

    if(rSdrPage.GetModel())
    {
        rSdrPage.GetModel()->SetChanged();
        SdrHint aHint(SdrHintKind::PageOrderChange, &rSdrPage);
        rSdrPage.GetModel()->Broadcast(aHint);
    }
}

SdrPageProperties::SdrPageProperties(SdrPage& rSdrPage)
:   SfxListener(),
    mpSdrPage(&rSdrPage),
    mpStyleSheet(nullptr),
    maProperties(mpSdrPage->GetModel()->GetItemPool(), svl::Items<XATTR_FILL_FIRST, XATTR_FILL_LAST>{})
{
    if(!rSdrPage.IsMasterPage())
    {
        maProperties.Put(XFillStyleItem(drawing::FillStyle_NONE));
    }
}

SdrPageProperties::~SdrPageProperties()
{
    ImpRemoveStyleSheet();
}

void SdrPageProperties::Notify(SfxBroadcaster& /*rBC*/, const SfxHint& rHint)
{
    switch(rHint.GetId())
    {
        case SfxHintId::DataChanged :
            {
                // notify change, broadcast
                ImpPageChange(*mpSdrPage);
                break;
            }
        case SfxHintId::Dying :
            {
                // Style needs to be forgotten
                ImpRemoveStyleSheet();
                break;
            }
        default: break;
    }
}

bool SdrPageProperties::isUsedByModel() const
{
    assert(mpSdrPage);
    return mpSdrPage->IsInserted();
}


void SdrPageProperties::PutItemSet(const SfxItemSet& rSet)
{
    OSL_ENSURE(!mpSdrPage->IsMasterPage(), "Item set at MasterPage Attributes (!)");
    maProperties.Put(rSet);
    ImpPageChange(*mpSdrPage);
}

void SdrPageProperties::PutItem(const SfxPoolItem& rItem)
{
    OSL_ENSURE(!mpSdrPage->IsMasterPage(), "Item set at MasterPage Attributes (!)");
    maProperties.Put(rItem);
    ImpPageChange(*mpSdrPage);
}

void SdrPageProperties::ClearItem(const sal_uInt16 nWhich)
{
    maProperties.ClearItem(nWhich);
    ImpPageChange(*mpSdrPage);
}

void SdrPageProperties::SetStyleSheet(SfxStyleSheet* pStyleSheet)
{
    if(pStyleSheet)
    {
        ImpAddStyleSheet(*pStyleSheet);
    }
    else
    {
        ImpRemoveStyleSheet();
    }

    ImpPageChange(*mpSdrPage);
}


SdrPage::SdrPage(SdrModel& rNewModel, bool bMasterPage)
:   SdrObjList(&rNewModel, this),
    mpViewContact(nullptr),
    mnWidth(10),
    mnHeight(10),
    mnBorderLeft(0),
    mnBorderUpper(0),
    mnBorderRight(0),
    mnBorderLower(0),
    mpLayerAdmin(new SdrLayerAdmin(&rNewModel.GetLayerAdmin())),
    mpSdrPageProperties(nullptr),
    mpMasterPageDescriptor(nullptr),
    nPageNum(0),
    mbMaster(bMasterPage),
    mbInserted(false),
    mbObjectsNotPersistent(false),
    mbPageBorderOnlyLeftRight(false)
{
    aPrefVisiLayers.SetAll();
    eListKind = bMasterPage ? SdrObjListKind::MasterPage : SdrObjListKind::DrawPage;

    mpSdrPageProperties.reset(new SdrPageProperties(*this));
}

SdrPage::SdrPage(const SdrPage& rSrcPage)
:   SdrObjList(rSrcPage.pModel, this),
    tools::WeakBase< SdrPage >(),
    mpViewContact(nullptr),
    mnWidth(rSrcPage.mnWidth),
    mnHeight(rSrcPage.mnHeight),
    mnBorderLeft(rSrcPage.mnBorderLeft),
    mnBorderUpper(rSrcPage.mnBorderUpper),
    mnBorderRight(rSrcPage.mnBorderRight),
    mnBorderLower(rSrcPage.mnBorderLower),
    mpLayerAdmin(new SdrLayerAdmin(rSrcPage.pModel->GetLayerAdmin())),
    mpSdrPageProperties(nullptr),
    mpMasterPageDescriptor(nullptr),
    nPageNum(rSrcPage.nPageNum),
    mbMaster(rSrcPage.mbMaster),
    mbInserted(false),
    mbObjectsNotPersistent(rSrcPage.mbObjectsNotPersistent),
    mbPageBorderOnlyLeftRight(rSrcPage.mbPageBorderOnlyLeftRight)
{
    aPrefVisiLayers.SetAll();
}

SdrPage::~SdrPage()
{
    if( mxUnoPage.is() ) try
    {
        uno::Reference< lang::XComponent > xPageComponent( mxUnoPage, uno::UNO_QUERY_THROW );
        mxUnoPage.clear();
        xPageComponent->dispose();
    }
    catch( const uno::Exception& )
    {
        DBG_UNHANDLED_EXCEPTION();
    }

    // tell all the registered PageUsers that the page is in destruction
    // This causes some (all?) PageUsers to remove themselves from the list
    // of page users.  Therefore we have to use a copy of the list for the
    // iteration.
    sdr::PageUserVector aListCopy (maPageUsers.begin(), maPageUsers.end());
    for(sdr::PageUserVector::iterator aIterator = aListCopy.begin(); aIterator != aListCopy.end(); ++aIterator)
    {
        sdr::PageUser* pPageUser = *aIterator;
        DBG_ASSERT(pPageUser, "SdrPage::~SdrPage: corrupt PageUser list (!)");
        pPageUser->PageInDestruction(*this);
    }

    // Clear the vector. This means that user do not need to call RemovePageUser()
    // when they get called from PageInDestruction().
    maPageUsers.clear();

    mpLayerAdmin.reset();

    TRG_ClearMasterPage();

    mpViewContact.reset();
    mpSdrPageProperties.reset();
}

void SdrPage::lateInit(const SdrPage& rSrcPage, SdrModel* const pNewModel)
{
    assert(!mpViewContact);
    assert(!mpSdrPageProperties);
    assert(!mxUnoPage.is());

    if (pNewModel && (pNewModel != pModel))
    {
        pModel = pNewModel;
        impl_setModelForLayerAdmin(pNewModel);
    }

    // copy all the local parameters to make this instance
    // a valid copy of source page before copying and inserting
    // the contained objects
    mbMaster = rSrcPage.mbMaster;
    mbPageBorderOnlyLeftRight = rSrcPage.mbPageBorderOnlyLeftRight;
    aPrefVisiLayers = rSrcPage.aPrefVisiLayers;
    mnWidth = rSrcPage.mnWidth;
    mnHeight = rSrcPage.mnHeight;
    mnBorderLeft = rSrcPage.mnBorderLeft;
    mnBorderUpper = rSrcPage.mnBorderUpper;
    mnBorderRight = rSrcPage.mnBorderRight;
    mnBorderLower = rSrcPage.mnBorderLower;
    nPageNum = rSrcPage.nPageNum;

    if(rSrcPage.TRG_HasMasterPage())
    {
        TRG_SetMasterPage(rSrcPage.TRG_GetMasterPage());
        TRG_SetMasterPageVisibleLayers(rSrcPage.TRG_GetMasterPageVisibleLayers());
    }
    else
    {
        TRG_ClearMasterPage();
    }

    mbObjectsNotPersistent = rSrcPage.mbObjectsNotPersistent;

    {
        mpSdrPageProperties.reset(new SdrPageProperties(*this));

        if(!IsMasterPage())
        {
            mpSdrPageProperties->PutItemSet(rSrcPage.getSdrPageProperties().GetItemSet());
        }

        mpSdrPageProperties->SetStyleSheet(rSrcPage.getSdrPageProperties().GetStyleSheet());
    }

    // Now copy the contained objects
    SdrObjList::lateInit(rSrcPage);

    // be careful and correct eListKind, a member of SdrObjList which
    // will be changed by the SdrObjList::lateInit before...
    eListKind = (mbMaster) ? SdrObjListKind::MasterPage : SdrObjListKind::DrawPage;
}

SdrPage* SdrPage::Clone() const
{
    return Clone(nullptr);
}

SdrPage* SdrPage::Clone(SdrModel* pNewModel) const
{
    if (pNewModel==nullptr) pNewModel=pModel;
    SdrPage* pPage2=new SdrPage(*pNewModel);
    pPage2->lateInit(*this);
    return pPage2;
}

void SdrPage::SetSize(const Size& aSiz)
{
    bool bChanged(false);

    if(aSiz.Width() != mnWidth)
    {
        mnWidth = aSiz.Width();
        bChanged = true;
    }

    if(aSiz.Height() != mnHeight)
    {
        mnHeight = aSiz.Height();
        bChanged = true;
    }

    if(bChanged)
    {
        SetChanged();
    }
}

Size SdrPage::GetSize() const
{
    return Size(mnWidth,mnHeight);
}

sal_Int32 SdrPage::GetWidth() const
{
    return mnWidth;
}

void SdrPage::SetOrientation(Orientation eOri)
{
    // square: handle like portrait format
    Size aSiz(GetSize());
    if (aSiz.Width()!=aSiz.Height()) {
        if ((eOri==Orientation::Portrait) == (aSiz.Width()>aSiz.Height())) {
            SetSize(Size(aSiz.Height(),aSiz.Width()));
        }
    }
}

Orientation SdrPage::GetOrientation() const
{
    // square: handle like portrait format
    Orientation eRet=Orientation::Portrait;
    Size aSiz(GetSize());
    if (aSiz.Width()>aSiz.Height()) eRet=Orientation::Landscape;
    return eRet;
}

sal_Int32 SdrPage::GetHeight() const
{
    return mnHeight;
}

void  SdrPage::SetBorder(sal_Int32 nLft, sal_Int32 nUpp, sal_Int32 nRgt, sal_Int32 nLwr)
{
    bool bChanged(false);

    if(mnBorderLeft != nLft)
    {
        mnBorderLeft = nLft;
        bChanged = true;
    }

    if(mnBorderUpper != nUpp)
    {
        mnBorderUpper = nUpp;
        bChanged = true;
    }

    if(mnBorderRight != nRgt)
    {
        mnBorderRight = nRgt;
        bChanged = true;
    }

    if(mnBorderLower != nLwr)
    {
        mnBorderLower =  nLwr;
        bChanged = true;
    }

    if(bChanged)
    {
        SetChanged();
    }
}

void  SdrPage::SetLeftBorder(sal_Int32 nBorder)
{
    if(mnBorderLeft != nBorder)
    {
        mnBorderLeft = nBorder;
        SetChanged();
    }
}

void  SdrPage::SetUpperBorder(sal_Int32 nBorder)
{
    if(mnBorderUpper != nBorder)
    {
        mnBorderUpper = nBorder;
        SetChanged();
    }
}

void  SdrPage::SetRightBorder(sal_Int32 nBorder)
{
    if(mnBorderRight != nBorder)
    {
        mnBorderRight=nBorder;
        SetChanged();
    }
}

void  SdrPage::SetLowerBorder(sal_Int32 nBorder)
{
    if(mnBorderLower != nBorder)
    {
        mnBorderLower=nBorder;
        SetChanged();
    }
}

sal_Int32 SdrPage::GetLeftBorder() const
{
    return mnBorderLeft;
}

sal_Int32 SdrPage::GetUpperBorder() const
{
    return mnBorderUpper;
}

sal_Int32 SdrPage::GetRightBorder() const
{
    return mnBorderRight;
}

sal_Int32 SdrPage::GetLowerBorder() const
{
    return mnBorderLower;
}

void SdrPage::impl_setModelForLayerAdmin(SdrModel* const pNewModel)
{
    if (pNewModel!=nullptr) {
        mpLayerAdmin->SetParent(&pNewModel->GetLayerAdmin());
    } else {
        mpLayerAdmin->SetParent(nullptr);
    }
    mpLayerAdmin->SetModel(pNewModel);
}

void SdrPage::SetModel(SdrModel* pNewModel)
{
    SdrModel* pOldModel=pModel;
    SdrObjList::SetModel(pNewModel);

    if (pNewModel!=pOldModel)
    {
        impl_setModelForLayerAdmin( pNewModel );

        // create new SdrPageProperties with new model (due to SfxItemSet there)
        // and copy ItemSet and StyleSheet
        std::unique_ptr<SdrPageProperties> pNew(new SdrPageProperties(*this));

        if(!IsMasterPage())
        {
            const SfxItemSet& rOldSet = getSdrPageProperties().GetItemSet();
            SfxItemSet* pNewSet = rOldSet.Clone(false, &pNewModel->GetItemPool());
            //ensure checkForUniqueItem is called so new pages which have e.g.
            //XFillBitmapItem set, do not conflict with an existing XFillBitmapItem
            //with the same name but different properties
            SdrModel::MigrateItemSet(&rOldSet, pNewSet, pNewModel);
            pNew->PutItemSet(*pNewSet);
            delete pNewSet;
        }

        pNew->SetStyleSheet(getSdrPageProperties().GetStyleSheet());

        mpSdrPageProperties = std::move(pNew);
    }

    // update listeners at possible API wrapper object
    if( pOldModel != pNewModel )
    {
        if( mxUnoPage.is() )
        {
            SvxDrawPage* pPage2 = SvxDrawPage::getImplementation( mxUnoPage );
            if( pPage2 )
                pPage2->ChangeModel( pNewModel );
        }
    }
}


// #i68775# React on PageNum changes (from Model in most cases)
void SdrPage::SetPageNum(sal_uInt16 nNew)
{
    if(nNew != nPageNum)
    {
        // change
        nPageNum = nNew;

        // notify visualisations, also notifies e.g. buffered MasterPages
        ActionChanged();
    }
}

sal_uInt16 SdrPage::GetPageNum() const
{
    if (!mbInserted)
        return 0;

    if (mbMaster) {
        if (pModel && pModel->IsMPgNumsDirty())
            pModel->RecalcPageNums(true);
    } else {
        if (pModel && pModel->IsPagNumsDirty())
            pModel->RecalcPageNums(false);
    }
    return nPageNum;
}

void SdrPage::SetChanged()
{
    // For test purposes, use the new ViewContact for change
    // notification now.
    ActionChanged();

    if( pModel )
    {
        pModel->SetChanged();
    }
}


// MasterPage interface

void SdrPage::TRG_SetMasterPage(SdrPage& rNew)
{
    if(mpMasterPageDescriptor && &(mpMasterPageDescriptor->GetUsedPage()) == &rNew)
        return;

    if(mpMasterPageDescriptor)
        TRG_ClearMasterPage();

    mpMasterPageDescriptor = new sdr::MasterPageDescriptor(*this, rNew);
    GetViewContact().ActionChanged();
}

void SdrPage::TRG_ClearMasterPage()
{
    if(mpMasterPageDescriptor)
    {
        SetChanged();

        // the flushViewObjectContacts() will do needed invalidates by deleting the involved VOCs
        mpMasterPageDescriptor->GetUsedPage().GetViewContact().flushViewObjectContacts();

        delete mpMasterPageDescriptor;
        mpMasterPageDescriptor = nullptr;
    }
}

SdrPage& SdrPage::TRG_GetMasterPage() const
{
    DBG_ASSERT(mpMasterPageDescriptor != nullptr, "TRG_GetMasterPage(): No MasterPage available. Use TRG_HasMasterPage() before access (!)");
    return mpMasterPageDescriptor->GetUsedPage();
}

const SdrLayerIDSet& SdrPage::TRG_GetMasterPageVisibleLayers() const
{
    DBG_ASSERT(mpMasterPageDescriptor != nullptr, "TRG_GetMasterPageVisibleLayers(): No MasterPage available. Use TRG_HasMasterPage() before access (!)");
    return mpMasterPageDescriptor->GetVisibleLayers();
}

void SdrPage::TRG_SetMasterPageVisibleLayers(const SdrLayerIDSet& rNew)
{
    DBG_ASSERT(mpMasterPageDescriptor != nullptr, "TRG_SetMasterPageVisibleLayers(): No MasterPage available. Use TRG_HasMasterPage() before access (!)");
    mpMasterPageDescriptor->SetVisibleLayers(rNew);
}

sdr::contact::ViewContact& SdrPage::TRG_GetMasterPageDescriptorViewContact() const
{
    DBG_ASSERT(mpMasterPageDescriptor != nullptr, "TRG_GetMasterPageDescriptorViewContact(): No MasterPage available. Use TRG_HasMasterPage() before access (!)");
    return mpMasterPageDescriptor->GetViewContact();
}

// used from SdrModel::RemoveMasterPage
void SdrPage::TRG_ImpMasterPageRemoved(const SdrPage& rRemovedPage)
{
    if(TRG_HasMasterPage())
    {
        if(&TRG_GetMasterPage() == &rRemovedPage)
        {
            TRG_ClearMasterPage();
        }
    }
}

const SdrPageGridFrameList* SdrPage::GetGridFrameList(const SdrPageView* /*pPV*/, const tools::Rectangle* /*pRect*/) const
{
    return nullptr;
}

const SdrLayerAdmin& SdrPage::GetLayerAdmin() const
{
    return *mpLayerAdmin;
}

SdrLayerAdmin& SdrPage::GetLayerAdmin()
{
    return *mpLayerAdmin;
}

OUString SdrPage::GetLayoutName() const
{
    return OUString();
}

void SdrPage::SetInserted( bool bIns )
{
    if( mbInserted != bIns )
    {
        mbInserted = bIns;

        // #i120437# go over whole hierarchy, not only over object level null (seen from grouping)
        SdrObjListIter aIter(*this, SdrIterMode::DeepNoGroups);

         while ( aIter.IsMore() )
        {
            SdrObject* pObj = aIter.Next();
            if ( dynamic_cast<const SdrOle2Obj* >(pObj) !=  nullptr )
            {
                if( mbInserted )
                    static_cast<SdrOle2Obj*>(pObj)->Connect();
                else
                    static_cast<SdrOle2Obj*>(pObj)->Disconnect();
            }
        }
    }
}

void SdrPage::SetUnoPage(uno::Reference<drawing::XDrawPage> const& xNewPage)
{
    mxUnoPage = xNewPage;
}

uno::Reference< uno::XInterface > const & SdrPage::getUnoPage()
{
    if( !mxUnoPage.is() )
    {
        // create one
        mxUnoPage = createUnoPage();
    }

    return mxUnoPage;
}

uno::Reference< uno::XInterface > SdrPage::createUnoPage()
{
    css::uno::Reference< css::uno::XInterface > xInt =
        static_cast<cppu::OWeakObject*>( new SvxFmDrawPage( this ) );
    return xInt;
}

SfxStyleSheet* SdrPage::GetTextStyleSheetForObject( SdrObject* pObj ) const
{
    return pObj->GetStyleSheet();
}

/** returns an averaged background color of this page */
// #i75566# GetBackgroundColor -> GetPageBackgroundColor and bScreenDisplay hint value
Color SdrPage::GetPageBackgroundColor( SdrPageView const * pView, bool bScreenDisplay ) const
{
    Color aColor;

    if(bScreenDisplay && (!pView || pView->GetApplicationDocumentColor() == COL_AUTO))
    {
        svtools::ColorConfig aColorConfig;
        aColor = aColorConfig.GetColorValue( svtools::DOCCOLOR ).nColor;
    }
    else
    {
        aColor = pView->GetApplicationDocumentColor();
    }

    const SfxItemSet* pBackgroundFill = &getSdrPageProperties().GetItemSet();

    if(!IsMasterPage() && TRG_HasMasterPage())
    {
        if(drawing::FillStyle_NONE == pBackgroundFill->Get(XATTR_FILLSTYLE).GetValue())
        {
            pBackgroundFill = &TRG_GetMasterPage().getSdrPageProperties().GetItemSet();
        }
    }

    GetDraftFillColor(*pBackgroundFill, aColor);

    return aColor;
}

/** *deprecated, use GetBackgroundColor with SdrPageView */
Color SdrPage::GetPageBackgroundColor() const
// #i75566# GetBackgroundColor -> GetPageBackgroundColor
{
    return GetPageBackgroundColor( nullptr );
}

/** this method returns true if the object from the ViewObjectContact should
    be visible on this page while rendering.
    bEdit selects if visibility test is for an editing view or a final render,
    like printing.
*/
bool SdrPage::checkVisibility(
    const sdr::contact::ViewObjectContact& /*rOriginal*/,
    const sdr::contact::DisplayInfo& /*rDisplayInfo*/,
    bool /*bEdit*/)
{
    // this will be handled in the application if needed
    return true;
}

// DrawContact support: Methods for handling Page changes
void SdrPage::ActionChanged()
{
    // Do necessary ViewContact actions
    GetViewContact().ActionChanged();

    // #i48535# also handle MasterPage change
    if(TRG_HasMasterPage())
    {
        TRG_GetMasterPageDescriptorViewContact().ActionChanged();
    }
}

SdrPageProperties& SdrPage::getSdrPageProperties()
{
    return *mpSdrPageProperties;
}

const SdrPageProperties& SdrPage::getSdrPageProperties() const
{
    return *mpSdrPageProperties;
}

const SdrPageProperties* SdrPage::getCorrectSdrPageProperties() const
{
    if(mpMasterPageDescriptor)
    {
        return mpMasterPageDescriptor->getCorrectSdrPageProperties();
    }
    else
    {
        return &getSdrPageProperties();
    }
}


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