Version 5.4.3.2, tag libreoffice-5.4.3.2
[LibreOffice.git] / sw / inc / calbck.hxx
bloba6f07772c04560534d105de93b4a00ed34b49367
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 "swdllapi.h"
24 #include <ring.hxx>
25 #include <hintids.hxx>
26 #include <hints.hxx>
27 #include <typeinfo>
28 #include <type_traits>
31 class SwModify;
32 class SfxPoolItem;
33 class SfxHint;
36 SwModify and SwClient cooperate in propagating attribute changes.
37 If an attribute changes, the change is notified to all dependent
38 formats and other interested objects, e.g. Nodes. The clients will detect
39 if the change affects them. It could be that the changed attribute is
40 overruled in the receiving object so that its change does not become
41 effective or that the receiver is not interested in the particular attribute
42 in general (though probably in other attributes of the SwModify object they
43 are registered in).
44 As SwModify objects are derived from SwClient, they can create a chain of SwClient
45 objects where changes can get propagated through.
46 Each SwClient can be registered at only one SwModify object, while each SwModify
47 object is connected to a list of SwClient objects. If an object derived from SwClient
48 wants to get notifications from more than one SwModify object, it must create additional
49 SwClient objects. The SwDepend class allows to handle their notifications in the same
50 notification callback as it forwards the Modify() calls it receives to a "master"
51 SwClient implementation.
52 The SwIterator class allows to iterate over the SwClient objects registered at an
53 SwModify. For historical reasons its ability to use TypeInfo to restrict this iteration
54 to objects of a particular type created a lot of code that misuses SwClient-SwModify
55 relationships that basically should be used only for Modify/Notify callbacks.
56 This is still subject to refactoring.
59 class SwModify;
60 class SwClient;
61 template<typename E, typename S> class SwIterator;
63 namespace sw
65 class ClientIteratorBase;
66 struct SW_DLLPUBLIC LegacyModifyHint final: SfxHint
68 LegacyModifyHint(const SfxPoolItem* pOld, const SfxPoolItem* pNew) : m_pOld(pOld), m_pNew(pNew) {};
69 virtual ~LegacyModifyHint() override;
70 const SfxPoolItem* m_pOld;
71 const SfxPoolItem* m_pNew;
73 /// refactoring out the some of the more sane SwClient functionality
74 class SW_DLLPUBLIC WriterListener
76 friend class ::SwModify;
77 friend class ::sw::ClientIteratorBase;
78 private:
79 WriterListener* m_pLeft;
80 WriterListener* m_pRight; ///< double-linked list of other clients
82 WriterListener(WriterListener const&) = delete;
83 WriterListener& operator=(WriterListener const&) = delete;
85 protected:
86 WriterListener()
87 : m_pLeft(nullptr), m_pRight(nullptr)
89 virtual ~WriterListener() {};
90 virtual void SwClientNotify( const SwModify&, const SfxHint& rHint) =0;
91 public:
92 bool IsLast() const { return !m_pLeft && !m_pRight; }
95 // SwClient
96 class SW_DLLPUBLIC SwClient : public ::sw::WriterListener
98 // avoids making the details of the linked list and the callback method public
99 friend class SwModify;
100 friend class sw::ClientIteratorBase;
101 template<typename E, typename S> friend class SwIterator;
103 SwModify *m_pRegisteredIn; ///< event source
105 protected:
106 // single argument ctors shall be explicit.
107 inline explicit SwClient( SwModify* pToRegisterIn );
109 // write access to pRegisteredIn shall be granted only to the object itself (protected access)
110 SwModify* GetRegisteredInNonConst() const { return m_pRegisteredIn; }
112 public:
114 SwClient() : m_pRegisteredIn(nullptr) {}
115 virtual ~SwClient() override;
116 // callbacks received from SwModify (friend class - so these methods can be private)
117 // should be called only from SwModify the client is registered in
118 // mba: IMHO this method should be pure virtual
119 // DO NOT USE IN NEW CODE! use SwClientNotify instead.
120 virtual void Modify(const SfxPoolItem* pOldValue, const SfxPoolItem* pNewValue);
121 // when overriding this, you MUST call SwClient::SwClientModify() in the override!
122 virtual void SwClientNotify(const SwModify&, const SfxHint& rHint) override;
124 // in case an SwModify object is destroyed that itself is registered in another SwModify,
125 // its SwClient objects can decide to get registered to the latter instead by calling this method
126 void CheckRegistration( const SfxPoolItem *pOldValue );
128 // controlled access to Modify method
129 // mba: this is still considered a hack and it should be fixed; the name makes grep-ing easier
130 void ModifyNotification( const SfxPoolItem *pOldValue, const SfxPoolItem *pNewValue ) { this->Modify ( pOldValue, pNewValue ); }
131 void SwClientNotifyCall( const SwModify& rModify, const SfxHint& rHint ) { SwClientNotify( rModify, rHint ); }
133 const SwModify* GetRegisteredIn() const { return m_pRegisteredIn; }
134 SwModify* GetRegisteredIn() { return m_pRegisteredIn; }
137 // get information about attribute
138 virtual bool GetInfo( SfxPoolItem& ) const { return true; }
142 // SwModify
144 // class has a doubly linked list for dependencies
145 class SW_DLLPUBLIC SwModify: public SwClient
147 friend class sw::ClientIteratorBase;
148 template<typename E, typename S> friend class SwIterator;
149 sw::WriterListener* m_pWriterListeners; // the start of the linked list of clients
150 bool m_bModifyLocked : 1; // don't broadcast changes now
151 bool m_bLockClientList : 1; // may be set when this instance notifies its clients
152 bool m_bInCache : 1;
153 bool m_bInSwFntCache : 1;
155 // mba: IMHO this method should be pure virtual
156 // DO NOT USE IN NEW CODE! use CallSwClientNotify instead.
157 virtual void Modify( const SfxPoolItem* pOld, const SfxPoolItem *pNew) override
158 { NotifyClients( pOld, pNew ); };
160 SwModify(SwModify&) = delete;
161 SwModify &operator =(const SwModify&) = delete;
162 public:
163 SwModify()
164 : SwClient(nullptr), m_pWriterListeners(nullptr), m_bModifyLocked(false), m_bLockClientList(false), m_bInCache(false), m_bInSwFntCache(false)
166 explicit SwModify( SwModify* pToRegisterIn )
167 : SwClient(pToRegisterIn), m_pWriterListeners(nullptr), m_bModifyLocked(false), m_bLockClientList(false), m_bInCache(false), m_bInSwFntCache(false)
170 // broadcasting: send notifications to all clients
171 // DO NOT USE IN NEW CODE! use CallSwClientNotify instead.
172 void NotifyClients( const SfxPoolItem *pOldValue, const SfxPoolItem *pNewValue );
173 // the same, but without setting m_bModifyLocked or checking for any of the flags
174 // DO NOT USE IN NEW CODE! use CallSwClientNotify instead.
175 void ModifyBroadcast( const SfxPoolItem *pOldValue, const SfxPoolItem *pNewValue)
176 { CallSwClientNotify( sw::LegacyModifyHint{ pOldValue, pNewValue } ); };
178 // a more universal broadcasting mechanism
179 inline void CallSwClientNotify( const SfxHint& rHint ) const;
181 virtual ~SwModify() override;
183 void Add(SwClient *pDepend);
184 SwClient* Remove(SwClient *pDepend);
185 bool HasWriterListeners() const { return m_pWriterListeners; }
187 // get information about attribute
188 virtual bool GetInfo( SfxPoolItem& ) const override;
190 void LockModify() { m_bModifyLocked = true; }
191 void UnlockModify() { m_bModifyLocked = false; }
192 void SetInCache( bool bNew ) { m_bInCache = bNew; }
193 void SetInSwFntCache( bool bNew ) { m_bInSwFntCache = bNew; }
194 void SetInDocDTOR();
195 bool IsModifyLocked() const { return m_bModifyLocked; }
196 bool IsInCache() const { return m_bInCache; }
197 bool IsInSwFntCache() const { return m_bInSwFntCache; }
199 void CheckCaching( const sal_uInt16 nWhich );
200 bool HasOnlyOneListener() { return m_pWriterListeners && m_pWriterListeners->IsLast(); }
203 // SwDepend
206 * Helper class for objects that need to depend on more than one SwClient
208 class SW_DLLPUBLIC SwDepend final : public SwClient
210 SwClient *m_pToTell;
212 public:
213 SwDepend(SwClient *pTellHim, SwModify *pDepend) : SwClient(pDepend), m_pToTell(pTellHim) {}
215 SwClient* GetToTell() { return m_pToTell; }
217 /** get Client information */
218 virtual bool GetInfo( SfxPoolItem& rInfo) const override
219 { return m_pToTell == nullptr || m_pToTell->GetInfo( rInfo ); }
220 private:
221 virtual void Modify( const SfxPoolItem* pOldValue, const SfxPoolItem *pNewValue ) override
223 if( pNewValue && pNewValue->Which() == RES_OBJECTDYING )
224 CheckRegistration(pOldValue);
225 else if( m_pToTell )
226 m_pToTell->ModifyNotification(pOldValue, pNewValue);
228 virtual void SwClientNotify( const SwModify& rModify, const SfxHint& rHint ) override
229 { if(m_pToTell) m_pToTell->SwClientNotifyCall(rModify, rHint); }
232 namespace sw
234 class ClientIteratorBase : public sw::Ring< ::sw::ClientIteratorBase >
236 friend SwClient* SwModify::Remove(SwClient*);
237 friend void SwModify::Add(SwClient*);
238 protected:
239 const SwModify& m_rRoot;
240 // the current object in an iteration
241 WriterListener* m_pCurrent;
242 // in case the current object is already removed, the next object in the list
243 // is marked down to become the current object in the next step
244 // this is necessary because iteration requires access to members of the current object
245 WriterListener* m_pPosition;
246 static SW_DLLPUBLIC ClientIteratorBase* our_pClientIters;
248 ClientIteratorBase( const SwModify& rModify )
249 : m_rRoot(rModify)
251 MoveTo(our_pClientIters);
252 our_pClientIters = this;
253 m_pCurrent = m_pPosition = m_rRoot.m_pWriterListeners;
255 WriterListener* GetLeftOfPos() { return m_pPosition->m_pLeft; }
256 WriterListener* GetRightOfPos() { return m_pPosition->m_pRight; }
257 WriterListener* GoStart()
259 m_pPosition = m_rRoot.m_pWriterListeners;
260 if(m_pPosition)
261 while( m_pPosition->m_pLeft )
262 m_pPosition = m_pPosition->m_pLeft;
263 return m_pCurrent = m_pPosition;
265 ~ClientIteratorBase() override
267 assert(our_pClientIters);
268 if(our_pClientIters == this)
269 our_pClientIters = unique() ? nullptr : GetNextInRing();
270 MoveTo(nullptr);
272 // return "true" if an object was removed from a client chain in iteration
273 // adding objects to a client chain in iteration is forbidden
274 // SwModify::Add() asserts this
275 bool IsChanged() const { return m_pPosition != m_pCurrent; }
276 // ensures the iterator to point at a current client
277 WriterListener* Sync() { return m_pCurrent = m_pPosition; }
281 class SwPageDesc;
283 template< typename TElementType, typename TSource > class SwIterator final : private sw::ClientIteratorBase
285 //static_assert(!std::is_base_of<SwPageDesc,TSource>::value, "SwPageDesc as TSource is deprecated.");
286 static_assert(std::is_base_of<SwClient,TElementType>::value, "TElementType needs to be derived from SwClient.");
287 static_assert(std::is_base_of<SwModify,TSource>::value, "TSource needs to be derived from SwModify.");
288 public:
289 SwIterator( const TSource& rSrc ) : sw::ClientIteratorBase(rSrc) {}
290 TElementType* First()
292 GoStart();
293 if(!m_pPosition)
294 return nullptr;
295 m_pCurrent = nullptr;
296 return Next();
298 TElementType* Last()
300 if(!m_pPosition)
301 m_pPosition = m_rRoot.m_pWriterListeners;
302 if(!m_pPosition)
303 return static_cast<TElementType*>(Sync());
304 while(GetRightOfPos())
305 m_pPosition = GetRightOfPos();
306 if(dynamic_cast<const TElementType *>(m_pPosition) != nullptr)
307 return static_cast<TElementType*>(Sync());
308 return Previous();
310 TElementType* Next()
312 if(!IsChanged())
313 m_pPosition = GetRightOfPos();
314 while(m_pPosition && dynamic_cast<const TElementType *>(m_pPosition) == nullptr)
315 m_pPosition = GetRightOfPos();
316 return static_cast<TElementType*>(Sync());
318 TElementType* Previous()
320 m_pPosition = GetLeftOfPos();
321 while(m_pPosition && dynamic_cast<const TElementType *>(m_pPosition) == nullptr)
322 m_pPosition = GetLeftOfPos();
323 return static_cast<TElementType*>(Sync());
325 using sw::ClientIteratorBase::IsChanged;
328 template< typename TSource > class SwIterator<SwClient, TSource> final : private sw::ClientIteratorBase
330 static_assert(std::is_base_of<SwModify,TSource>::value, "TSource needs to be derived from SwModify");
331 public:
332 SwIterator( const TSource& rSrc ) : sw::ClientIteratorBase(rSrc) {}
333 SwClient* First()
334 { return static_cast<SwClient*>(GoStart()); }
335 SwClient* Next()
337 if(!IsChanged())
338 m_pPosition = GetRightOfPos();
339 return static_cast<SwClient*>(Sync());
341 using sw::ClientIteratorBase::IsChanged;
344 SwClient::SwClient( SwModify* pToRegisterIn )
345 : m_pRegisteredIn( nullptr )
347 if(pToRegisterIn)
348 pToRegisterIn->Add(this);
351 void SwModify::CallSwClientNotify( const SfxHint& rHint ) const
353 SwIterator<SwClient,SwModify> aIter(*this);
354 for(SwClient* pClient = aIter.First(); pClient; pClient = aIter.Next())
355 pClient->SwClientNotify( *this, rHint );
357 #endif
359 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */