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;
41class SwTable;
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
67namespace sw
68{
69 class ClientIteratorBase;
70 class ListenerEntry;
71 void ClientNotifyAttrChg(SwModify& rModify, const SwAttrSet& aSet, SwAttrSet& aOld, SwAttrSet& aNew);
72 struct SAL_DLLPUBLIC_RTTI 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 };
81 {
82 ModifyChangedHint(const SwModify* pNew) : m_pNew(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;
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
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 virtual const SwTable* DynCastTable() const { return nullptr; }
128 };
130}
131
132// SwClient
134{
135 // avoids making the details of the linked list and the callback method public
136 friend class SwModify;
138 friend class sw::ListenerEntry;
139 template<typename E, typename S, sw::IteratorMode> friend class SwIterator;
140
142
143protected:
144 // single argument ctors shall be explicit.
145 inline explicit SwClient( SwModify* pToRegisterIn );
146
147 // write access to pRegisteredIn shall be granted only to the object itself (protected access)
148 SwModify* GetRegisteredInNonConst() const { return m_pRegisteredIn; }
149
150 // when overriding this, you MUST call SwClient::SwClientNotify() in the override!
151 virtual void SwClientNotify(const SwModify&, const SfxHint& rHint) override;
152
153public:
154 SwClient() : m_pRegisteredIn(nullptr) {}
155 SwClient(SwClient&&) noexcept;
156 virtual ~SwClient() override;
157
158
159 // in case an SwModify object is destroyed that itself is registered in another SwModify,
160 // its SwClient objects can decide to get registered to the latter instead by calling this method
161 std::optional<sw::ModifyChangedHint> CheckRegistration( const SfxPoolItem* pOldValue );
162 // SwFormat wants to die different than the rest: It wants to reparent every client to its parent
163 // and then send a SwFormatChg hint.
164 void CheckRegistrationFormat(SwFormat& rOld);
165
166 const SwModify* GetRegisteredIn() const { return m_pRegisteredIn; }
167 SwModify* GetRegisteredIn() { return m_pRegisteredIn; }
168 void EndListeningAll();
169 void StartListeningToSameModifyAs(const SwClient&);
170
171
172 // get information about attribute
173 virtual bool GetInfo( SfxPoolItem& ) const { return true; }
174};
175
176
177// SwModify
178
179// class has a doubly linked list for dependencies
181{
184 template<typename E, typename S, sw::IteratorMode> friend class SwIterator;
185 sw::WriterListener* m_pWriterListeners; // the start of the linked list of clients
186 bool m_bModifyLocked; // don't broadcast changes now
187
188 SwModify(SwModify const &) = delete;
189 SwModify &operator =(const SwModify&) = delete;
190protected:
191 virtual void SwClientNotify(const SwModify&, const SfxHint& rHint) override;
192public:
194 : SwClient(), m_pWriterListeners(nullptr), m_bModifyLocked(false)
195 {}
196
197 // broadcasting mechanism
198 virtual void CallSwClientNotify( const SfxHint& rHint ) const;
199
200 virtual ~SwModify() override;
201
202 void Add(SwClient *pDepend);
203 SwClient* Remove(SwClient *pDepend);
204 bool HasWriterListeners() const { return m_pWriterListeners; }
205 bool HasOnlyOneListener() const { return m_pWriterListeners && m_pWriterListeners->IsLast(); }
206
207 // get information about attribute
208 virtual bool GetInfo( SfxPoolItem& ) const override;
209
210 void LockModify() { m_bModifyLocked = true; }
211 void UnlockModify() { m_bModifyLocked = false; }
212 bool IsModifyLocked() const { return m_bModifyLocked; }
213};
214
215template<typename TElementType, typename TSource, sw::IteratorMode eMode> class SwIterator;
216
217namespace sw
218{
219
220 // this class is part of the migration: it still forwards the "old"
221 // SwModify events and announces them both to the old SwClients still
222 // registered and also to the new SvtListeners.
223 // Still: in the long run the SwClient/SwModify interface should not be
224 // used anymore, in which case a BroadcasterMixin should be enough instead
225 // then.
226 class SW_DLLPUBLIC SAL_LOPLUGIN_ANNOTATE("crosscast") BroadcastingModify :
227 public SwModify, public BroadcasterMixin
228 {
229 public:
230 virtual void CallSwClientNotify(const SfxHint& rHint) const override;
231 };
232 // this should be hidden but sadly SwIterator template needs it...
233 class ListenerEntry final : public SwClient
234 {
235 private:
236 template<typename E, typename S, sw::IteratorMode> friend class ::SwIterator;
238
239 public:
240 ListenerEntry(SwClient *const pTellHim, SwModify *const pDepend)
241 : SwClient(pDepend), m_pToTell(pTellHim)
242 {}
243 ListenerEntry(ListenerEntry const &) = delete;
245 ListenerEntry(ListenerEntry&& other) noexcept
246 : SwClient(std::move(other))
247 , m_pToTell(other.m_pToTell)
248 { }
250 {
251 m_pToTell = other.m_pToTell;
252 other.GetRegisteredIn()->Add(this);
253 other.EndListeningAll();
254 return *this;
255 }
256
258 virtual bool GetInfo( SfxPoolItem& rInfo) const override;
259 private:
260 virtual void SwClientNotify(const SwModify& rModify, const SfxHint& rHint) override;
261 };
262
264 {
266 std::vector<ListenerEntry> m_vDepends;
267 public:
269 WriterMultiListener& operator=(WriterMultiListener const&) = delete; // MSVC2015 workaround
270 WriterMultiListener(WriterMultiListener const&) = delete; // MSVC2015 workaround
272 void StartListening(SwModify* pDepend);
273 void EndListening(SwModify* pDepend);
274 bool IsListeningTo(const SwModify* const pDepend) const;
275 void EndListeningAll();
276 };
277 class ClientIteratorBase : public sw::Ring< ::sw::ClientIteratorBase >
278 {
280 friend void SwModify::Add(SwClient*);
281 protected:
283 // the current object in an iteration
285 // in case the current object is already removed, the next object in the list
286 // is marked down to become the current object in the next step
287 // this is necessary because iteration requires access to members of the current object
290
291 ClientIteratorBase( const SwModify& rModify )
292 : m_rRoot(rModify)
293 {
295#if defined __GNUC__ && __GNUC__ >= 12 && !defined __clang__
296#pragma GCC diagnostic push
297#pragma GCC diagnostic ignored "-Wdangling-pointer"
298#endif
299 s_pClientIters = this;
300#if defined __GNUC__ && __GNUC__ >= 12 && !defined __clang__
301#pragma GCC diagnostic pop
302#endif
304 }
308 {
310 if(m_pPosition)
311 while( m_pPosition->m_pLeft )
314 return m_pCurrent;
315 }
317 {
318 assert(s_pClientIters);
319 if(s_pClientIters == this)
320 s_pClientIters = unique() ? nullptr : GetNextInRing();
321 MoveTo(nullptr);
322 }
323 // return "true" if an object was removed from a client chain in iteration
324 // adding objects to a client chain in iteration is forbidden
325 // SwModify::Add() asserts this
326 bool IsChanged() const { return m_pPosition != m_pCurrent; }
327 // ensures the iterator to point at a current client
329 };
330}
331
332namespace sw::detail
333{
334 // Dynamic casting can be expensive when used a lot, so for certain type combinations,
335 // we have faster routines.
336 template<typename CastDest>
337 inline const CastDest * internal_dyn_cast(const sw::WriterListener * pSource)
338 {
339 return dynamic_cast<const CastDest *>(pSource);
340 }
341 template<>
342 inline const SwTable* internal_dyn_cast(const sw::WriterListener * pSource)
343 {
344 return pSource->DynCastTable();
345 }
346 template<>
347 inline const SwCellFrame* internal_dyn_cast(const sw::WriterListener * pSource)
348 {
349 return pSource->DynCastCellFrame();
350 }
351 template<>
352 inline const SwTabFrame* internal_dyn_cast(const sw::WriterListener * pSource)
353 {
354 return pSource->DynCastTabFrame();
355 }
356 template<>
357 inline const SwRowFrame* internal_dyn_cast(const sw::WriterListener * pSource)
358 {
359 return pSource->DynCastRowFrame();
360 }
361} // namespace sw::detail
362
363template<typename TElementType, typename TSource,
365 : private sw::ClientIteratorBase
366{
367 //static_assert(!std::is_base_of<SwPageDesc,TSource>::value, "SwPageDesc as TSource is deprecated.");
368 static_assert(std::is_base_of<SwClient,TElementType>::value, "TElementType needs to be derived from SwClient.");
369 static_assert(std::is_base_of<SwModify,TSource>::value, "TSource needs to be derived from SwModify.");
370public:
371 SwIterator( const TSource& rSrc ) : sw::ClientIteratorBase(rSrc) {}
372 TElementType* First()
373 {
374 GoStart();
375 if(!m_pPosition)
376 return nullptr;
377 m_pCurrent = nullptr;
378 return Next();
379 }
380 TElementType* Next()
381 {
382 if(!IsChanged())
385 while (m_pPosition)
386 {
388 {
389 if (auto const pLE = dynamic_cast<sw::ListenerEntry const*>(m_pPosition))
390 {
391 pCurrent = pLE->m_pToTell;
392 }
393 }
394 if (sw::detail::internal_dyn_cast<TElementType>(pCurrent) == nullptr)
395 {
397 pCurrent = m_pPosition;
398 }
399 else
400 break;
401 }
402 Sync();
403 return static_cast<TElementType*>(pCurrent);
404 }
406};
407
408template< typename TSource > class SwIterator<SwClient, TSource> final : private sw::ClientIteratorBase
409{
410 static_assert(std::is_base_of<SwModify,TSource>::value, "TSource needs to be derived from SwModify");
411public:
412 SwIterator( const TSource& rSrc ) : sw::ClientIteratorBase(rSrc) {}
414 { return static_cast<SwClient*>(GoStart()); }
416 {
417 if(!IsChanged())
419 return static_cast<SwClient*>(Sync());
420 }
422};
423
425 : m_pRegisteredIn( nullptr )
426{
427 if(pToRegisterIn)
428 pToRegisterIn->Add(this);
429}
430
431#endif
432
433/* 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:166
SwModify * m_pRegisteredIn
event source
Definition: calbck.hxx:141
virtual bool GetInfo(SfxPoolItem &) const
Definition: calbck.hxx:173
SwModify * GetRegisteredInNonConst() const
Definition: calbck.hxx:148
SwClient()
Definition: calbck.hxx:154
SwModify * GetRegisteredIn()
Definition: calbck.hxx:167
Base class for various Writer styles.
Definition: format.hxx:47
SwIterator(const TSource &rSrc)
Definition: calbck.hxx:412
TElementType * Next()
Definition: calbck.hxx:380
TElementType * First()
Definition: calbck.hxx:372
SwIterator(const TSource &rSrc)
Definition: calbck.hxx:371
void LockModify()
Definition: calbck.hxx:210
bool IsModifyLocked() const
Definition: calbck.hxx:212
void UnlockModify()
Definition: calbck.hxx:211
void Add(SwClient *pDepend)
Definition: calbck.cxx:172
SwClient * Remove(SwClient *pDepend)
Definition: calbck.cxx:225
bool m_bModifyLocked
Definition: calbck.hxx:186
bool HasWriterListeners() const
Definition: calbck.hxx:204
SwModify(SwModify const &)=delete
bool HasOnlyOneListener() const
Definition: calbck.hxx:205
SwModify()
Definition: calbck.hxx:193
sw::WriterListener * m_pWriterListeners
Definition: calbck.hxx:185
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:49
SwTable is one table in the document model, containing rows (which contain cells).
Definition: swtable.hxx:113
SvtBroadcaster m_aNotifier
Definition: calbck.hxx:94
BroadcasterMixin(BroadcasterMixin const &)=default
SvtBroadcaster & GetNotifier()
Definition: calbck.hxx:102
BroadcasterMixin()=default
BroadcasterMixin & operator=(const BroadcasterMixin &)
Definition: calbck.hxx:98
const SwModify & m_rRoot
Definition: calbck.hxx:282
bool IsChanged() const
Definition: calbck.hxx:326
WriterListener * GetLeftOfPos()
Definition: calbck.hxx:305
static SW_DLLPUBLIC ClientIteratorBase * s_pClientIters
Definition: calbck.hxx:289
WriterListener * GetRightOfPos()
Definition: calbck.hxx:306
WriterListener * m_pCurrent
Definition: calbck.hxx:284
~ClientIteratorBase() override
Definition: calbck.hxx:316
WriterListener * m_pPosition
Definition: calbck.hxx:288
WriterListener * Sync()
Definition: calbck.hxx:328
ClientIteratorBase(const SwModify &rModify)
Definition: calbck.hxx:291
WriterListener * GoStart()
Definition: calbck.hxx:307
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:245
ListenerEntry(ListenerEntry const &)=delete
ListenerEntry & operator=(ListenerEntry &&other) noexcept
Definition: calbck.hxx:249
SwClient * m_pToTell
Definition: calbck.hxx:237
virtual bool GetInfo(SfxPoolItem &rInfo) const override
get Client information
Definition: calbck.cxx:35
ListenerEntry(SwClient *const pTellHim, SwModify *const pDepend)
Definition: calbck.hxx:240
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:106
WriterListener(WriterListener const &)=delete
bool IsLast() const
Definition: calbck.hxx:123
WriterListener * m_pLeft
Definition: calbck.hxx:110
virtual const SwCellFrame * DynCastCellFrame() const
Definition: calbck.hxx:124
virtual void SwClientNotify(const SwModify &, const SfxHint &rHint)=0
virtual const SwRowFrame * DynCastRowFrame() const
Definition: calbck.hxx:126
WriterListener & operator=(WriterListener const &)=delete
virtual ~WriterListener() COVERITY_NOEXCEPT_FALSE
Definition: calbck.hxx:120
virtual const SwTable * DynCastTable() const
Definition: calbck.hxx:127
WriterListener * m_pRight
double-linked list of other clients
Definition: calbck.hxx:111
virtual const SwTabFrame * DynCastTabFrame() const
Definition: calbck.hxx:125
WriterMultiListener(WriterMultiListener const &)=delete
std::vector< ListenerEntry > m_vDepends
Definition: calbck.hxx:266
WriterMultiListener & operator=(WriterMultiListener const &)=delete
SwClient & m_rToTell
Definition: calbck.hxx:265
class SAL_DLLPUBLIC_RTTI SAL_LOPLUGIN_ANNOTATE("crosscast") SwFlowFrame
Base class that provides the general functionalities for frames that are allowed at page breaks (flow...
Definition: flowfrm.hxx:59
SfxHintId
Mode eMode
const CastDest * internal_dyn_cast(const sw::WriterListener *pSource)
Definition: calbck.hxx:337
Dialog to specify the properties of date form field.
IteratorMode
Definition: calbck.hxx:129
void ClientNotifyAttrChg(SwModify &rModify, const SwAttrSet &aSet, SwAttrSet &aOld, SwAttrSet &aNew)
Definition: calbck.cxx:331
sal_uInt16 GetWhich() const
Definition: calbck.hxx:75
const SfxPoolItem * m_pOld
Definition: calbck.hxx:77
LegacyModifyHint(const SfxPoolItem *pOld, const SfxPoolItem *pNew)
Definition: calbck.hxx:74
const SfxPoolItem * m_pNew
Definition: calbck.hxx:78
ModifyChangedHint(const SwModify *pNew)
Definition: calbck.hxx:82
const SwModify * m_pNew
Definition: calbck.hxx:82
#define SW_DLLPUBLIC
Definition: swdllapi.h:28