Bump version to 21.06.18.1
[LibreOffice.git] / sw / inc / calbck.hxx
blob8fbb2e87e0b204251a9a808ed044736f534cfd3d
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 #ifndef INCLUDED_SW_INC_CALBCK_HXX
21 #define INCLUDED_SW_INC_CALBCK_HXX
23 #include <cassert>
25 #include <svl/hint.hxx>
26 #include <svl/broadcast.hxx>
27 #include <svl/poolitem.hxx>
28 #include "swdllapi.h"
29 #include "ring.hxx"
30 #include <type_traits>
31 #include <vector>
32 #include <memory>
34 class SwModify;
35 class SfxPoolItem;
36 class SwAttrSet;
39 SwModify and SwClient cooperate in propagating attribute changes.
40 If an attribute changes, the change is notified to all dependent
41 formats and other interested objects, e.g. Nodes. The clients will detect
42 if the change affects them. It could be that the changed attribute is
43 overruled in the receiving object so that its change does not become
44 effective or that the receiver is not interested in the particular attribute
45 in general (though probably in other attributes of the SwModify object they
46 are registered in).
47 As SwModify objects are derived from SwClient, they can create a chain of SwClient
48 objects where changes can get propagated through.
49 Each SwClient can be registered at only one SwModify object, while each SwModify
50 object is connected to a list of SwClient objects. If an object derived from SwClient
51 wants to get notifications from more than one SwModify object, it must create additional
52 SwClient objects. The SwDepend class allows to handle their notifications in the same
53 notification callback as it forwards the Modify() calls it receives to a "master"
54 SwClient implementation.
55 The SwIterator class allows to iterate over the SwClient objects registered at an
56 SwModify. For historical reasons its ability to use TypeInfo to restrict this iteration
57 to objects of a particular type created a lot of code that misuses SwClient-SwModify
58 relationships that basically should be used only for Modify/Notify callbacks.
59 This is still subject to refactoring.
62 namespace sw
64 class ClientIteratorBase;
65 struct LegacyModifyHint final: SfxHint
67 LegacyModifyHint(const SfxPoolItem* pOld, const SfxPoolItem* pNew) : m_pOld(pOld), m_pNew(pNew) {};
68 sal_uInt16 GetWhich() const { return m_pOld ? m_pOld->Which() : m_pNew ? m_pNew->Which() : 0; };
69 virtual ~LegacyModifyHint() override;
70 const SfxPoolItem* m_pOld;
71 const SfxPoolItem* m_pNew;
73 struct ModifyChangedHint final: SfxHint
75 ModifyChangedHint(const SwModify* pNew) : m_pNew(pNew) {};
76 virtual ~ModifyChangedHint() override;
77 const SwModify* m_pNew;
79 // Observer pattern using svl implementation
80 // use this instead of SwClient/SwModify wherever possible
81 // In writer layout, this might not always be possible,
82 // but for listeners outside of it (e.g. unocore) this should be used.
83 // The only "magic" signal this class issues is a ModifyChangedHint
84 // proclaiming its death. It does NOT however provide a new SwModify for
85 // listeners to switch to like the old SwModify/SwClient did, as that leads
86 // to madness.
87 class SW_DLLPUBLIC BroadcasterMixin {
88 SvtBroadcaster m_aNotifier;
89 public:
90 BroadcasterMixin() = default;
91 BroadcasterMixin(BroadcasterMixin const &) = default;
92 BroadcasterMixin& operator=(const BroadcasterMixin&)
94 return *this; // Listeners are never copied or moved.
96 SvtBroadcaster& GetNotifier() { return m_aNotifier; }
98 /// refactoring out the same of the more sane SwClient functionality
99 class SW_DLLPUBLIC WriterListener
101 friend class ::SwModify;
102 friend class ::sw::ClientIteratorBase;
103 private:
104 WriterListener* m_pLeft;
105 WriterListener* m_pRight; ///< double-linked list of other clients
107 WriterListener(WriterListener const&) = delete;
108 WriterListener& operator=(WriterListener const&) = delete;
110 protected:
111 WriterListener()
112 : m_pLeft(nullptr), m_pRight(nullptr)
114 virtual ~WriterListener() COVERITY_NOEXCEPT_FALSE {}
115 virtual void SwClientNotify( const SwModify&, const SfxHint& rHint) =0;
116 public:
117 bool IsLast() const { return !m_pLeft && !m_pRight; }
119 enum class IteratorMode { Exact, UnwrapMulti };
122 // SwClient
123 class SW_DLLPUBLIC SwClient : public ::sw::WriterListener
125 // avoids making the details of the linked list and the callback method public
126 friend class SwModify;
127 friend class sw::ClientIteratorBase;
128 template<typename E, typename S, sw::IteratorMode> friend class SwIterator;
130 SwModify *m_pRegisteredIn; ///< event source
132 protected:
133 // single argument ctors shall be explicit.
134 inline explicit SwClient( SwModify* pToRegisterIn );
136 // write access to pRegisteredIn shall be granted only to the object itself (protected access)
137 SwModify* GetRegisteredInNonConst() const { return m_pRegisteredIn; }
139 // when overriding this, you MUST call SwClient::SwClientModify() in the override!
140 virtual void SwClientNotify(const SwModify&, const SfxHint& rHint) override;
142 public:
143 SwClient() : m_pRegisteredIn(nullptr) {}
144 SwClient(SwClient&&) noexcept;
145 virtual ~SwClient() override;
146 // callbacks received from SwModify (friend class - so these methods can be private)
147 // should be called only from SwModify the client is registered in
148 // mba: IMHO this method should be pure virtual
149 // DO NOT USE IN NEW CODE! use SwClientNotify instead.
150 virtual void Modify(const SfxPoolItem*, const SfxPoolItem*);
153 // in case an SwModify object is destroyed that itself is registered in another SwModify,
154 // its SwClient objects can decide to get registered to the latter instead by calling this method
155 std::unique_ptr<sw::ModifyChangedHint> CheckRegistration( const SfxPoolItem* pOldValue );
157 // DO NOT USE IN NEW CODE! Used to directly call the event handler from
158 // outside the class. It is generally wrong to call the message handler
159 // directly: either it should be a proper stand-alone member function
160 // or by the observed object sending the hint properly itself.
161 void SwClientNotifyCall( const SwModify& rModify, const SfxHint& rHint ) { SwClientNotify( rModify, rHint ); }
163 const SwModify* GetRegisteredIn() const { return m_pRegisteredIn; }
164 SwModify* GetRegisteredIn() { return m_pRegisteredIn; }
165 void EndListeningAll();
166 void StartListeningToSameModifyAs(const SwClient&);
169 // get information about attribute
170 virtual bool GetInfo( SfxPoolItem& ) const { return true; }
174 // SwModify
176 // class has a doubly linked list for dependencies
177 class SW_DLLPUBLIC SwModify: public SwClient
179 friend class sw::ClientIteratorBase;
180 template<typename E, typename S, sw::IteratorMode> friend class SwIterator;
181 sw::WriterListener* m_pWriterListeners; // the start of the linked list of clients
182 bool m_bModifyLocked : 1; // don't broadcast changes now
183 bool m_bInCache : 1;
184 bool m_bInSwFntCache : 1;
186 // mba: IMHO this method should be pure virtual
187 // DO NOT USE IN NEW CODE! use CallSwClientNotify instead.
188 virtual void Modify( const SfxPoolItem* pOld, const SfxPoolItem *pNew) override
189 { NotifyClients( pOld, pNew ); };
191 SwModify(SwModify const &) = delete;
192 SwModify &operator =(const SwModify&) = delete;
193 public:
194 SwModify()
195 : SwClient(), m_pWriterListeners(nullptr), m_bModifyLocked(false), m_bInCache(false), m_bInSwFntCache(false)
198 // broadcasting: send notifications to all clients
199 // DO NOT USE IN NEW CODE! use CallSwClientNotify instead.
200 void NotifyClients( const SfxPoolItem *pOldValue, const SfxPoolItem *pNewValue );
202 // a more universal broadcasting mechanism
203 virtual void CallSwClientNotify( const SfxHint& rHint ) const;
205 virtual ~SwModify() override;
207 void Add(SwClient *pDepend);
208 SwClient* Remove(SwClient *pDepend);
209 bool HasWriterListeners() const { return m_pWriterListeners; }
211 // get information about attribute
212 virtual bool GetInfo( SfxPoolItem& ) const override;
214 void LockModify() { m_bModifyLocked = true; }
215 void UnlockModify() { m_bModifyLocked = false; }
216 void SetInCache( bool bNew ) { m_bInCache = bNew; }
217 void SetInSwFntCache( bool bNew ) { m_bInSwFntCache = bNew; }
218 void SetInDocDTOR();
219 bool IsModifyLocked() const { return m_bModifyLocked; }
220 bool IsInCache() const { return m_bInCache; }
221 bool IsInSwFntCache() const { return m_bInSwFntCache; }
223 void CheckCaching( const sal_uInt16 nWhich );
224 bool HasOnlyOneListener() const { return m_pWriterListeners && m_pWriterListeners->IsLast(); }
227 template<typename TElementType, typename TSource, sw::IteratorMode eMode> class SwIterator;
229 namespace sw
231 void ClientNotifyAttrChg(SwModify& rModify, const SwAttrSet& aSet, SwAttrSet& aOld, SwAttrSet& aNew);
233 // this class is part of the migration: it still forwards the "old"
234 // SwModify events and announces them both to the old SwClients still
235 // registered and also to the new SvtListeners.
236 // Still: in the long run the SwClient/SwModify interface should not be
237 // used anymore, in which case a BroadcasterMixin should be enough instead
238 // then.
239 class SW_DLLPUBLIC BroadcastingModify : public SwModify, public BroadcasterMixin {
240 public:
241 virtual void CallSwClientNotify(const SfxHint& rHint) const override;
243 // this should be hidden but sadly SwIterator template needs it...
244 class ListenerEntry final : public SwClient
246 private:
247 template<typename E, typename S, sw::IteratorMode> friend class ::SwIterator;
248 SwClient *m_pToTell;
250 public:
251 ListenerEntry(SwClient *const pTellHim, SwModify *const pDepend)
252 : SwClient(pDepend), m_pToTell(pTellHim)
254 ListenerEntry(ListenerEntry const &) = delete;
255 ListenerEntry& operator=(ListenerEntry const&) = delete;
256 ListenerEntry(ListenerEntry&& other) noexcept
257 : SwClient(std::move(other))
258 , m_pToTell(other.m_pToTell)
260 ListenerEntry& operator=(ListenerEntry&& other) noexcept
262 m_pToTell = other.m_pToTell;
263 other.GetRegisteredIn()->Add(this);
264 other.EndListeningAll();
265 return *this;
268 /** get Client information */
269 virtual bool GetInfo( SfxPoolItem& rInfo) const override;
270 private:
271 virtual void Modify(const SfxPoolItem* pOldValue, const SfxPoolItem *pNewValue) override;
272 virtual void SwClientNotify(const SwModify& rModify, const SfxHint& rHint) override;
275 class SW_DLLPUBLIC WriterMultiListener final
277 SwClient& m_rToTell;
278 std::vector<ListenerEntry> m_vDepends;
279 public:
280 WriterMultiListener(SwClient& rToTell);
281 WriterMultiListener& operator=(WriterMultiListener const&) = delete; // MSVC2015 workaround
282 WriterMultiListener(WriterMultiListener const&) = delete; // MSVC2015 workaround
283 ~WriterMultiListener();
284 void StartListening(SwModify* pDepend);
285 void EndListening(SwModify* pDepend);
286 bool IsListeningTo(const SwModify* const pDepend) const;
287 void EndListeningAll();
289 class ClientIteratorBase : public sw::Ring< ::sw::ClientIteratorBase >
291 friend SwClient* SwModify::Remove(SwClient*);
292 friend void SwModify::Add(SwClient*);
293 protected:
294 const SwModify& m_rRoot;
295 // the current object in an iteration
296 WriterListener* m_pCurrent;
297 // in case the current object is already removed, the next object in the list
298 // is marked down to become the current object in the next step
299 // this is necessary because iteration requires access to members of the current object
300 WriterListener* m_pPosition;
301 static SW_DLLPUBLIC ClientIteratorBase* s_pClientIters;
303 ClientIteratorBase( const SwModify& rModify )
304 : m_rRoot(rModify)
306 MoveTo(s_pClientIters);
307 s_pClientIters = this;
308 m_pCurrent = m_pPosition = m_rRoot.m_pWriterListeners;
310 WriterListener* GetLeftOfPos() { return m_pPosition->m_pLeft; }
311 WriterListener* GetRightOfPos() { return m_pPosition->m_pRight; }
312 WriterListener* GoStart()
314 m_pPosition = m_rRoot.m_pWriterListeners;
315 if(m_pPosition)
316 while( m_pPosition->m_pLeft )
317 m_pPosition = m_pPosition->m_pLeft;
318 m_pCurrent = m_pPosition;
319 return m_pCurrent;
321 ~ClientIteratorBase() override
323 assert(s_pClientIters);
324 if(s_pClientIters == this)
325 s_pClientIters = unique() ? nullptr : GetNextInRing();
326 MoveTo(nullptr);
328 // return "true" if an object was removed from a client chain in iteration
329 // adding objects to a client chain in iteration is forbidden
330 // SwModify::Add() asserts this
331 bool IsChanged() const { return m_pPosition != m_pCurrent; }
332 // ensures the iterator to point at a current client
333 WriterListener* Sync() { m_pCurrent = m_pPosition; return m_pCurrent; }
337 template<typename TElementType, typename TSource,
338 sw::IteratorMode eMode = sw::IteratorMode::Exact> class SwIterator final
339 : private sw::ClientIteratorBase
341 //static_assert(!std::is_base_of<SwPageDesc,TSource>::value, "SwPageDesc as TSource is deprecated.");
342 static_assert(std::is_base_of<SwClient,TElementType>::value, "TElementType needs to be derived from SwClient.");
343 static_assert(std::is_base_of<SwModify,TSource>::value, "TSource needs to be derived from SwModify.");
344 public:
345 SwIterator( const TSource& rSrc ) : sw::ClientIteratorBase(rSrc) {}
346 TElementType* First()
348 GoStart();
349 if(!m_pPosition)
350 return nullptr;
351 m_pCurrent = nullptr;
352 return Next();
354 TElementType* Next()
356 if(!IsChanged())
357 m_pPosition = GetRightOfPos();
358 sw::WriterListener *pCurrent(m_pPosition);
359 while (m_pPosition)
361 if (eMode == sw::IteratorMode::UnwrapMulti)
363 if (auto const pLE = dynamic_cast<sw::ListenerEntry const*>(m_pPosition))
365 pCurrent = pLE->m_pToTell;
368 if (dynamic_cast<const TElementType *>(pCurrent) == nullptr)
370 m_pPosition = GetRightOfPos();
371 pCurrent = m_pPosition;
373 else
374 break;
376 Sync();
377 return static_cast<TElementType*>(pCurrent);
379 using sw::ClientIteratorBase::IsChanged;
382 template< typename TSource > class SwIterator<SwClient, TSource> final : private sw::ClientIteratorBase
384 static_assert(std::is_base_of<SwModify,TSource>::value, "TSource needs to be derived from SwModify");
385 public:
386 SwIterator( const TSource& rSrc ) : sw::ClientIteratorBase(rSrc) {}
387 SwClient* First()
388 { return static_cast<SwClient*>(GoStart()); }
389 SwClient* Next()
391 if(!IsChanged())
392 m_pPosition = GetRightOfPos();
393 return static_cast<SwClient*>(Sync());
395 using sw::ClientIteratorBase::IsChanged;
398 SwClient::SwClient( SwModify* pToRegisterIn )
399 : m_pRegisteredIn( nullptr )
401 if(pToRegisterIn)
402 pToRegisterIn->Add(this);
405 #endif
407 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */