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 #include <optional>
34 
35 class SwModify;
36 class SwFormat;
37 class SfxPoolItem;
38 class SwAttrSet;
39 
40 /*
41  SwModify and SwClient cooperate in propagating attribute changes.
42  If an attribute changes, the change is notified to all dependent
43  formats and other interested objects, e.g. Nodes. The clients will detect
44  if the change affects them. It could be that the changed attribute is
45  overruled in the receiving object so that its change does not become
46  effective or that the receiver is not interested in the particular attribute
47  in general (though probably in other attributes of the SwModify object they
48  are registered in).
49  As SwModify objects are derived from SwClient, they can create a chain of SwClient
50  objects where changes can get propagated through.
51  Each SwClient can be registered at only one SwModify object, while each SwModify
52  object is connected to a list of SwClient objects. If an object derived from SwClient
53  wants to get notifications from more than one SwModify object, it must create additional
54  SwClient objects. The SwDepend class allows to handle their notifications in the same
55  notification callback as it forwards the Modify() calls it receives to a "master"
56  SwClient implementation.
57  The SwIterator class allows to iterate over the SwClient objects registered at an
58  SwModify. For historical reasons its ability to use TypeInfo to restrict this iteration
59  to objects of a particular type created a lot of code that misuses SwClient-SwModify
60  relationships that basically should be used only for Modify/Notify callbacks.
61  This is still subject to refactoring.
62  */
63 
64 namespace sw
65 {
66  class ClientIteratorBase;
67  class ListenerEntry;
68  void ClientNotifyAttrChg(SwModify& rModify, const SwAttrSet& aSet, SwAttrSet& aOld, SwAttrSet& aNew);
69  struct LegacyModifyHint final: SfxHint
70  {
71  LegacyModifyHint(const SfxPoolItem* pOld, const SfxPoolItem* pNew) : SfxHint(SfxHintId::SwLegacyModify), m_pOld(pOld), m_pNew(pNew) {};
72  sal_uInt16 GetWhich() const { return m_pOld ? m_pOld->Which() : m_pNew ? m_pNew->Which() : 0; };
73  virtual ~LegacyModifyHint() override;
76  };
77  struct ModifyChangedHint final: SfxHint
78  {
79  ModifyChangedHint(const SwModify* pNew) : m_pNew(pNew) {};
80  const SwModify* m_pNew;
81  };
82  // Observer pattern using svl implementation
83  // use this instead of SwClient/SwModify wherever possible
84  // In writer layout, this might not always be possible,
85  // but for listeners outside of it (e.g. unocore) this should be used.
86  // The only "magic" signal this class issues is a ModifyChangedHint
87  // proclaiming its death. It does NOT however provide a new SwModify for
88  // listeners to switch to like the old SwModify/SwClient did, as that leads
89  // to madness.
92  public:
93  BroadcasterMixin() = default;
94  BroadcasterMixin(BroadcasterMixin const &) = default;
96  {
97  return *this; // Listeners are never copied or moved.
98  }
99  SvtBroadcaster& GetNotifier() { return m_aNotifier; }
100  };
103  {
104  friend class ::SwModify;
105  friend class ::sw::ClientIteratorBase;
106  private:
109 
110  WriterListener(WriterListener const&) = delete;
111  WriterListener& operator=(WriterListener const&) = delete;
112 
113  protected:
115  : m_pLeft(nullptr), m_pRight(nullptr)
116  {}
117  virtual ~WriterListener() COVERITY_NOEXCEPT_FALSE {}
118  virtual void SwClientNotify( const SwModify&, const SfxHint& rHint) =0;
119  public:
120  bool IsLast() const { return !m_pLeft && !m_pRight; }
121  };
122  enum class IteratorMode { Exact, UnwrapMulti };
123 }
124 
125 // SwClient
127 {
128  // avoids making the details of the linked list and the callback method public
129  friend class SwModify;
131  friend class sw::ListenerEntry;
132  template<typename E, typename S, sw::IteratorMode> friend class SwIterator;
133 
135 
136 protected:
137  // single argument ctors shall be explicit.
138  inline explicit SwClient( SwModify* pToRegisterIn );
139 
140  // write access to pRegisteredIn shall be granted only to the object itself (protected access)
141  SwModify* GetRegisteredInNonConst() const { return m_pRegisteredIn; }
142 
143  // when overriding this, you MUST call SwClient::SwClientModify() in the override!
144  virtual void SwClientNotify(const SwModify&, const SfxHint& rHint) override;
145 
146 public:
147  SwClient() : m_pRegisteredIn(nullptr) {}
148  SwClient(SwClient&&) noexcept;
149  virtual ~SwClient() override;
150 
151 
152  // in case an SwModify object is destroyed that itself is registered in another SwModify,
153  // its SwClient objects can decide to get registered to the latter instead by calling this method
154  std::optional<sw::ModifyChangedHint> CheckRegistration( const SfxPoolItem* pOldValue );
155  // SwFormat wants to die different than the rest: It wants to reparent every client to its parent
156  // and then send a SwFormatChg hint.
157  void CheckRegistrationFormat(SwFormat& rOld);
158 
159  const SwModify* GetRegisteredIn() const { return m_pRegisteredIn; }
160  SwModify* GetRegisteredIn() { return m_pRegisteredIn; }
161  void EndListeningAll();
162  void StartListeningToSameModifyAs(const SwClient&);
163 
164 
165  // get information about attribute
166  virtual bool GetInfo( SfxPoolItem& ) const { return true; }
167 };
168 
169 
170 // SwModify
171 
172 // class has a doubly linked list for dependencies
174 {
177  template<typename E, typename S, sw::IteratorMode> friend class SwIterator;
178  sw::WriterListener* m_pWriterListeners; // the start of the linked list of clients
179  bool m_bModifyLocked; // don't broadcast changes now
180 
181  SwModify(SwModify const &) = delete;
182  SwModify &operator =(const SwModify&) = delete;
183 protected:
184  virtual void SwClientNotify(const SwModify&, const SfxHint& rHint) override;
185 public:
187  : SwClient(), m_pWriterListeners(nullptr), m_bModifyLocked(false)
188  {}
189 
190  // broadcasting mechanism
191  virtual void CallSwClientNotify( const SfxHint& rHint ) const;
192 
193  virtual ~SwModify() override;
194 
195  void Add(SwClient *pDepend);
196  SwClient* Remove(SwClient *pDepend);
197  bool HasWriterListeners() const { return m_pWriterListeners; }
198  bool HasOnlyOneListener() const { return m_pWriterListeners && m_pWriterListeners->IsLast(); }
199 
200  // get information about attribute
201  virtual bool GetInfo( SfxPoolItem& ) const override;
202 
203  void LockModify() { m_bModifyLocked = true; }
204  void UnlockModify() { m_bModifyLocked = false; }
205  bool IsModifyLocked() const { return m_bModifyLocked; }
206 };
207 
208 template<typename TElementType, typename TSource, sw::IteratorMode eMode> class SwIterator;
209 
210 namespace sw
211 {
212 
213  // this class is part of the migration: it still forwards the "old"
214  // SwModify events and announces them both to the old SwClients still
215  // registered and also to the new SvtListeners.
216  // Still: in the long run the SwClient/SwModify interface should not be
217  // used anymore, in which case a BroadcasterMixin should be enough instead
218  // then.
220  public:
221  virtual void CallSwClientNotify(const SfxHint& rHint) const override;
222  };
223  // this should be hidden but sadly SwIterator template needs it...
224  class ListenerEntry final : public SwClient
225  {
226  private:
227  template<typename E, typename S, sw::IteratorMode> friend class ::SwIterator;
229 
230  public:
231  ListenerEntry(SwClient *const pTellHim, SwModify *const pDepend)
232  : SwClient(pDepend), m_pToTell(pTellHim)
233  {}
234  ListenerEntry(ListenerEntry const &) = delete;
235  ListenerEntry& operator=(ListenerEntry const&) = delete;
236  ListenerEntry(ListenerEntry&& other) noexcept
237  : SwClient(std::move(other))
238  , m_pToTell(other.m_pToTell)
239  { }
241  {
242  m_pToTell = other.m_pToTell;
243  other.GetRegisteredIn()->Add(this);
244  other.EndListeningAll();
245  return *this;
246  }
247 
249  virtual bool GetInfo( SfxPoolItem& rInfo) const override;
250  private:
251  virtual void SwClientNotify(const SwModify& rModify, const SfxHint& rHint) override;
252  };
253 
255  {
257  std::vector<ListenerEntry> m_vDepends;
258  public:
259  WriterMultiListener(SwClient& rToTell);
260  WriterMultiListener& operator=(WriterMultiListener const&) = delete; // MSVC2015 workaround
261  WriterMultiListener(WriterMultiListener const&) = delete; // MSVC2015 workaround
263  void StartListening(SwModify* pDepend);
264  void EndListening(SwModify* pDepend);
265  bool IsListeningTo(const SwModify* const pDepend) const;
266  void EndListeningAll();
267  };
268  class ClientIteratorBase : public sw::Ring< ::sw::ClientIteratorBase >
269  {
271  friend void SwModify::Add(SwClient*);
272  protected:
274  // the current object in an iteration
276  // in case the current object is already removed, the next object in the list
277  // is marked down to become the current object in the next step
278  // this is necessary because iteration requires access to members of the current object
281 
282  ClientIteratorBase( const SwModify& rModify )
283  : m_rRoot(rModify)
284  {
285  MoveTo(s_pClientIters);
286  s_pClientIters = this;
287  m_pCurrent = m_pPosition = m_rRoot.m_pWriterListeners;
288  }
289  WriterListener* GetLeftOfPos() { return m_pPosition->m_pLeft; }
290  WriterListener* GetRightOfPos() { return m_pPosition->m_pRight; }
292  {
293  m_pPosition = m_rRoot.m_pWriterListeners;
294  if(m_pPosition)
295  while( m_pPosition->m_pLeft )
296  m_pPosition = m_pPosition->m_pLeft;
297  m_pCurrent = m_pPosition;
298  return m_pCurrent;
299  }
301  {
302  assert(s_pClientIters);
303  if(s_pClientIters == this)
304  s_pClientIters = unique() ? nullptr : GetNextInRing();
305  MoveTo(nullptr);
306  }
307  // return "true" if an object was removed from a client chain in iteration
308  // adding objects to a client chain in iteration is forbidden
309  // SwModify::Add() asserts this
310  bool IsChanged() const { return m_pPosition != m_pCurrent; }
311  // ensures the iterator to point at a current client
312  WriterListener* Sync() { m_pCurrent = m_pPosition; return m_pCurrent; }
313  };
314 }
315 
316 template<typename TElementType, typename TSource,
318  : private sw::ClientIteratorBase
319 {
320  //static_assert(!std::is_base_of<SwPageDesc,TSource>::value, "SwPageDesc as TSource is deprecated.");
321  static_assert(std::is_base_of<SwClient,TElementType>::value, "TElementType needs to be derived from SwClient.");
322  static_assert(std::is_base_of<SwModify,TSource>::value, "TSource needs to be derived from SwModify.");
323 public:
324  SwIterator( const TSource& rSrc ) : sw::ClientIteratorBase(rSrc) {}
325  TElementType* First()
326  {
327  GoStart();
328  if(!m_pPosition)
329  return nullptr;
330  m_pCurrent = nullptr;
331  return Next();
332  }
333  TElementType* Next()
334  {
335  if(!IsChanged())
337  sw::WriterListener *pCurrent(m_pPosition);
338  while (m_pPosition)
339  {
341  {
342  if (auto const pLE = dynamic_cast<sw::ListenerEntry const*>(m_pPosition))
343  {
344  pCurrent = pLE->m_pToTell;
345  }
346  }
347  if (dynamic_cast<const TElementType *>(pCurrent) == nullptr)
348  {
350  pCurrent = m_pPosition;
351  }
352  else
353  break;
354  }
355  Sync();
356  return static_cast<TElementType*>(pCurrent);
357  }
359 };
360 
361 template< typename TSource > class SwIterator<SwClient, TSource> final : private sw::ClientIteratorBase
362 {
363  static_assert(std::is_base_of<SwModify,TSource>::value, "TSource needs to be derived from SwModify");
364 public:
365  SwIterator( const TSource& rSrc ) : sw::ClientIteratorBase(rSrc) {}
367  { return static_cast<SwClient*>(GoStart()); }
369  {
370  if(!IsChanged())
372  return static_cast<SwClient*>(Sync());
373  }
375 };
376 
377 SwClient::SwClient( SwModify* pToRegisterIn )
378  : m_pRegisteredIn( nullptr )
379 {
380  if(pToRegisterIn)
381  pToRegisterIn->Add(this);
382 }
383 
384 #endif
385 
386 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
BroadcasterMixin & operator=(const BroadcasterMixin &)
Definition: calbck.hxx:95
WriterListener * Sync()
Definition: calbck.hxx:312
SfxHintId
void Add(SwClient *pDepend)
Definition: calbck.cxx:173
const SwModify & m_rRoot
Definition: calbck.hxx:273
sal_uInt16 GetWhich() const
Definition: calbck.hxx:72
refactoring out the same of the more sane SwClient functionality
Definition: calbck.hxx:102
ListenerEntry & operator=(ListenerEntry const &)=delete
WriterListener * GoStart()
Definition: calbck.hxx:291
virtual ~LegacyModifyHint() override
Definition: calbck.cxx:57
friend class SwModify
Definition: calbck.hxx:129
SvtBroadcaster m_aNotifier
Definition: calbck.hxx:91
TElementType * Next()
Definition: calbck.hxx:333
const SfxPoolItem * m_pNew
Definition: calbck.hxx:75
Dialog to specify the properties of date form field.
WriterListener * m_pLeft
Definition: calbck.hxx:107
virtual void SwClientNotify(const SwModify &, const SfxHint &rHint) override
Definition: calbck.cxx:121
SwClient()
Definition: calbck.hxx:147
SwClient * m_pToTell
Definition: calbck.hxx:228
SwClient * Remove(SwClient *pDepend)
Definition: calbck.cxx:226
bool HasWriterListeners() const
Definition: calbck.hxx:197
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:79
WriterListener * GetLeftOfPos()
Definition: calbck.hxx:289
SwIterator(const TSource &rSrc)
Definition: calbck.hxx:324
Mode eMode
bool HasOnlyOneListener() const
Definition: calbck.hxx:198
void ClientNotifyAttrChg(SwModify &rModify, const SwAttrSet &aSet, SwAttrSet &aOld, SwAttrSet &aNew)
Definition: calbck.cxx:332
Base class for various Writer styles.
Definition: format.hxx:46
virtual void SwClientNotify(const SwModify &, const SfxHint &rHint)=0
ListenerEntry(ListenerEntry &&other) noexcept
Definition: calbck.hxx:236
LegacyModifyHint(const SfxPoolItem *pOld, const SfxPoolItem *pNew)
Definition: calbck.hxx:71
std::vector< ListenerEntry > m_vDepends
Definition: calbck.hxx:257
TElementType * First()
Definition: calbck.hxx:325
sw::WriterListener * m_pWriterListeners
Definition: calbck.hxx:178
ListenerEntry(SwClient *const pTellHim, SwModify *const pDepend)
Definition: calbck.hxx:231
static SW_DLLPUBLIC ClientIteratorBase * s_pClientIters
Definition: calbck.hxx:280
~ClientIteratorBase() override
Definition: calbck.hxx:300
::sw::ClientIteratorBase * GetNextInRing()
Definition: ring.hxx:84
void UnlockModify()
Definition: calbck.hxx:204
virtual bool GetInfo(SfxPoolItem &rInfo) const override
get Client information
Definition: calbck.cxx:36
WriterListener * m_pRight
double-linked list of other clients
Definition: calbck.hxx:108
void LockModify()
Definition: calbck.hxx:203
ClientIteratorBase(const SwModify &rModify)
Definition: calbck.hxx:282
SwIterator(const TSource &rSrc)
Definition: calbck.hxx:365
bool IsChanged() const
Definition: calbck.hxx:310
SwModify()
Definition: calbck.hxx:186
WriterListener * GetRightOfPos()
Definition: calbck.hxx:290
#define SW_DLLPUBLIC
Definition: swdllapi.h:28
WriterListener * m_pCurrent
Definition: calbck.hxx:275
ModifyChangedHint(const SwModify *pNew)
Definition: calbck.hxx:79
SvtBroadcaster & GetNotifier()
Definition: calbck.hxx:99
SwClient & m_rToTell
Definition: calbck.hxx:256
bool IsModifyLocked() const
Definition: calbck.hxx:205
bool IsLast() const
Definition: calbck.hxx:120
const SfxPoolItem * m_pOld
Definition: calbck.hxx:74
const SwModify * GetRegisteredIn() const
Definition: calbck.hxx:159
WriterListener & operator=(WriterListener const &)=delete
virtual void SwClientNotify(const SwModify &rModify, const SfxHint &rHint) override
Definition: calbck.cxx:38
SwModify * GetRegisteredIn()
Definition: calbck.hxx:160
virtual bool GetInfo(SfxPoolItem &) const
Definition: calbck.hxx:166
SwModify * m_pRegisteredIn
event source
Definition: calbck.hxx:134
virtual ~WriterListener() COVERITY_NOEXCEPT_FALSE
Definition: calbck.hxx:117
SwModify * GetRegisteredInNonConst() const
Definition: calbck.hxx:141
sal_uInt16 Which() const
bool m_bModifyLocked
Definition: calbck.hxx:179
ListenerEntry & operator=(ListenerEntry &&other) noexcept
Definition: calbck.hxx:240
IteratorMode
Definition: calbck.hxx:122
WriterListener * m_pPosition
Definition: calbck.hxx:279