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(), m_pWriterListeners(nullptr), m_bModifyLocked(false), m_bLockClientList(false), m_bInCache(false), m_bInSwFntCache(false)
191  {}
192 
193  // broadcasting: send notifications to all clients
194  // DO NOT USE IN NEW CODE! use CallSwClientNotify instead.
195  void NotifyClients( const SfxPoolItem *pOldValue, const SfxPoolItem *pNewValue );
196  // the same, but without setting m_bModifyLocked or checking for any of the flags
197  // DO NOT USE IN NEW CODE! use CallSwClientNotify instead.
198  void ModifyBroadcast( const SfxPoolItem *pOldValue, const SfxPoolItem *pNewValue)
199  { CallSwClientNotify( sw::LegacyModifyHint{ pOldValue, pNewValue } ); };
200 
201  // a more universal broadcasting mechanism
202  virtual void CallSwClientNotify( const SfxHint& rHint ) const;
203 
204  virtual ~SwModify() override;
205 
206  void Add(SwClient *pDepend);
207  SwClient* Remove(SwClient *pDepend);
208  bool HasWriterListeners() const { return m_pWriterListeners; }
209 
210  // get information about attribute
211  virtual bool GetInfo( SfxPoolItem& ) const override;
212 
213  void LockModify() { m_bModifyLocked = true; }
214  void UnlockModify() { m_bModifyLocked = false; }
215  void SetInCache( bool bNew ) { m_bInCache = bNew; }
216  void SetInSwFntCache( bool bNew ) { m_bInSwFntCache = bNew; }
217  void SetInDocDTOR();
218  bool IsModifyLocked() const { return m_bModifyLocked; }
219  bool IsInCache() const { return m_bInCache; }
220  bool IsInSwFntCache() const { return m_bInSwFntCache; }
221 
222  void CheckCaching( const sal_uInt16 nWhich );
223  bool HasOnlyOneListener() const { return m_pWriterListeners && m_pWriterListeners->IsLast(); }
224 };
225 
226 template<typename TElementType, typename TSource, sw::IteratorMode eMode> class SwIterator;
227 
228 namespace sw
229 {
230  // this class is part of the migration: it still forwards the "old"
231  // SwModify events and announces them both to the old SwClients still
232  // registered and also to the new SvtListeners.
233  // Still: in the long run the SwClient/SwModify interface should not be
234  // used anymore, in which case a BroadcasterMixin should be enough instead
235  // then.
237  public:
238  virtual void CallSwClientNotify(const SfxHint& rHint) const override;
239  };
240  // this should be hidden but sadly SwIterator template needs it...
241  class ListenerEntry final : public SwClient
242  {
243  private:
244  template<typename E, typename S, sw::IteratorMode> friend class ::SwIterator;
246 
247  public:
248  ListenerEntry(SwClient *const pTellHim, SwModify *const pDepend)
249  : SwClient(pDepend), m_pToTell(pTellHim)
250  {}
251  ListenerEntry(ListenerEntry const &) = delete;
252  ListenerEntry& operator=(ListenerEntry const&) = delete;
253  ListenerEntry(ListenerEntry&& other) noexcept
254  : SwClient(std::move(other))
255  , m_pToTell(other.m_pToTell)
256  { }
258  {
259  m_pToTell = other.m_pToTell;
260  other.GetRegisteredIn()->Add(this);
261  other.EndListeningAll();
262  return *this;
263  }
264 
266  virtual bool GetInfo( SfxPoolItem& rInfo) const override;
267  private:
268  virtual void Modify(const SfxPoolItem* pOldValue, const SfxPoolItem *pNewValue) override;
269  virtual void SwClientNotify(const SwModify& rModify, const SfxHint& rHint) override;
270  };
271 
273  {
275  std::vector<ListenerEntry> m_vDepends;
276  public:
277  WriterMultiListener(SwClient& rToTell);
278  WriterMultiListener& operator=(WriterMultiListener const&) = delete; // MSVC2015 workaround
279  WriterMultiListener(WriterMultiListener const&) = delete; // MSVC2015 workaround
281  void StartListening(SwModify* pDepend);
282  void EndListening(SwModify* pDepend);
283  bool IsListeningTo(const SwModify* const pDepend) const;
284  void EndListeningAll();
285  };
286  class ClientIteratorBase : public sw::Ring< ::sw::ClientIteratorBase >
287  {
289  friend void SwModify::Add(SwClient*);
290  protected:
292  // the current object in an iteration
294  // in case the current object is already removed, the next object in the list
295  // is marked down to become the current object in the next step
296  // this is necessary because iteration requires access to members of the current object
299 
300  ClientIteratorBase( const SwModify& rModify )
301  : m_rRoot(rModify)
302  {
303  MoveTo(s_pClientIters);
304  s_pClientIters = this;
305  m_pCurrent = m_pPosition = m_rRoot.m_pWriterListeners;
306  }
307  WriterListener* GetLeftOfPos() { return m_pPosition->m_pLeft; }
308  WriterListener* GetRightOfPos() { return m_pPosition->m_pRight; }
310  {
311  m_pPosition = m_rRoot.m_pWriterListeners;
312  if(m_pPosition)
313  while( m_pPosition->m_pLeft )
314  m_pPosition = m_pPosition->m_pLeft;
315  return m_pCurrent = m_pPosition;
316  }
318  {
319  assert(s_pClientIters);
320  if(s_pClientIters == this)
321  s_pClientIters = unique() ? nullptr : GetNextInRing();
322  MoveTo(nullptr);
323  }
324  // return "true" if an object was removed from a client chain in iteration
325  // adding objects to a client chain in iteration is forbidden
326  // SwModify::Add() asserts this
327  bool IsChanged() const { return m_pPosition != m_pCurrent; }
328  // ensures the iterator to point at a current client
329  WriterListener* Sync() { return m_pCurrent = m_pPosition; }
330  };
331 }
332 
333 template<typename TElementType, typename TSource,
335  : private sw::ClientIteratorBase
336 {
337  //static_assert(!std::is_base_of<SwPageDesc,TSource>::value, "SwPageDesc as TSource is deprecated.");
338  static_assert(std::is_base_of<SwClient,TElementType>::value, "TElementType needs to be derived from SwClient.");
339  static_assert(std::is_base_of<SwModify,TSource>::value, "TSource needs to be derived from SwModify.");
340 public:
341  SwIterator( const TSource& rSrc ) : sw::ClientIteratorBase(rSrc) {}
342  TElementType* First()
343  {
344  GoStart();
345  if(!m_pPosition)
346  return nullptr;
347  m_pCurrent = nullptr;
348  return Next();
349  }
350  TElementType* Last()
351  {
352  if(!m_pPosition)
354  if(!m_pPosition)
355  return static_cast<TElementType*>(Sync());
356  while(GetRightOfPos())
358  sw::WriterListener * pCurrent(m_pPosition);
359  if (eMode == sw::IteratorMode::UnwrapMulti)
360  {
361  if (auto const pLE = dynamic_cast<sw::ListenerEntry const*>(pCurrent))
362  {
363  pCurrent = pLE->m_pToTell;
364  }
365  }
366  if (dynamic_cast<const TElementType *>(pCurrent) != nullptr)
367  {
368  Sync();
369  return static_cast<TElementType*>(pCurrent);
370  }
371  return Previous();
372  }
373  TElementType* Next()
374  {
375  if(!IsChanged())
377  sw::WriterListener *pCurrent(m_pPosition);
378  while (m_pPosition)
379  {
380  if (eMode == sw::IteratorMode::UnwrapMulti)
381  {
382  if (auto const pLE = dynamic_cast<sw::ListenerEntry const*>(m_pPosition))
383  {
384  pCurrent = pLE->m_pToTell;
385  }
386  }
387  if (dynamic_cast<const TElementType *>(pCurrent) == nullptr)
388  {
390  pCurrent = m_pPosition;
391  }
392  else
393  break;
394  }
395  Sync();
396  return static_cast<TElementType*>(pCurrent);
397  }
398  TElementType* Previous()
399  {
401  sw::WriterListener *pCurrent(m_pPosition);
402  while (m_pPosition)
403  {
404  if (eMode == sw::IteratorMode::UnwrapMulti)
405  {
406  if (auto const pLE = dynamic_cast<sw::ListenerEntry const*>(m_pPosition))
407  {
408  pCurrent = pLE->m_pToTell;
409  }
410  }
411  if (dynamic_cast<const TElementType *>(pCurrent) == nullptr)
412  {
414  pCurrent = m_pPosition;
415  }
416  else
417  break;
418  }
419  Sync();
420  return static_cast<TElementType*>(pCurrent);
421  }
423 };
424 
425 template< typename TSource > class SwIterator<SwClient, TSource> final : private sw::ClientIteratorBase
426 {
427  static_assert(std::is_base_of<SwModify,TSource>::value, "TSource needs to be derived from SwModify");
428 public:
429  SwIterator( const TSource& rSrc ) : sw::ClientIteratorBase(rSrc) {}
431  { return static_cast<SwClient*>(GoStart()); }
433  {
434  if(!IsChanged())
436  return static_cast<SwClient*>(Sync());
437  }
439 };
440 
441 SwClient::SwClient( SwModify* pToRegisterIn )
442  : m_pRegisteredIn( nullptr )
443 {
444  if(pToRegisterIn)
445  pToRegisterIn->Add(this);
446 }
447 
448 #endif
449 
450 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
BroadcasterMixin & operator=(const BroadcasterMixin &)
Definition: calbck.hxx:89
WriterListener * Sync()
Definition: calbck.hxx:329
void Add(SwClient *pDepend)
Definition: calbck.cxx:217
const SwModify & m_rRoot
Definition: calbck.hxx:291
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:220
WriterListener * GoStart()
Definition: calbck.hxx:309
friend class SwModify
Definition: calbck.hxx:123
SvtBroadcaster m_aNotifier
Definition: calbck.hxx:85
TElementType * Next()
Definition: calbck.hxx:373
const SfxPoolItem * m_pNew
Definition: calbck.hxx:68
void SetInCache(bool bNew)
Definition: calbck.hxx:215
Dialog to specify the properties of date form field.
WriterListener * m_pLeft
Definition: calbck.hxx:101
SwClient()
Definition: calbck.hxx:137
SwClient * m_pToTell
Definition: calbck.hxx:245
SwClient * Remove(SwClient *pDepend)
Definition: calbck.cxx:259
bool HasWriterListeners() const
Definition: calbck.hxx:208
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:307
virtual void Modify(const SfxPoolItem *pOldValue, const SfxPoolItem *pNewValue) override
Definition: calbck.cxx:33
SwIterator(const TSource &rSrc)
Definition: calbck.hxx:341
bool HasOnlyOneListener() const
Definition: calbck.hxx:223
TElementType * Previous()
Definition: calbck.hxx:398
virtual void SwClientNotify(const SwModify &, const SfxHint &rHint)=0
ListenerEntry(ListenerEntry &&other) noexcept
Definition: calbck.hxx:253
void ModifyBroadcast(const SfxPoolItem *pOldValue, const SfxPoolItem *pNewValue)
Definition: calbck.hxx:198
LegacyModifyHint(const SfxPoolItem *pOld, const SfxPoolItem *pNew)
Definition: calbck.hxx:65
std::vector< ListenerEntry > m_vDepends
Definition: calbck.hxx:275
TElementType * First()
Definition: calbck.hxx:342
sw::WriterListener * m_pWriterListeners
Definition: calbck.hxx:175
ListenerEntry(SwClient *const pTellHim, SwModify *const pDepend)
Definition: calbck.hxx:248
static SW_DLLPUBLIC ClientIteratorBase * s_pClientIters
Definition: calbck.hxx:298
~ClientIteratorBase() override
Definition: calbck.hxx:317
::sw::ClientIteratorBase * GetNextInRing()
Definition: ring.hxx:84
void UnlockModify()
Definition: calbck.hxx:214
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:213
ClientIteratorBase(const SwModify &rModify)
Definition: calbck.hxx:300
SwIterator(const TSource &rSrc)
Definition: calbck.hxx:429
bool IsChanged() const
Definition: calbck.hxx:327
SwModify()
Definition: calbck.hxx:189
WriterListener * GetRightOfPos()
Definition: calbck.hxx:308
#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:293
ModifyChangedHint(const SwModify *pNew)
Definition: calbck.hxx:72
SvtBroadcaster & GetNotifier()
Definition: calbck.hxx:93
SwClient & m_rToTell
Definition: calbck.hxx:274
bool IsModifyLocked() const
Definition: calbck.hxx:218
TElementType * Last()
Definition: calbck.hxx:350
bool IsLast() const
Definition: calbck.hxx:114
bool IsInCache() const
Definition: calbck.hxx:219
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:257
void SetInSwFntCache(bool bNew)
Definition: calbck.hxx:216
IteratorMode
Definition: calbck.hxx:116
WriterListener * m_pPosition
Definition: calbck.hxx:297