Branch libreoffice-5-0-4
[LibreOffice.git] / sw / inc / calbck.hxx
bloba27b7e9abe885b5110c6af7b2fcaee2e0cee6f25
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 <tools/rtti.hxx>
24 #include "swdllapi.h"
25 #include <boost/noncopyable.hpp>
26 #include <ring.hxx>
27 #include <hintids.hxx>
28 #include <hints.hxx>
29 #include <typeinfo>
30 #include <type_traits>
33 class SwModify;
34 class SfxPoolItem;
35 class SfxHint;
38 SwModify and SwClient cooperate in propagating attribute changes.
39 If an attribute changes, the change is notified to all dependent
40 formats and other interested objects, e.g. Nodes. The clients will detect
41 if the change affects them. It could be that the changed attribute is
42 overruled in the receiving object so that its change does not become
43 effective or that the receiver is not interested in the particular attribute
44 in general (though probably in other attributes of the SwModify object they
45 are registered in).
46 As SwModify objects are derived from SwClient, they can create a chain of SwClient
47 objects where changes can get propagated through.
48 Each SwClient can be registered at only one SwModify object, while each SwModify
49 object is connected to a list of SwClient objects. If an object derived from SwClient
50 wants to get notifications from more than one SwModify object, it must create additional
51 SwClient objects. The SwDepend class allows to handle their notifications in the same
52 notification callback as it forwards the Modify() calls it receives to a "master"
53 SwClient implementation.
54 The SwIterator class allows to iterate over the SwClient objects registered at an
55 SwModify. For historical reasons its ability to use TypeInfo to restrict this iteration
56 to objects of a particular type created a lot of code that misuses SwClient-SwModify
57 relationships that basically should be used only for Modify/Notify callbacks.
58 This is still subject to refactoring.
61 class SwModify;
62 class SwClient;
63 template<typename E, typename S> class SwIterator;
65 namespace sw
67 class ClientIteratorBase;
68 struct LegacyModifyHint SAL_FINAL: SfxHint
70 LegacyModifyHint(const SfxPoolItem* pOld, const SfxPoolItem* pNew) : m_pOld(pOld), m_pNew(pNew) {};
71 const SfxPoolItem* m_pOld;
72 const SfxPoolItem* m_pNew;
74 /// refactoring out the some of the more sane SwClient functionality
75 class SW_DLLPUBLIC WriterListener : ::boost::noncopyable
77 friend class ::SwModify;
78 friend class ::sw::ClientIteratorBase;
79 private:
80 WriterListener* m_pLeft;
81 WriterListener* m_pRight; ///< double-linked list of other clients
82 protected:
83 WriterListener()
84 : m_pLeft(nullptr), m_pRight(nullptr)
86 virtual ~WriterListener() {};
87 virtual void SwClientNotify( const SwModify&, const SfxHint& rHint) =0;
88 public:
89 bool IsLast() const { return !m_pLeft && !m_pRight; }
92 // SwClient
93 class SW_DLLPUBLIC SwClient : ::sw::WriterListener
95 // avoids making the details of the linked list and the callback method public
96 friend class SwModify;
97 friend class sw::ClientIteratorBase;
98 template<typename E, typename S> friend class SwIterator;
100 SwModify *pRegisteredIn; ///< event source
102 protected:
103 // single argument ctors shall be explicit.
104 inline explicit SwClient( SwModify* pToRegisterIn );
106 // write access to pRegisteredIn shall be granted only to the object itself (protected access)
107 SwModify* GetRegisteredInNonConst() const { return pRegisteredIn; }
109 public:
111 SwClient() : pRegisteredIn(nullptr) {}
112 virtual ~SwClient() SAL_OVERRIDE;
113 // callbacks received from SwModify (friend class - so these methods can be private)
114 // should be called only from SwModify the client is registered in
115 // mba: IMHO this method should be pure virtual
116 // DO NOT USE IN NEW CODE! use SwClientNotify instead.
117 virtual void Modify( const SfxPoolItem* pOldValue, const SfxPoolItem* pNewValue )
118 { CheckRegistration( pOldValue, pNewValue ); }
119 // when overriding this, you MUST call SwClient::SwClientModify() in the override!
120 virtual void SwClientNotify( const SwModify&, const SfxHint& rHint) SAL_OVERRIDE
122 if(typeid(rHint) == typeid(sw::LegacyModifyHint))
124 auto pLegacyHint(static_cast<const sw::LegacyModifyHint*>(&rHint));
125 Modify(pLegacyHint->m_pOld, pLegacyHint->m_pNew);
129 // in case an SwModify object is destroyed that itself is registered in another SwModify,
130 // its SwClient objects can decide to get registered to the latter instead by calling this method
131 void CheckRegistration( const SfxPoolItem *pOldValue, const SfxPoolItem *pNewValue );
133 // controlled access to Modify method
134 // mba: this is still considered a hack and it should be fixed; the name makes grep-ing easier
135 void ModifyNotification( const SfxPoolItem *pOldValue, const SfxPoolItem *pNewValue ) { this->Modify ( pOldValue, pNewValue ); }
136 void SwClientNotifyCall( const SwModify& rModify, const SfxHint& rHint ) { SwClientNotify( rModify, rHint ); }
138 const SwModify* GetRegisteredIn() const { return pRegisteredIn; }
139 SwModify* GetRegisteredIn() { return pRegisteredIn; }
141 // needed for class SwClientIter
142 TYPEINFO();
144 // get information about attribute
145 virtual bool GetInfo( SfxPoolItem& ) const { return true; }
149 // SwModify
151 // class has a doubly linked list for dependencies
152 class SW_DLLPUBLIC SwModify: public SwClient
154 friend class sw::ClientIteratorBase;
155 template<typename E, typename S> friend class SwIterator;
156 sw::WriterListener* m_pWriterListeners; // the start of the linked list of clients
157 bool m_bModifyLocked : 1; // don't broadcast changes now
158 bool bLockClientList : 1; // may be set when this instance notifies its clients
159 bool m_bInDocDTOR : 1; // workaround for problems when a lot of objects are destroyed
160 bool m_bInCache : 1;
161 bool m_bInSwFntCache : 1;
163 // mba: IMHO this method should be pure virtual
164 // DO NOT USE IN NEW CODE! use CallSwClientNotify instead.
165 virtual void Modify( const SfxPoolItem* pOld, const SfxPoolItem *pNew) SAL_OVERRIDE
166 { NotifyClients( pOld, pNew ); };
168 public:
169 SwModify()
170 : SwClient(nullptr), m_pWriterListeners(nullptr), m_bModifyLocked(false), bLockClientList(false), m_bInDocDTOR(false), m_bInCache(false), m_bInSwFntCache(false)
172 explicit SwModify( SwModify* pToRegisterIn )
173 : SwClient(pToRegisterIn), m_pWriterListeners(nullptr), m_bModifyLocked(false), bLockClientList(false), m_bInDocDTOR(false), m_bInCache(false), m_bInSwFntCache(false)
176 // broadcasting: send notifications to all clients
177 // DO NOT USE IN NEW CODE! use CallSwClientNotify instead.
178 void NotifyClients( const SfxPoolItem *pOldValue, const SfxPoolItem *pNewValue );
179 // the same, but without setting m_bModifyLocked or checking for any of the flags
180 // DO NOT USE IN NEW CODE! use CallSwClientNotify instead.
181 void ModifyBroadcast( const SfxPoolItem *pOldValue, const SfxPoolItem *pNewValue)
182 { CallSwClientNotify( sw::LegacyModifyHint{ pOldValue, pNewValue } ); };
184 // a more universal broadcasting mechanism
185 inline void CallSwClientNotify( const SfxHint& rHint ) const;
187 virtual ~SwModify();
189 void Add(SwClient *pDepend);
190 SwClient* Remove(SwClient *pDepend);
191 bool HasWriterListeners() const { return m_pWriterListeners; }
193 // get information about attribute
194 virtual bool GetInfo( SfxPoolItem& ) const SAL_OVERRIDE;
196 void LockModify() { m_bModifyLocked = true; }
197 void UnlockModify() { m_bModifyLocked = false; }
198 void SetInCache( bool bNew ) { m_bInCache = bNew; }
199 void SetInSwFntCache( bool bNew ) { m_bInSwFntCache = bNew; }
200 void SetInDocDTOR() { m_bInDocDTOR = true; }
201 bool IsModifyLocked() const { return m_bModifyLocked; }
202 bool IsInDocDTOR() const { return m_bInDocDTOR; }
203 bool IsInCache() const { return m_bInCache; }
204 bool IsInSwFntCache() const { return m_bInSwFntCache; }
206 void CheckCaching( const sal_uInt16 nWhich );
207 bool HasOnlyOneListener() { return m_pWriterListeners && m_pWriterListeners->IsLast(); }
210 // SwDepend
213 * Helper class for objects that need to depend on more than one SwClient
215 class SW_DLLPUBLIC SwDepend SAL_FINAL : public SwClient
217 SwClient *m_pToTell;
219 public:
220 SwDepend() : m_pToTell(nullptr) {}
221 SwDepend(SwClient *pTellHim, SwModify *pDepend) : SwClient(pDepend), m_pToTell(pTellHim) {}
223 SwClient* GetToTell() { return m_pToTell; }
225 /** get Client information */
226 virtual bool GetInfo( SfxPoolItem& rInfo) const SAL_OVERRIDE
227 { return m_pToTell == nullptr || m_pToTell->GetInfo( rInfo ); }
228 protected:
229 virtual void Modify( const SfxPoolItem* pOldValue, const SfxPoolItem *pNewValue ) SAL_OVERRIDE
231 if( pNewValue && pNewValue->Which() == RES_OBJECTDYING )
232 CheckRegistration(pOldValue,pNewValue);
233 else if( m_pToTell )
234 m_pToTell->ModifyNotification(pOldValue, pNewValue);
236 virtual void SwClientNotify( const SwModify& rModify, const SfxHint& rHint ) SAL_OVERRIDE
237 { if(m_pToTell) m_pToTell->SwClientNotifyCall(rModify, rHint); }
240 namespace sw
242 class ClientIteratorBase : public sw::Ring< ::sw::ClientIteratorBase >
244 friend SwClient* SwModify::Remove(SwClient*);
245 friend void SwModify::Add(SwClient*);
246 protected:
247 const SwModify& m_rRoot;
248 // the current object in an iteration
249 WriterListener* m_pCurrent;
250 // in case the current object is already removed, the next object in the list
251 // is marked down to become the current object in the next step
252 // this is necessary because iteration requires access to members of the current object
253 WriterListener* m_pPosition;
254 static SW_DLLPUBLIC ClientIteratorBase* our_pClientIters;
256 ClientIteratorBase( const SwModify& rModify )
257 : m_rRoot(rModify)
259 MoveTo(our_pClientIters);
260 our_pClientIters = this;
261 m_pCurrent = m_pPosition = const_cast<WriterListener*>(m_rRoot.m_pWriterListeners);
263 WriterListener* GetLeftOfPos() { return m_pPosition->m_pLeft; }
264 WriterListener* GetRightOfPos() { return m_pPosition->m_pRight; }
265 WriterListener* GoStart()
267 if((m_pPosition = const_cast<WriterListener*>(m_rRoot.m_pWriterListeners)))
268 while( m_pPosition->m_pLeft )
269 m_pPosition = m_pPosition->m_pLeft;
270 return m_pCurrent = m_pPosition;
272 ~ClientIteratorBase() SAL_OVERRIDE
274 assert(our_pClientIters);
275 if(our_pClientIters == this)
276 our_pClientIters = unique() ? nullptr : GetNextInRing();
277 MoveTo(nullptr);
279 // return "true" if an object was removed from a client chain in iteration
280 // adding objects to a client chain in iteration is forbidden
281 // SwModify::Add() asserts this
282 bool IsChanged() const { return m_pPosition != m_pCurrent; }
283 // ensures the iterator to point at a current client
284 WriterListener* Sync() { return m_pCurrent = m_pPosition; }
288 template< typename TElementType, typename TSource > class SwIterator SAL_FINAL : private sw::ClientIteratorBase
290 static_assert(std::is_base_of<SwClient,TElementType>::value, "TElementType needs to be derived from SwClient");
291 static_assert(std::is_base_of<SwModify,TSource>::value, "TSource needs to be derived from SwModify");
292 public:
293 SwIterator( const TSource& rSrc ) : sw::ClientIteratorBase(rSrc) {}
294 TElementType* First()
296 GoStart();
297 if(!m_pPosition)
298 return nullptr;
299 m_pCurrent = nullptr;
300 return Next();
302 TElementType* Last()
304 if(!m_pPosition)
305 m_pPosition = const_cast<sw::WriterListener*>(m_rRoot.m_pWriterListeners);
306 if(!m_pPosition)
307 return static_cast<TElementType*>(Sync());
308 while(GetRightOfPos())
309 m_pPosition = GetRightOfPos();
310 if(static_cast<SwClient*>(m_pPosition)->IsA(TYPE(TElementType)))
311 return static_cast<TElementType*>(Sync());
312 return Previous();
314 TElementType* Next()
316 if(!IsChanged())
317 m_pPosition = GetRightOfPos();
318 while(m_pPosition && !static_cast<SwClient*>(m_pPosition)->IsA( TYPE(TElementType) ) )
319 m_pPosition = GetRightOfPos();
320 return static_cast<TElementType*>(Sync());
322 TElementType* Previous()
324 m_pPosition = GetLeftOfPos();
325 while(m_pPosition && !static_cast<SwClient*>(m_pPosition)->IsA( TYPE(TElementType) ) )
326 m_pPosition = GetLeftOfPos();
327 return static_cast<TElementType*>(Sync());
329 using sw::ClientIteratorBase::IsChanged;
332 template< typename TSource > class SwIterator<SwClient, TSource> SAL_FINAL : private sw::ClientIteratorBase
334 static_assert(std::is_base_of<SwModify,TSource>::value, "TSource needs to be derived from SwModify");
335 public:
336 SwIterator( const TSource& rSrc ) : sw::ClientIteratorBase(rSrc) {}
337 SwClient* First()
338 { return static_cast<SwClient*>(GoStart()); }
339 SwClient* Last()
341 if(!m_pPosition)
342 m_pPosition = const_cast<sw::WriterListener*>(m_rRoot.m_pWriterListeners);
343 if(!m_pPosition)
344 return m_pCurrent = nullptr;
345 while(GetRightOfPos())
346 m_pPosition = GetRightOfPos();
347 return static_cast<SwClient*>(Sync());
349 SwClient* Next()
351 if(!IsChanged())
352 m_pPosition = GetRightOfPos();
353 return static_cast<SwClient*>(Sync());
355 SwClient* Previous()
357 m_pPosition = GetLeftOfPos();
358 return static_cast<SwClient*>(Sync());
360 using sw::ClientIteratorBase::IsChanged;
363 SwClient::SwClient( SwModify* pToRegisterIn )
364 : pRegisteredIn( nullptr )
366 if(pToRegisterIn)
367 pToRegisterIn->Add(this);
370 void SwModify::CallSwClientNotify( const SfxHint& rHint ) const
372 SwIterator<SwClient,SwModify> aIter(*this);
373 for(SwClient* pClient = aIter.First(); pClient; pClient = aIter.Next())
374 pClient->SwClientNotify( *this, rHint );
376 #endif
378 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */