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 class SwCellFrame;
40 class SwTabFrame;
41 class SwRowFrame;
42 
43 /*
44  SwModify and SwClient cooperate in propagating attribute changes.
45  If an attribute changes, the change is notified to all dependent
46  formats and other interested objects, e.g. Nodes. The clients will detect
47  if the change affects them. It could be that the changed attribute is
48  overruled in the receiving object so that its change does not become
49  effective or that the receiver is not interested in the particular attribute
50  in general (though probably in other attributes of the SwModify object they
51  are registered in).
52  As SwModify objects are derived from SwClient, they can create a chain of SwClient
53  objects where changes can get propagated through.
54  Each SwClient can be registered at only one SwModify object, while each SwModify
55  object is connected to a list of SwClient objects. If an object derived from SwClient
56  wants to get notifications from more than one SwModify object, it must create additional
57  SwClient objects. The SwDepend class allows to handle their notifications in the same
58  notification callback as it forwards the Modify() calls it receives to a "master"
59  SwClient implementation.
60  The SwIterator class allows to iterate over the SwClient objects registered at an
61  SwModify. For historical reasons its ability to use TypeInfo to restrict this iteration
62  to objects of a particular type created a lot of code that misuses SwClient-SwModify
63  relationships that basically should be used only for Modify/Notify callbacks.
64  This is still subject to refactoring.
65  */
66 
67 namespace sw
68 {
69  class ClientIteratorBase;
70  class ListenerEntry;
71  void ClientNotifyAttrChg(SwModify& rModify, const SwAttrSet& aSet, SwAttrSet& aOld, SwAttrSet& aNew);
72  struct LegacyModifyHint final: SfxHint
73  {
74  LegacyModifyHint(const SfxPoolItem* pOld, const SfxPoolItem* pNew) : SfxHint(SfxHintId::SwLegacyModify), m_pOld(pOld), m_pNew(pNew) {};
75  sal_uInt16 GetWhich() const { return m_pOld ? m_pOld->Which() : m_pNew ? m_pNew->Which() : 0; };
76  virtual ~LegacyModifyHint() override;
79  };
80  struct ModifyChangedHint final: SfxHint
81  {
82  ModifyChangedHint(const SwModify* pNew) : m_pNew(pNew) {};
83  const SwModify* m_pNew;
84  };
85  // Observer pattern using svl implementation
86  // use this instead of SwClient/SwModify wherever possible
87  // In writer layout, this might not always be possible,
88  // but for listeners outside of it (e.g. unocore) this should be used.
89  // The only "magic" signal this class issues is a ModifyChangedHint
90  // proclaiming its death. It does NOT however provide a new SwModify for
91  // listeners to switch to like the old SwModify/SwClient did, as that leads
92  // to madness.
95  public:
96  BroadcasterMixin() = default;
97  BroadcasterMixin(BroadcasterMixin const &) = default;
99  {
100  return *this; // Listeners are never copied or moved.
101  }
102  SvtBroadcaster& GetNotifier() { return m_aNotifier; }
103  };
106  {
107  friend class ::SwModify;
108  friend class ::sw::ClientIteratorBase;
109  private:
112 
113  WriterListener(WriterListener const&) = delete;
114  WriterListener& operator=(WriterListener const&) = delete;
115 
116  protected:
118  : m_pLeft(nullptr), m_pRight(nullptr)
119  {}
120  virtual ~WriterListener() COVERITY_NOEXCEPT_FALSE {}
121  virtual void SwClientNotify( const SwModify&, const SfxHint& rHint) =0;
122  public:
123  bool IsLast() const { return !m_pLeft && !m_pRight; }
124  virtual const SwCellFrame* DynCastCellFrame() const { return nullptr; }
125  virtual const SwTabFrame* DynCastTabFrame() const { return nullptr; }
126  virtual const SwRowFrame* DynCastRowFrame() const { return nullptr; }
127  };
128  enum class IteratorMode { Exact, UnwrapMulti };
129 }
130 
131 // SwClient
133 {
134  // avoids making the details of the linked list and the callback method public
135  friend class SwModify;
137  friend class sw::ListenerEntry;
138  template<typename E, typename S, sw::IteratorMode> friend class SwIterator;
139 
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)
147  SwModify* GetRegisteredInNonConst() const { return m_pRegisteredIn; }
148 
149  // when overriding this, you MUST call SwClient::SwClientModify() in the override!
150  virtual void SwClientNotify(const SwModify&, const SfxHint& rHint) override;
151 
152 public:
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 
165  const SwModify* GetRegisteredIn() const { return m_pRegisteredIn; }
166  SwModify* GetRegisteredIn() { return m_pRegisteredIn; }
167  void EndListeningAll();
168  void StartListeningToSameModifyAs(const SwClient&);
169 
170 
171  // get information about attribute
172  virtual bool GetInfo( SfxPoolItem& ) const { return true; }
173 };
174 
175 
176 // SwModify
177 
178 // class has a doubly linked list for dependencies
180 {
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:
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 *pDepend);
202  SwClient* Remove(SwClient *pDepend);
203  bool HasWriterListeners() const { return m_pWriterListeners; }
204  bool HasOnlyOneListener() const { return m_pWriterListeners && m_pWriterListeners->IsLast(); }
205 
206  // get information about attribute
207  virtual bool GetInfo( SfxPoolItem& ) const override;
208 
209  void LockModify() { m_bModifyLocked = true; }
210  void UnlockModify() { m_bModifyLocked = false; }
211  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.
226  public:
227  virtual void CallSwClientNotify(const SfxHint& rHint) const override;
228  };
229  // this should be hidden but sadly SwIterator template needs it...
230  class ListenerEntry final : public SwClient
231  {
232  private:
233  template<typename E, typename S, sw::IteratorMode> friend class ::SwIterator;
235 
236  public:
237  ListenerEntry(SwClient *const pTellHim, SwModify *const pDepend)
238  : SwClient(pDepend), m_pToTell(pTellHim)
239  {}
240  ListenerEntry(ListenerEntry const &) = delete;
241  ListenerEntry& operator=(ListenerEntry const&) = delete;
242  ListenerEntry(ListenerEntry&& other) noexcept
243  : SwClient(std::move(other))
244  , m_pToTell(other.m_pToTell)
245  { }
247  {
248  m_pToTell = other.m_pToTell;
249  other.GetRegisteredIn()->Add(this);
250  other.EndListeningAll();
251  return *this;
252  }
253 
255  virtual bool GetInfo( SfxPoolItem& rInfo) const override;
256  private:
257  virtual void SwClientNotify(const SwModify& rModify, const SfxHint& rHint) override;
258  };
259 
261  {
263  std::vector<ListenerEntry> m_vDepends;
264  public:
265  WriterMultiListener(SwClient& rToTell);
266  WriterMultiListener& operator=(WriterMultiListener const&) = delete; // MSVC2015 workaround
267  WriterMultiListener(WriterMultiListener const&) = delete; // MSVC2015 workaround
269  void StartListening(SwModify* pDepend);
270  void EndListening(SwModify* pDepend);
271  bool IsListeningTo(const SwModify* const pDepend) const;
272  void EndListeningAll();
273  };
274  class ClientIteratorBase : public sw::Ring< ::sw::ClientIteratorBase >
275  {
277  friend void SwModify::Add(SwClient*);
278  protected:
280  // the current object in an iteration
282  // in case the current object is already removed, the next object in the list
283  // is marked down to become the current object in the next step
284  // this is necessary because iteration requires access to members of the current object
287 
288  ClientIteratorBase( const SwModify& rModify )
289  : m_rRoot(rModify)
290  {
291  MoveTo(s_pClientIters);
292  s_pClientIters = this;
293  m_pCurrent = m_pPosition = m_rRoot.m_pWriterListeners;
294  }
295  WriterListener* GetLeftOfPos() { return m_pPosition->m_pLeft; }
296  WriterListener* GetRightOfPos() { return m_pPosition->m_pRight; }
298  {
299  m_pPosition = m_rRoot.m_pWriterListeners;
300  if(m_pPosition)
301  while( m_pPosition->m_pLeft )
302  m_pPosition = m_pPosition->m_pLeft;
303  m_pCurrent = m_pPosition;
304  return m_pCurrent;
305  }
307  {
308  assert(s_pClientIters);
309  if(s_pClientIters == this)
310  s_pClientIters = unique() ? nullptr : GetNextInRing();
311  MoveTo(nullptr);
312  }
313  // return "true" if an object was removed from a client chain in iteration
314  // adding objects to a client chain in iteration is forbidden
315  // SwModify::Add() asserts this
316  bool IsChanged() const { return m_pPosition != m_pCurrent; }
317  // ensures the iterator to point at a current client
318  WriterListener* Sync() { m_pCurrent = m_pPosition; return m_pCurrent; }
319  };
320 }
321 
322 namespace sw::detail
323 {
324  // Dynamic casting can be expensive when used a lot, so for certain type combinations,
325  // we have faster routines.
326  template<typename CastDest>
327  inline const CastDest * internal_dyn_cast(const sw::WriterListener * pSource)
328  {
329  return dynamic_cast<const CastDest *>(pSource);
330  }
331  template<>
332  inline const SwCellFrame* internal_dyn_cast(const sw::WriterListener * pSource)
333  {
334  return pSource->DynCastCellFrame();
335  }
336  template<>
337  inline const SwTabFrame* internal_dyn_cast(const sw::WriterListener * pSource)
338  {
339  return pSource->DynCastTabFrame();
340  }
341  template<>
342  inline const SwRowFrame* internal_dyn_cast(const sw::WriterListener * pSource)
343  {
344  return pSource->DynCastRowFrame();
345  }
346 } // namespace sw::detail
347 
348 template<typename TElementType, typename TSource,
350  : private sw::ClientIteratorBase
351 {
352  //static_assert(!std::is_base_of<SwPageDesc,TSource>::value, "SwPageDesc as TSource is deprecated.");
353  static_assert(std::is_base_of<SwClient,TElementType>::value, "TElementType needs to be derived from SwClient.");
354  static_assert(std::is_base_of<SwModify,TSource>::value, "TSource needs to be derived from SwModify.");
355 public:
356  SwIterator( const TSource& rSrc ) : sw::ClientIteratorBase(rSrc) {}
357  TElementType* First()
358  {
359  GoStart();
360  if(!m_pPosition)
361  return nullptr;
362  m_pCurrent = nullptr;
363  return Next();
364  }
365  TElementType* Next()
366  {
367  if(!IsChanged())
369  sw::WriterListener *pCurrent(m_pPosition);
370  while (m_pPosition)
371  {
373  {
374  if (auto const pLE = dynamic_cast<sw::ListenerEntry const*>(m_pPosition))
375  {
376  pCurrent = pLE->m_pToTell;
377  }
378  }
379  if (sw::detail::internal_dyn_cast<TElementType>(pCurrent) == nullptr)
380  {
382  pCurrent = m_pPosition;
383  }
384  else
385  break;
386  }
387  Sync();
388  return static_cast<TElementType*>(pCurrent);
389  }
391 };
392 
393 template< typename TSource > class SwIterator<SwClient, TSource> final : private sw::ClientIteratorBase
394 {
395  static_assert(std::is_base_of<SwModify,TSource>::value, "TSource needs to be derived from SwModify");
396 public:
397  SwIterator( const TSource& rSrc ) : sw::ClientIteratorBase(rSrc) {}
399  { return static_cast<SwClient*>(GoStart()); }
401  {
402  if(!IsChanged())
404  return static_cast<SwClient*>(Sync());
405  }
407 };
408 
409 SwClient::SwClient( SwModify* pToRegisterIn )
410  : m_pRegisteredIn( nullptr )
411 {
412  if(pToRegisterIn)
413  pToRegisterIn->Add(this);
414 }
415 
416 #endif
417 
418 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
BroadcasterMixin & operator=(const BroadcasterMixin &)
Definition: calbck.hxx:98
WriterListener * Sync()
Definition: calbck.hxx:318
SfxHintId
void Add(SwClient *pDepend)
Definition: calbck.cxx:173
virtual const SwTabFrame * DynCastTabFrame() const
Definition: calbck.hxx:125
virtual const SwCellFrame * DynCastCellFrame() const
Definition: calbck.hxx:124
const SwModify & m_rRoot
Definition: calbck.hxx:279
sal_uInt16 GetWhich() const
Definition: calbck.hxx:75
refactoring out the same of the more sane SwClient functionality
Definition: calbck.hxx:105
ListenerEntry & operator=(ListenerEntry const &)=delete
WriterListener * GoStart()
Definition: calbck.hxx:297
virtual ~LegacyModifyHint() override
Definition: calbck.cxx:57
friend class SwModify
Definition: calbck.hxx:135
SwTabFrame is one table in the document layout, containing rows (which contain cells).
Definition: tabfrm.hxx:46
SvtBroadcaster m_aNotifier
Definition: calbck.hxx:94
TElementType * Next()
Definition: calbck.hxx:365
const SfxPoolItem * m_pNew
Definition: calbck.hxx:78
const CastDest * internal_dyn_cast(const sw::WriterListener *pSource)
Definition: calbck.hxx:327
Dialog to specify the properties of date form field.
WriterListener * m_pLeft
Definition: calbck.hxx:110
virtual void SwClientNotify(const SwModify &, const SfxHint &rHint) override
Definition: calbck.cxx:121
SwClient()
Definition: calbck.hxx:153
SwClient * m_pToTell
Definition: calbck.hxx:234
SwClient * Remove(SwClient *pDepend)
Definition: calbck.cxx:226
bool HasWriterListeners() const
Definition: calbck.hxx:203
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:82
WriterListener * GetLeftOfPos()
Definition: calbck.hxx:295
SwIterator(const TSource &rSrc)
Definition: calbck.hxx:356
Mode eMode
bool HasOnlyOneListener() const
Definition: calbck.hxx:204
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:242
LegacyModifyHint(const SfxPoolItem *pOld, const SfxPoolItem *pNew)
Definition: calbck.hxx:74
std::vector< ListenerEntry > m_vDepends
Definition: calbck.hxx:263
TElementType * First()
Definition: calbck.hxx:357
sw::WriterListener * m_pWriterListeners
Definition: calbck.hxx:184
ListenerEntry(SwClient *const pTellHim, SwModify *const pDepend)
Definition: calbck.hxx:237
static SW_DLLPUBLIC ClientIteratorBase * s_pClientIters
Definition: calbck.hxx:286
~ClientIteratorBase() override
Definition: calbck.hxx:306
::sw::ClientIteratorBase * GetNextInRing()
Definition: ring.hxx:84
void UnlockModify()
Definition: calbck.hxx:210
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:111
void LockModify()
Definition: calbck.hxx:209
ClientIteratorBase(const SwModify &rModify)
Definition: calbck.hxx:288
SwIterator(const TSource &rSrc)
Definition: calbck.hxx:397
bool IsChanged() const
Definition: calbck.hxx:316
SwModify()
Definition: calbck.hxx:192
WriterListener * GetRightOfPos()
Definition: calbck.hxx:296
#define SW_DLLPUBLIC
Definition: swdllapi.h:28
WriterListener * m_pCurrent
Definition: calbck.hxx:281
ModifyChangedHint(const SwModify *pNew)
Definition: calbck.hxx:82
SvtBroadcaster & GetNotifier()
Definition: calbck.hxx:102
SwClient & m_rToTell
Definition: calbck.hxx:262
bool IsModifyLocked() const
Definition: calbck.hxx:211
bool IsLast() const
Definition: calbck.hxx:123
const SfxPoolItem * m_pOld
Definition: calbck.hxx:77
const SwModify * GetRegisteredIn() const
Definition: calbck.hxx:165
WriterListener & operator=(WriterListener const &)=delete
virtual void SwClientNotify(const SwModify &rModify, const SfxHint &rHint) override
Definition: calbck.cxx:38
virtual const SwRowFrame * DynCastRowFrame() const
Definition: calbck.hxx:126
SwModify * GetRegisteredIn()
Definition: calbck.hxx:166
virtual bool GetInfo(SfxPoolItem &) const
Definition: calbck.hxx:172
SwModify * m_pRegisteredIn
event source
Definition: calbck.hxx:140
virtual ~WriterListener() COVERITY_NOEXCEPT_FALSE
Definition: calbck.hxx:120
SwModify * GetRegisteredInNonConst() const
Definition: calbck.hxx:147
sal_uInt16 Which() const
bool m_bModifyLocked
Definition: calbck.hxx:185
SwCellFrame is one table cell in the document layout.
Definition: cellfrm.hxx:30
ListenerEntry & operator=(ListenerEntry &&other) noexcept
Definition: calbck.hxx:246
IteratorMode
Definition: calbck.hxx:128
SwRowFrame is one table row in the document layout.
Definition: rowfrm.hxx:28
WriterListener * m_pPosition
Definition: calbck.hxx:285