/* -*- 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 <set>
#include <unordered_set>

#include <svx/svdpage.hxx>

#include <o3tl/safeint.hxx>
#include <string.h>

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

#include <svtools/colorcfg.hxx>
#include <svx/svdetc.hxx>
#include <svx/svdobj.hxx>
#include <svx/svdogrp.hxx>
#include <svx/svdoedge.hxx>
#include <svx/svdoole2.hxx>
#include <svx/svditer.hxx>
#include <svx/svdmodel.hxx>
#include <svx/svdlayer.hxx>
#include <svx/svdpagv.hxx>
#include <svx/xfillit0.hxx>
#include <svx/fmdpage.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>

#include <com/sun/star/lang/IllegalArgumentException.hpp>

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

static const sal_Int32 InitialObjectContainerCapacity (64);

////////////////////////////////////////////////////////////////////////////////////////////////////
// helper to allow changing parent at SdrObject, but only from SdrObjList

void SetParentAtSdrObjectFromSdrObjList(SdrObject& rSdrObject, SdrObjList* pNew)
{
    rSdrObject.setParentOfSdrObject(pNew);
}

//////////////////////////////////////////////////////////////////////////////

SdrObjList::SdrObjList()
:   maList(),
    maSdrObjListOutRect(),
    maSdrObjListSnapRect(),
    mbObjOrdNumsDirty(false),
    mbRectsDirty(false),
    mxNavigationOrder(),
    mbIsNavigationOrderDirty(false)
{
    maList.reserve(InitialObjectContainerCapacity);
}

void SdrObjList::impClearSdrObjList(bool bBroadcast)
{
    SdrModel* pSdrModelFromRemovedSdrObject(nullptr);

    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();

        if(bBroadcast)
        {
            if(nullptr == pSdrModelFromRemovedSdrObject)
            {
                pSdrModelFromRemovedSdrObject = &pObj->getSdrModelFromSdrObject();
            }

            // sent remove hint (after removal, see RemoveObject())
            // TTTT SdrPage not needed, can be accessed using SdrObject
            SdrHint aHint(SdrHintKind::ObjectRemoved, *pObj, getSdrPageFromSdrObjList());
            pObj->getSdrModelFromSdrObject().Broadcast(aHint);
        }

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

    if(bBroadcast && nullptr != pSdrModelFromRemovedSdrObject)
    {
        pSdrModelFromRemovedSdrObject->SetChanged();
    }
}

void SdrObjList::ClearSdrObjList()
{
    // clear SdrObjects with broadcasting
    impClearSdrObjList(true);
}

SdrObjList::~SdrObjList()
{
    // clear SdrObjects without broadcasting
    impClearSdrObjList(false);
}

SdrPage* SdrObjList::getSdrPageFromSdrObjList() const
{
    // default is no page and returns zero
    return nullptr;
}

SdrObject* SdrObjList::getSdrObjectFromSdrObjList() const
{
    // default is no SdrObject (SdrObjGroup)
    return nullptr;
}

void SdrObjList::CopyObjects(const SdrObjList& rSrcList)
{
    // clear SdrObjects with broadcasting
    ClearSdrObjList();

    mbObjOrdNumsDirty = false;
    mbRectsDirty = false;
    size_t nCloneErrCnt(0);
    const size_t nCount(rSrcList.GetObjCount());

    if(nullptr == getSdrObjectFromSdrObjList() && nullptr == getSdrPageFromSdrObjList())
    {
        OSL_ENSURE(false, "SdrObjList which is not part of SdrPage or SdrObject (!)");
        return;
    }

    SdrModel& rTargetSdrModel(nullptr == getSdrObjectFromSdrObjList()
        ? getSdrPageFromSdrObjList()->getSdrModelFromSdrPage()
        : getSdrObjectFromSdrObjList()->getSdrModelFromSdrObject());

    for (size_t no(0); no < nCount; ++no)
    {
        SdrObject* pSO(rSrcList.GetObj(no));
        SdrObject* pDO(pSO->CloneSdrObject(rTargetSdrModel));

        if(nullptr != pDO)
        {
            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::CreateMarkedObjModel() 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->getParentSdrObjListFromSdrObject()!=pSrcEdge->getParentSdrObjListFromSdrObject()) pSrcNode1=nullptr; // can't do this
                if (pSrcNode2!=nullptr && pSrcNode2->getParentSdrObjListFromSdrObject()!=pSrcEdge->getParentSdrObjListFromSdrObject()) 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_uInt32 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_uInt32 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::RecalcObjOrdNums()
{
    const size_t nCount = GetObjCount();
    for (size_t no=0; no<nCount; ++no) {
        SdrObject* pObj=GetObj(no);
        pObj->SetOrdNum(no);
    }
    mbObjOrdNumsDirty=false;
}

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

void SdrObjList::SetSdrObjListRectsDirty()
{
    mbRectsDirty=true;
    SdrObject* pParentSdrObject(getSdrObjectFromSdrObjList());

    if(nullptr != pParentSdrObject)
    {
        pParentSdrObject->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) mbObjOrdNumsDirty=true;
    pObj->SetOrdNum(nPos);
    SetParentAtSdrObjectFromSdrObjList(*pObj, this);

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

    if (!mbRectsDirty) {
        mbRectsDirty = true;
    }
    pObj->InsertedStateChange(); // calls the UserCall (among others)
}

void SdrObjList::InsertObjectThenMakeNameUnique(SdrObject* pObj)
{
    std::unordered_set<rtl::OUString> aNameSet;
    InsertObjectThenMakeNameUnique(pObj, aNameSet);
}

void SdrObjList::InsertObjectThenMakeNameUnique(SdrObject* pObj, std::unordered_set<OUString>& rNameSet, size_t nPos)
{
    InsertObject(pObj, nPos);
    if (!pObj->GetName().isEmpty())
    {
        pObj->MakeNameUnique(rNameSet);
        SdrObjList* pSdrObjList = pObj->GetSubList(); // group
        if (pSdrObjList)
        {
            SdrObject* pListObj;
            SdrObjListIter aIter(pSdrObjList, SdrIterMode::DeepWithGroups);
            while (aIter.IsMore())
            {
                pListObj = aIter.Next();
                pListObj->MakeNameUnique(rNameSet);
            }
        }
    }
}

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(getSdrObjectFromSdrObjList())
        {
            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.
        SdrObject* pParentSdrObject(getSdrObjectFromSdrObjList());

        if(pParentSdrObject)
        {
            // only repaint here
            pParentSdrObject->ActionChanged();
        }

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

        pObj->getSdrModelFromSdrObject().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.");

        // tdf#121022 Do first remove from SdrObjList - InsertedStateChange
        // relies now on IsInserted which uses getParentSdrObjListFromSdrObject
        SetParentAtSdrObjectFromSdrObjList(*pObj, nullptr);

        // calls UserCall, among other
        pObj->InsertedStateChange();

        if (!mbObjOrdNumsDirty)
        {
            // optimizing for the case that the last object has to be removed
            if (nObjNum+1!=nCount) {
                mbObjOrdNumsDirty=true;
            }
        }
        SetSdrObjListRectsDirty();
    }
    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.");

        // TODO: We need a different broadcast here.
        if (pObj->getSdrPageFromSdrObject()!=nullptr)
        {
            SdrHint aHint(SdrHintKind::ObjectRemoved, *pObj);
            pObj->getSdrModelFromSdrObject().Broadcast(aHint);
        }

        pObj->getSdrModelFromSdrObject().SetChanged();

        // tdf#121022 Do first remove from SdrObjList - InsertedStateChange
        // relies now on IsInserted which uses getParentSdrObjListFromSdrObject
        SetParentAtSdrObjectFromSdrObjList(*pObj, nullptr);

        // calls, among other things, the UserCall
        pObj->InsertedStateChange();

        if (!mbObjOrdNumsDirty)
        {
            // optimization for the case that the last object is removed
            if (nObjNum+1!=nCount) {
                mbObjOrdNumsDirty=true;
            }
        }

        SetSdrObjListRectsDirty();
        SdrObject* pParentSdrObject(getSdrObjectFromSdrObjList());

        if(pParentSdrObject && !GetObjCount())
        {
            // empty group created; it needs to be repainted since it's
            // visualization changes
            pParentSdrObject->ActionChanged();
        }
    }
    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.");

        // TODO: We need a different broadcast here.
        if (pObj->getSdrPageFromSdrObject()!=nullptr)
        {
            SdrHint aHint(SdrHintKind::ObjectRemoved, *pObj);
            pObj->getSdrModelFromSdrObject().Broadcast(aHint);
        }

        // Change parent and replace in SdrObjList
        SetParentAtSdrObjectFromSdrObjList(*pObj, nullptr);
        ReplaceObjectInContainer(*pNewObj,nObjNum);

        // tdf#121022 InsertedStateChange uses the parent
        // to detect if pObj is inserted or not, so have to call
        // it *after* changing these settings, else an obviously wrong
        // 'SdrUserCallType::Inserted' would be sent
        pObj->InsertedStateChange();

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

        // Setup data at new SdrObject - it already *is* inserted to
        // the SdrObjList due to 'ReplaceObjectInContainer' above
        pNewObj->SetOrdNum(nObjNum);
        SetParentAtSdrObjectFromSdrObjList(*pNewObj, this);

        // Inform the parent about change to allow invalidations at
        // evtl. existing parent visualisations, but also react on
        // newly inserted SdrObjects (as e.g. GraphCtrlUserCall does)
        impChildInserted(*pNewObj);

        pNewObj->InsertedStateChange();

        // TODO: We need a different broadcast here.
        if (pNewObj->getSdrPageFromSdrObject()!=nullptr) {
            SdrHint aHint(SdrHintKind::ObjectInserted, *pNewObj);
            pNewObj->getSdrModelFromSdrObject().Broadcast(aHint);
        }

        pNewObj->getSdrModelFromSdrObject().SetChanged();

        SetSdrObjListRectsDirty();
    }
    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);
        mbObjOrdNumsDirty=true;

        // TODO: We need a different broadcast here.
        if (pObj->getSdrPageFromSdrObject()!=nullptr)
            pObj->getSdrModelFromSdrObject().Broadcast(SdrHint(SdrHintKind::ObjectChange, *pObj));
        pObj->getSdrModelFromSdrObject().SetChanged();
    }
    return pObj;
}

void SdrObjList::sort( std::vector<sal_Int32>& sortOrder)
{
    // no negative indexes and indexes larger than maList size are allowed
    auto it = std::find_if( sortOrder.begin(), sortOrder.end(), [this](const sal_Int32& rIt)
         { return ( rIt < 0 || o3tl::make_unsigned(rIt) >= maList.size() ); } );
    if ( it != sortOrder.end())
        throw css::lang::IllegalArgumentException("negative index of shape", nullptr, 1);

    // no duplicates
    std::vector<bool> aNoDuplicates(sortOrder.size(), false);
    for (size_t i = 0; i < sortOrder.size(); ++i )
    {
        size_t idx =  static_cast<size_t>( sortOrder[i] );

        if ( aNoDuplicates[idx] )
            throw css::lang::IllegalArgumentException("duplicate index of shape", nullptr, 2);

        aNoDuplicates[idx] = true;
    }

    // example sortOrder [2 0 1]
    // example maList [T T S T T] ( T T = shape with textbox, S = just a shape )
    // (shapes at positions 0 and 2 have a textbox)

    std::vector<SdrObject*> aNewList(maList.size());
    std::set<sal_Int32> aShapesWithTextbox;
    std::vector<sal_Int32> aIncrements;
    std::vector<sal_Int32> aDuplicates;

    if ( maList.size() > 1)
    {
        for (size_t i = 1; i< maList.size(); ++i)
        {
            // if this shape is a textbox, then look at its left neighbour
            // (shape this textbox is in)
            // and insert the number of textboxes to the left of it
            if (maList[i]->IsTextBox())
              aShapesWithTextbox.insert( i - 1 - aShapesWithTextbox.size() );
        }
        // example aShapesWithTextbox [0 2]
    }

    if (aShapesWithTextbox.size() != maList.size() - sortOrder.size())
    {
        throw lang::IllegalArgumentException("mismatch of no. of shapes", nullptr, 0);
    }

    for (size_t i = 0; i< sortOrder.size(); ++i)
    {

         if (aShapesWithTextbox.count(sortOrder[i]) > 0)
             aDuplicates.push_back(sortOrder[i]);

         aDuplicates.push_back(sortOrder[i]);

         // example aDuplicates [2 2 0 0 1]
    }
    assert(aDuplicates.size() == maList.size());

    aIncrements.push_back(0);
    for (size_t i = 1; i< sortOrder.size(); ++i)
    {
         if (aShapesWithTextbox.count(i - 1))
             aIncrements.push_back(aIncrements[i-1] + 1 );
         else
             aIncrements.push_back(aIncrements[i-1]);

         // example aIncrements [0 1 1]
    }
    assert(aIncrements.size() == sortOrder.size());

    std::vector<sal_Int32> aNewSortOrder(maList.size());
    sal_Int32 nPrev = -1;
    for (size_t i = 0; i< aDuplicates.size(); ++i)
    {
        if (nPrev != aDuplicates[i])
            aNewSortOrder[i] = aDuplicates[i] + aIncrements[aDuplicates[i]];
        else
            aNewSortOrder[i] = aNewSortOrder[i-1] + 1;

        nPrev = aDuplicates[i];

        // example aNewSortOrder [3 4 0 1 2]
    }
    assert(aNewSortOrder.size() == maList.size());

#ifndef NDEBUG
    {
        std::vector<sal_Int32> tmp(aNewSortOrder);
        std::sort(tmp.begin(), tmp.end());
        for (size_t i = 0; i < tmp.size(); ++i)
        {
            assert(size_t(tmp[i]) == i);
        }
    }
#endif

    for (size_t i = 0; i < aNewSortOrder.size(); ++i)
    {
        aNewList[i] = maList[ aNewSortOrder[i] ];
        aNewList[i]->SetOrdNum(i);
    }

    std::swap(aNewList, maList);
}

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

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 maSdrObjListOutRect in empty state to represent an invalid state, thus
    // it's a step in the right direction.
    if (mbRectsDirty || maSdrObjListOutRect.IsEmpty())
    {
        const_cast<SdrObjList*>(this)->RecalcRects();
        const_cast<SdrObjList*>(this)->mbRectsDirty=false;
    }
    return maSdrObjListOutRect;
}

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())
    {
        SdrObject* pObj = aIter.Next();
        if (pObj->GetObjIdentifier() != OBJ_EDGE)
            continue;

        SdrEdgeObj* pSdrEdgeObj = static_cast< SdrEdgeObj* >(pObj);
        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
{
    return maList[nNum];
}


bool SdrObjList::IsReadOnly() const
{
    bool bRet(false);
    SdrObject* pParentSdrObject(getSdrObjectFromSdrObjList());

    if(nullptr != pParentSdrObject)
    {
        SdrPage* pSdrPage(pParentSdrObject->getSdrPageFromSdrObject());

        if(nullptr != pSdrPage)
        {
            bRet = pSdrPage->IsReadOnly();
        }
    }

    return bRet;
}

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 != 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 == nullptr)
    {
        mxNavigationOrder.reset(new std::vector<tools::WeakReference<SdrObject>>(maList.begin(),
            maList.end()));
    }
    OSL_ASSERT(mxNavigationOrder != nullptr);
    OSL_ASSERT( mxNavigationOrder->size() == maList.size());

    tools::WeakReference<SdrObject> aReference (&rObject);

    // Look up the object whose navigation position is to be changed.
    auto 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.
        rObject.getSdrModelFromSdrObject().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 != nullptr)
        {
            mbIsNavigationOrderDirty = false;

            sal_uInt32 nIndex (0);
            for (auto& rpObject : *mxNavigationOrder)
            {
                rpObject->SetNavigationPosition(nIndex);
                ++nIndex;
            }
        }
    }

    return mxNavigationOrder != nullptr;
}


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

        if (mxNavigationOrder == nullptr)
            mxNavigationOrder.reset(new std::vector<tools::WeakReference<SdrObject>>(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);
    mbObjOrdNumsDirty=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());
        tools::WeakReference<SdrObject> aReference (maList[nObjectPosition]);
        auto iObject = ::std::find(
            mxNavigationOrder->begin(),
            mxNavigationOrder->end(),
            aReference);
        if (iObject != mxNavigationOrder->end())
            mxNavigationOrder->erase(iObject);

        mxNavigationOrder->push_back(&rNewObject);

        mbIsNavigationOrderDirty = true;
    }

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


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

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

    maList.erase(maList.begin()+nObjectPosition);
    mbObjOrdNumsDirty=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

std::unique_ptr<sdr::contact::ViewContact> SdrPage::CreateObjectSpecificViewContact()
{
    return std::make_unique<sdr::contact::ViewContactOfSdrPage>(*this);
}

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

    return *mpViewContact;
}

sdr::contact::ViewContact& SdrPage::GetViewContact()
{
    if (!mpViewContact)
        mpViewContact = 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());
    }
}

static void ImpPageChange(SdrPage& rSdrPage)
{
    rSdrPage.ActionChanged();
    rSdrPage.getSdrModelFromSdrPage().SetChanged();
    SdrHint aHint(SdrHintKind::PageOrderChange, &rSdrPage);
    rSdrPage.getSdrModelFromSdrPage().Broadcast(aHint);
}

SdrPageProperties::SdrPageProperties(SdrPage& rSdrPage)
:   SfxListener(),
    mpSdrPage(&rSdrPage),
    mpStyleSheet(nullptr),
    maProperties(
        mpSdrPage->getSdrModelFromSdrPage().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& rModel, bool bMasterPage)
:   SdrObjList(),
    tools::WeakBase(),
    maPageUsers(),
    mrSdrModelFromSdrPage(rModel),
    mnWidth(10),
    mnHeight(10),
    mnBorderLeft(0),
    mnBorderUpper(0),
    mnBorderRight(0),
    mnBorderLower(0),
    mpLayerAdmin(new SdrLayerAdmin(&rModel.GetLayerAdmin())),
    mxUnoPage(),
    nPageNum(0),
    mbMaster(bMasterPage),
    mbInserted(false),
    mbObjectsNotPersistent(false),
    mbPageBorderOnlyLeftRight(false)
{
    mpSdrPageProperties.reset(new SdrPageProperties(*this));
}

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("svx");
    }

    // 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::PageUser* pPageUser : aListCopy)
    {
        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)
{
    assert(!mpViewContact);
    assert(!mxUnoPage.is());

    // 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;
    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
    if(0 != rSrcPage.GetObjCount())
    {
        CopyObjects(rSrcPage);
    }
}

SdrPage* SdrPage::CloneSdrPage(SdrModel& rTargetModel) const
{
    SdrPage* pClonedPage(new SdrPage(rTargetModel));
    pClonedPage->lateInit(*this);
    return pClonedPage;
}

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())) {
            // coverity[swapped_arguments : FALSE] - this is in the correct order
            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;
}

// #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 (getSdrModelFromSdrPage().IsMPgNumsDirty())
            getSdrModelFromSdrPage().RecalcPageNums(true);
    } else {
        if (getSdrModelFromSdrPage().IsPagNumsDirty())
            getSdrModelFromSdrPage().RecalcPageNums(false);
    }
    return nPageNum;
}

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

SdrPage* SdrPage::getSdrPageFromSdrObjList() const
{
    return const_cast< SdrPage* >(this);
}

// MasterPage interface

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

    if(mpMasterPageDescriptor)
        TRG_ClearMasterPage();

    mpMasterPageDescriptor.reset(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();

        mpMasterPageDescriptor.reset();
    }
}

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();
        }
    }
}

void SdrPage::MakePageObjectsNamesUnique()
{
    std::unordered_set<OUString> aNameSet;
    for (size_t no(0); no < GetObjCount(); ++no)
    {
        SdrObject* pObj(GetObj(no));
        if(nullptr != pObj)
        {
            if (!pObj->GetName().isEmpty())
            {
                pObj->MakeNameUnique(aNameSet);
                SdrObjList* pSdrObjList = pObj->GetSubList(); // group
                if (pSdrObjList)
                {
                    SdrObject* pListObj;
                    SdrObjListIter aIter(pSdrObjList, SdrIterMode::DeepWithGroups);
                    while (aIter.IsMore())
                    {
                        pListObj = aIter.Next();
                        pListObj->MakeNameUnique(aNameSet);
                    }
                }
            }
        }
    }
}

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: */