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 .
24 #include <svl/hint.hxx>
25 #include <svl/broadcast.hxx>
26 #include <svl/poolitem.hxx>
29 #include <type_traits>
41 class SwFindNearestNode
;
44 SwModify and SwClient cooperate in propagating attribute changes.
45 If an attribute changes, the change is notified to all dependent
46 formats and other interested objects, e.g. Nodes. The clients will detect
47 if the change affects them. It could be that the changed attribute is
48 overruled in the receiving object so that its change does not become
49 effective or that the receiver is not interested in the particular attribute
50 in general (though probably in other attributes of the SwModify object they
52 As SwModify objects are derived from SwClient, they can create a chain of SwClient
53 objects where changes can get propagated through.
54 Each SwClient can be registered at only one SwModify object, while each SwModify
55 object is connected to a list of SwClient objects. If an object derived from SwClient
56 wants to get notifications from more than one SwModify object, it must create additional
57 SwClient objects. The SwDepend class allows to handle their notifications in the same
58 notification callback as it forwards the Modify() calls it receives to a "master"
59 SwClient implementation.
60 The SwIterator class allows to iterate over the SwClient objects registered at an
61 SwModify. For historical reasons its ability to use TypeInfo to restrict this iteration
62 to objects of a particular type created a lot of code that misuses SwClient-SwModify
63 relationships that basically should be used only for Modify/Notify callbacks.
64 This is still subject to refactoring.
69 class ClientIteratorBase
;
71 void ClientNotifyAttrChg(SwModify
& rModify
, const SwAttrSet
& aSet
, SwAttrSet
& aOld
, SwAttrSet
& aNew
);
72 struct SAL_DLLPUBLIC_RTTI LegacyModifyHint final
: SfxHint
74 LegacyModifyHint(const SfxPoolItem
* pOld
, const SfxPoolItem
* pNew
) : SfxHint(SfxHintId::SwLegacyModify
), m_pOld(pOld
), m_pNew(pNew
) {};
75 sal_uInt16
GetWhich() const { return m_pOld
? m_pOld
->Which() : m_pNew
? m_pNew
->Which() : 0; };
76 virtual ~LegacyModifyHint() override
;
77 const SfxPoolItem
* m_pOld
;
78 const SfxPoolItem
* m_pNew
;
80 struct ModifyChangedHint final
: SfxHint
82 ModifyChangedHint(const SwModify
* pNew
) : SfxHint(SfxHintId::SwModifyChanged
), m_pNew(pNew
) {};
83 const SwModify
* m_pNew
;
85 // Observer pattern using svl implementation
86 // use this instead of SwClient/SwModify wherever possible
87 // In writer layout, this might not always be possible,
88 // but for listeners outside of it (e.g. unocore) this should be used.
89 // The only "magic" signal this class issues is a ModifyChangedHint
90 // proclaiming its death. It does NOT however provide a new SwModify for
91 // listeners to switch to like the old SwModify/SwClient did, as that leads
93 class SW_DLLPUBLIC BroadcasterMixin
{
94 SvtBroadcaster m_aNotifier
;
96 BroadcasterMixin() = default;
97 BroadcasterMixin(BroadcasterMixin
const &) = default;
98 BroadcasterMixin
& operator=(const BroadcasterMixin
&)
100 return *this; // Listeners are never copied or moved.
102 SvtBroadcaster
& GetNotifier() { return m_aNotifier
; }
104 /// refactoring out the same of the more sane SwClient functionality
105 class SAL_DLLPUBLIC_RTTI WriterListener
107 friend class ::SwModify
;
108 friend class ::sw::ClientIteratorBase
;
110 WriterListener
* m_pLeft
;
111 WriterListener
* m_pRight
; ///< double-linked list of other clients
113 WriterListener(WriterListener
const&) = delete;
114 WriterListener
& operator=(WriterListener
const&) = delete;
118 : m_pLeft(nullptr), m_pRight(nullptr)
120 virtual ~WriterListener() COVERITY_NOEXCEPT_FALSE
{}
121 virtual void SwClientNotify( const SwModify
&, const SfxHint
& rHint
) =0;
123 bool IsLast() const { return !m_pLeft
&& !m_pRight
; }
124 virtual const SwCellFrame
* DynCastCellFrame() const { return nullptr; }
125 virtual const SwTabFrame
* DynCastTabFrame() const { return nullptr; }
126 virtual const SwRowFrame
* DynCastRowFrame() const { return nullptr; }
127 virtual const SwTable
* DynCastTable() const { return nullptr; }
129 enum class IteratorMode
{ Exact
, UnwrapMulti
};
133 class SW_DLLPUBLIC SwClient
: public ::sw::WriterListener
135 // avoids making the details of the linked list and the callback method public
136 friend class SwModify
;
137 friend class sw::ClientIteratorBase
;
138 friend class sw::ListenerEntry
;
139 template<typename E
, typename S
, sw::IteratorMode
> friend class SwIterator
;
141 SwModify
*m_pRegisteredIn
; ///< event source
144 // single argument ctors shall be explicit.
145 inline explicit SwClient( SwModify
* pToRegisterIn
);
147 // write access to pRegisteredIn shall be granted only to the object itself (protected access)
148 SwModify
* GetRegisteredInNonConst() const { return m_pRegisteredIn
; }
150 // when overriding this, you MUST call SwClient::SwClientNotify() in the override!
151 virtual void SwClientNotify(const SwModify
&, const SfxHint
& rHint
) override
;
154 SwClient() : m_pRegisteredIn(nullptr) {}
155 SwClient(SwClient
&&) noexcept
;
156 virtual ~SwClient() override
;
159 // in case an SwModify object is destroyed that itself is registered in another SwModify,
160 // its SwClient objects can decide to get registered to the latter instead by calling this method
161 std::optional
<sw::ModifyChangedHint
> CheckRegistration( const SfxPoolItem
* pOldValue
);
162 // SwFormat wants to die different than the rest: It wants to reparent every client to its parent
163 // and then send a SwFormatChg hint.
164 void CheckRegistrationFormat(SwFormat
& rOld
);
166 const SwModify
* GetRegisteredIn() const { return m_pRegisteredIn
; }
167 SwModify
* GetRegisteredIn() { return m_pRegisteredIn
; }
168 void EndListeningAll();
169 void StartListeningToSameModifyAs(const SwClient
&);
172 // get information about attribute
173 virtual bool GetInfo( SwFindNearestNode
& ) const { return true; }
179 // class has a doubly linked list for dependencies
180 class SW_DLLPUBLIC SwModify
: public SwClient
182 friend class sw::ClientIteratorBase
;
183 friend void sw::ClientNotifyAttrChg(SwModify
&, const SwAttrSet
&, SwAttrSet
&, SwAttrSet
&);
184 template<typename E
, typename S
, sw::IteratorMode
> friend class SwIterator
;
185 sw::WriterListener
* m_pWriterListeners
; // the start of the linked list of clients
186 bool m_bModifyLocked
; // don't broadcast changes now
188 SwModify(SwModify
const &) = delete;
189 SwModify
&operator =(const SwModify
&) = delete;
191 virtual void SwClientNotify(const SwModify
&, const SfxHint
& rHint
) override
;
194 : SwClient(), m_pWriterListeners(nullptr), m_bModifyLocked(false)
197 // broadcasting mechanism
198 virtual void CallSwClientNotify( const SfxHint
& rHint
) const;
200 virtual ~SwModify() override
;
202 void Add(SwClient
& rDepend
);
203 void Remove(SwClient
& rDepend
);
204 bool HasWriterListeners() const { return m_pWriterListeners
; }
205 bool HasOnlyOneListener() const { return m_pWriterListeners
&& m_pWriterListeners
->IsLast(); }
207 // get information about attribute
208 virtual bool GetInfo( SwFindNearestNode
& ) const override
;
210 void LockModify() { m_bModifyLocked
= true; }
211 void UnlockModify() { m_bModifyLocked
= false; }
212 bool IsModifyLocked() const { return m_bModifyLocked
; }
215 template<typename TElementType
, typename TSource
, sw::IteratorMode eMode
> class SwIterator
;
220 // this class is part of the migration: it still forwards the "old"
221 // SwModify events and announces them both to the old SwClients still
222 // registered and also to the new SvtListeners.
223 // Still: in the long run the SwClient/SwModify interface should not be
224 // used anymore, in which case a BroadcasterMixin should be enough instead
226 class SW_DLLPUBLIC
SAL_LOPLUGIN_ANNOTATE("crosscast") BroadcastingModify
:
227 public SwModify
, public BroadcasterMixin
230 virtual void CallSwClientNotify(const SfxHint
& rHint
) const override
;
232 // this should be hidden but sadly SwIterator template needs it...
233 class ListenerEntry final
: public SwClient
236 template<typename E
, typename S
, sw::IteratorMode
> friend class ::SwIterator
;
240 ListenerEntry(SwClient
*const pTellHim
, SwModify
*const pDepend
)
241 : SwClient(pDepend
), m_pToTell(pTellHim
)
243 ListenerEntry(ListenerEntry
const &) = delete;
244 ListenerEntry
& operator=(ListenerEntry
const&) = delete;
245 ListenerEntry(ListenerEntry
&& other
) noexcept
246 : SwClient(std::move(other
))
247 , m_pToTell(other
.m_pToTell
)
249 ListenerEntry
& operator=(ListenerEntry
&& other
) noexcept
251 m_pToTell
= other
.m_pToTell
;
252 other
.GetRegisteredIn()->Add(*this);
253 other
.EndListeningAll();
257 /** get Client information */
258 virtual bool GetInfo( SwFindNearestNode
& rInfo
) const override
;
260 virtual void SwClientNotify(const SwModify
& rModify
, const SfxHint
& rHint
) override
;
263 class SW_DLLPUBLIC WriterMultiListener final
266 std::vector
<ListenerEntry
> m_vDepends
;
268 WriterMultiListener(SwClient
& rToTell
);
269 WriterMultiListener
& operator=(WriterMultiListener
const&) = delete; // MSVC2015 workaround
270 WriterMultiListener(WriterMultiListener
const&) = delete; // MSVC2015 workaround
271 ~WriterMultiListener();
272 void StartListening(SwModify
* pDepend
);
273 void EndListening(SwModify
* pDepend
);
274 bool IsListeningTo(const SwModify
* const pDepend
) const;
275 void EndListeningAll();
277 class ClientIteratorBase
: public sw::Ring
< ::sw::ClientIteratorBase
>
279 friend void SwModify::Remove(SwClient
&);
280 friend void SwModify::Add(SwClient
&);
282 const SwModify
& m_rRoot
;
283 // the current object in an iteration
284 WriterListener
* m_pCurrent
;
285 // in case the current object is already removed, the next object in the list
286 // is marked down to become the current object in the next step
287 // this is necessary because iteration requires access to members of the current object
288 WriterListener
* m_pPosition
;
289 static SW_DLLPUBLIC ClientIteratorBase
* s_pClientIters
;
291 ClientIteratorBase( const SwModify
& rModify
)
294 MoveTo(s_pClientIters
);
295 #if defined __GNUC__ && !defined __clang__
296 #pragma GCC diagnostic push
297 #pragma GCC diagnostic ignored "-Wdangling-pointer"
299 s_pClientIters
= this;
300 #if defined __GNUC__ && !defined __clang__
301 #pragma GCC diagnostic pop
303 m_pCurrent
= m_pPosition
= m_rRoot
.m_pWriterListeners
;
305 WriterListener
* GetLeftOfPos() { return m_pPosition
->m_pLeft
; }
306 WriterListener
* GetRightOfPos() { return m_pPosition
->m_pRight
; }
307 WriterListener
* GoStart()
309 m_pPosition
= m_rRoot
.m_pWriterListeners
;
311 while( m_pPosition
->m_pLeft
)
312 m_pPosition
= m_pPosition
->m_pLeft
;
313 m_pCurrent
= m_pPosition
;
316 ~ClientIteratorBase() override
318 assert(s_pClientIters
);
319 if(s_pClientIters
== this)
320 s_pClientIters
= unique() ? nullptr : GetNextInRing();
323 // return "true" if an object was removed from a client chain in iteration
324 // adding objects to a client chain in iteration is forbidden
325 // SwModify::Add() asserts this
326 bool IsChanged() const { return m_pPosition
!= m_pCurrent
; }
327 // ensures the iterator to point at a current client
328 WriterListener
* Sync() { m_pCurrent
= m_pPosition
; return m_pCurrent
; }
334 // Dynamic casting can be expensive when used a lot, so for certain type combinations,
335 // we have faster routines.
336 template<typename CastDest
>
337 inline const CastDest
* internal_dyn_cast(const sw::WriterListener
* pSource
)
339 return dynamic_cast<const CastDest
*>(pSource
);
342 inline const SwTable
* internal_dyn_cast(const sw::WriterListener
* pSource
)
344 return pSource
->DynCastTable();
347 inline const SwCellFrame
* internal_dyn_cast(const sw::WriterListener
* pSource
)
349 return pSource
->DynCastCellFrame();
352 inline const SwTabFrame
* internal_dyn_cast(const sw::WriterListener
* pSource
)
354 return pSource
->DynCastTabFrame();
357 inline const SwRowFrame
* internal_dyn_cast(const sw::WriterListener
* pSource
)
359 return pSource
->DynCastRowFrame();
361 } // namespace sw::detail
363 template<typename TElementType
, typename TSource
,
364 sw::IteratorMode eMode
= sw::IteratorMode::Exact
> class SwIterator final
365 : private sw::ClientIteratorBase
367 //static_assert(!std::is_base_of<SwPageDesc,TSource>::value, "SwPageDesc as TSource is deprecated.");
368 static_assert(std::is_base_of
<SwClient
,TElementType
>::value
, "TElementType needs to be derived from SwClient.");
369 static_assert(std::is_base_of
<SwModify
,TSource
>::value
, "TSource needs to be derived from SwModify.");
371 SwIterator( const TSource
& rSrc
) : sw::ClientIteratorBase(rSrc
) {}
372 TElementType
* First()
377 m_pCurrent
= nullptr;
383 m_pPosition
= GetRightOfPos();
384 sw::WriterListener
*pCurrent(m_pPosition
);
387 if (eMode
== sw::IteratorMode::UnwrapMulti
)
389 if (auto const pLE
= dynamic_cast<sw::ListenerEntry
const*>(m_pPosition
))
391 pCurrent
= pLE
->m_pToTell
;
394 if (sw::detail::internal_dyn_cast
<TElementType
>(pCurrent
) == nullptr)
396 m_pPosition
= GetRightOfPos();
397 pCurrent
= m_pPosition
;
403 return static_cast<TElementType
*>(pCurrent
);
405 using sw::ClientIteratorBase::IsChanged
;
408 template< typename TSource
> class SwIterator
<SwClient
, TSource
> final
: private sw::ClientIteratorBase
410 static_assert(std::is_base_of
<SwModify
,TSource
>::value
, "TSource needs to be derived from SwModify");
412 SwIterator( const TSource
& rSrc
) : sw::ClientIteratorBase(rSrc
) {}
414 { return static_cast<SwClient
*>(GoStart()); }
418 m_pPosition
= GetRightOfPos();
419 return static_cast<SwClient
*>(Sync());
421 using sw::ClientIteratorBase::IsChanged
;
424 SwClient::SwClient( SwModify
* pToRegisterIn
)
425 : m_pRegisteredIn( nullptr )
428 pToRegisterIn
->Add(*this);
431 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */