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 "swdllapi.h"
28 #include "ring.hxx"
29 #include <type_traits>
30 #include <vector>
31 #include <memory>
32 
33 class SwModify;
34 class SfxPoolItem;
35 
36 /*
37  SwModify and SwClient cooperate in propagating attribute changes.
38  If an attribute changes, the change is notified to all dependent
39  formats and other interested objects, e.g. Nodes. The clients will detect
40  if the change affects them. It could be that the changed attribute is
41  overruled in the receiving object so that its change does not become
42  effective or that the receiver is not interested in the particular attribute
43  in general (though probably in other attributes of the SwModify object they
44  are registered in).
45  As SwModify objects are derived from SwClient, they can create a chain of SwClient
46  objects where changes can get propagated through.
47  Each SwClient can be registered at only one SwModify object, while each SwModify
48  object is connected to a list of SwClient objects. If an object derived from SwClient
49  wants to get notifications from more than one SwModify object, it must create additional
50  SwClient objects. The SwDepend class allows to handle their notifications in the same
51  notification callback as it forwards the Modify() calls it receives to a "master"
52  SwClient implementation.
53  The SwIterator class allows to iterate over the SwClient objects registered at an
54  SwModify. For historical reasons its ability to use TypeInfo to restrict this iteration
55  to objects of a particular type created a lot of code that misuses SwClient-SwModify
56  relationships that basically should be used only for Modify/Notify callbacks.
57  This is still subject to refactoring.
58  */
59 
60 namespace sw
61 {
62  class ClientIteratorBase;
64  {
65  LegacyModifyHint(const SfxPoolItem* pOld, const SfxPoolItem* pNew) : m_pOld(pOld), m_pNew(pNew) {};
66  virtual ~LegacyModifyHint() override;
69  };
71  {
72  ModifyChangedHint(const SwModify* pNew) : m_pNew(pNew) {};
73  virtual ~ModifyChangedHint() override;
74  const SwModify* m_pNew;
75  };
76  // Observer pattern using svl implementation
77  // use this instead of SwClient/SwModify wherever possible
78  // In writer layout, this might not always be possible,
79  // but for listeners outside of it (e.g. unocore) this should be used.
80  // The only "magic" signal this class issues is a ModifyChangedHint
81  // proclaiming its death. It does NOT however provide a new SwModify for
82  // listeners to switch to like the old SwModify/SwClient did, as that leads
83  // to madness.
86  public:
87  BroadcasterMixin() = default;
88  BroadcasterMixin(BroadcasterMixin const &) = default;
90  {
91  return *this; // Listeners are never copied or moved.
92  }
93  SvtBroadcaster& GetNotifier() { return m_aNotifier; }
94  };
97  {
98  friend class ::SwModify;
99  friend class ::sw::ClientIteratorBase;
100  private:
103 
104  WriterListener(WriterListener const&) = delete;
105  WriterListener& operator=(WriterListener const&) = delete;
106 
107  protected:
109  : m_pLeft(nullptr), m_pRight(nullptr)
110  {}
111  virtual ~WriterListener() COVERITY_NOEXCEPT_FALSE {}
112  virtual void SwClientNotify( const SwModify&, const SfxHint& rHint) =0;
113  public:
114  bool IsLast() const { return !m_pLeft && !m_pRight; }
115  };
116  enum class IteratorMode { Exact, UnwrapMulti };
117 }
118 
119 // SwClient
121 {
122  // avoids making the details of the linked list and the callback method public
123  friend class SwModify;
125  template<typename E, typename S, sw::IteratorMode> friend class SwIterator;
126 
128 
129 protected:
130  // single argument ctors shall be explicit.
131  inline explicit SwClient( SwModify* pToRegisterIn );
132 
133  // write access to pRegisteredIn shall be granted only to the object itself (protected access)
134  SwModify* GetRegisteredInNonConst() const { return m_pRegisteredIn; }
135 
136 public:
137  SwClient() : m_pRegisteredIn(nullptr) {}
138  SwClient(SwClient&&) noexcept;
139  virtual ~SwClient() override;
140  // callbacks received from SwModify (friend class - so these methods can be private)
141  // should be called only from SwModify the client is registered in
142  // mba: IMHO this method should be pure virtual
143  // DO NOT USE IN NEW CODE! use SwClientNotify instead.
144  virtual void Modify(const SfxPoolItem* pOldValue, const SfxPoolItem* pNewValue);
145  // when overriding this, you MUST call SwClient::SwClientModify() in the override!
146  virtual void SwClientNotify(const SwModify&, const SfxHint& rHint) override;
147 
148  // in case an SwModify object is destroyed that itself is registered in another SwModify,
149  // its SwClient objects can decide to get registered to the latter instead by calling this method
150  std::unique_ptr<sw::ModifyChangedHint> CheckRegistration( const SfxPoolItem* pOldValue );
151 
152  // controlled access to Modify method
153  // mba: this is still considered a hack and it should be fixed; the name makes grep-ing easier
154  virtual void ModifyNotification( const SfxPoolItem *pOldValue, const SfxPoolItem *pNewValue ) { Modify ( pOldValue, pNewValue ); }
155  void SwClientNotifyCall( const SwModify& rModify, const SfxHint& rHint ) { SwClientNotify( rModify, rHint ); }
156 
157  const SwModify* GetRegisteredIn() const { return m_pRegisteredIn; }
158  SwModify* GetRegisteredIn() { return m_pRegisteredIn; }
159  void EndListeningAll();
160  void StartListeningToSameModifyAs(const SwClient&);
161 
162 
163  // get information about attribute
164  virtual bool GetInfo( SfxPoolItem& ) const { return true; }
165 };
166 
167 
168 // SwModify
169 
170 // class has a doubly linked list for dependencies
172 {
174  template<typename E, typename S, sw::IteratorMode> friend class SwIterator;
175  sw::WriterListener* m_pWriterListeners; // the start of the linked list of clients
176  bool m_bModifyLocked : 1; // don't broadcast changes now
177  bool m_bLockClientList : 1; // may be set when this instance notifies its clients
178  bool m_bInCache : 1;
179  bool m_bInSwFntCache : 1;
180 
181  // mba: IMHO this method should be pure virtual
182  // DO NOT USE IN NEW CODE! use CallSwClientNotify instead.
183  virtual void Modify( const SfxPoolItem* pOld, const SfxPoolItem *pNew) override
184  { NotifyClients( pOld, pNew ); };
185 
186  SwModify(SwModify const &) = delete;
187  SwModify &operator =(const SwModify&) = delete;
188 public:
190  : SwClient(nullptr), m_pWriterListeners(nullptr), m_bModifyLocked(false), m_bLockClientList(false), m_bInCache(false), m_bInSwFntCache(false)
191  {}
192  explicit SwModify( SwModify* pToRegisterIn )
193  : SwClient(pToRegisterIn), m_pWriterListeners(nullptr), m_bModifyLocked(false), m_bLockClientList(false), m_bInCache(false), m_bInSwFntCache(false)
194  {}
195 
196  // broadcasting: send notifications to all clients
197  // DO NOT USE IN NEW CODE! use CallSwClientNotify instead.
198  void NotifyClients( const SfxPoolItem *pOldValue, const SfxPoolItem *pNewValue );
199  // the same, but without setting m_bModifyLocked or checking for any of the flags
200  // DO NOT USE IN NEW CODE! use CallSwClientNotify instead.
201  void ModifyBroadcast( const SfxPoolItem *pOldValue, const SfxPoolItem *pNewValue)
202  { CallSwClientNotify( sw::LegacyModifyHint{ pOldValue, pNewValue } ); };
203 
204  // a more universal broadcasting mechanism
205  virtual void CallSwClientNotify( const SfxHint& rHint ) const;
206 
207  virtual ~SwModify() override;
208 
209  void Add(SwClient *pDepend);
210  SwClient* Remove(SwClient *pDepend);
211  bool HasWriterListeners() const { return m_pWriterListeners; }
212 
213  // get information about attribute
214  virtual bool GetInfo( SfxPoolItem& ) const override;
215 
216  void LockModify() { m_bModifyLocked = true; }
217  void UnlockModify() { m_bModifyLocked = false; }
218  void SetInCache( bool bNew ) { m_bInCache = bNew; }
219  void SetInSwFntCache( bool bNew ) { m_bInSwFntCache = bNew; }
220  void SetInDocDTOR();
221  bool IsModifyLocked() const { return m_bModifyLocked; }
222  bool IsInCache() const { return m_bInCache; }
223  bool IsInSwFntCache() const { return m_bInSwFntCache; }
224 
225  void CheckCaching( const sal_uInt16 nWhich );
226  bool HasOnlyOneListener() { return m_pWriterListeners && m_pWriterListeners->IsLast(); }
227 };
228 
229 template<typename TElementType, typename TSource, sw::IteratorMode eMode> class SwIterator;
230 
231 namespace sw
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  return m_pCurrent = m_pPosition;
319  }
321  {
322  assert(s_pClientIters);
323  if(s_pClientIters == this)
324  s_pClientIters = unique() ? nullptr : GetNextInRing();
325  MoveTo(nullptr);
326  }
327  // return "true" if an object was removed from a client chain in iteration
328  // adding objects to a client chain in iteration is forbidden
329  // SwModify::Add() asserts this
330  bool IsChanged() const { return m_pPosition != m_pCurrent; }
331  // ensures the iterator to point at a current client
332  WriterListener* Sync() { return m_pCurrent = m_pPosition; }
333  };
334 }
335 
336 template<typename TElementType, typename TSource,
338  : private sw::ClientIteratorBase
339 {
340  //static_assert(!std::is_base_of<SwPageDesc,TSource>::value, "SwPageDesc as TSource is deprecated.");
341  static_assert(std::is_base_of<SwClient,TElementType>::value, "TElementType needs to be derived from SwClient.");
342  static_assert(std::is_base_of<SwModify,TSource>::value, "TSource needs to be derived from SwModify.");
343 public:
344  SwIterator( const TSource& rSrc ) : sw::ClientIteratorBase(rSrc) {}
345  TElementType* First()
346  {
347  GoStart();
348  if(!m_pPosition)
349  return nullptr;
350  m_pCurrent = nullptr;
351  return Next();
352  }
353  TElementType* Last()
354  {
355  if(!m_pPosition)
357  if(!m_pPosition)
358  return static_cast<TElementType*>(Sync());
359  while(GetRightOfPos())
361  sw::WriterListener * pCurrent(m_pPosition);
362  if (eMode == sw::IteratorMode::UnwrapMulti)
363  {
364  if (auto const pLE = dynamic_cast<sw::ListenerEntry const*>(pCurrent))
365  {
366  pCurrent = pLE->m_pToTell;
367  }
368  }
369  if (dynamic_cast<const TElementType *>(pCurrent) != nullptr)
370  {
371  Sync();
372  return static_cast<TElementType*>(pCurrent);
373  }
374  return Previous();
375  }
376  TElementType* Next()
377  {
378  if(!IsChanged())
380  sw::WriterListener *pCurrent(m_pPosition);
381  while (m_pPosition)
382  {
383  if (eMode == sw::IteratorMode::UnwrapMulti)
384  {
385  if (auto const pLE = dynamic_cast<sw::ListenerEntry const*>(m_pPosition))
386  {
387  pCurrent = pLE->m_pToTell;
388  }
389  }
390  if (dynamic_cast<const TElementType *>(pCurrent) == nullptr)
391  {
393  pCurrent = m_pPosition;
394  }
395  else
396  break;
397  }
398  Sync();
399  return static_cast<TElementType*>(pCurrent);
400  }
401  TElementType* Previous()
402  {
404  sw::WriterListener *pCurrent(m_pPosition);
405  while (m_pPosition)
406  {
407  if (eMode == sw::IteratorMode::UnwrapMulti)
408  {
409  if (auto const pLE = dynamic_cast<sw::ListenerEntry const*>(m_pPosition))
410  {
411  pCurrent = pLE->m_pToTell;
412  }
413  }
414  if (dynamic_cast<const TElementType *>(pCurrent) == nullptr)
415  {
417  pCurrent = m_pPosition;
418  }
419  else
420  break;
421  }
422  Sync();
423  return static_cast<TElementType*>(pCurrent);
424  }
426 };
427 
428 template< typename TSource > class SwIterator<SwClient, TSource> final : private sw::ClientIteratorBase
429 {
430  static_assert(std::is_base_of<SwModify,TSource>::value, "TSource needs to be derived from SwModify");
431 public:
432  SwIterator( const TSource& rSrc ) : sw::ClientIteratorBase(rSrc) {}
434  { return static_cast<SwClient*>(GoStart()); }
436  {
437  if(!IsChanged())
439  return static_cast<SwClient*>(Sync());
440  }
442 };
443 
444 SwClient::SwClient( SwModify* pToRegisterIn )
445  : m_pRegisteredIn( nullptr )
446 {
447  if(pToRegisterIn)
448  pToRegisterIn->Add(this);
449 }
450 
451 #endif
452 
453 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
BroadcasterMixin & operator=(const BroadcasterMixin &)
Definition: calbck.hxx:89
WriterListener * Sync()
Definition: calbck.hxx:332
void Add(SwClient *pDepend)
Definition: calbck.cxx:217
const SwModify & m_rRoot
Definition: calbck.hxx:294
refactoring out the same of the more sane SwClient functionality
Definition: calbck.hxx:96
ListenerEntry & operator=(ListenerEntry const &)=delete
bool IsInSwFntCache() const
Definition: calbck.hxx:223
WriterListener * GoStart()
Definition: calbck.hxx:312
SwModify(SwModify *pToRegisterIn)
Definition: calbck.hxx:192
friend class SwModify
Definition: calbck.hxx:123
SvtBroadcaster m_aNotifier
Definition: calbck.hxx:85
TElementType * Next()
Definition: calbck.hxx:376
const SfxPoolItem * m_pNew
Definition: calbck.hxx:68
void SetInCache(bool bNew)
Definition: calbck.hxx:218
Dialog to specify the properties of date form field.
Definition: accfrmobj.cxx:40
WriterListener * m_pLeft
Definition: calbck.hxx:101
SwClient()
Definition: calbck.hxx:137
SwClient * m_pToTell
Definition: calbck.hxx:248
SwClient * Remove(SwClient *pDepend)
Definition: calbck.cxx:259
bool HasWriterListeners() const
Definition: calbck.hxx:211
void SwClientNotifyCall(const SwModify &rModify, const SfxHint &rHint)
Definition: calbck.hxx:155
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:74
WriterListener * GetLeftOfPos()
Definition: calbck.hxx:310
virtual void Modify(const SfxPoolItem *pOldValue, const SfxPoolItem *pNewValue) override
Definition: calbck.cxx:33
SwIterator(const TSource &rSrc)
Definition: calbck.hxx:344
TElementType * Previous()
Definition: calbck.hxx:401
virtual void SwClientNotify(const SwModify &, const SfxHint &rHint)=0
ListenerEntry(ListenerEntry &&other) noexcept
Definition: calbck.hxx:256
void ModifyBroadcast(const SfxPoolItem *pOldValue, const SfxPoolItem *pNewValue)
Definition: calbck.hxx:201
LegacyModifyHint(const SfxPoolItem *pOld, const SfxPoolItem *pNew)
Definition: calbck.hxx:65
std::vector< ListenerEntry > m_vDepends
Definition: calbck.hxx:278
TElementType * First()
Definition: calbck.hxx:345
sw::WriterListener * m_pWriterListeners
Definition: calbck.hxx:175
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:320
::sw::ClientIteratorBase * GetNextInRing()
Definition: ring.hxx:84
bool HasOnlyOneListener()
Definition: calbck.hxx:226
void UnlockModify()
Definition: calbck.hxx:217
virtual bool GetInfo(SfxPoolItem &rInfo) const override
get Client information
Definition: calbck.cxx:31
WriterListener * m_pRight
double-linked list of other clients
Definition: calbck.hxx:102
void LockModify()
Definition: calbck.hxx:216
ClientIteratorBase(const SwModify &rModify)
Definition: calbck.hxx:303
SwIterator(const TSource &rSrc)
Definition: calbck.hxx:432
bool IsChanged() const
Definition: calbck.hxx:330
SwModify()
Definition: calbck.hxx:189
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:183
WriterListener * m_pCurrent
Definition: calbck.hxx:296
ModifyChangedHint(const SwModify *pNew)
Definition: calbck.hxx:72
SvtBroadcaster & GetNotifier()
Definition: calbck.hxx:93
SwClient & m_rToTell
Definition: calbck.hxx:277
bool IsModifyLocked() const
Definition: calbck.hxx:221
TElementType * Last()
Definition: calbck.hxx:353
bool IsLast() const
Definition: calbck.hxx:114
bool IsInCache() const
Definition: calbck.hxx:222
const SfxPoolItem * m_pOld
Definition: calbck.hxx:67
const SwModify * GetRegisteredIn() const
Definition: calbck.hxx:157
WriterListener & operator=(WriterListener const &)=delete
virtual void SwClientNotify(const SwModify &rModify, const SfxHint &rHint) override
Definition: calbck.cxx:38
SwModify * GetRegisteredIn()
Definition: calbck.hxx:158
virtual bool GetInfo(SfxPoolItem &) const
Definition: calbck.hxx:164
SwModify * m_pRegisteredIn
event source
Definition: calbck.hxx:127
virtual ~WriterListener() COVERITY_NOEXCEPT_FALSE
Definition: calbck.hxx:111
SwModify * GetRegisteredInNonConst() const
Definition: calbck.hxx:134
ListenerEntry & operator=(ListenerEntry &&other) noexcept
Definition: calbck.hxx:260
void SetInSwFntCache(bool bNew)
Definition: calbck.hxx:219
IteratorMode
Definition: calbck.hxx:116
WriterListener * m_pPosition
Definition: calbck.hxx:300