tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / sw / inc / calbck.hxx
blob58a336bbc92b4986d5cc3ea011fef1fcc4c911f8
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #pragma once
22 #include <cassert>
24 #include <svl/hint.hxx>
25 #include <svl/broadcast.hxx>
26 #include <svl/poolitem.hxx>
27 #include "swdllapi.h"
28 #include "ring.hxx"
29 #include <type_traits>
30 #include <vector>
31 #include <optional>
33 class SwModify;
34 class SwFormat;
35 class SfxPoolItem;
36 class SwAttrSet;
37 class SwCellFrame;
38 class SwTabFrame;
39 class SwRowFrame;
40 class SwTable;
41 class SwFindNearestNode;
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.
67 namespace sw
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
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;
77 const SfxPoolItem* m_pOld;
78 const SfxPoolItem* m_pNew;
80 struct ModifyChangedHint final: SfxHint
82 ModifyChangedHint(const SwModify* pNew) : SfxHint(SfxHintId::SwModifyChanged), m_pNew(pNew) {};
83 const SwModify* m_pNew;
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.
93 class SW_DLLPUBLIC BroadcasterMixin {
94 SvtBroadcaster m_aNotifier;
95 public:
96 BroadcasterMixin() = default;
97 BroadcasterMixin(BroadcasterMixin const &) = default;
98 BroadcasterMixin& operator=(const BroadcasterMixin&)
100 return *this; // Listeners are never copied or moved.
102 SvtBroadcaster& GetNotifier() { return m_aNotifier; }
104 /// refactoring out the same of the more sane SwClient functionality
105 class SAL_DLLPUBLIC_RTTI WriterListener
107 friend class ::SwModify;
108 friend class ::sw::ClientIteratorBase;
109 private:
110 WriterListener* m_pLeft;
111 WriterListener* m_pRight; ///< double-linked list of other clients
113 WriterListener(WriterListener const&) = delete;
114 WriterListener& operator=(WriterListener const&) = delete;
116 protected:
117 WriterListener()
118 : m_pLeft(nullptr), m_pRight(nullptr)
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; }
129 enum class IteratorMode { Exact, UnwrapMulti };
132 // SwClient
133 class SW_DLLPUBLIC SwClient : public ::sw::WriterListener
135 // avoids making the details of the linked list and the callback method public
136 friend class SwModify;
137 friend class sw::ClientIteratorBase;
138 friend class sw::ListenerEntry;
139 template<typename E, typename S, sw::IteratorMode> friend class SwIterator;
141 SwModify *m_pRegisteredIn; ///< event source
143 protected:
144 // single argument ctors shall be explicit.
145 inline explicit SwClient( SwModify* pToRegisterIn );
147 // write access to pRegisteredIn shall be granted only to the object itself (protected access)
148 SwModify* GetRegisteredInNonConst() const { return m_pRegisteredIn; }
150 // when overriding this, you MUST call SwClient::SwClientNotify() in the override!
151 virtual void SwClientNotify(const SwModify&, const SfxHint& rHint) override;
153 public:
154 SwClient() : m_pRegisteredIn(nullptr) {}
155 SwClient(SwClient&&) noexcept;
156 virtual ~SwClient() override;
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);
166 const SwModify* GetRegisteredIn() const { return m_pRegisteredIn; }
167 SwModify* GetRegisteredIn() { return m_pRegisteredIn; }
168 void EndListeningAll();
169 void StartListeningToSameModifyAs(const SwClient&);
172 // get information about attribute
173 virtual bool GetInfo( SwFindNearestNode& ) const { return true; }
177 // SwModify
179 // class has a doubly linked list for dependencies
180 class SW_DLLPUBLIC SwModify: public SwClient
182 friend class sw::ClientIteratorBase;
183 friend void sw::ClientNotifyAttrChg(SwModify&, const SwAttrSet&, SwAttrSet&, SwAttrSet&);
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
188 SwModify(SwModify const &) = delete;
189 SwModify &operator =(const SwModify&) = delete;
190 protected:
191 virtual void SwClientNotify(const SwModify&, const SfxHint& rHint) override;
192 public:
193 SwModify()
194 : SwClient(), m_pWriterListeners(nullptr), m_bModifyLocked(false)
197 // broadcasting mechanism
198 virtual void CallSwClientNotify( const SfxHint& rHint ) const;
200 virtual ~SwModify() override;
202 void Add(SwClient& rDepend);
203 void Remove(SwClient& rDepend);
204 bool HasWriterListeners() const { return m_pWriterListeners; }
205 bool HasOnlyOneListener() const { return m_pWriterListeners && m_pWriterListeners->IsLast(); }
207 // get information about attribute
208 virtual bool GetInfo( SwFindNearestNode& ) const override;
210 void LockModify() { m_bModifyLocked = true; }
211 void UnlockModify() { m_bModifyLocked = false; }
212 bool IsModifyLocked() const { return m_bModifyLocked; }
215 template<typename TElementType, typename TSource, sw::IteratorMode eMode> class SwIterator;
217 namespace sw
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
229 public:
230 virtual void CallSwClientNotify(const SfxHint& rHint) const override;
232 // this should be hidden but sadly SwIterator template needs it...
233 class ListenerEntry final : public SwClient
235 private:
236 template<typename E, typename S, sw::IteratorMode> friend class ::SwIterator;
237 SwClient *m_pToTell;
239 public:
240 ListenerEntry(SwClient *const pTellHim, SwModify *const pDepend)
241 : SwClient(pDepend), m_pToTell(pTellHim)
243 ListenerEntry(ListenerEntry const &) = delete;
244 ListenerEntry& operator=(ListenerEntry const&) = delete;
245 ListenerEntry(ListenerEntry&& other) noexcept
246 : SwClient(std::move(other))
247 , m_pToTell(other.m_pToTell)
249 ListenerEntry& operator=(ListenerEntry&& other) noexcept
251 m_pToTell = other.m_pToTell;
252 other.GetRegisteredIn()->Add(*this);
253 other.EndListeningAll();
254 return *this;
257 /** get Client information */
258 virtual bool GetInfo( SwFindNearestNode& rInfo) const override;
259 private:
260 virtual void SwClientNotify(const SwModify& rModify, const SfxHint& rHint) override;
263 class SW_DLLPUBLIC WriterMultiListener final
265 SwClient& m_rToTell;
266 std::vector<ListenerEntry> m_vDepends;
267 public:
268 WriterMultiListener(SwClient& rToTell);
269 WriterMultiListener& operator=(WriterMultiListener const&) = delete; // MSVC2015 workaround
270 WriterMultiListener(WriterMultiListener const&) = delete; // MSVC2015 workaround
271 ~WriterMultiListener();
272 void StartListening(SwModify* pDepend);
273 void EndListening(SwModify* pDepend);
274 bool IsListeningTo(const SwModify* const pDepend) const;
275 void EndListeningAll();
277 class ClientIteratorBase : public sw::Ring< ::sw::ClientIteratorBase >
279 friend void SwModify::Remove(SwClient&);
280 friend void SwModify::Add(SwClient&);
281 protected:
282 const SwModify& m_rRoot;
283 // the current object in an iteration
284 WriterListener* m_pCurrent;
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
288 WriterListener* m_pPosition;
289 static SW_DLLPUBLIC ClientIteratorBase* s_pClientIters;
291 ClientIteratorBase( const SwModify& rModify )
292 : m_rRoot(rModify)
294 MoveTo(s_pClientIters);
295 #if defined __GNUC__ && !defined __clang__
296 #pragma GCC diagnostic push
297 #pragma GCC diagnostic ignored "-Wdangling-pointer"
298 #endif
299 s_pClientIters = this;
300 #if defined __GNUC__ && !defined __clang__
301 #pragma GCC diagnostic pop
302 #endif
303 m_pCurrent = m_pPosition = m_rRoot.m_pWriterListeners;
305 WriterListener* GetLeftOfPos() { return m_pPosition->m_pLeft; }
306 WriterListener* GetRightOfPos() { return m_pPosition->m_pRight; }
307 WriterListener* GoStart()
309 m_pPosition = m_rRoot.m_pWriterListeners;
310 if(m_pPosition)
311 while( m_pPosition->m_pLeft )
312 m_pPosition = m_pPosition->m_pLeft;
313 m_pCurrent = m_pPosition;
314 return m_pCurrent;
316 ~ClientIteratorBase() override
318 assert(s_pClientIters);
319 if(s_pClientIters == this)
320 s_pClientIters = unique() ? nullptr : GetNextInRing();
321 MoveTo(nullptr);
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
328 WriterListener* Sync() { m_pCurrent = m_pPosition; return m_pCurrent; }
332 namespace sw::detail
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)
339 return dynamic_cast<const CastDest *>(pSource);
341 template<>
342 inline const SwTable* internal_dyn_cast(const sw::WriterListener * pSource)
344 return pSource->DynCastTable();
346 template<>
347 inline const SwCellFrame* internal_dyn_cast(const sw::WriterListener * pSource)
349 return pSource->DynCastCellFrame();
351 template<>
352 inline const SwTabFrame* internal_dyn_cast(const sw::WriterListener * pSource)
354 return pSource->DynCastTabFrame();
356 template<>
357 inline const SwRowFrame* internal_dyn_cast(const sw::WriterListener * pSource)
359 return pSource->DynCastRowFrame();
361 } // namespace sw::detail
363 template<typename TElementType, typename TSource,
364 sw::IteratorMode eMode = sw::IteratorMode::Exact> class SwIterator final
365 : private sw::ClientIteratorBase
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.");
370 public:
371 SwIterator( const TSource& rSrc ) : sw::ClientIteratorBase(rSrc) {}
372 TElementType* First()
374 GoStart();
375 if(!m_pPosition)
376 return nullptr;
377 m_pCurrent = nullptr;
378 return Next();
380 TElementType* Next()
382 if(!IsChanged())
383 m_pPosition = GetRightOfPos();
384 sw::WriterListener *pCurrent(m_pPosition);
385 while (m_pPosition)
387 if (eMode == sw::IteratorMode::UnwrapMulti)
389 if (auto const pLE = dynamic_cast<sw::ListenerEntry const*>(m_pPosition))
391 pCurrent = pLE->m_pToTell;
394 if (sw::detail::internal_dyn_cast<TElementType>(pCurrent) == nullptr)
396 m_pPosition = GetRightOfPos();
397 pCurrent = m_pPosition;
399 else
400 break;
402 Sync();
403 return static_cast<TElementType*>(pCurrent);
405 using sw::ClientIteratorBase::IsChanged;
408 template< typename TSource > class SwIterator<SwClient, TSource> final : private sw::ClientIteratorBase
410 static_assert(std::is_base_of<SwModify,TSource>::value, "TSource needs to be derived from SwModify");
411 public:
412 SwIterator( const TSource& rSrc ) : sw::ClientIteratorBase(rSrc) {}
413 SwClient* First()
414 { return static_cast<SwClient*>(GoStart()); }
415 SwClient* Next()
417 if(!IsChanged())
418 m_pPosition = GetRightOfPos();
419 return static_cast<SwClient*>(Sync());
421 using sw::ClientIteratorBase::IsChanged;
424 SwClient::SwClient( SwModify* pToRegisterIn )
425 : m_pRegisteredIn( nullptr )
427 if(pToRegisterIn)
428 pToRegisterIn->Add(*this);
431 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */