xref: /core/sw/inc/calbck.hxx (revision 2cb410d1)
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