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
25 #include <svl/hint.hxx>
26 #include <svl/broadcast.hxx>
27 #include <svl/poolitem.hxx>
30 #include <type_traits>
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
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.
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
87 class SW_DLLPUBLIC BroadcasterMixin
{
88 SvtBroadcaster m_aNotifier
;
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
;
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;
112 : m_pLeft(nullptr), m_pRight(nullptr)
114 virtual ~WriterListener() COVERITY_NOEXCEPT_FALSE
{}
115 virtual void SwClientNotify( const SwModify
&, const SfxHint
& rHint
) =0;
117 bool IsLast() const { return !m_pLeft
&& !m_pRight
; }
119 enum class IteratorMode
{ Exact
, UnwrapMulti
};
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
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
;
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; }
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
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;
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
; }
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
;
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
239 class SW_DLLPUBLIC BroadcastingModify
: public SwModify
, public BroadcasterMixin
{
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
247 template<typename E
, typename S
, sw::IteratorMode
> friend class ::SwIterator
;
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();
268 /** get Client information */
269 virtual bool GetInfo( SfxPoolItem
& rInfo
) const override
;
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
278 std::vector
<ListenerEntry
> m_vDepends
;
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
*);
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
)
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
;
316 while( m_pPosition
->m_pLeft
)
317 m_pPosition
= m_pPosition
->m_pLeft
;
318 m_pCurrent
= m_pPosition
;
321 ~ClientIteratorBase() override
323 assert(s_pClientIters
);
324 if(s_pClientIters
== this)
325 s_pClientIters
= unique() ? nullptr : GetNextInRing();
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.");
345 SwIterator( const TSource
& rSrc
) : sw::ClientIteratorBase(rSrc
) {}
346 TElementType
* First()
351 m_pCurrent
= nullptr;
357 m_pPosition
= GetRightOfPos();
358 sw::WriterListener
*pCurrent(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
;
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");
386 SwIterator( const TSource
& rSrc
) : sw::ClientIteratorBase(rSrc
) {}
388 { return static_cast<SwClient
*>(GoStart()); }
392 m_pPosition
= GetRightOfPos();
393 return static_cast<SwClient
*>(Sync());
395 using sw::ClientIteratorBase::IsChanged
;
398 SwClient::SwClient( SwModify
* pToRegisterIn
)
399 : m_pRegisteredIn( nullptr )
402 pToRegisterIn
->Add(this);
407 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */