/* -*- 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 <cppuhelper/exc_hlp.hxx>
 
#include <vcl/wrkwin.hxx>
#include <sfx2/docfile.hxx>
#include <sot/storage.hxx>
#include <sfx2/app.hxx>
#include <svl/itemset.hxx>
 
#include <sfx2/fcontnr.hxx>
#include <svx/svdopath.hxx>
#include <svx/svditer.hxx>
#include <svl/style.hxx>
#include <sfx2/linkmgr.hxx>
#include <svx/svdpagv.hxx>
#include <svx/svdogrp.hxx>
#include <svx/svdundo.hxx>
#include <vcl/weld.hxx>
#include <sot/formats.hxx>
#include <xmloff/autolayout.hxx>
 
#include <strings.hrc>
#include <drawdoc.hxx>
#include <sdmod.hxx>
#include <sdpage.hxx>
#include <stlpool.hxx>
#include <sdresid.hxx>
#include <sdiocmpt.hxx>
#include <strmname.h>
#include <anminfo.hxx>
#include <customshowlist.hxx>
#include <sdxfer.hxx>
 
#include <unmovss.hxx>
#include <unchss.hxx>
#include <unprlout.hxx>
#include <DrawDocShell.hxx>
#include <GraphicDocShell.hxx>
#include <ViewShell.hxx>
#include <View.hxx>
#include <ViewShellBase.hxx>
#include <strings.hxx>
 
using namespace ::com::sun::star;
 
/** Concrete incarnations get called by lcl_IterateBookmarkPages, for
    every page in the bookmark document/list
 */
 
class InsertBookmarkAsPage_FindDuplicateLayouts
{
public:
    explicit InsertBookmarkAsPage_FindDuplicateLayouts( std::vector<OUString> &rLayoutsToTransfer )
        : mrLayoutsToTransfer(rLayoutsToTransfer) {}
    void operator()( SdDrawDocument&, SdPage const *, bool, SdDrawDocument* );
private:
    std::vector<OUString> &mrLayoutsToTransfer;
};
 
void InsertBookmarkAsPage_FindDuplicateLayouts::operator()( SdDrawDocument& rDoc, SdPage const * pBMMPage, bool bRenameDuplicates, SdDrawDocument* pBookmarkDoc )
{
    // now check for duplicate masterpage and layout names
 
    OUString aLayout( pBMMPage->GetLayoutName() );
    sal_Int32 nIndex = aLayout.indexOf( SD_LT_SEPARATOR );
    if( nIndex != -1 )
        aLayout = aLayout.copy(0, nIndex);
 
    std::vector<OUString>::const_iterator pIter =
            find(mrLayoutsToTransfer.begin(),mrLayoutsToTransfer.end(),aLayout);
 
    bool bFound = pIter != mrLayoutsToTransfer.end();
 
    const sal_uInt16 nMPageCount = rDoc.GetMasterPageCount();
    for (sal_uInt16 nMPage = 0; nMPage < nMPageCount && !bFound; nMPage++)
    {
        // Do the layouts already exist within the document?
        SdPage* pTestPage = static_cast<SdPage*>( rDoc.GetMasterPage(nMPage) );
        OUString aTest(pTestPage->GetLayoutName());
        sal_Int32 nIndex2 = aTest.indexOf( SD_LT_SEPARATOR );
        if( nIndex2 != -1 )
            aTest = aTest.copy(0, nIndex2);
 
        if (aTest == aLayout && pBMMPage->GetPageKind() == pTestPage->GetPageKind())
        {
            // Ignore Layouts with "Default" these seem to be special - in the sense that there are lot of assumption all over Impress
            // about this
            if( bRenameDuplicates && aTest != SdResId( STR_LAYOUT_DEFAULT_NAME ) && !(pTestPage->Equals(*pBMMPage)) )
            {
                pBookmarkDoc->RenameLayoutTemplate(
                    pBMMPage->GetLayoutName(), pBMMPage->GetName() + "_");
                aLayout = pBMMPage->GetName();
 
                break;
            }
            else
                bFound = true;
        }
    }
 
    if (!bFound)
        mrLayoutsToTransfer.push_back(aLayout);
}
 
// Inserts a bookmark as a page
static void lcl_IterateBookmarkPages( SdDrawDocument &rDoc, SdDrawDocument* pBookmarkDoc,
                               const std::vector<OUString> &rBookmarkList, sal_uInt16 nBMSdPageCount,
                               InsertBookmarkAsPage_FindDuplicateLayouts& rPageIterator, bool bRenameDuplicates )
{
 
    // Refactored copy'n'pasted layout name collection from InsertBookmarkAsPage
 
    int nPos, nEndPos;
 
    if( rBookmarkList.empty() )
    {
        // no list? whole source document
        nEndPos = nBMSdPageCount;
    }
    else
    {
        // bookmark list? number of entries
        nEndPos = rBookmarkList.size();
    }
 
    SdPage* pBMPage;
 
    // iterate over number of pages to insert
    for (nPos = 0; nPos < nEndPos; ++nPos)
    {
        // the master page associated to the nPos'th page to insert
        SdPage* pBMMPage = nullptr;
 
        if( rBookmarkList.empty() )
        {
            // simply take master page of nPos'th page in source document
            pBMMPage = static_cast<SdPage*>(&(pBookmarkDoc->GetSdPage(static_cast<sal_uInt16>(nPos), PageKind::Standard)->TRG_GetMasterPage()));
        }
        else
        {
            // fetch nPos'th entry from bookmark list, and determine master page
            OUString aBMPgName(rBookmarkList[nPos]);
            bool  bIsMasterPage;
 
            sal_uInt16 nBMPage = pBookmarkDoc->GetPageByName( aBMPgName, bIsMasterPage );
 
            if (nBMPage != SDRPAGE_NOTFOUND)
            {
                pBMPage = static_cast<SdPage*>( pBookmarkDoc->GetPage(nBMPage) );
            }
            else
            {
                pBMPage = nullptr;
            }
 
            // enforce that bookmarked page is a standard page and not already a master page
            if (pBMPage && pBMPage->GetPageKind()==PageKind::Standard && !pBMPage->IsMasterPage())
            {
                const sal_uInt16 nBMSdPage = (nBMPage - 1) / 2;
                pBMMPage = static_cast<SdPage*> (&(pBookmarkDoc->GetSdPage(nBMSdPage, PageKind::Standard)->TRG_GetMasterPage()));
            }
        }
 
        // successfully determined valid (bookmarked) page?
        if( pBMMPage )
        {
            // yes, call functor
            rPageIterator( rDoc, pBMMPage, bRenameDuplicates, pBookmarkDoc );
        }
    }
}
 
// Opens a bookmark document
SdDrawDocument* SdDrawDocument::OpenBookmarkDoc(SfxMedium* pMedium)
{
    bool bOK = true;
    SdDrawDocument* pBookmarkDoc = nullptr;
    OUString aBookmarkName = pMedium->GetName();
    std::shared_ptr<const SfxFilter> pFilter = pMedium->GetFilter();
    if ( !pFilter )
    {
        pMedium->UseInteractionHandler( true );
        SfxGetpApp()->GetFilterMatcher().GuessFilter(*pMedium, pFilter);
    }
 
    if ( !pFilter )
    {
        bOK = false;
    }
    else if ( !aBookmarkName.isEmpty() && maBookmarkFile != aBookmarkName )
    {
        bool bCreateGraphicShell = pFilter->GetServiceName() == "com.sun.star.drawing.DrawingDocument";
        bool bCreateImpressShell = pFilter->GetServiceName() == "com.sun.star.presentation.PresentationDocument";
        if ( bCreateGraphicShell || bCreateImpressShell )
        {
            CloseBookmarkDoc();
 
            // Create a DocShell, as OLE objects might be contained in the
            // document. (Persist)
            // If that wasn't the case, we could load the model directly.
            if ( bCreateGraphicShell )
                // Draw
                mxBookmarkDocShRef = new ::sd::GraphicDocShell(SfxObjectCreateMode::STANDARD);
            else
                // Impress
                mxBookmarkDocShRef = new ::sd::DrawDocShell(SfxObjectCreateMode::STANDARD, true, DocumentType::Impress);
 
            bOK = mxBookmarkDocShRef->DoLoad(pMedium);
            if( bOK )
            {
                maBookmarkFile = aBookmarkName;
                pBookmarkDoc = mxBookmarkDocShRef->GetDoc();
            }
        }
    }
 
    DBG_ASSERT(!aBookmarkName.isEmpty(), "Empty document name!");
 
    if (!bOK)
    {
        std::unique_ptr<weld::MessageDialog> xErrorBox(Application::CreateMessageDialog(nullptr,
                                                       VclMessageType::Warning, VclButtonsType::Ok, SdResId(STR_READ_DATA_ERROR)));
        xErrorBox->run();
 
        CloseBookmarkDoc();
        pBookmarkDoc = nullptr;
    }
    else if (mxBookmarkDocShRef.is())
    {
        pBookmarkDoc = mxBookmarkDocShRef->GetDoc();
    }
 
    return pBookmarkDoc;
}
 
// Opens a bookmark document
SdDrawDocument* SdDrawDocument::OpenBookmarkDoc(const OUString& rBookmarkFile)
{
    SdDrawDocument* pBookmarkDoc = nullptr;
 
    if (!rBookmarkFile.isEmpty() && maBookmarkFile != rBookmarkFile)
    {
        std::unique_ptr<SfxMedium> xMedium(new SfxMedium(rBookmarkFile, StreamMode::READ));
        pBookmarkDoc = OpenBookmarkDoc(xMedium.release());
    }
    else if (mxBookmarkDocShRef.is())
    {
        pBookmarkDoc = mxBookmarkDocShRef->GetDoc();
    }
 
    return pBookmarkDoc;
}
 
// Inserts a bookmark (page or object)
void SdDrawDocument::InsertBookmark(
    const std::vector<OUString> &rBookmarkList,     // List of names of the bookmarks to be inserted
    std::vector<OUString> &rExchangeList,           // List of the names to be used
    bool bLink,                                     // Insert bookmarks as links?
    sal_uInt16 nInsertPos,                          // Insertion position of pages
    ::sd::DrawDocShell* pBookmarkDocSh,             // If set, this is the source document
    Point const * pObjPos)                                 // Insertion position of objects
{
    bool bOK = true;
    bool bInsertPages = false;
 
    if (rBookmarkList.empty())
    {
        // Insert all pages
        bInsertPages = true;
    }
    else
    {
        SdDrawDocument* pBookmarkDoc = nullptr;
 
        if (pBookmarkDocSh)
        {
            pBookmarkDoc = pBookmarkDocSh->GetDoc();
        }
        else if ( mxBookmarkDocShRef.is() )
        {
            pBookmarkDoc = mxBookmarkDocShRef->GetDoc();
        }
        else
            bOK = false;
 
        std::vector<OUString>::const_iterator pIter;
        for ( pIter = rBookmarkList.begin(); bOK && pIter != rBookmarkList.end() && !bInsertPages; ++pIter )
        {
            // Is there a page name in the bookmark list?
            bool    bIsMasterPage;
            if( pBookmarkDoc->GetPageByName( *pIter, bIsMasterPage ) != SDRPAGE_NOTFOUND )
            {
                // Found the page
                bInsertPages = true;
            }
        }
    }
 
    bool bCalcObjCount = !rExchangeList.empty();
 
    if ( bOK && bInsertPages )
    {
        // Insert all page bookmarks
        bOK = InsertBookmarkAsPage(rBookmarkList, &rExchangeList, bLink, false/*bReplace*/,
                                   nInsertPos, false/*bNoDialogs*/, pBookmarkDocSh, true/*bCopy*/, true, false);
    }
 
    if ( bOK && !rBookmarkList.empty() )
    {
        // Insert all object bookmarks
        InsertBookmarkAsObject(rBookmarkList, rExchangeList,
                                     pBookmarkDocSh, pObjPos, bCalcObjCount);
    }
}
 
namespace
{
 
void
lcl_removeUnusedStyles(SfxStyleSheetBasePool* const pStyleSheetPool, StyleSheetCopyResultVector& rStyles)
{
    StyleSheetCopyResultVector aUsedStyles;
    aUsedStyles.reserve(rStyles.size());
    for (const auto& a : rStyles)
    {
        if (a.m_xStyleSheet->IsUsed())
            aUsedStyles.push_back(a);
        else
            pStyleSheetPool->Remove(a.m_xStyleSheet.get());
    }
    rStyles = aUsedStyles;
}
 
SfxStyleSheet *lcl_findStyle(StyleSheetCopyResultVector& rStyles, const OUString& aStyleName)
{
    for (const auto& a : rStyles)
    {
        if (a.m_xStyleSheet->GetName().startsWith(aStyleName))
            return a.m_xStyleSheet.get();
    }
    return nullptr;
}
 
}
 
bool SdDrawDocument::InsertBookmarkAsPage(
    const std::vector<OUString> &rBookmarkList,
    std::vector<OUString> *pExchangeList,            // List of names to be used
    bool bLink,
    bool bReplace,
    sal_uInt16 nInsertPos,
    bool bNoDialogs,
    ::sd::DrawDocShell* pBookmarkDocSh,
    bool bCopy,
    bool bMergeMasterPages,
    bool bPreservePageNames)
{
    bool bContinue = true;
    bool bScaleObjects = false;
    sal_uInt16 nReplacedStandardPages = 0;
 
    SdDrawDocument* pBookmarkDoc = nullptr;
    OUString aBookmarkName;
 
    if (pBookmarkDocSh)
    {
        pBookmarkDoc = pBookmarkDocSh->GetDoc();
 
        if (pBookmarkDocSh->GetMedium())
        {
            aBookmarkName = pBookmarkDocSh->GetMedium()->GetName();
        }
    }
    else if ( mxBookmarkDocShRef.is() )
    {
        pBookmarkDoc = mxBookmarkDocShRef->GetDoc();
        aBookmarkName = maBookmarkFile;
    }
    else
    {
        return false;
    }
 
    const sal_uInt16 nSdPageCount = GetSdPageCount(PageKind::Standard);
    const sal_uInt16 nBMSdPageCount = pBookmarkDoc->GetSdPageCount(PageKind::Standard);
    const sal_uInt16 nMPageCount = GetMasterPageCount();
 
    if (nSdPageCount==0 || nBMSdPageCount==0 || nMPageCount==0)
    {
        return false;
    }
 
    // Store the size and some other properties of the first page and notes
    // page so that inserted pages can be properly scaled even when inserted
    // before the first page.
    // Note that the pointers are used later on as general page pointers.
    SdPage* pRefPage = GetSdPage(0, PageKind::Standard);
    Size  aSize(pRefPage->GetSize());
    sal_Int32 nLeft  = pRefPage->GetLeftBorder();
    sal_Int32 nRight = pRefPage->GetRightBorder();
    sal_Int32 nUpper = pRefPage->GetUpperBorder();
    sal_Int32 nLower = pRefPage->GetLowerBorder();
    Orientation eOrient = pRefPage->GetOrientation();
 
    SdPage* pNPage = GetSdPage(0, PageKind::Notes);
    Size aNSize(pNPage->GetSize());
    sal_Int32 nNLeft  = pNPage->GetLeftBorder();
    sal_Int32 nNRight = pNPage->GetRightBorder();
    sal_Int32 nNUpper = pNPage->GetUpperBorder();
    sal_Int32 nNLower = pNPage->GetLowerBorder();
    Orientation eNOrient = pNPage->GetOrientation();
 
    // Adapt page size and margins to those of the later pages?
    pRefPage = GetSdPage(nSdPageCount - 1, PageKind::Standard);
 
    if( bNoDialogs )
    {
        // If this is clipboard, then no need to scale objects:
        // this will make copied masters to differ from the originals,
        // and thus InsertBookmarkAsPage_FindDuplicateLayouts will
        // duplicate masters on insert to same document
        bTransportContainer = (SD_MOD()->pTransferClip &&
                               SD_MOD()->pTransferClip->GetWorkDocument() == this);
        if (!bTransportContainer)
        {
            if (rBookmarkList.empty())
                bScaleObjects = pRefPage->IsScaleObjects();
            else
                bScaleObjects = true;
        }
    }
    else
    {
        SdPage* pBMPage = pBookmarkDoc->GetSdPage(0,PageKind::Standard);
 
        if (pBMPage->GetSize()        != pRefPage->GetSize()         ||
            pBMPage->GetLeftBorder()   != pRefPage->GetLeftBorder()    ||
            pBMPage->GetRightBorder()   != pRefPage->GetRightBorder()    ||
            pBMPage->GetUpperBorder()   != pRefPage->GetUpperBorder()    ||
            pBMPage->GetLowerBorder()   != pRefPage->GetLowerBorder())
        {
            OUString aStr(SdResId(STR_SCALE_OBJECTS));
            std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(nullptr,
                                                           VclMessageType::Question, VclButtonsType::YesNo,
                                                           aStr));
            xQueryBox->add_button(Button::GetStandardText(StandardButtonType::Cancel), RET_CANCEL);
            sal_uInt16 nBut = xQueryBox->run();
 
            bScaleObjects = nBut == RET_YES;
            bContinue     = nBut != RET_CANCEL;
 
            if (!bContinue)
            {
                return bContinue;
            }
        }
    }
 
    // Get the necessary presentation stylesheets and transfer them before
    // the pages, else, the text objects won't reference their styles anymore.
    SfxUndoManager* pUndoMgr = nullptr;
    if( mpDocSh )
    {
        pUndoMgr = mpDocSh->GetUndoManager();
        ViewShellId nViewShellId(-1);
        if (sd::ViewShell* pViewShell = mpDocSh->GetViewShell())
            nViewShellId = pViewShell->GetViewShellBase().GetViewShellId();
        pUndoMgr->EnterListAction(SdResId(STR_UNDO_INSERTPAGES), "", 0, nViewShellId);
    }
 
    // Refactored copy'n'pasted layout name collection into IterateBookmarkPages
 
    std::vector<OUString> aLayoutsToTransfer;
    InsertBookmarkAsPage_FindDuplicateLayouts aSearchFunctor( aLayoutsToTransfer );
    lcl_IterateBookmarkPages( *this, pBookmarkDoc, rBookmarkList, nBMSdPageCount, aSearchFunctor, ( rBookmarkList.empty() && pBookmarkDoc != this ) );
 
    // Copy the style that we actually need.
    SdStyleSheetPool& rBookmarkStyleSheetPool = dynamic_cast<SdStyleSheetPool&>(*pBookmarkDoc->GetStyleSheetPool());
    SdStyleSheetPool& rStyleSheetPool = dynamic_cast<SdStyleSheetPool&>(*GetStyleSheetPool());
 
    // When copying styles, also copy the master pages!
    if( !aLayoutsToTransfer.empty() )
        bMergeMasterPages = true;
 
    std::vector<OUString>::const_iterator pIter;
    for ( pIter = aLayoutsToTransfer.begin(); pIter != aLayoutsToTransfer.end(); ++pIter )
    {
        StyleSheetCopyResultVector aCreatedStyles;
        OUString layoutName = *pIter;
 
        rStyleSheetPool.CopyLayoutSheets(layoutName, rBookmarkStyleSheetPool,aCreatedStyles);
 
        if(!aCreatedStyles.empty())
        {
            if( pUndoMgr )
            {
                SdMoveStyleSheetsUndoAction* pMovStyles = new SdMoveStyleSheetsUndoAction(this, aCreatedStyles, true);
                pUndoMgr->AddUndoAction(pMovStyles);
            }
        }
    }
 
    // Copy styles. This unconditionally copies all styles, even those
    // that are not used in any of the inserted pages. The unused styles
    // are then removed at the end of the function, where we also create
    // undo records for the inserted styles.
    StyleSheetCopyResultVector aNewGraphicStyles;
    OUString aRenameStr;
    if(!bReplace && !bNoDialogs)
        aRenameStr = "_";
    rStyleSheetPool.RenameAndCopyGraphicSheets(rBookmarkStyleSheetPool, aNewGraphicStyles, aRenameStr);
    StyleSheetCopyResultVector aNewCellStyles;
    rStyleSheetPool.CopyCellSheets(rBookmarkStyleSheetPool, aNewCellStyles);
 
    // TODO handle undo of table styles too
    rStyleSheetPool.CopyTableStyles(rBookmarkStyleSheetPool);
 
    // Insert document
 
    const bool bUndo = IsUndoEnabled();
 
    if( bUndo )
        BegUndo(SdResId(STR_UNDO_INSERTPAGES));
 
    if (rBookmarkList.empty())
    {
        if (nInsertPos >= GetPageCount())
        {
            // Add pages to the end
            nInsertPos = GetPageCount();
        }
 
        sal_uInt16 nActualInsertPos = nInsertPos;
 
        sal_uInt16 nBMSdPage;
        std::set<sal_uInt16> aRenameSet;
        std::map<sal_uInt16,OUString> aNameMap;
 
        for (nBMSdPage=0; nBMSdPage < nBMSdPageCount; nBMSdPage++)
        {
            SdPage* pBMPage = pBookmarkDoc->GetSdPage(nBMSdPage, PageKind::Standard);
            OUString sName(pBMPage->GetName());
            bool    bIsMasterPage;
 
            if (bLink)
            {
                // Remember the names of all pages
                aNameMap.insert(std::make_pair(nBMSdPage,sName));
            }
 
            // Have to check for duplicate names here, too
            // don't change name if source and dest model are the same!
            if( pBookmarkDoc != this &&
                GetPageByName(sName, bIsMasterPage ) != SDRPAGE_NOTFOUND )
            {
                // delay renaming *after* pages are copied (might destroy source otherwise)
                aRenameSet.insert(nBMSdPage);
            }
        }
 
        Merge(*pBookmarkDoc,
              1,                 // Not the handout page
              0xFFFF,            // But all others
              nActualInsertPos,  // Insert at position ...
              bMergeMasterPages, // Move master pages?
              false,             // But only the master pages used
              true,              // Create an undo action
              bCopy);            // Copy (or merge) pages?
 
        for (nBMSdPage=0; nBMSdPage < nBMSdPageCount; nBMSdPage++)
        {
            SdPage* pPage       = static_cast<SdPage*>( GetPage(nActualInsertPos) );
            SdPage* pNotesPage  = static_cast<SdPage*>( GetPage(nActualInsertPos+1) );
 
            // delay renaming *after* pages are copied (might destroy source otherwise)
            if( aRenameSet.find(nBMSdPage) != aRenameSet.end() )
            {
                // Page name already in use -> Use default name for default and
                // notes page
                pPage->SetName(OUString());
                pNotesPage->SetName(OUString());
            }
 
            if (bLink)
            {
                OUString aName(aNameMap[nBMSdPage]);
 
                // Assemble all link names
                pPage->SetFileName(aBookmarkName);
                pPage->SetBookmarkName(aName);
            }
 
            nActualInsertPos += 2;
        }
    }
    else
    {
        // Insert selected pages
        SdPage* pBMPage;
 
        if (nInsertPos >= GetPageCount())
        {
            // Add pages to the end
            bReplace = false;
            nInsertPos = GetPageCount();
        }
 
        sal_uInt16 nActualInsertPos = nInsertPos;
 
        // Collect the bookmarked pages
        ::std::vector<SdPage*> aBookmarkedPages (rBookmarkList.size(), nullptr);
        for ( size_t nPos = 0, n = rBookmarkList.size(); nPos < n; ++nPos)
        {
            OUString aPgName(rBookmarkList[nPos]);
            bool    bIsMasterPage;
            sal_uInt16  nBMPage = pBookmarkDoc->GetPageByName( aPgName, bIsMasterPage );
 
            if (nBMPage != SDRPAGE_NOTFOUND)
            {
                aBookmarkedPages[nPos] =  dynamic_cast<SdPage*>(pBookmarkDoc->GetPage(nBMPage));
            }
        }
 
        for ( size_t nPos = 0, n = rBookmarkList.size(); nPos < n; ++nPos)
        {
            pBMPage = aBookmarkedPages[nPos];
            sal_uInt16 nBMPage = pBMPage!=nullptr ? pBMPage->GetPageNum() : SDRPAGE_NOTFOUND;
 
            if (pBMPage && pBMPage->GetPageKind()==PageKind::Standard && !pBMPage->IsMasterPage())
            {
                // It has to be a default page
                bool bMustRename = false;
 
                // delay renaming *after* pages are copied (might destroy source otherwise)
                // don't change name if source and dest model are the same!
                // avoid renaming if replacing the same page
                OUString aPgName(rBookmarkList[nPos]);
                bool    bIsMasterPage;
                sal_uInt16 nPageSameName = GetPageByName(aPgName, bIsMasterPage);
                if( pBookmarkDoc != this &&
                    nPageSameName != SDRPAGE_NOTFOUND &&
                    ( !bReplace ||
                      nPageSameName != nActualInsertPos ) )
                {
                    bMustRename = true;
                }
 
                SdPage* pBookmarkPage = pBMPage;
                if (bReplace )
                {
                    ReplacePageInCustomShows( dynamic_cast< SdPage* >( GetPage( nActualInsertPos ) ), pBookmarkPage );
                }
 
                Merge(*pBookmarkDoc,
                      nBMPage,           // From page (default page)
                      nBMPage+1,         // To page (notes page)
                      nActualInsertPos,  // Insert at position
                      bMergeMasterPages, // Move master pages?
                      false,             // But only the master pages used
                      true,              // Create undo action
                      bCopy);            // Copy (or merge) pages?
 
                if( bReplace )
                {
                    if( GetPage( nActualInsertPos ) != pBookmarkPage )
                    {
                        // bookmark page was not moved but cloned, so update custom shows again
                        ReplacePageInCustomShows( pBookmarkPage, dynamic_cast< SdPage* >( GetPage( nActualInsertPos ) ) );
                    }
                }
 
                if( bMustRename )
                {
                    // Page name already in use -> use default name for default and
                    // notes page
                    SdPage* pPage = static_cast<SdPage*>( GetPage(nActualInsertPos) );
                    pPage->SetName(OUString());
                    SdPage* pNotesPage = static_cast<SdPage*>( GetPage(nActualInsertPos+1) );
                    pNotesPage->SetName(OUString());
                }
 
                if (bLink)
                {
                    SdPage* pPage = static_cast<SdPage*>( GetPage(nActualInsertPos) );
                    pPage->SetFileName(aBookmarkName);
                    pPage->SetBookmarkName(aPgName);
                }
 
                if (bReplace)
                {
                    // Remove page and notes page.
                    const sal_uInt16 nDestPageNum(nActualInsertPos + 2);
                    SdPage* pStandardPage = nullptr;
 
                    if(nDestPageNum < GetPageCount())
                    {
                        pStandardPage = static_cast<SdPage*>(GetPage(nDestPageNum));
                    }
 
                    if (pStandardPage)
                    {
                        if( bPreservePageNames )
                        {
                            // Take old slide names for inserted pages
                            SdPage* pPage = static_cast<SdPage*>( GetPage(nActualInsertPos) );
                            pPage->SetName( pStandardPage->GetRealName() );
                        }
 
                        if( bUndo )
                            AddUndo(GetSdrUndoFactory().CreateUndoDeletePage(*pStandardPage));
 
                        RemovePage(nDestPageNum);
 
                        if( !bUndo )
                            delete pStandardPage;
                    }
 
                    SdPage* pNotesPage = nullptr;
 
                    if(nDestPageNum < GetPageCount())
                    {
                        pNotesPage = static_cast<SdPage*>(GetPage(nDestPageNum));
                    }
 
                    if (pNotesPage)
                    {
                        if( bPreservePageNames )
                        {
                            // Take old slide names for inserted pages
                            SdPage* pNewNotesPage = static_cast<SdPage*>( GetPage(nActualInsertPos+1));
                            if( pNewNotesPage )
                                pNewNotesPage->SetName( pStandardPage->GetRealName() );
                        }
 
                        if( bUndo )
                            AddUndo(GetSdrUndoFactory().CreateUndoDeletePage(*pNotesPage));
 
                        RemovePage(nDestPageNum);
 
                        if( !bUndo )
                            delete pNotesPage;
                    }
 
                    nReplacedStandardPages++;
                }
 
                nActualInsertPos += 2;
            }
        }
    }
 
    // We might have duplicate master pages now, as the drawing engine does not
    // recognize duplicates. Remove these now.
    sal_uInt16 nNewMPageCount = GetMasterPageCount();
 
    // Go backwards, so the numbers don't become messed up
    for (sal_uInt16 nPage = nNewMPageCount - 1; nPage >= nMPageCount; nPage--)
    {
        pRefPage = static_cast<SdPage*>( GetMasterPage(nPage) );
        OUString aMPLayout(pRefPage->GetLayoutName());
        PageKind eKind = pRefPage->GetPageKind();
 
        // Does this already exist?
        for (sal_uInt16 nTest = 0; nTest < nMPageCount; nTest++)
        {
            SdPage* pTest = static_cast<SdPage*>( GetMasterPage(nTest) );
            OUString aTest(pTest->GetLayoutName());
 
            // nInsertPos > 2 is always true when inserting into non-empty models
            if ( nInsertPos > 2 &&
                 aTest == aMPLayout &&
                 eKind == pTest->GetPageKind() )
            {
                if( bUndo )
                    AddUndo(GetSdrUndoFactory().CreateUndoDeletePage(*pRefPage));
 
                RemoveMasterPage(nPage);
 
                if( !bUndo )
                    delete pRefPage;
                nNewMPageCount--;
                break;
            }
        }
    }
 
    // nInsertPos > 2 is always true when inserting into non-empty models
    if (nInsertPos > 0)
    {
        sal_uInt16 nSdPageStart = (nInsertPos - 1) / 2;
        sal_uInt16 nSdPageEnd = GetSdPageCount(PageKind::Standard) - nSdPageCount +
                            nSdPageStart - 1;
        const bool bRemoveEmptyPresObj =
                (pBookmarkDoc->GetDocumentType() == DocumentType::Impress) &&
                (GetDocumentType() == DocumentType::Draw);
 
        if( bReplace )
        {
            nSdPageEnd = nSdPageStart + nReplacedStandardPages - 1;
        }
 
        std::vector<OUString>::iterator pExchangeIter;
 
        if (pExchangeList)
            pExchangeIter = pExchangeList->begin();
 
        for (sal_uInt16 nSdPage = nSdPageStart; nSdPage <= nSdPageEnd; nSdPage++)
        {
            pRefPage = GetSdPage(nSdPage, PageKind::Standard);
 
            if (pExchangeList && pExchangeIter != pExchangeList->end())
            {
                // Get the name to use from Exchange list
                OUString aExchangeName(*pExchangeIter);
                pRefPage->SetName(aExchangeName);
                Broadcast(SdrHint(SdrHintKind::PageOrderChange, pRefPage));
 
                SdPage* pNewNotesPage = GetSdPage(nSdPage, PageKind::Notes);
                pNewNotesPage->SetName(aExchangeName);
                Broadcast(SdrHint(SdrHintKind::PageOrderChange, pNewNotesPage));
 
                ++pExchangeIter;
            }
 
            OUString aLayout(pRefPage->GetLayoutName());
            sal_Int32 nIndex = aLayout.indexOf( SD_LT_SEPARATOR );
            if( nIndex != -1 )
                aLayout = aLayout.copy(0, nIndex);
 
            // update layout and referred master page
            pRefPage->SetPresentationLayout(aLayout);
            if( bUndo )
                AddUndo( GetSdrUndoFactory().CreateUndoPageChangeMasterPage( *pRefPage ) );
 
            if (bScaleObjects)
            {
                ::tools::Rectangle aBorderRect(nLeft, nUpper, nRight, nLower);
                pRefPage->ScaleObjects(aSize, aBorderRect, true);
            }
            pRefPage->SetSize(aSize);
            pRefPage->SetBorder(nLeft, nUpper, nRight, nLower);
            pRefPage->SetOrientation( eOrient );
 
            if( bRemoveEmptyPresObj )
                pRefPage->RemoveEmptyPresentationObjects();
 
            pRefPage = GetSdPage(nSdPage, PageKind::Notes);
 
            // update layout and referred master page
            pRefPage->SetPresentationLayout(aLayout);
            if( bUndo )
                AddUndo( GetSdrUndoFactory().CreateUndoPageChangeMasterPage( *pRefPage ) );
 
            if (bScaleObjects)
            {
                ::tools::Rectangle aBorderRect(nNLeft, nNUpper, nNRight, nNLower);
                pRefPage->ScaleObjects(aNSize, aBorderRect, true);
            }
 
            pRefPage->SetSize(aNSize);
            pRefPage->SetBorder(nNLeft, nNUpper, nNRight, nNLower);
            pRefPage->SetOrientation( eNOrient );
 
            if( bRemoveEmptyPresObj )
                pRefPage->RemoveEmptyPresentationObjects();
        }
 
        ///Remove processed elements, to avoid doings hacks in InsertBookmarkAsObject
        if ( pExchangeList )
            pExchangeList->erase(pExchangeList->begin(),pExchangeIter);
 
        for (sal_uInt16 nPage = nMPageCount; nPage < nNewMPageCount; nPage++)
        {
            pRefPage = static_cast<SdPage*>( GetMasterPage(nPage) );
            if (pRefPage->GetPageKind() == PageKind::Standard)
            {
                if (bScaleObjects)
                {
                    ::tools::Rectangle aBorderRect(nLeft, nUpper, nRight, nLower);
                    pRefPage->ScaleObjects(aSize, aBorderRect, true);
                }
                pRefPage->SetSize(aSize);
                pRefPage->SetBorder(nLeft, nUpper, nRight, nLower);
                pRefPage->SetOrientation( eOrient );
            }
            else        // Can only be notes
            {
                if (bScaleObjects)
                {
                    ::tools::Rectangle aBorderRect(nNLeft, nNUpper, nNRight, nNLower);
                    pRefPage->ScaleObjects(aNSize, aBorderRect, true);
                }
                pRefPage->SetSize(aNSize);
                pRefPage->SetBorder(nNLeft, nNUpper, nNRight, nNLower);
                pRefPage->SetOrientation( eNOrient );
            }
 
            if( bRemoveEmptyPresObj )
                pRefPage->RemoveEmptyPresentationObjects();
        }
    }
 
    // Make absolutely sure no double masterpages are there
    RemoveUnnecessaryMasterPages(nullptr, true);
 
    // Rename object styles if necessary
    if(!aRenameStr.isEmpty())
    {
        try
        {
            for(sal_uInt32 p = nInsertPos; p < sal_uInt32(nInsertPos) + sal_uInt32(nBMSdPageCount); p++)
            {
                SdPage *pPg = static_cast<SdPage *>( GetPage(p) );
                for(size_t i = 0; pPg && (i < pPg->GetObjCount()); ++i)
                {
                    if(pPg->GetObj(i)->GetStyleSheet())
                    {
                        OUString aStyleName = pPg->GetObj(i)->GetStyleSheet()->GetName();
                        SfxStyleSheet *pSheet = lcl_findStyle(aNewGraphicStyles, aStyleName + aRenameStr);
                        if(pSheet != nullptr)
                            pPg->GetObj(i)->SetStyleSheet(pSheet, true);
                    }
                }
            }
        }
        catch(...)
        {
            OSL_FAIL("Exception while renaming styles @ SdDrawDocument::InsertBookmarkAsPage");
        }
    }
    // remove copied styles not used on any inserted page and create
    // undo records
    // WARNING: SdMoveStyleSheetsUndoAction clears the passed list of
    // styles, so it cannot be used after this point
    lcl_removeUnusedStyles(GetStyleSheetPool(), aNewGraphicStyles);
    if (!aNewGraphicStyles.empty() && pUndoMgr)
        pUndoMgr->AddUndoAction(new SdMoveStyleSheetsUndoAction(this, aNewGraphicStyles, true));
    lcl_removeUnusedStyles(GetStyleSheetPool(), aNewCellStyles);
    if (!aNewCellStyles.empty() && pUndoMgr)
        pUndoMgr->AddUndoAction(new SdMoveStyleSheetsUndoAction(this, aNewCellStyles, true));
 
    if( bUndo )
        EndUndo();
 
    if (pUndoMgr)
        pUndoMgr->LeaveListAction();
 
    return bContinue;
}
 
// Inserts a bookmark as an object
bool SdDrawDocument::InsertBookmarkAsObject(
    const std::vector<OUString> &rBookmarkList,
    const std::vector<OUString> &rExchangeList,            // List of names to use
    ::sd::DrawDocShell* pBookmarkDocSh,
    Point const * pObjPos,
    bool bCalcObjCount)
{
    bool bOK = true;
    bool bOLEObjFound = false;
    std::unique_ptr<::sd::View> pBMView;
 
    SdDrawDocument* pBookmarkDoc = nullptr;
 
    if (pBookmarkDocSh)
    {
        pBookmarkDoc = pBookmarkDocSh->GetDoc();
    }
    else if ( mxBookmarkDocShRef.is() )
    {
        pBookmarkDoc = mxBookmarkDocShRef->GetDoc();
    }
    else
    {
        return false;
    }
 
    if (rBookmarkList.empty())
    {
        pBMView.reset(new ::sd::View(*pBookmarkDoc, nullptr));
        pBMView->EndListening(*pBookmarkDoc);
        pBMView->MarkAll();
    }
    else
    {
        SdrPage* pPage;
        SdrPageView* pPV;
 
        std::vector<OUString>::const_iterator pIter;
        for ( pIter = rBookmarkList.begin(); pIter != rBookmarkList.end(); ++pIter )
        {
            // Get names of bookmarks from the list
            SdrObject* pObj = pBookmarkDoc->GetObj(*pIter);
 
            if (pObj)
            {
                // Found an object
                if (pObj->GetObjInventor() == SdrInventor::Default &&
                    pObj->GetObjIdentifier() == OBJ_OLE2)
                {
                    bOLEObjFound = true;
                }
 
                if (!pBMView)
                {
                    // Create View for the first time
                    pBMView.reset(new ::sd::View(*pBookmarkDoc, nullptr));
                    pBMView->EndListening(*pBookmarkDoc);
                }
 
                pPage = pObj->getSdrPageFromSdrObject();
 
                if (pPage->IsMasterPage())
                {
                    pPV = pBMView->ShowSdrPage(pBMView->GetModel()->GetMasterPage(pPage->GetPageNum()));
                }
                else
                {
                    pPV = pBMView->GetSdrPageView();
                    if( !pPV || (pPV->GetPage() != pPage))
                        pPV = pBMView->ShowSdrPage(pPage);
                }
 
                pBMView->MarkObj(pObj, pPV);
            }
        }
    }
 
    if (pBMView)
    {
        // Insert selected objects
        ::sd::View* pView = new ::sd::View(*this, nullptr);
        pView->EndListening(*this);
 
        // Look for the page into which the objects are supposed to be inserted
        SdrPage* pPage = GetSdPage(0, PageKind::Standard);
 
        if (mpDocSh)
        {
            ::sd::ViewShell* pViewSh = mpDocSh->GetViewShell();
 
            if (pViewSh)
            {
                // Which page is currently in view?
                SdrPageView* pPV = pViewSh->GetView()->GetSdrPageView();
 
                if (pPV)
                {
                    pPage = pPV->GetPage();
                }
                else if (pViewSh->GetActualPage())
                {
                    pPage = pViewSh->GetActualPage();
                }
            }
        }
 
        Point aObjPos;
 
        if (pObjPos)
        {
            aObjPos = *pObjPos;
        }
        else
        {
            aObjPos = ::tools::Rectangle(Point(), pPage->GetSize()).Center();
        }
 
        size_t nCountBefore = 0;
 
        if (!rExchangeList.empty() || bCalcObjCount)
        {
            // Sort OrdNums and get the number of objects before inserting
            pPage->RecalcObjOrdNums();
            nCountBefore = pPage->GetObjCount();
        }
 
        if (bOLEObjFound)
            pBMView->GetDoc().SetAllocDocSh(true);
 
        SdDrawDocument* pTmpDoc = static_cast<SdDrawDocument*>( pBMView->CreateMarkedObjModel().release() );
        bOK = pView->Paste(*pTmpDoc, aObjPos, pPage, SdrInsertFlags::NONE);
 
        if (bOLEObjFound)
            pBMView->GetDoc().SetAllocDocSh(false);
 
        if (!bOLEObjFound)
            delete pTmpDoc;         // Would otherwise be destroyed by DocShell
 
        delete pView;
 
        if (!rExchangeList.empty())
        {
            // Get number of objects after inserting.
            const size_t nCount = pPage->GetObjCount();
 
            std::vector<OUString>::const_iterator pIter = rExchangeList.begin();
            for (size_t nObj = nCountBefore; nObj < nCount; ++nObj)
            {
                // Get the name to use from the Exchange list
                if (pIter != rExchangeList.end())
                {
                    if (pPage->GetObj(nObj))
                    {
                        pPage->GetObj(nObj)->SetName(*pIter);
                    }
 
                    ++pIter;
                }
            }
        }
    }
 
    return bOK;
}
 
// Stops the bookmark insertion
void SdDrawDocument::CloseBookmarkDoc()
{
    if (mxBookmarkDocShRef.is())
    {
        mxBookmarkDocShRef->DoClose();
    }
 
    mxBookmarkDocShRef.clear();
    maBookmarkFile.clear();
}
 
// Is this document read-only?
bool SdDrawDocument::IsReadOnly() const
{
    return false;
}
 
// In the subsequent AllocModel() a DocShell (xAllocedDocShRef) is created.
// Any pre-existing DocShell is deleted
void SdDrawDocument::SetAllocDocSh(bool bAlloc)
{
    mbAllocDocSh = bAlloc;
 
    if(mxAllocedDocShRef.is())
    {
        mxAllocedDocShRef->DoClose();
    }
 
    mxAllocedDocShRef.clear();
}
 
// Return list of CustomShows (create it, too, if necessary)
SdCustomShowList* SdDrawDocument::GetCustomShowList(bool bCreate)
{
    if (!mpCustomShowList && bCreate)
    {
        mpCustomShowList.reset(new SdCustomShowList);
    }
 
    return mpCustomShowList.get();
}
 
// Remove unused master pages and layouts
void SdDrawDocument::RemoveUnnecessaryMasterPages(SdPage* pMasterPage, bool bOnlyDuplicatePages, bool bUndo)
{
    ::sd::View* pView = nullptr;
    SfxUndoManager* pUndoMgr = nullptr;
 
    if( bUndo && !IsUndoEnabled() )
        bUndo = false;
 
    if (mpDocSh)
    {
        pUndoMgr = mpDocSh->GetUndoManager();
 
        if (mpDocSh->GetViewShell())
            pView = mpDocSh->GetViewShell()->GetView();
    }
 
    // Check all master pages
    sal_uInt16 nSdMasterPageCount = GetMasterSdPageCount( PageKind::Standard );
    for (sal_Int32 nMPage = nSdMasterPageCount - 1; nMPage >= 0; nMPage--)
    {
        SdPage* pMaster = pMasterPage;
        SdPage* pNotesMaster = nullptr;
 
        if (!pMaster)
        {
            pMaster = GetMasterSdPage( static_cast<sal_uInt16>(nMPage), PageKind::Standard );
            pNotesMaster = GetMasterSdPage( static_cast<sal_uInt16>(nMPage), PageKind::Notes );
        }
        else
        {
            for ( sal_uInt16 nMPg = 0; nMPg < GetMasterPageCount(); nMPg++ )
            {
                if ( pMaster == GetMasterPage( nMPg ) )
                {
                    pNotesMaster = static_cast<SdPage*>( GetMasterPage( ++nMPg ) );
                    break;
                }
            }
        }
 
        DBG_ASSERT( pMaster->GetPageKind() == PageKind::Standard, "wrong page kind" );
 
        if ( pMaster->GetPageKind() == PageKind::Standard &&
             GetMasterPageUserCount( pMaster ) == 0 &&
             pNotesMaster )
        {
            // Do not delete master pages that have their precious flag set
            bool bDeleteMaster = !pMaster->IsPrecious();
            OUString aLayoutName = pMaster->GetLayoutName();
 
            if(bOnlyDuplicatePages )
            {
                // remove only duplicate pages
                bDeleteMaster = false;
                for (sal_uInt16 i = 0; i < GetMasterSdPageCount( PageKind::Standard ); i++)
                {
                    SdPage* pMPg = GetMasterSdPage( i, PageKind::Standard );
                    if( pMPg != pMaster &&
                        pMPg->GetLayoutName() == aLayoutName )
                    {
                        // duplicate page found -> remove it
                        bDeleteMaster = true;
                    }
                }
            }
 
            if( bDeleteMaster )
            {
                if (pView)
                {
                    // if MasterPage is visible hide on pageview
                    SdrPageView* pPgView = pView->GetSdrPageView();
                    if (pPgView)
                    {
                        SdrPage* pShownPage = pPgView->GetPage();
                        if( (pShownPage == pMaster) || (pShownPage == pNotesMaster) )
                        {
                            pView->HideSdrPage();
                            pView->ShowSdrPage( GetSdPage( 0, PageKind::Standard ) );
                        }
                    }
                }
 
                if( bUndo )
                {
                    BegUndo();
                    AddUndo( GetSdrUndoFactory().CreateUndoDeletePage( *pNotesMaster ) );
                }
 
                RemoveMasterPage( pNotesMaster->GetPageNum() );
 
                if( !bUndo )
                    delete pNotesMaster;
 
                if( bUndo )
                    AddUndo(GetSdrUndoFactory().CreateUndoDeletePage(*pMaster));
 
                RemoveMasterPage( pMaster->GetPageNum() );
 
                if( !bUndo )
                    delete pMaster;
 
                if( bUndo )
                    EndUndo();  // do this here already, so Joe's actions happen _between_ our own
 
                // Delete old, unused layout stylesheets
                bool bDeleteOldStyleSheets = true;
                for ( sal_uInt16 nMPg = 0;
                          nMPg < GetMasterPageCount() && bDeleteOldStyleSheets;
                       nMPg++ )
                {
                    SdPage* pMPg = static_cast<SdPage*>( GetMasterPage(nMPg) );
                    if (pMPg->GetLayoutName() == aLayoutName)
                    {
                        bDeleteOldStyleSheets = false;
                    }
                }
 
                if (bDeleteOldStyleSheets)
                {
                    SdStyleSheetVector aRemove;
                    static_cast<SdStyleSheetPool*>( mxStyleSheetPool.get())->CreateLayoutSheetList( aLayoutName, aRemove );
 
                    if( bUndo )
                    {
                        StyleSheetCopyResultVector aUndoRemove;
                        aUndoRemove.reserve(aRemove.size());
                        for (const auto& a : aRemove)
                            aUndoRemove.emplace_back(a.get(), true);
                        // This list belongs to UndoAction
                        SdMoveStyleSheetsUndoAction* pMovStyles = new SdMoveStyleSheetsUndoAction(this, aUndoRemove, false);
 
                        if (pUndoMgr)
                            pUndoMgr->AddUndoAction(pMovStyles);
                    }
 
                    for( SdStyleSheetVector::iterator iter = aRemove.begin(); iter != aRemove.end(); ++iter )
                        static_cast<SdStyleSheetPool*>( mxStyleSheetPool.get())->Remove((*iter).get());
                }
            }
        }
 
        if (pMasterPage)
            break;                      // Just this one master page!
    }
}
 
/** Exchange master page
  *
  * Either the nSdPageNum gets a new, own master page or the master page is
  * exchanged completely (which then applies to all pages).
  *
  * nSdPageNum   : page number that the new master page should get.
  * rLayoutName  : LayoutName of the new master page
  * pSourceDoc   : document (template) to get the master page from
  * bMaster      : exchange the master page of nSdPageNum
  * bCheckMasters: remove unused master pages
  *
  * If pSourceDoc == NULL, an empty master page is applied.
  * If rLayoutName is empty, the first master page is used.
  */
// #i121863# factored out functionality
bool isMasterPageLayoutNameUnique(const SdDrawDocument& rDoc, const OUString& rCandidate)
{
    if (rCandidate.isEmpty())
    {
        return false;
    }
 
    const sal_uInt16 nPageCount(rDoc.GetMasterPageCount());
 
    for(sal_uInt16 a(0); a < nPageCount; a++)
    {
        const SdrPage* pCandidate = rDoc.GetMasterPage(a);
        OUString aPageLayoutName(pCandidate->GetLayoutName());
        sal_Int32 nIndex = aPageLayoutName.indexOf(SD_LT_SEPARATOR);
        if( nIndex != -1 )
            aPageLayoutName = aPageLayoutName.copy(0, nIndex);
 
        if(aPageLayoutName == rCandidate)
        {
            return false;
        }
    }
 
    return true;
}
 
// #i121863# factored out functinality
OUString createNewMasterPageLayoutName(const SdDrawDocument& rDoc)
{
    const OUString aBaseName(SdResId(STR_LAYOUT_DEFAULT_NAME));
    sal_uInt16 nCount(0);
    for (;;)
    {
        OUString aRetval = aBaseName;
        if(nCount)
        {
            aRetval += OUString::number(nCount);
        }
        if (isMasterPageLayoutNameUnique(rDoc, aRetval))
            return aRetval;
        nCount++;
    }
}
 
void SdDrawDocument::SetMasterPage(sal_uInt16 nSdPageNum,
                                   const OUString& rLayoutName,
                                   SdDrawDocument* pSourceDoc,
                                   bool bMaster,
                                   bool bCheckMasters)
{
    SfxUndoManager* pUndoMgr = nullptr;
 
    if( mpDocSh )
    {
        mpDocSh->SetWaitCursor( true );
        pUndoMgr = mpDocSh->GetUndoManager();
    }
 
    const bool bUndo = pUndoMgr && IsUndoEnabled();
 
    if (bUndo)
    {
        ViewShellId nViewShellId(-1);
        if (sd::ViewShell* pViewShell = mpDocSh->GetViewShell())
            nViewShellId = pViewShell->GetViewShellBase().GetViewShellId();
        pUndoMgr->EnterListAction(SdResId(STR_UNDO_SET_PRESLAYOUT), OUString(), 0, nViewShellId);
    }
 
    SdPage* pSelectedPage   = GetSdPage(nSdPageNum, PageKind::Standard);
    SdPage* pNotes          = static_cast<SdPage*>( GetPage(pSelectedPage->GetPageNum()+1) );
    SdPage& rOldMaster      = static_cast<SdPage&>(pSelectedPage->TRG_GetMasterPage());
    SdPage& rOldNotesMaster = static_cast<SdPage&>(pNotes->TRG_GetMasterPage());
    SdPage* pMaster         = nullptr;
    SdPage* pNotesMaster    = nullptr;
    OUString aOldPageLayoutName(pSelectedPage->GetLayoutName());
    OUString aOldLayoutName(aOldPageLayoutName);
    sal_Int32 nIndex = aOldLayoutName.indexOf( SD_LT_SEPARATOR );
    if( nIndex != -1 )
        aOldLayoutName = aOldLayoutName.copy(0, nIndex);
 
    if (pSourceDoc)
    {
        std::vector<StyleReplaceData> aReplList; // List of replaced stylesheets
        bool bLayoutReloaded = false;   // Was ex. layout reloaded?
 
        // LayoutName, Page and Notes page
        if (rLayoutName.isEmpty())
        {
            // No LayoutName: take first MasterPage
            pMaster = pSourceDoc->GetMasterSdPage(0, PageKind::Standard);
            pNotesMaster = pSourceDoc->GetMasterSdPage(0, PageKind::Notes);
        }
        else
        {
            OUString aSearchFor = rLayoutName + SD_LT_SEPARATOR STR_LAYOUT_OUTLINE;
 
            for (sal_uInt16 nMP = 0; nMP < pSourceDoc->GetMasterPageCount(); ++nMP)
            {
                SdPage* pMP = static_cast<SdPage*>( pSourceDoc->GetMasterPage(nMP) );
 
                if (pMP->GetLayoutName() == aSearchFor)
                {
                    if (pMP->GetPageKind() == PageKind::Standard)
                        pMaster = pMP;
                    if (pMP->GetPageKind() == PageKind::Notes)
                        pNotesMaster = pMP;
                }
                if (pMaster && pNotesMaster)
                    break;
            }
            DBG_ASSERT(pMaster, "MasterPage (Standard page) not found");
            DBG_ASSERT(pNotesMaster, "MasterPage (Notes page) not found");
 
            // this should not happen, but looking at crash reports, it does
            if( (pMaster == nullptr) || (pNotesMaster == nullptr) )
            {
                // so take the first MasterPage
                pMaster = pSourceDoc->GetMasterSdPage(0, PageKind::Standard);
                pNotesMaster = pSourceDoc->GetMasterSdPage(0, PageKind::Notes);
            }
        }
 
        // we should never reach this, but one never knows...
        if( (pMaster == nullptr) || (pNotesMaster == nullptr) )
        {
            if (bUndo)
                pUndoMgr->LeaveListAction();
 
            if( mpDocSh )
                mpDocSh->SetWaitCursor( false );
 
            OSL_FAIL( "SdDrawDocument::SetMasterPage() failed!" );
 
            return;
        }
 
        const OUString aOriginalNewLayoutName( pMaster->GetName() );
        OUString aTargetNewLayoutName(aOriginalNewLayoutName);
 
        if (pSourceDoc != this)
        {
            // #i121863# clone masterpages, they are from another model (!)
            SdPage* pNewNotesMaster = dynamic_cast< SdPage* >(pNotesMaster->CloneSdrPage(*this));
            SdPage* pNewMaster = dynamic_cast< SdPage* >(pMaster->CloneSdrPage(*this));
 
            if(!pNewNotesMaster || !pNewMaster)
            {
                delete pNewNotesMaster;
                delete pNewMaster;
                OSL_FAIL("SdDrawDocument::SetMasterPage() cloning of MasterPage/NoteAmsterPage failed!" );
                return;
            }
 
            pNotesMaster = pNewNotesMaster;
            pMaster = pNewMaster;
 
            // layout name needs to be unique
            aTargetNewLayoutName = pMaster->GetLayoutName();
            sal_Int32 nIndex2 = aTargetNewLayoutName.indexOf(SD_LT_SEPARATOR);
            if( nIndex2 != -1 )
                aTargetNewLayoutName = aTargetNewLayoutName.copy(0, nIndex2);
 
            if(!isMasterPageLayoutNameUnique(*this, aTargetNewLayoutName))
            {
                aTargetNewLayoutName = createNewMasterPageLayoutName(*this);
 
                OUString aTemp(aTargetNewLayoutName);
                aTemp += SD_LT_SEPARATOR;
                aTemp += STR_LAYOUT_OUTLINE;
 
                pMaster->SetName(aTargetNewLayoutName);
                pMaster->SetLayoutName(aTemp);
 
                pNotesMaster->SetName(aTargetNewLayoutName);
                pNotesMaster->SetLayoutName(aTemp);
            }
        }
 
        if (pSourceDoc != this)
        {
            const sal_uInt16 nMasterPageCount = GetMasterPageCount();
            for ( sal_uInt16 nMPage = 0; nMPage < nMasterPageCount; nMPage++ )
            {
                SdPage* pCheckMaster = static_cast<SdPage*>(GetMasterPage(nMPage));
                if( pCheckMaster->GetName() == aTargetNewLayoutName )
                {
                    bLayoutReloaded = true;
                    break;
                }
            }
 
            // Correct or create presentation templates --
            // only worry about presentation templates
            OUString aName;
            SdStyleSheetPool* pSourceStyleSheetPool = static_cast<SdStyleSheetPool*>( pSourceDoc->GetStyleSheetPool() );
            pSourceStyleSheetPool->SetSearchMask(SfxStyleFamily::Page);
            static_cast<SdStyleSheetPool*>( mxStyleSheetPool.get())->SetSearchMask(SfxStyleFamily::Page);
 
            StyleSheetCopyResultVector aCreatedStyles;          // List of created stylesheets
            SfxStyleSheetBase* pHisSheet = pSourceStyleSheetPool->First();
 
            while (pHisSheet)
            {
                aName = pHisSheet->GetName();
 
                // #i121863# search in source styles with original style name from source of
                // evtl. cloned master (not-cloned, renamed for uniqueness)
                if( aName.startsWith( aOriginalNewLayoutName ) )
                {
                    // #i121863# build name of evtl. cloned master style to search for
                    if(aOriginalNewLayoutName != aTargetNewLayoutName)
                    {
                        const sal_Int32 nPos(aName.indexOf(SD_LT_SEPARATOR));
                        aName = aTargetNewLayoutName + aName.copy(nPos);
                    }
 
                    SfxStyleSheet* pMySheet = static_cast<SfxStyleSheet*>( mxStyleSheetPool->Find(aName, SfxStyleFamily::Page) );
 
                    if (pMySheet)
                    {
                        // A stylesheet of the same name already exists -> overwrite contents
                        bool bTest = pMySheet->SetName(pHisSheet->GetName());
                        DBG_ASSERT(bTest, "Renaming StyleSheet failed.");
                        pMySheet->GetItemSet().ClearItem();  // Delete all
 
                        if (bUndo)
                        {
                            StyleSheetUndoAction* pUndoChStyle = new StyleSheetUndoAction(this,
                                                                 pMySheet, &pHisSheet->GetItemSet());
                            pUndoMgr->AddUndoAction(pUndoChStyle);
                        }
                        pMySheet->GetItemSet().Put(pHisSheet->GetItemSet());
                        pMySheet->Broadcast(SfxHint(SfxHintId::DataChanged));
                    }
                    else
                    {
                        // create new style
                        OUString aHelpFile;
                        pMySheet = static_cast<SfxStyleSheet*>( &mxStyleSheetPool->Make(aName, SfxStyleFamily::Page, pHisSheet->GetMask()) );
                        pMySheet->SetHelpId( aHelpFile, pHisSheet->GetHelpId(aHelpFile) );
                        pMySheet->GetItemSet().ClearItem();  // Delete all
                        pMySheet->GetItemSet().Put(pHisSheet->GetItemSet());
 
                        aCreatedStyles.emplace_back(static_cast<SdStyleSheet*>(pMySheet), true);
                    }
 
                    StyleReplaceData aReplData;
                    aReplData.nNewFamily = pMySheet->GetFamily();
                    aReplData.nFamily    = pMySheet->GetFamily();
                    aReplData.aNewName   = pMySheet->GetName();
 
                    // #i121863# re-create original name of style used at page where to replace with
                    // this new style
                    OUString aTemp(pMySheet->GetName());
                    const sal_Int32 nPos(aTemp.indexOf(SD_LT_SEPARATOR));
                    aTemp = aOldLayoutName + aTemp.copy(nPos);
                    aReplData.aName = aTemp;
                    aReplList.push_back(aReplData);
                }
 
                pHisSheet = static_cast<SfxStyleSheet*>( pSourceStyleSheetPool->Next() );
            }
 
            // If new styles were created: re-create parent chaining of the item
            // sets in the styles.
            if(!aCreatedStyles.empty())
            {
                std::vector<StyleReplaceData>::iterator pRDataIter;
                for ( pRDataIter = aReplList.begin(); pRDataIter != aReplList.end(); ++pRDataIter )
                {
                    SfxStyleSheetBase* pSOld = mxStyleSheetPool->Find(pRDataIter->aName);
                    SfxStyleSheetBase* pSNew = mxStyleSheetPool->Find(pRDataIter->aNewName);
 
                    if (pSOld && pSNew)
                    {
                        const OUString& rParentOfOld = pSOld->GetParent();
                        const OUString& rParentOfNew = pSNew->GetParent();
 
                        if (!rParentOfOld.isEmpty() && rParentOfNew.isEmpty())
                        {
                            std::vector<StyleReplaceData>::iterator pRDIter;
                            for ( pRDIter = aReplList.begin(); pRDIter != aReplList.end(); ++pRDIter )
                            {
                                if ((pRDIter->aName == rParentOfOld) && (pRDIter->aName != pRDIter->aNewName))
                                {
                                    OUString aParentOfNew(pRDIter->aNewName);
                                    pSNew->SetParent(aParentOfNew);
                                    break;
                                }
                            }
                        }
                    }
                }
 
                // Now look for all of them when searching
                pSourceStyleSheetPool->SetSearchMask(SfxStyleFamily::All);
                mxStyleSheetPool->SetSearchMask(SfxStyleFamily::All);
            }
 
            if (bUndo && !aCreatedStyles.empty())
            {
                // Add UndoAction for creating and inserting the stylesheets to
                // the top of the UndoManager
                SdMoveStyleSheetsUndoAction* pMovStyles = new SdMoveStyleSheetsUndoAction( this, aCreatedStyles, true);
                pUndoMgr->AddUndoAction(pMovStyles);
            }
        }
 
        // Create layout name based upon the name of the page layout of the
        // master page
        OUString aPageLayoutName(pMaster->GetLayoutName());
        OUString aLayoutName = aPageLayoutName;
        sal_Int32 nIndex2 = aLayoutName.indexOf( SD_LT_SEPARATOR );
        if( nIndex2 != -1 )
            aLayoutName = aLayoutName.copy( 0, nIndex2);
 
        // #i121863# Do *not* remove from original document any longer, it is potentially used there
        // and would lead to crashes. Rely on the automatic process of removing unused masterpages
        // (see RemoveUnnecessaryMasterPages)
        //if (pSourceDoc != this)
        //{
        //    // Remove from the source document
        //    pSourceDoc->RemoveMasterPage(pNotesMaster->GetPageNum());
        //    pSourceDoc->RemoveMasterPage(pMaster->GetPageNum());
        //}
 
        // Register the new master pages with the document and then use
        // the new presentation layout for the default and notes pages
        if (pSourceDoc != this)
        {
            // Insert the master pages:
            // Insert master pages from new layouts at the end.
            // If a layout is being replaced, however, insert them before the
            // position of the old master page, so from now on the new master
            // page will be found when searching (e.g.
            // SdPage::SetPresentationLayout).
            sal_uInt16 nInsertPos = rOldMaster.GetPageNum();
            BegUndo();
 
            if (!bLayoutReloaded)
                nInsertPos = 0xFFFF;
            InsertMasterPage(pMaster, nInsertPos);
            if( bUndo )
                AddUndo(GetSdrUndoFactory().CreateUndoNewPage(*pMaster));
 
            nInsertPos++;
            if (!bLayoutReloaded)
                nInsertPos = 0xFFFF;
            InsertMasterPage(pNotesMaster, nInsertPos);
            if( bUndo )
            {
                AddUndo(GetSdrUndoFactory().CreateUndoNewPage(*pNotesMaster));
 
                EndUndo(); // do this here already, so Joe's actions happen _between_ our own.
            }
        }
 
        // Fill list with pages
        std::vector<SdPage*> aPageList;
 
//      #98456, this has to be removed according to CL (KA 07/08/2002)
//      #109884# but we need them again to restore the styles of the presentation objects while undo
        aPageList.push_back(pMaster);
        aPageList.push_back(pNotesMaster);
 
        if (bMaster || bLayoutReloaded)
        {
            for (sal_uInt16 nPage = 1; nPage < GetPageCount(); nPage++)
            {
                SdPage* pPage = static_cast<SdPage*>( GetPage(nPage) );
                OUString aTest = pPage->GetLayoutName();
                if (aTest == aOldPageLayoutName)
                {
                    aPageList.push_back(pPage);
                }
            }
 
        }
        else
        {
            aPageList.push_back(pSelectedPage);
            aPageList.push_back(pNotes);
        }
 
        for (std::vector<SdPage*>::iterator pIter = aPageList.begin(); pIter != aPageList.end(); ++pIter)
        {
            SdPage* pPage = *pIter;
            AutoLayout eAutoLayout = pPage->GetAutoLayout();
 
            if( bUndo )
            {
                SdPresentationLayoutUndoAction * pPLUndoAction =
                    new SdPresentationLayoutUndoAction
                        (this,
                        pPage->IsMasterPage() ? aLayoutName : aOldLayoutName,
                        aLayoutName,
                         eAutoLayout, eAutoLayout, false, *pIter);
                pUndoMgr->AddUndoAction(pPLUndoAction);
            }
            pPage->SetPresentationLayout(aLayoutName);
            pPage->SetAutoLayout(eAutoLayout);
        }
 
        // Adapt new master pages
        if (pSourceDoc != this)
        {
            Size aSize(rOldMaster.GetSize());
            ::tools::Rectangle aBorderRect(rOldMaster.GetLeftBorder(),
                                  rOldMaster.GetUpperBorder(),
                                  rOldMaster.GetRightBorder(),
                                  rOldMaster.GetLowerBorder());
            pMaster->ScaleObjects(aSize, aBorderRect, true);
            pMaster->SetSize(aSize);
            pMaster->SetBorder(rOldMaster.GetLeftBorder(),
                               rOldMaster.GetUpperBorder(),
                               rOldMaster.GetRightBorder(),
                               rOldMaster.GetLowerBorder());
            pMaster->SetOrientation( rOldMaster.GetOrientation() );
            pMaster->SetAutoLayout(pMaster->GetAutoLayout());
 
            aSize = rOldNotesMaster.GetSize();
            ::tools::Rectangle aNotesBorderRect(rOldNotesMaster.GetLeftBorder(),
                                       rOldNotesMaster.GetUpperBorder(),
                                       rOldNotesMaster.GetRightBorder(),
                                       rOldNotesMaster.GetLowerBorder());
            pNotesMaster->ScaleObjects(aSize, aNotesBorderRect, true);
            pNotesMaster->SetSize(aSize);
            pNotesMaster->SetBorder(rOldNotesMaster.GetLeftBorder(),
                                    rOldNotesMaster.GetUpperBorder(),
                                    rOldNotesMaster.GetRightBorder(),
                                    rOldNotesMaster.GetLowerBorder());
            pNotesMaster->SetOrientation( rOldNotesMaster.GetOrientation() );
            pNotesMaster->SetAutoLayout(pNotesMaster->GetAutoLayout());
 
            if( (pSourceDoc->GetDocumentType() == DocumentType::Impress) &&
                (GetDocumentType() == DocumentType::Draw) )
            {
                pMaster->RemoveEmptyPresentationObjects();
                pNotesMaster->RemoveEmptyPresentationObjects();
            }
        }
    }
    else
    {
        // Find a new name for the layout
        OUString aName(createNewMasterPageLayoutName(*this));
        OUString aPageLayoutName(aName + SD_LT_SEPARATOR STR_LAYOUT_OUTLINE);
 
        // Generate new stylesheets
        static_cast<SdStyleSheetPool*>( mxStyleSheetPool.get())->CreateLayoutStyleSheets(aName);
        SdStyleSheetVector aCreatedStyles;
        static_cast<SdStyleSheetPool*>( mxStyleSheetPool.get())->CreateLayoutSheetList(aName, aCreatedStyles);
 
        if( bUndo )
        {
            StyleSheetCopyResultVector aUndoInsert;
            aUndoInsert.reserve(aCreatedStyles.size());
            for (const auto& a : aCreatedStyles)
                aUndoInsert.emplace_back(a.get(), true);
            SdMoveStyleSheetsUndoAction* pMovStyles = new SdMoveStyleSheetsUndoAction(this, aUndoInsert, true);
            pUndoMgr->AddUndoAction(pMovStyles);
        }
 
        // Generate new master pages and register them with the document
        if( bUndo )
            BegUndo();
 
        pMaster = AllocSdPage(true);
        pMaster->SetSize(pSelectedPage->GetSize());
        pMaster->SetBorder(pSelectedPage->GetLeftBorder(),
                           pSelectedPage->GetUpperBorder(),
                           pSelectedPage->GetRightBorder(),
                           pSelectedPage->GetLowerBorder() );
        pMaster->SetName(aName);
        pMaster->SetLayoutName(aPageLayoutName);
        InsertMasterPage(pMaster);
 
        if( bUndo )
            AddUndo(GetSdrUndoFactory().CreateUndoNewPage(*pMaster));
 
        pMaster->SetAutoLayout(AUTOLAYOUT_NONE, true, true);
 
        pNotesMaster = AllocSdPage(true);
        pNotesMaster->SetPageKind(PageKind::Notes);
        pNotesMaster->SetSize(pNotes->GetSize());
        pNotesMaster->SetBorder(pNotes->GetLeftBorder(),
                                pNotes->GetUpperBorder(),
                                pNotes->GetRightBorder(),
                                pNotes->GetLowerBorder() );
        pNotesMaster->SetName(aName);
        pNotesMaster->SetLayoutName(aPageLayoutName);
        InsertMasterPage(pNotesMaster);
 
        if( bUndo )
            AddUndo(GetSdrUndoFactory().CreateUndoNewPage(*pNotesMaster));
 
        pNotesMaster->SetAutoLayout(AUTOLAYOUT_NOTES, true, true);
 
        if( bUndo )
            EndUndo();
 
        // Create a list of affected default and notes pages
        std::vector<SdPage*> aPageList;
        if (bMaster)
        {
            for (sal_uInt16 nPage = 1; nPage < GetPageCount(); nPage++)
            {
                SdPage* pPage = static_cast<SdPage*>( GetPage(nPage) );
                if (pPage->GetLayoutName() == aOldPageLayoutName)
                {
                    aPageList.push_back(pPage);
                }
            }
        }
        else
        {
            aPageList.push_back(pSelectedPage);
            aPageList.push_back(pNotes);
        }
 
        // Set presentation layout and AutoLayout for the affected pages
        for ( std::vector<SdPage*>::iterator pIter = aPageList.begin(); pIter != aPageList.end(); ++pIter )
        {
            AutoLayout eOldAutoLayout = (*pIter)->GetAutoLayout();
            AutoLayout eNewAutoLayout =
                (*pIter)->GetPageKind() == PageKind::Standard ? AUTOLAYOUT_NONE : AUTOLAYOUT_NOTES;
 
            if( bUndo )
            {
                SdPresentationLayoutUndoAction * pPLUndoAction =
                    new SdPresentationLayoutUndoAction
                            (this, aOldLayoutName, aName,
                             eOldAutoLayout, eNewAutoLayout, true,
                             *pIter);
                pUndoMgr->AddUndoAction(pPLUndoAction);
            }
 
            (*pIter)->SetPresentationLayout(aName);
            (*pIter)->SetAutoLayout(eNewAutoLayout);
        }
    }
 
    // If the old master pages aren't used anymore, they and their styles have
    // to be removed.
    if (bCheckMasters)
    {
        // Check all
        RemoveUnnecessaryMasterPages();
    }
    else
    {
        // Check only the master page that was replaced
        RemoveUnnecessaryMasterPages(&rOldMaster);
    }
 
    if( bUndo )
        pUndoMgr->LeaveListAction();
 
    if( mpDocSh )
        mpDocSh->SetWaitCursor( false );
}
 
void SdDrawDocument::Merge(SdrModel& rSourceModel,
               sal_uInt16 nFirstPageNum, sal_uInt16 nLastPageNum,
               sal_uInt16 nDestPos,
               bool bMergeMasterPages, bool bAllMasterPages,
               bool bUndo, bool bTreadSourceAsConst)
{
    sal_uInt16 nMasterPageCount = GetMasterPageCount();
    SdrModel::Merge( rSourceModel, nFirstPageNum, nLastPageNum, nDestPos, bMergeMasterPages, bAllMasterPages, bUndo, bTreadSourceAsConst );
 
    // add style family for each new master page
    for( sal_uInt16 nMaster = nMasterPageCount; nMaster < GetMasterPageCount(); nMaster++ )
    {
        SdPage* pPage = static_cast< SdPage* >( GetMasterPage( nMaster ) );
        if( pPage && pPage->IsMasterPage() && (pPage->GetPageKind() == PageKind::Standard) )
        {
            // new master page created, add its style family
            SdStyleSheetPool* pStylePool = static_cast<SdStyleSheetPool*>( GetStyleSheetPool() );
            if( pStylePool )
                pStylePool->AddStyleFamily( pPage );
        }
    }
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V774 The 'pStandardPage' pointer was used after the memory was released.

V581 The conditional expressions of the 'if' statements situated alongside each other are identical. Check lines: 1796, 1807.