summaryrefslogtreecommitdiffstats
path: root/sd/source/ui/sidebar/MasterPageObserver.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sd/source/ui/sidebar/MasterPageObserver.cxx')
-rw-r--r--sd/source/ui/sidebar/MasterPageObserver.cxx297
1 files changed, 297 insertions, 0 deletions
diff --git a/sd/source/ui/sidebar/MasterPageObserver.cxx b/sd/source/ui/sidebar/MasterPageObserver.cxx
new file mode 100644
index 0000000000..724b7b5e8f
--- /dev/null
+++ b/sd/source/ui/sidebar/MasterPageObserver.cxx
@@ -0,0 +1,297 @@
+/* -*- 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] = 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 = 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: */