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 #ifndef INCLUDED_SW_INC_CALBCK_HXX
21 #define INCLUDED_SW_INC_CALBCK_HXX
23 #include <tools/rtti.hxx>
25 #include <boost/noncopyable.hpp>
27 #include <hintids.hxx>
30 #include <type_traits>
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
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.
63 template<typename E
, typename S
> class SwIterator
;
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
;
80 WriterListener
* m_pLeft
;
81 WriterListener
* m_pRight
; ///< double-linked list of other clients
84 : m_pLeft(nullptr), m_pRight(nullptr)
86 virtual ~WriterListener() {};
87 virtual void SwClientNotify( const SwModify
&, const SfxHint
& rHint
) =0;
89 bool IsLast() const { return !m_pLeft
&& !m_pRight
; }
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
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
; }
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
144 // get information about attribute
145 virtual bool GetInfo( SfxPoolItem
& ) const { return true; }
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
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
); };
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;
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(); }
213 * Helper class for objects that need to depend on more than one SwClient
215 class SW_DLLPUBLIC SwDepend SAL_FINAL
: public SwClient
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
); }
229 virtual void Modify( const SfxPoolItem
* pOldValue
, const SfxPoolItem
*pNewValue
) SAL_OVERRIDE
231 if( pNewValue
&& pNewValue
->Which() == RES_OBJECTDYING
)
232 CheckRegistration(pOldValue
,pNewValue
);
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
); }
242 class ClientIteratorBase
: public sw::Ring
< ::sw::ClientIteratorBase
>
244 friend SwClient
* SwModify::Remove(SwClient
*);
245 friend void SwModify::Add(SwClient
*);
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
)
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();
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");
293 SwIterator( const TSource
& rSrc
) : sw::ClientIteratorBase(rSrc
) {}
294 TElementType
* First()
299 m_pCurrent
= nullptr;
305 m_pPosition
= const_cast<sw::WriterListener
*>(m_rRoot
.m_pWriterListeners
);
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());
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");
336 SwIterator( const TSource
& rSrc
) : sw::ClientIteratorBase(rSrc
) {}
338 { return static_cast<SwClient
*>(GoStart()); }
342 m_pPosition
= const_cast<sw::WriterListener
*>(m_rRoot
.m_pWriterListeners
);
344 return m_pCurrent
= nullptr;
345 while(GetRightOfPos())
346 m_pPosition
= GetRightOfPos();
347 return static_cast<SwClient
*>(Sync());
352 m_pPosition
= GetRightOfPos();
353 return static_cast<SwClient
*>(Sync());
357 m_pPosition
= GetLeftOfPos();
358 return static_cast<SwClient
*>(Sync());
360 using sw::ClientIteratorBase::IsChanged
;
363 SwClient::SwClient( SwModify
* pToRegisterIn
)
364 : pRegisteredIn( nullptr )
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
);
378 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */