LibreOffice Module sw (master)  1
calbck.hxx
Go to the documentation of this file.
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 #ifndef INCLUDED_SW_INC_CALBCK_HXX
21 #define INCLUDED_SW_INC_CALBCK_HXX
22 
23 #include <cassert>
24 
25 #include <svl/hint.hxx>
26 #include <svl/broadcast.hxx>
27 #include <svl/poolitem.hxx>
28 #include "swdllapi.h"
29 #include "ring.hxx"
30 #include <type_traits>
31 #include <vector>
32 #include <memory>
33 
34 class SwModify;
35 class SfxPoolItem;
36 class SwAttrSet;
37 
38 /*
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
46  are registered in).
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.
60  */
61 
62 namespace sw
63 {
64  class ClientIteratorBase;
65  struct LegacyModifyHint final: SfxHint
66  {
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;
72  };
73  struct ModifyChangedHint final: SfxHint
74  {
75  ModifyChangedHint(const SwModify* pNew) : m_pNew(pNew) {};
76  virtual ~ModifyChangedHint() override;
77  const SwModify* m_pNew;
78  };
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
86  // to madness.
89  public:
90  BroadcasterMixin() = default;
91  BroadcasterMixin(BroadcasterMixin const &) = default;
93  {
94  return *this; // Listeners are never copied or moved.
95  }
96  SvtBroadcaster& GetNotifier() { return m_aNotifier; }
97  };
100  {
101  friend class ::SwModify;
102  friend class ::sw::ClientIteratorBase;
103  private:
106 
107  WriterListener(WriterListener const&) = delete;
108  WriterListener& operator=(WriterListener const&) = delete;
109 
110  protected:
112  : m_pLeft(nullptr), m_pRight(nullptr)
113  {}
114  virtual ~WriterListener() COVERITY_NOEXCEPT_FALSE {}
115  virtual void SwClientNotify( const SwModify&, const SfxHint& rHint) =0;
116  public:
117  bool IsLast() const { return !m_pLeft && !m_pRight; }
118  };
119  enum class IteratorMode { Exact, UnwrapMulti };
120 }
121 
122 // SwClient
124 {
125  // avoids making the details of the linked list and the callback method public
126  friend class SwModify;
128  template<typename E, typename S, sw::IteratorMode> friend class SwIterator;
129 
131 
132 protected:
133  // single argument ctors shall be explicit.
134  inline explicit SwClient( SwModify* pToRegisterIn );
135 
136  // write access to pRegisteredIn shall be granted only to the object itself (protected access)
137  SwModify* GetRegisteredInNonConst() const { return m_pRegisteredIn; }
138 
139  // when overriding this, you MUST call SwClient::SwClientModify() in the override!
140  virtual void SwClientNotify(const SwModify&, const SfxHint& rHint) override;
141 
142 public:
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*);
151 
152 
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 );
156 
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 ); }
162 
163  const SwModify* GetRegisteredIn() const { return m_pRegisteredIn; }
164  SwModify* GetRegisteredIn() { return m_pRegisteredIn; }
165  void EndListeningAll();
166  void StartListeningToSameModifyAs(const SwClient&);
167 
168 
169  // get information about attribute
170  virtual bool GetInfo( SfxPoolItem& ) const { return true; }
171 };
172 
173 
174 // SwModify
175 
176 // class has a doubly linked list for dependencies
178 {
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
183  bool m_bInCache : 1;
184  bool m_bInSwFntCache : 1;
185 
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 ); };
190 
191  SwModify(SwModify const &) = delete;
192  SwModify &operator =(const SwModify&) = delete;
193 public:
195  : SwClient(), m_pWriterListeners(nullptr), m_bModifyLocked(false), m_bInCache(false), m_bInSwFntCache(false)
196  {}
197 
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 );
201 
202  // a more universal broadcasting mechanism
203  virtual void CallSwClientNotify( const SfxHint& rHint ) const;
204 
205  virtual ~SwModify() override;
206 
207  void Add(SwClient *pDepend);
208  SwClient* Remove(SwClient *pDepend);
209  bool HasWriterListeners() const { return m_pWriterListeners; }
210 
211  // get information about attribute
212  virtual bool GetInfo( SfxPoolItem& ) const override;
213 
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; }
218  void SetInDocDTOR();
219  bool IsModifyLocked() const { return m_bModifyLocked; }
220  bool IsInCache() const { return m_bInCache; }
221  bool IsInSwFntCache() const { return m_bInSwFntCache; }
222 
223  void CheckCaching( const sal_uInt16 nWhich );
224  bool HasOnlyOneListener() const { return m_pWriterListeners && m_pWriterListeners->IsLast(); }
225 };
226 
227 template<typename TElementType, typename TSource, sw::IteratorMode eMode> class SwIterator;
228 
229 namespace sw
230 {
231  void ClientNotifyAttrChg(SwModify& rModify, const SwAttrSet& aSet, SwAttrSet& aOld, SwAttrSet& aNew);
232 
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
238  // then.
240  public:
241  virtual void CallSwClientNotify(const SfxHint& rHint) const override;
242  };
243  // this should be hidden but sadly SwIterator template needs it...
244  class ListenerEntry final : public SwClient
245  {
246  private:
247  template<typename E, typename S, sw::IteratorMode> friend class ::SwIterator;
249 
250  public:
251  ListenerEntry(SwClient *const pTellHim, SwModify *const pDepend)
252  : SwClient(pDepend), m_pToTell(pTellHim)
253  {}
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)
259  { }
261  {
262  m_pToTell = other.m_pToTell;
263  other.GetRegisteredIn()->Add(this);
264  other.EndListeningAll();
265  return *this;
266  }
267 
269  virtual bool GetInfo( SfxPoolItem& rInfo) const override;
270  private:
271  virtual void Modify(const SfxPoolItem* pOldValue, const SfxPoolItem *pNewValue) override;
272  virtual void SwClientNotify(const SwModify& rModify, const SfxHint& rHint) override;
273  };
274 
276  {
278  std::vector<ListenerEntry> m_vDepends;
279  public:
280  WriterMultiListener(SwClient& rToTell);
281  WriterMultiListener& operator=(WriterMultiListener const&) = delete; // MSVC2015 workaround
282  WriterMultiListener(WriterMultiListener const&) = delete; // MSVC2015 workaround
284  void StartListening(SwModify* pDepend);
285  void EndListening(SwModify* pDepend);
286  bool IsListeningTo(const SwModify* const pDepend) const;
287  void EndListeningAll();
288  };
289  class ClientIteratorBase : public sw::Ring< ::sw::ClientIteratorBase >
290  {
292  friend void SwModify::Add(SwClient*);
293  protected:
295  // the current object in an iteration
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
302 
303  ClientIteratorBase( const SwModify& rModify )
304  : m_rRoot(rModify)
305  {
306  MoveTo(s_pClientIters);
307  s_pClientIters = this;
308  m_pCurrent = m_pPosition = m_rRoot.m_pWriterListeners;
309  }
310  WriterListener* GetLeftOfPos() { return m_pPosition->m_pLeft; }
311  WriterListener* GetRightOfPos() { return m_pPosition->m_pRight; }
313  {
314  m_pPosition = m_rRoot.m_pWriterListeners;
315  if(m_pPosition)
316  while( m_pPosition->m_pLeft )
317  m_pPosition = m_pPosition->m_pLeft;
318  m_pCurrent = m_pPosition;
319  return m_pCurrent;
320  }
322  {
323  assert(s_pClientIters);
324  if(s_pClientIters == this)
325  s_pClientIters = unique() ? nullptr : GetNextInRing();
326  MoveTo(nullptr);
327  }
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; }
334  };
335 }
336 
337 template<typename TElementType, typename TSource,
339  : private sw::ClientIteratorBase
340 {
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.");
344 public:
345  SwIterator( const TSource& rSrc ) : sw::ClientIteratorBase(rSrc) {}
346  TElementType* First()
347  {
348  GoStart();
349  if(!m_pPosition)
350  return nullptr;
351  m_pCurrent = nullptr;
352  return Next();
353  }
354  TElementType* Next()
355  {
356  if(!IsChanged())
358  sw::WriterListener *pCurrent(m_pPosition);
359  while (m_pPosition)
360  {
362  {
363  if (auto const pLE = dynamic_cast<sw::ListenerEntry const*>(m_pPosition))
364  {
365  pCurrent = pLE->m_pToTell;
366  }
367  }
368  if (dynamic_cast<const TElementType *>(pCurrent) == nullptr)
369  {
371  pCurrent = m_pPosition;
372  }
373  else
374  break;
375  }
376  Sync();
377  return static_cast<TElementType*>(pCurrent);
378  }
380 };
381 
382 template< typename TSource > class SwIterator<SwClient, TSource> final : private sw::ClientIteratorBase
383 {
384  static_assert(std::is_base_of<SwModify,TSource>::value, "TSource needs to be derived from SwModify");
385 public:
386  SwIterator( const TSource& rSrc ) : sw::ClientIteratorBase(rSrc) {}
388  { return static_cast<SwClient*>(GoStart()); }
390  {
391  if(!IsChanged())
393  return static_cast<SwClient*>(Sync());
394  }
396 };
397 
398 SwClient::SwClient( SwModify* pToRegisterIn )
399  : m_pRegisteredIn( nullptr )
400 {
401  if(pToRegisterIn)
402  pToRegisterIn->Add(this);
403 }
404 
405 #endif
406 
407 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
BroadcasterMixin & operator=(const BroadcasterMixin &)
Definition: calbck.hxx:92
WriterListener * Sync()
Definition: calbck.hxx:333
void Add(SwClient *pDepend)
Definition: calbck.cxx:199
const SwModify & m_rRoot
Definition: calbck.hxx:294
sal_uInt16 GetWhich() const
Definition: calbck.hxx:68
refactoring out the same of the more sane SwClient functionality
Definition: calbck.hxx:99
ListenerEntry & operator=(ListenerEntry const &)=delete
bool IsInSwFntCache() const
Definition: calbck.hxx:221
WriterListener * GoStart()
Definition: calbck.hxx:312
virtual ~LegacyModifyHint() override
Definition: calbck.cxx:59
friend class SwModify
Definition: calbck.hxx:126
SvtBroadcaster m_aNotifier
Definition: calbck.hxx:88
TElementType * Next()
Definition: calbck.hxx:354
const SfxPoolItem * m_pNew
Definition: calbck.hxx:71
void SetInCache(bool bNew)
Definition: calbck.hxx:216
Dialog to specify the properties of date form field.
WriterListener * m_pLeft
Definition: calbck.hxx:104
SwClient()
Definition: calbck.hxx:143
SwClient * m_pToTell
Definition: calbck.hxx:248
SwClient * Remove(SwClient *pDepend)
Definition: calbck.cxx:252
bool HasWriterListeners() const
Definition: calbck.hxx:209
void MoveTo(::sw::ClientIteratorBase *pDestRing)
Removes this item from its current ring container and adds it to another ring container.
const SwModify * m_pNew
Definition: calbck.hxx:77
WriterListener * GetLeftOfPos()
Definition: calbck.hxx:310
virtual void Modify(const SfxPoolItem *pOldValue, const SfxPoolItem *pNewValue) override
Definition: calbck.cxx:36
const BorderLinePrimitive2D *pCandidateB assert(pCandidateA)
SwIterator(const TSource &rSrc)
Definition: calbck.hxx:345
Mode eMode
bool HasOnlyOneListener() const
Definition: calbck.hxx:224
void ClientNotifyAttrChg(SwModify &rModify, const SwAttrSet &aSet, SwAttrSet &aOld, SwAttrSet &aNew)
Definition: calbck.cxx:376
virtual void SwClientNotify(const SwModify &, const SfxHint &rHint)=0
ListenerEntry(ListenerEntry &&other) noexcept
Definition: calbck.hxx:256
LegacyModifyHint(const SfxPoolItem *pOld, const SfxPoolItem *pNew)
Definition: calbck.hxx:67
std::vector< ListenerEntry > m_vDepends
Definition: calbck.hxx:278
TElementType * First()
Definition: calbck.hxx:346
sw::WriterListener * m_pWriterListeners
Definition: calbck.hxx:181
ListenerEntry(SwClient *const pTellHim, SwModify *const pDepend)
Definition: calbck.hxx:251
static SW_DLLPUBLIC ClientIteratorBase * s_pClientIters
Definition: calbck.hxx:301
~ClientIteratorBase() override
Definition: calbck.hxx:321
::sw::ClientIteratorBase * GetNextInRing()
Definition: ring.hxx:84
void UnlockModify()
Definition: calbck.hxx:215
virtual bool GetInfo(SfxPoolItem &rInfo) const override
get Client information
Definition: calbck.cxx:34
WriterListener * m_pRight
double-linked list of other clients
Definition: calbck.hxx:105
void LockModify()
Definition: calbck.hxx:214
ClientIteratorBase(const SwModify &rModify)
Definition: calbck.hxx:303
SwIterator(const TSource &rSrc)
Definition: calbck.hxx:386
bool IsChanged() const
Definition: calbck.hxx:331
SwModify()
Definition: calbck.hxx:194
WriterListener * GetRightOfPos()
Definition: calbck.hxx:311
#define SW_DLLPUBLIC
Definition: swdllapi.h:28
virtual void Modify(const SfxPoolItem *pOld, const SfxPoolItem *pNew) override
Definition: calbck.hxx:188
WriterListener * m_pCurrent
Definition: calbck.hxx:296
ModifyChangedHint(const SwModify *pNew)
Definition: calbck.hxx:75
SvtBroadcaster & GetNotifier()
Definition: calbck.hxx:96
SwClient & m_rToTell
Definition: calbck.hxx:277
bool IsModifyLocked() const
Definition: calbck.hxx:219
bool IsLast() const
Definition: calbck.hxx:117
bool IsInCache() const
Definition: calbck.hxx:220
const SfxPoolItem * m_pOld
Definition: calbck.hxx:70
const SwModify * GetRegisteredIn() const
Definition: calbck.hxx:163
WriterListener & operator=(WriterListener const &)=delete
virtual void SwClientNotify(const SwModify &rModify, const SfxHint &rHint) override
Definition: calbck.cxx:41
SwModify * GetRegisteredIn()
Definition: calbck.hxx:164
virtual bool GetInfo(SfxPoolItem &) const
Definition: calbck.hxx:170
SwModify * m_pRegisteredIn
event source
Definition: calbck.hxx:130
virtual ~WriterListener() COVERITY_NOEXCEPT_FALSE
Definition: calbck.hxx:114
SwModify * GetRegisteredInNonConst() const
Definition: calbck.hxx:137
virtual ~ModifyChangedHint() override
Definition: calbck.cxx:60
sal_uInt16 Which() const
ListenerEntry & operator=(ListenerEntry &&other) noexcept
Definition: calbck.hxx:260
void SetInSwFntCache(bool bNew)
Definition: calbck.hxx:217
IteratorMode
Definition: calbck.hxx:119
WriterListener * m_pPosition
Definition: calbck.hxx:300