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