Version 6.1.0.2, tag libreoffice-6.1.0.2
[LibreOffice.git] / sd / source / ui / sidebar / MasterPageObserver.cxx
blobc6fa9dc227fb6b0c6f8a891ed6830454380548f3
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <MasterPageObserver.hxx>
22 #include <algorithm>
23 #include <iterator>
24 #include <drawdoc.hxx>
25 #include <sdpage.hxx>
26 #include <set>
27 #include <unordered_map>
28 #include <memory>
29 #include <vector>
30 #include <svl/lstner.hxx>
31 #include <osl/doublecheckedlocking.h>
32 #include <osl/getglobalmutex.hxx>
34 namespace sd {
36 class MasterPageObserver::Implementation
37 : public SfxListener
39 public:
40 /** The single instance of this class. It is created on demand when
41 Instance() is called for the first time.
43 static MasterPageObserver* mpInstance;
45 /** The master page observer will listen to events of this document and
46 detect changes of the use of master pages.
48 void RegisterDocument (SdDrawDocument& rDocument);
50 /** The master page observer will stop to listen to events of this
51 document.
53 void UnregisterDocument (SdDrawDocument& rDocument);
55 /** Add a listener that is informed of master pages that are newly
56 assigned to slides or become unassigned.
57 @param rEventListener
58 The event listener to call for future events. Call
59 RemoveEventListener() before the listener is destroyed.
61 void AddEventListener (const Link<MasterPageObserverEvent&,void>& rEventListener);
63 /** Remove the given listener from the list of listeners.
64 @param rEventListener
65 After this method returns the given listener is not called back
66 from this object. Passing a listener that has not
67 been registered before is safe and is silently ignored.
69 void RemoveEventListener (const Link<MasterPageObserverEvent&,void>& rEventListener);
71 private:
72 ::std::vector<Link<MasterPageObserverEvent&,void>> maListeners;
74 struct DrawDocHash {
75 size_t operator()(SdDrawDocument* argument) const
76 { return reinterpret_cast<unsigned long>(argument); }
78 typedef std::unordered_map<SdDrawDocument*,
79 MasterPageObserver::MasterPageNameSet,
80 DrawDocHash>
81 MasterPageContainer;
82 MasterPageContainer maUsedMasterPages;
84 virtual void Notify(
85 SfxBroadcaster& rBroadcaster,
86 const SfxHint& rHint) override;
88 void AnalyzeUsedMasterPages (SdDrawDocument& rDocument);
90 void SendEvent (MasterPageObserverEvent& rEvent);
93 MasterPageObserver* MasterPageObserver::Implementation::mpInstance = nullptr;
95 //===== MasterPageObserver ====================================================
97 MasterPageObserver& MasterPageObserver::Instance()
99 if (Implementation::mpInstance == nullptr)
101 ::osl::GetGlobalMutex aMutexFunctor;
102 ::osl::MutexGuard aGuard (aMutexFunctor());
103 if (Implementation::mpInstance == nullptr)
105 MasterPageObserver* pInstance = new MasterPageObserver ();
106 SdGlobalResourceContainer::Instance().AddResource (
107 ::std::unique_ptr<SdGlobalResource>(pInstance));
108 OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
109 Implementation::mpInstance = pInstance;
112 else
114 OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
117 DBG_ASSERT(Implementation::mpInstance!=nullptr,
118 "MasterPageObserver::Instance(): instance is NULL");
119 return *Implementation::mpInstance;
122 void MasterPageObserver::RegisterDocument (SdDrawDocument& rDocument)
124 mpImpl->RegisterDocument (rDocument);
127 void MasterPageObserver::UnregisterDocument (SdDrawDocument& rDocument)
129 mpImpl->UnregisterDocument (rDocument);
132 void MasterPageObserver::AddEventListener (const Link<MasterPageObserverEvent&,void>& rEventListener)
135 mpImpl->AddEventListener (rEventListener);
138 void MasterPageObserver::RemoveEventListener (const Link<MasterPageObserverEvent&,void>& rEventListener)
140 mpImpl->RemoveEventListener (rEventListener);
143 MasterPageObserver::MasterPageObserver()
144 : mpImpl (new Implementation)
147 MasterPageObserver::~MasterPageObserver()
150 //===== MasterPageObserver::Implementation ====================================
152 void MasterPageObserver::Implementation::RegisterDocument (
153 SdDrawDocument& rDocument)
155 // Gather the names of all the master pages in the given document.
156 MasterPageContainer::mapped_type aMasterPageSet;
157 sal_uInt16 nMasterPageCount = rDocument.GetMasterSdPageCount(PageKind::Standard);
158 for (sal_uInt16 nIndex=0; nIndex<nMasterPageCount; nIndex++)
160 SdPage* pMasterPage = rDocument.GetMasterSdPage (nIndex, PageKind::Standard);
161 if (pMasterPage != nullptr)
162 aMasterPageSet.insert (pMasterPage->GetName());
165 bool bAlreadyExists = maUsedMasterPages.find(&rDocument) != maUsedMasterPages.end();
166 maUsedMasterPages[&rDocument] = aMasterPageSet;
168 if (!bAlreadyExists)
169 StartListening (rDocument);
172 void MasterPageObserver::Implementation::UnregisterDocument (
173 SdDrawDocument& rDocument)
175 EndListening (rDocument);
177 MasterPageContainer::iterator aMasterPageDescriptor(maUsedMasterPages.find(&rDocument));
178 if(aMasterPageDescriptor != maUsedMasterPages.end())
179 maUsedMasterPages.erase(aMasterPageDescriptor);
182 void MasterPageObserver::Implementation::AddEventListener (
183 const Link<MasterPageObserverEvent&,void>& rEventListener)
185 if (::std::find (
186 maListeners.begin(),
187 maListeners.end(),
188 rEventListener) == maListeners.end())
190 maListeners.push_back (rEventListener);
192 // Tell the new listener about all the master pages that are
193 // currently in use.
194 MasterPageContainer::iterator aDocumentIterator;
195 for (aDocumentIterator=maUsedMasterPages.begin();
196 aDocumentIterator!=maUsedMasterPages.end();
197 ++aDocumentIterator)
199 ::std::set<OUString>::reverse_iterator aNameIterator;
200 for (aNameIterator=aDocumentIterator->second.rbegin();
201 aNameIterator!=aDocumentIterator->second.rend();
202 ++aNameIterator)
204 MasterPageObserverEvent aEvent (
205 MasterPageObserverEvent::ET_MASTER_PAGE_EXISTS,
206 *aNameIterator);
207 SendEvent (aEvent);
213 void MasterPageObserver::Implementation::RemoveEventListener (
214 const Link<MasterPageObserverEvent&,void>& rEventListener)
216 maListeners.erase (
217 ::std::find (
218 maListeners.begin(),
219 maListeners.end(),
220 rEventListener));
223 void MasterPageObserver::Implementation::Notify(
224 SfxBroadcaster& rBroadcaster,
225 const SfxHint& rHint)
227 const SdrHint* pSdrHint = dynamic_cast<const SdrHint*>(&rHint);
228 if (pSdrHint)
230 switch (pSdrHint->GetKind())
232 case SdrHintKind::PageOrderChange:
233 // Process the modified set of pages only when the number of
234 // standard and notes master pages are equal. This test
235 // filters out events that are sent in between the insertion
236 // of a new standard master page and a new notes master
237 // page.
238 if (dynamic_cast< const SdDrawDocument *>( &rBroadcaster ) != nullptr)
240 SdDrawDocument& rDocument (
241 static_cast<SdDrawDocument&>(rBroadcaster));
242 if (rDocument.GetMasterSdPageCount(PageKind::Standard)
243 == rDocument.GetMasterSdPageCount(PageKind::Notes))
245 AnalyzeUsedMasterPages (rDocument);
248 break;
250 default:
251 break;
256 void MasterPageObserver::Implementation::AnalyzeUsedMasterPages (
257 SdDrawDocument& rDocument)
259 // Create a set of names of the master pages used by the given document.
260 sal_uInt16 nMasterPageCount = rDocument.GetMasterSdPageCount(PageKind::Standard);
261 ::std::set<OUString> aCurrentMasterPages;
262 for (sal_uInt16 nIndex=0; nIndex<nMasterPageCount; nIndex++)
264 SdPage* pMasterPage = rDocument.GetMasterSdPage (nIndex, PageKind::Standard);
265 if (pMasterPage != nullptr)
266 aCurrentMasterPages.insert (pMasterPage->GetName());
269 typedef ::std::vector<OUString> StringList;
270 StringList aNewMasterPages;
271 StringList aRemovedMasterPages;
272 MasterPageContainer::iterator aOldMasterPagesDescriptor (
273 maUsedMasterPages.find(&rDocument));
274 if (aOldMasterPagesDescriptor != maUsedMasterPages.end())
276 StringList::iterator I;
278 ::std::set<OUString>::iterator J;
280 // Send events about the newly used master pages.
281 ::std::set_difference (
282 aCurrentMasterPages.begin(),
283 aCurrentMasterPages.end(),
284 aOldMasterPagesDescriptor->second.begin(),
285 aOldMasterPagesDescriptor->second.end(),
286 ::std::back_insert_iterator<StringList>(aNewMasterPages));
287 for (I=aNewMasterPages.begin(); I!=aNewMasterPages.end(); ++I)
289 MasterPageObserverEvent aEvent (
290 MasterPageObserverEvent::ET_MASTER_PAGE_ADDED,
291 *I);
292 SendEvent (aEvent);
295 // Send events about master pages that are not used any longer.
296 ::std::set_difference (
297 aOldMasterPagesDescriptor->second.begin(),
298 aOldMasterPagesDescriptor->second.end(),
299 aCurrentMasterPages.begin(),
300 aCurrentMasterPages.end(),
301 ::std::back_insert_iterator<StringList>(aRemovedMasterPages));
302 for (I=aRemovedMasterPages.begin(); I!=aRemovedMasterPages.end(); ++I)
304 MasterPageObserverEvent aEvent (
305 MasterPageObserverEvent::ET_MASTER_PAGE_REMOVED,
306 *I);
307 SendEvent (aEvent);
310 // Store the new list of master pages.
311 aOldMasterPagesDescriptor->second = aCurrentMasterPages;
315 void MasterPageObserver::Implementation::SendEvent (
316 MasterPageObserverEvent& rEvent)
318 for (auto& aLink : maListeners)
320 aLink.Call(rEvent);
324 } // end of namespace sd
326 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */