/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include namespace sd { class MasterPageObserver::Implementation : public SfxListener { public: /** The single instance of this class. It is created on demand when Instance() is called for the first time. */ static MasterPageObserver* mpInstance; /** 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& 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& rEventListener); private: ::std::vector> maListeners; struct DrawDocHash { size_t operator()(SdDrawDocument* argument) const { return reinterpret_cast(argument); } }; typedef std::unordered_map MasterPageContainer; MasterPageContainer maUsedMasterPages; virtual void Notify( SfxBroadcaster& rBroadcaster, const SfxHint& rHint) override; void AnalyzeUsedMasterPages (SdDrawDocument& rDocument); void SendEvent (MasterPageObserverEvent& rEvent); }; MasterPageObserver* MasterPageObserver::Implementation::mpInstance = nullptr; //===== MasterPageObserver ==================================================== MasterPageObserver& MasterPageObserver::Instance() { if (Implementation::mpInstance == nullptr) { ::osl::GetGlobalMutex aMutexFunctor; ::osl::MutexGuard aGuard (aMutexFunctor()); if (Implementation::mpInstance == nullptr) { MasterPageObserver* pInstance = new MasterPageObserver (); SdGlobalResourceContainer::Instance().AddResource ( ::std::unique_ptr(pInstance)); OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); Implementation::mpInstance = pInstance; } } else { OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); } DBG_ASSERT(Implementation::mpInstance!=nullptr, "MasterPageObserver::Instance(): instance is NULL"); return *Implementation::mpInstance; } void MasterPageObserver::RegisterDocument (SdDrawDocument& rDocument) { mpImpl->RegisterDocument (rDocument); } void MasterPageObserver::UnregisterDocument (SdDrawDocument& rDocument) { mpImpl->UnregisterDocument (rDocument); } void MasterPageObserver::AddEventListener (const Link& rEventListener) { mpImpl->AddEventListener (rEventListener); } void MasterPageObserver::RemoveEventListener (const Link& 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; nIndexGetName()); } 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& 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::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& 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(&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( &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 aCurrentMasterPages; for (sal_uInt16 nIndex=0; nIndexGetName()); } std::vector aNewMasterPages; std::vector 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: */