1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
24 #include <drawdoc.hxx>
27 #include <unordered_map>
30 #include <svl/lstner.hxx>
31 #include <osl/doublecheckedlocking.h>
32 #include <osl/getglobalmutex.hxx>
36 class MasterPageObserver::Implementation
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
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.
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.
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
);
72 ::std::vector
<Link
<MasterPageObserverEvent
&,void>> maListeners
;
75 size_t operator()(SdDrawDocument
* argument
) const
76 { return reinterpret_cast<unsigned long>(argument
); }
78 typedef std::unordered_map
<SdDrawDocument
*,
79 MasterPageObserver::MasterPageNameSet
,
82 MasterPageContainer maUsedMasterPages
;
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
;
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
;
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
)
188 rEventListener
) == maListeners
.end())
190 maListeners
.push_back (rEventListener
);
192 // Tell the new listener about all the master pages that are
194 MasterPageContainer::iterator aDocumentIterator
;
195 for (aDocumentIterator
=maUsedMasterPages
.begin();
196 aDocumentIterator
!=maUsedMasterPages
.end();
199 ::std::set
<OUString
>::reverse_iterator aNameIterator
;
200 for (aNameIterator
=aDocumentIterator
->second
.rbegin();
201 aNameIterator
!=aDocumentIterator
->second
.rend();
204 MasterPageObserverEvent
aEvent (
205 MasterPageObserverEvent::ET_MASTER_PAGE_EXISTS
,
213 void MasterPageObserver::Implementation::RemoveEventListener (
214 const Link
<MasterPageObserverEvent
&,void>& rEventListener
)
223 void MasterPageObserver::Implementation::Notify(
224 SfxBroadcaster
& rBroadcaster
,
225 const SfxHint
& rHint
)
227 const SdrHint
* pSdrHint
= dynamic_cast<const SdrHint
*>(&rHint
);
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
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
);
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
,
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
,
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
)
324 } // end of namespace sd
326 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */