1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20 #pragma once
21
22 #include <cassert>
23
24 #include <svl/hint.hxx>
25 #include <svl/broadcast.hxx>
26 #include <svl/poolitem.hxx>
27 #include "swdllapi.h"
28 #include "ring.hxx"
29 #include <type_traits>
30 #include <vector>
31 #include <optional>
32
33 class SwModify;
34 class SwFormat;
35 class SfxPoolItem;
36 class SwAttrSet;
37 class SwCellFrame;
38 class SwTabFrame;
39 class SwRowFrame;
40 class SwTable;
41
42 /*
43 SwModify and SwClient cooperate in propagating attribute changes.
44 If an attribute changes, the change is notified to all dependent
45 formats and other interested objects, e.g. Nodes. The clients will detect
46 if the change affects them. It could be that the changed attribute is
47 overruled in the receiving object so that its change does not become
48 effective or that the receiver is not interested in the particular attribute
49 in general (though probably in other attributes of the SwModify object they
50 are registered in).
51 As SwModify objects are derived from SwClient, they can create a chain of SwClient
52 objects where changes can get propagated through.
53 Each SwClient can be registered at only one SwModify object, while each SwModify
54 object is connected to a list of SwClient objects. If an object derived from SwClient
55 wants to get notifications from more than one SwModify object, it must create additional
56 SwClient objects. The SwDepend class allows to handle their notifications in the same
57 notification callback as it forwards the Modify() calls it receives to a "master"
58 SwClient implementation.
59 The SwIterator class allows to iterate over the SwClient objects registered at an
60 SwModify. For historical reasons its ability to use TypeInfo to restrict this iteration
61 to objects of a particular type created a lot of code that misuses SwClient-SwModify
62 relationships that basically should be used only for Modify/Notify callbacks.
63 This is still subject to refactoring.
64 */
65
66 namespace sw
67 {
68 class ClientIteratorBase;
69 class ListenerEntry;
70 void ClientNotifyAttrChg(SwModify& rModify, const SwAttrSet& aSet, SwAttrSet& aOld, SwAttrSet& aNew);
71 struct SAL_DLLPUBLIC_RTTI LegacyModifyHint final: SfxHint
72 {
LegacyModifyHintsw::LegacyModifyHint73 LegacyModifyHint(const SfxPoolItem* pOld, const SfxPoolItem* pNew) : SfxHint(SfxHintId::SwLegacyModify), m_pOld(pOld), m_pNew(pNew) {};
GetWhichsw::LegacyModifyHint74 sal_uInt16 GetWhich() const { return m_pOld ? m_pOld->Which() : m_pNew ? m_pNew->Which() : 0; };
75 virtual ~LegacyModifyHint() override;
76 const SfxPoolItem* m_pOld;
77 const SfxPoolItem* m_pNew;
78 };
79 struct ModifyChangedHint final: SfxHint
80 {
ModifyChangedHintsw::ModifyChangedHint81 ModifyChangedHint(const SwModify* pNew) : SfxHint(SfxHintId::SwModifyChanged), m_pNew(pNew) {};
82 const SwModify* m_pNew;
83 };
84 // Observer pattern using svl implementation
85 // use this instead of SwClient/SwModify wherever possible
86 // In writer layout, this might not always be possible,
87 // but for listeners outside of it (e.g. unocore) this should be used.
88 // The only "magic" signal this class issues is a ModifyChangedHint
89 // proclaiming its death. It does NOT however provide a new SwModify for
90 // listeners to switch to like the old SwModify/SwClient did, as that leads
91 // to madness.
92 class SW_DLLPUBLIC BroadcasterMixin {
93 SvtBroadcaster m_aNotifier;
94 public:
95 BroadcasterMixin() = default;
96 BroadcasterMixin(BroadcasterMixin const &) = default;
operator =(const BroadcasterMixin &)97 BroadcasterMixin& operator=(const BroadcasterMixin&)
98 {
99 return *this; // Listeners are never copied or moved.
100 }
GetNotifier()101 SvtBroadcaster& GetNotifier() { return m_aNotifier; }
102 };
103 /// refactoring out the same of the more sane SwClient functionality
104 class SAL_DLLPUBLIC_RTTI WriterListener
105 {
106 friend class ::SwModify;
107 friend class ::sw::ClientIteratorBase;
108 private:
109 WriterListener* m_pLeft;
110 WriterListener* m_pRight; ///< double-linked list of other clients
111
112 WriterListener(WriterListener const&) = delete;
113 WriterListener& operator=(WriterListener const&) = delete;
114
115 protected:
WriterListener()116 WriterListener()
117 : m_pLeft(nullptr), m_pRight(nullptr)
118 {}
~WriterListener()119 virtual ~WriterListener() COVERITY_NOEXCEPT_FALSE {}
120 virtual void SwClientNotify( const SwModify&, const SfxHint& rHint) =0;
121 public:
IsLast() const122 bool IsLast() const { return !m_pLeft && !m_pRight; }
DynCastCellFrame() const123 virtual const SwCellFrame* DynCastCellFrame() const { return nullptr; }
DynCastTabFrame() const124 virtual const SwTabFrame* DynCastTabFrame() const { return nullptr; }
DynCastRowFrame() const125 virtual const SwRowFrame* DynCastRowFrame() const { return nullptr; }
DynCastTable() const126 virtual const SwTable* DynCastTable() const { return nullptr; }
127 };
128 enum class IteratorMode { Exact, UnwrapMulti };
129 }
130
131 // SwClient
132 class SW_DLLPUBLIC SwClient : public ::sw::WriterListener
133 {
134 // avoids making the details of the linked list and the callback method public
135 friend class SwModify;
136 friend class sw::ClientIteratorBase;
137 friend class sw::ListenerEntry;
138 template<typename E, typename S, sw::IteratorMode> friend class SwIterator;
139
140 SwModify *m_pRegisteredIn; ///< event source
141
142 protected:
143 // single argument ctors shall be explicit.
144 inline explicit SwClient( SwModify* pToRegisterIn );
145
146 // write access to pRegisteredIn shall be granted only to the object itself (protected access)
GetRegisteredInNonConst() const147 SwModify* GetRegisteredInNonConst() const { return m_pRegisteredIn; }
148
149 // when overriding this, you MUST call SwClient::SwClientNotify() in the override!
150 virtual void SwClientNotify(const SwModify&, const SfxHint& rHint) override;
151
152 public:
SwClient()153 SwClient() : m_pRegisteredIn(nullptr) {}
154 SwClient(SwClient&&) noexcept;
155 virtual ~SwClient() override;
156
157
158 // in case an SwModify object is destroyed that itself is registered in another SwModify,
159 // its SwClient objects can decide to get registered to the latter instead by calling this method
160 std::optional<sw::ModifyChangedHint> CheckRegistration( const SfxPoolItem* pOldValue );
161 // SwFormat wants to die different than the rest: It wants to reparent every client to its parent
162 // and then send a SwFormatChg hint.
163 void CheckRegistrationFormat(SwFormat& rOld);
164
GetRegisteredIn() const165 const SwModify* GetRegisteredIn() const { return m_pRegisteredIn; }
GetRegisteredIn()166 SwModify* GetRegisteredIn() { return m_pRegisteredIn; }
167 void EndListeningAll();
168 void StartListeningToSameModifyAs(const SwClient&);
169
170
171 // get information about attribute
GetInfo(SfxPoolItem &) const172 virtual bool GetInfo( SfxPoolItem& ) const { return true; }
173 };
174
175
176 // SwModify
177
178 // class has a doubly linked list for dependencies
179 class SW_DLLPUBLIC SwModify: public SwClient
180 {
181 friend class sw::ClientIteratorBase;
182 friend void sw::ClientNotifyAttrChg(SwModify&, const SwAttrSet&, SwAttrSet&, SwAttrSet&);
183 template<typename E, typename S, sw::IteratorMode> friend class SwIterator;
184 sw::WriterListener* m_pWriterListeners; // the start of the linked list of clients
185 bool m_bModifyLocked; // don't broadcast changes now
186
187 SwModify(SwModify const &) = delete;
188 SwModify &operator =(const SwModify&) = delete;
189 protected:
190 virtual void SwClientNotify(const SwModify&, const SfxHint& rHint) override;
191 public:
SwModify()192 SwModify()
193 : SwClient(), m_pWriterListeners(nullptr), m_bModifyLocked(false)
194 {}
195
196 // broadcasting mechanism
197 virtual void CallSwClientNotify( const SfxHint& rHint ) const;
198
199 virtual ~SwModify() override;
200
201 void Add(SwClient& rDepend);
202 void Remove(SwClient& rDepend);
HasWriterListeners() const203 bool HasWriterListeners() const { return m_pWriterListeners; }
HasOnlyOneListener() const204 bool HasOnlyOneListener() const { return m_pWriterListeners && m_pWriterListeners->IsLast(); }
205
206 // get information about attribute
207 virtual bool GetInfo( SfxPoolItem& ) const override;
208
LockModify()209 void LockModify() { m_bModifyLocked = true; }
UnlockModify()210 void UnlockModify() { m_bModifyLocked = false; }
IsModifyLocked() const211 bool IsModifyLocked() const { return m_bModifyLocked; }
212 };
213
214 template<typename TElementType, typename TSource, sw::IteratorMode eMode> class SwIterator;
215
216 namespace sw
217 {
218
219 // this class is part of the migration: it still forwards the "old"
220 // SwModify events and announces them both to the old SwClients still
221 // registered and also to the new SvtListeners.
222 // Still: in the long run the SwClient/SwModify interface should not be
223 // used anymore, in which case a BroadcasterMixin should be enough instead
224 // then.
225 class SW_DLLPUBLIC SAL_LOPLUGIN_ANNOTATE("crosscast") BroadcastingModify :
226 public SwModify, public BroadcasterMixin
227 {
228 public:
229 virtual void CallSwClientNotify(const SfxHint& rHint) const override;
230 };
231 // this should be hidden but sadly SwIterator template needs it...
232 class ListenerEntry final : public SwClient
233 {
234 private:
235 template<typename E, typename S, sw::IteratorMode> friend class ::SwIterator;
236 SwClient *m_pToTell;
237
238 public:
ListenerEntry(SwClient * const pTellHim,SwModify * const pDepend)239 ListenerEntry(SwClient *const pTellHim, SwModify *const pDepend)
240 : SwClient(pDepend), m_pToTell(pTellHim)
241 {}
242 ListenerEntry(ListenerEntry const &) = delete;
243 ListenerEntry& operator=(ListenerEntry const&) = delete;
ListenerEntry(ListenerEntry && other)244 ListenerEntry(ListenerEntry&& other) noexcept
245 : SwClient(std::move(other))
246 , m_pToTell(other.m_pToTell)
247 { }
operator =(ListenerEntry && other)248 ListenerEntry& operator=(ListenerEntry&& other) noexcept
249 {
250 m_pToTell = other.m_pToTell;
251 other.GetRegisteredIn()->Add(*this);
252 other.EndListeningAll();
253 return *this;
254 }
255
256 /** get Client information */
257 virtual bool GetInfo( SfxPoolItem& rInfo) const override;
258 private:
259 virtual void SwClientNotify(const SwModify& rModify, const SfxHint& rHint) override;
260 };
261
262 class SW_DLLPUBLIC WriterMultiListener final
263 {
264 SwClient& m_rToTell;
265 std::vector<ListenerEntry> m_vDepends;
266 public:
267 WriterMultiListener(SwClient& rToTell);
268 WriterMultiListener& operator=(WriterMultiListener const&) = delete; // MSVC2015 workaround
269 WriterMultiListener(WriterMultiListener const&) = delete; // MSVC2015 workaround
270 ~WriterMultiListener();
271 void StartListening(SwModify* pDepend);
272 void EndListening(SwModify* pDepend);
273 bool IsListeningTo(const SwModify* const pDepend) const;
274 void EndListeningAll();
275 };
276 class ClientIteratorBase : public sw::Ring< ::sw::ClientIteratorBase >
277 {
278 friend void SwModify::Remove(SwClient&);
279 friend void SwModify::Add(SwClient&);
280 protected:
281 const SwModify& m_rRoot;
282 // the current object in an iteration
283 WriterListener* m_pCurrent;
284 // in case the current object is already removed, the next object in the list
285 // is marked down to become the current object in the next step
286 // this is necessary because iteration requires access to members of the current object
287 WriterListener* m_pPosition;
288 static SW_DLLPUBLIC ClientIteratorBase* s_pClientIters;
289
ClientIteratorBase(const SwModify & rModify)290 ClientIteratorBase( const SwModify& rModify )
291 : m_rRoot(rModify)
292 {
293 MoveTo(s_pClientIters);
294 #if defined __GNUC__ && !defined __clang__
295 #pragma GCC diagnostic push
296 #pragma GCC diagnostic ignored "-Wdangling-pointer"
297 #endif
298 s_pClientIters = this;
299 #if defined __GNUC__ && !defined __clang__
300 #pragma GCC diagnostic pop
301 #endif
302 m_pCurrent = m_pPosition = m_rRoot.m_pWriterListeners;
303 }
GetLeftOfPos()304 WriterListener* GetLeftOfPos() { return m_pPosition->m_pLeft; }
GetRightOfPos()305 WriterListener* GetRightOfPos() { return m_pPosition->m_pRight; }
GoStart()306 WriterListener* GoStart()
307 {
308 m_pPosition = m_rRoot.m_pWriterListeners;
309 if(m_pPosition)
310 while( m_pPosition->m_pLeft )
311 m_pPosition = m_pPosition->m_pLeft;
312 m_pCurrent = m_pPosition;
313 return m_pCurrent;
314 }
~ClientIteratorBase()315 ~ClientIteratorBase() override
316 {
317 assert(s_pClientIters);
318 if(s_pClientIters == this)
319 s_pClientIters = unique() ? nullptr : GetNextInRing();
320 MoveTo(nullptr);
321 }
322 // return "true" if an object was removed from a client chain in iteration
323 // adding objects to a client chain in iteration is forbidden
324 // SwModify::Add() asserts this
IsChanged() const325 bool IsChanged() const { return m_pPosition != m_pCurrent; }
326 // ensures the iterator to point at a current client
Sync()327 WriterListener* Sync() { m_pCurrent = m_pPosition; return m_pCurrent; }
328 };
329 }
330
331 namespace sw::detail
332 {
333 // Dynamic casting can be expensive when used a lot, so for certain type combinations,
334 // we have faster routines.
335 template<typename CastDest>
internal_dyn_cast(const sw::WriterListener * pSource)336 inline const CastDest * internal_dyn_cast(const sw::WriterListener * pSource)
337 {
338 return dynamic_cast<const CastDest *>(pSource);
339 }
340 template<>
internal_dyn_cast(const sw::WriterListener * pSource)341 inline const SwTable* internal_dyn_cast(const sw::WriterListener * pSource)
342 {
343 return pSource->DynCastTable();
344 }
345 template<>
internal_dyn_cast(const sw::WriterListener * pSource)346 inline const SwCellFrame* internal_dyn_cast(const sw::WriterListener * pSource)
347 {
348 return pSource->DynCastCellFrame();
349 }
350 template<>
internal_dyn_cast(const sw::WriterListener * pSource)351 inline const SwTabFrame* internal_dyn_cast(const sw::WriterListener * pSource)
352 {
353 return pSource->DynCastTabFrame();
354 }
355 template<>
internal_dyn_cast(const sw::WriterListener * pSource)356 inline const SwRowFrame* internal_dyn_cast(const sw::WriterListener * pSource)
357 {
358 return pSource->DynCastRowFrame();
359 }
360 } // namespace sw::detail
361
362 template<typename TElementType, typename TSource,
363 sw::IteratorMode eMode = sw::IteratorMode::Exact> class SwIterator final
364 : private sw::ClientIteratorBase
365 {
366 //static_assert(!std::is_base_of<SwPageDesc,TSource>::value, "SwPageDesc as TSource is deprecated.");
367 static_assert(std::is_base_of<SwClient,TElementType>::value, "TElementType needs to be derived from SwClient.");
368 static_assert(std::is_base_of<SwModify,TSource>::value, "TSource needs to be derived from SwModify.");
369 public:
SwIterator(const TSource & rSrc)370 SwIterator( const TSource& rSrc ) : sw::ClientIteratorBase(rSrc) {}
First()371 TElementType* First()
372 {
373 GoStart();
374 if(!m_pPosition)
375 return nullptr;
376 m_pCurrent = nullptr;
377 return Next();
378 }
Next()379 TElementType* Next()
380 {
381 if(!IsChanged())
382 m_pPosition = GetRightOfPos();
383 sw::WriterListener *pCurrent(m_pPosition);
384 while (m_pPosition)
385 {
386 if (eMode == sw::IteratorMode::UnwrapMulti)
387 {
388 if (auto const pLE = dynamic_cast<sw::ListenerEntry const*>(m_pPosition))
389 {
390 pCurrent = pLE->m_pToTell;
391 }
392 }
393 if (sw::detail::internal_dyn_cast<TElementType>(pCurrent) == nullptr)
394 {
395 m_pPosition = GetRightOfPos();
396 pCurrent = m_pPosition;
397 }
398 else
399 break;
400 }
401 Sync();
402 return static_cast<TElementType*>(pCurrent);
403 }
404 using sw::ClientIteratorBase::IsChanged;
405 };
406
407 template< typename TSource > class SwIterator<SwClient, TSource> final : private sw::ClientIteratorBase
408 {
409 static_assert(std::is_base_of<SwModify,TSource>::value, "TSource needs to be derived from SwModify");
410 public:
SwIterator(const TSource & rSrc)411 SwIterator( const TSource& rSrc ) : sw::ClientIteratorBase(rSrc) {}
First()412 SwClient* First()
413 { return static_cast<SwClient*>(GoStart()); }
Next()414 SwClient* Next()
415 {
416 if(!IsChanged())
417 m_pPosition = GetRightOfPos();
418 return static_cast<SwClient*>(Sync());
419 }
420 using sw::ClientIteratorBase::IsChanged;
421 };
422
SwClient(SwModify * pToRegisterIn)423 SwClient::SwClient( SwModify* pToRegisterIn )
424 : m_pRegisteredIn( nullptr )
425 {
426 if(pToRegisterIn)
427 pToRegisterIn->Add(*this);
428 }
429
430 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
431