1
0
Fork 0
libreoffice/sd/source/ui/sidebar/MasterPageObserver.cxx
Daniel Baumann 8e63e14cf6
Adding upstream version 4:25.2.3.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-22 16:20:04 +02:00

297 lines
9.7 KiB
C++

/* -*- 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 <MasterPageObserver.hxx>
#include <algorithm>
#include <iterator>
#include <drawdoc.hxx>
#include <sdpage.hxx>
#include <set>
#include <unordered_map>
#include <memory>
#include <vector>
#include <svl/lstner.hxx>
#include <osl/doublecheckedlocking.h>
#include <osl/getglobalmutex.hxx>
#include <tools/debug.hxx>
namespace sd {
class MasterPageObserver::Implementation
: public SfxListener
{
public:
/** The master page observer will listen to events of this document and
detect changes of the use of master pages.
*/
void RegisterDocument (SdDrawDocument& rDocument);
/** The master page observer will stop to listen to events of this
document.
*/
void UnregisterDocument (SdDrawDocument& rDocument);
/** Add a listener that is informed of master pages that are newly
assigned to slides or become unassigned.
@param rEventListener
The event listener to call for future events. Call
RemoveEventListener() before the listener is destroyed.
*/
void AddEventListener (const Link<MasterPageObserverEvent&,void>& rEventListener);
/** Remove the given listener from the list of listeners.
@param rEventListener
After this method returns the given listener is not called back
from this object. Passing a listener that has not
been registered before is safe and is silently ignored.
*/
void RemoveEventListener (const Link<MasterPageObserverEvent&,void>& rEventListener);
private:
::std::vector<Link<MasterPageObserverEvent&,void>> maListeners;
struct DrawDocHash {
size_t operator()(SdDrawDocument* argument) const
{ return reinterpret_cast<sal_uIntPtr>(argument); }
};
typedef std::unordered_map<SdDrawDocument*,
MasterPageObserver::MasterPageNameSet,
DrawDocHash>
MasterPageContainer;
MasterPageContainer maUsedMasterPages;
virtual void Notify(
SfxBroadcaster& rBroadcaster,
const SfxHint& rHint) override;
void AnalyzeUsedMasterPages (SdDrawDocument& rDocument);
void SendEvent (MasterPageObserverEvent& rEvent);
};
//===== MasterPageObserver ====================================================
MasterPageObserver& MasterPageObserver::Instance()
{
static MasterPageObserver* gInstance = []()
{
MasterPageObserver* pInstance = new MasterPageObserver ();
SdGlobalResourceContainer::Instance().AddResource (
::std::unique_ptr<SdGlobalResource>(pInstance));
return pInstance;
}();
return *gInstance;
}
void MasterPageObserver::RegisterDocument (SdDrawDocument& rDocument)
{
mpImpl->RegisterDocument (rDocument);
}
void MasterPageObserver::UnregisterDocument (SdDrawDocument& rDocument)
{
mpImpl->UnregisterDocument (rDocument);
}
void MasterPageObserver::AddEventListener (const Link<MasterPageObserverEvent&,void>& rEventListener)
{
mpImpl->AddEventListener (rEventListener);
}
void MasterPageObserver::RemoveEventListener (const Link<MasterPageObserverEvent&,void>& rEventListener)
{
mpImpl->RemoveEventListener (rEventListener);
}
MasterPageObserver::MasterPageObserver()
: mpImpl (new Implementation)
{}
MasterPageObserver::~MasterPageObserver()
{}
//===== MasterPageObserver::Implementation ====================================
void MasterPageObserver::Implementation::RegisterDocument (
SdDrawDocument& rDocument)
{
// Gather the names of all the master pages in the given document.
MasterPageContainer::mapped_type aMasterPageSet;
sal_uInt16 nMasterPageCount = rDocument.GetMasterSdPageCount(PageKind::Standard);
for (sal_uInt16 nIndex=0; nIndex<nMasterPageCount; nIndex++)
{
SdPage* pMasterPage = rDocument.GetMasterSdPage (nIndex, PageKind::Standard);
if (pMasterPage != nullptr)
aMasterPageSet.insert (pMasterPage->GetName());
}
bool bAlreadyExists = maUsedMasterPages.find(&rDocument) != maUsedMasterPages.end();
maUsedMasterPages[&rDocument] = std::move(aMasterPageSet);
if (!bAlreadyExists)
StartListening (rDocument);
}
void MasterPageObserver::Implementation::UnregisterDocument (
SdDrawDocument& rDocument)
{
EndListening (rDocument);
MasterPageContainer::iterator aMasterPageDescriptor(maUsedMasterPages.find(&rDocument));
if(aMasterPageDescriptor != maUsedMasterPages.end())
maUsedMasterPages.erase(aMasterPageDescriptor);
}
void MasterPageObserver::Implementation::AddEventListener (
const Link<MasterPageObserverEvent&,void>& rEventListener)
{
if (::std::find (
maListeners.begin(),
maListeners.end(),
rEventListener) != maListeners.end())
return;
maListeners.push_back (rEventListener);
// Tell the new listener about all the master pages that are
// currently in use.
for (const auto& rDocument : maUsedMasterPages)
{
::std::set<OUString>::reverse_iterator aNameIterator;
for (aNameIterator=rDocument.second.rbegin();
aNameIterator!=rDocument.second.rend();
++aNameIterator)
{
MasterPageObserverEvent aEvent (
MasterPageObserverEvent::ET_MASTER_PAGE_EXISTS,
*aNameIterator);
SendEvent (aEvent);
}
}
}
void MasterPageObserver::Implementation::RemoveEventListener (
const Link<MasterPageObserverEvent&,void>& rEventListener)
{
maListeners.erase (
::std::find (
maListeners.begin(),
maListeners.end(),
rEventListener));
}
void MasterPageObserver::Implementation::Notify(
SfxBroadcaster& rBroadcaster,
const SfxHint& rHint)
{
if (rHint.GetId() != SfxHintId::ThisIsAnSdrHint)
return;
const SdrHint* pSdrHint = static_cast<const SdrHint*>(&rHint);
switch (pSdrHint->GetKind())
{
case SdrHintKind::PageOrderChange:
// Process the modified set of pages only when the number of
// standard and notes master pages are equal. This test
// filters out events that are sent in between the insertion
// of a new standard master page and a new notes master
// page.
if (auto pDrawDocument = dynamic_cast<SdDrawDocument *>( &rBroadcaster ))
{
if (pDrawDocument->GetMasterSdPageCount(PageKind::Standard)
== pDrawDocument->GetMasterSdPageCount(PageKind::Notes))
{
AnalyzeUsedMasterPages (*pDrawDocument);
}
}
break;
default:
break;
}
}
void MasterPageObserver::Implementation::AnalyzeUsedMasterPages (
SdDrawDocument& rDocument)
{
// Create a set of names of the master pages used by the given document.
sal_uInt16 nMasterPageCount = rDocument.GetMasterSdPageCount(PageKind::Standard);
::std::set<OUString> aCurrentMasterPages;
for (sal_uInt16 nIndex=0; nIndex<nMasterPageCount; nIndex++)
{
SdPage* pMasterPage = rDocument.GetMasterSdPage (nIndex, PageKind::Standard);
if (pMasterPage != nullptr)
aCurrentMasterPages.insert (pMasterPage->GetName());
}
std::vector<OUString> aNewMasterPages;
std::vector<OUString> aRemovedMasterPages;
MasterPageContainer::iterator aOldMasterPagesDescriptor (
maUsedMasterPages.find(&rDocument));
if (aOldMasterPagesDescriptor == maUsedMasterPages.end())
return;
// Send events about the newly used master pages.
::std::set_difference (
aCurrentMasterPages.begin(),
aCurrentMasterPages.end(),
aOldMasterPagesDescriptor->second.begin(),
aOldMasterPagesDescriptor->second.end(),
std::back_inserter(aNewMasterPages));
for (const auto& aNewMasterPage : aNewMasterPages)
{
MasterPageObserverEvent aEvent (
MasterPageObserverEvent::ET_MASTER_PAGE_ADDED,
aNewMasterPage);
SendEvent (aEvent);
}
// Send events about master pages that are not used any longer.
::std::set_difference (
aOldMasterPagesDescriptor->second.begin(),
aOldMasterPagesDescriptor->second.end(),
aCurrentMasterPages.begin(),
aCurrentMasterPages.end(),
std::back_inserter(aRemovedMasterPages));
for (const auto& aRemovedMasterPage : aRemovedMasterPages)
{
MasterPageObserverEvent aEvent (
MasterPageObserverEvent::ET_MASTER_PAGE_REMOVED,
aRemovedMasterPage);
SendEvent (aEvent);
}
// Store the new list of master pages.
aOldMasterPagesDescriptor->second = std::move(aCurrentMasterPages);
}
void MasterPageObserver::Implementation::SendEvent (
MasterPageObserverEvent& rEvent)
{
for (const auto& aLink : maListeners)
{
aLink.Call(rEvent);
}
}
} // end of namespace sd
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */