LibreOffice Module sw (master) 1
calbck.cxx
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
21#include <algorithm>
22#include <format.hxx>
23#include <hintids.hxx>
24#include <hints.hxx>
25#include <osl/diagnose.h>
26#include <sal/log.hxx>
27#include <tools/debug.hxx>
28
29#ifdef DBG_UTIL
30#include <sal/backtrace.hxx>
31#endif
32
33namespace sw
34{
36 { return m_pToTell == nullptr || m_pToTell->GetInfo( rInfo ); }
37 void ListenerEntry::SwClientNotify(const SwModify& rModify, const SfxHint& rHint)
38 {
39 if (rHint.GetId() == SfxHintId::SwLegacyModify)
40 {
41 auto pLegacyHint = static_cast<const sw::LegacyModifyHint*>(&rHint);
42 if (pLegacyHint->m_pNew && pLegacyHint->m_pNew->Which() == RES_OBJECTDYING)
43 {
44 auto pModifyChanged = CheckRegistration(pLegacyHint->m_pOld);
45 if (pModifyChanged)
46 m_pToTell->SwClientNotify(rModify, *pModifyChanged);
47 }
48 else if (m_pToTell)
49 m_pToTell->SwClientNotify(rModify, rHint);
50 }
51 else if (m_pToTell)
52 m_pToTell->SwClientNotify(rModify, rHint);
53 }
54}
55
57
59 : m_pRegisteredIn(nullptr)
60{
61 if(o.m_pRegisteredIn)
62 {
63 o.m_pRegisteredIn->Add(this);
64 o.EndListeningAll();
65 }
66}
67
69{
70 if(GetRegisteredIn())
72 OSL_ENSURE( !m_pRegisteredIn || m_pRegisteredIn->HasWriterListeners(), "SwModify still known, but Client already disconnected!" );
73 if( m_pRegisteredIn && m_pRegisteredIn->HasWriterListeners() )
74 m_pRegisteredIn->Remove( this );
75}
76
77std::optional<sw::ModifyChangedHint> SwClient::CheckRegistration( const SfxPoolItem* pOld )
78{
80 // this method only handles notification about dying SwModify objects
81 if( !pOld || pOld->Which() != RES_OBJECTDYING )
82 return {};
83
84 assert(dynamic_cast<const SwPtrMsgPoolItem*>(pOld));
85 const SwPtrMsgPoolItem* pDead = static_cast<const SwPtrMsgPoolItem*>(pOld);
86 if(pDead->pObject != m_pRegisteredIn)
87 {
88 // we should only care received death notes from objects we are following
89 return {};
90 }
91 // I've got a notification from the object I know
92 SwModify* pAbove = m_pRegisteredIn->GetRegisteredIn();
93 if(pAbove)
94 {
95 // if the dying object itself was listening at an SwModify, I take over
96 // adding myself to pAbove will automatically remove me from my current pRegisteredIn
97 pAbove->Add(this);
98 }
99 else
100 {
101 // destroy connection
102 EndListeningAll();
103 }
104 return sw::ModifyChangedHint(pAbove);
105}
106
108{
109 assert(GetRegisteredIn() == &rOld);
110 auto pNew = rOld.DerivedFrom();
111 SAL_INFO("sw.core", "reparenting " << typeid(*this).name() << " at " << this << " from " << typeid(rOld).name() << " at " << &rOld << " to " << typeid(*pNew).name() << " at " << pNew);
112 assert(pNew);
113 pNew->Add(this);
114 const SwFormatChg aOldFormat(&rOld);
115 const SwFormatChg aNewFormat(pNew);
116 const sw::LegacyModifyHint aHint(&aOldFormat, &aNewFormat);
117 SwClientNotify(rOld, aHint);
118}
119
120void SwClient::SwClientNotify(const SwModify&, const SfxHint& rHint)
121{
122 if (rHint.GetId() != SfxHintId::SwLegacyModify)
123 return;
124 auto pLegacyHint = static_cast<const sw::LegacyModifyHint*>(&rHint);
125 CheckRegistration(pLegacyHint->m_pOld);
126};
127
129{
130 if(other.m_pRegisteredIn)
131 other.m_pRegisteredIn->Add(this);
132 else
133 EndListeningAll();
134}
135
137{
138 if(m_pRegisteredIn)
139 m_pRegisteredIn->Remove(this);
140}
141
143{
145 OSL_ENSURE( !IsModifyLocked(), "Modify destroyed but locked." );
146
147 // notify all clients that they shall remove themselves
148 SwPtrMsgPoolItem aDyObject( RES_OBJECTDYING, this );
149 SwModify::SwClientNotify(*this, sw::LegacyModifyHint(&aDyObject, &aDyObject));
150
151 const bool hasListenersOnDeath = m_pWriterListeners;
152 (void)hasListenersOnDeath;
153 while(m_pWriterListeners)
154 {
155 SAL_WARN("sw.core", "lost a client of type: " << typeid(*m_pWriterListeners).name() << " at " << m_pWriterListeners << " still registered on type: " << typeid(*this).name() << " at " << this << ".");
156 static_cast<SwClient*>(m_pWriterListeners)->CheckRegistration(&aDyObject);
157 }
158 assert(!hasListenersOnDeath);
159}
160
161bool SwModify::GetInfo( SfxPoolItem& rInfo ) const
162{
163 if(!m_pWriterListeners)
164 return true;
166 for(SwClient* pClient = aIter.First(); pClient; pClient = aIter.Next())
167 if(!pClient->GetInfo( rInfo ))
168 return false;
169 return true;
170}
171
172void SwModify::Add( SwClient* pDepend )
173{
175#ifdef DBG_UTIL
176 // You should not EVER use SwModify directly in new code:
177 // - Preexisting SwModifys should only ever be used via sw::BroadcastingModify.
178 // This includes sw::BroadcastMixin, which is the long-term target (without
179 // SwModify).
180 // - New classes should use sw::BroadcastMixin alone.
181 if(!dynamic_cast<sw::BroadcastingModify*>(this))
182 {
183 auto pBT = sal::backtrace_get(20);
184 SAL_WARN("sw.core", "Modify that is not broadcasting used!\n" << sal::backtrace_to_string(pBT.get()));
185 }
186#endif
187
188 if(pDepend->m_pRegisteredIn == this)
189 return;
190
191#if OSL_DEBUG_LEVEL > 0
193 {
194 for(auto& rIter : sw::ClientIteratorBase::s_pClientIters->GetRingContainer())
195 {
196 SAL_WARN_IF(&rIter.m_rRoot == m_pWriterListeners, "sw.core", "a " << typeid(*pDepend).name() << " client added as listener to a " << typeid(*this).name() << " during client iteration.");
197 }
198 }
199#endif
200 // deregister new client in case it is already registered elsewhere
201 if( pDepend->m_pRegisteredIn != nullptr )
202 pDepend->m_pRegisteredIn->Remove( pDepend );
203
204 if( !m_pWriterListeners )
205 {
206 // first client added
207 m_pWriterListeners = pDepend;
208 m_pWriterListeners->m_pLeft = nullptr;
209 m_pWriterListeners->m_pRight = nullptr;
210 }
211 else
212 {
213 // append client
214 pDepend->m_pRight = m_pWriterListeners->m_pRight;
215 m_pWriterListeners->m_pRight = pDepend;
216 pDepend->m_pLeft = m_pWriterListeners;
217 if( pDepend->m_pRight )
218 pDepend->m_pRight->m_pLeft = pDepend;
219 }
220
221 // connect client to me
222 pDepend->m_pRegisteredIn = this;
223}
224
226{
228 assert(pDepend->m_pRegisteredIn == this);
229
230 // SwClient is my listener
231 // remove it from my list
232 ::sw::WriterListener* pR = pDepend->m_pRight;
233 ::sw::WriterListener* pL = pDepend->m_pLeft;
234 if( m_pWriterListeners == pDepend )
235 m_pWriterListeners = pL ? pL : pR;
236
237 if( pL )
238 pL->m_pRight = pR;
239 if( pR )
240 pR->m_pLeft = pL;
241
242 // update ClientIterators
244 {
245 for(auto& rIter : sw::ClientIteratorBase::s_pClientIters->GetRingContainer())
246 {
247 if (&rIter.m_rRoot == this &&
248 (rIter.m_pCurrent == pDepend || rIter.m_pPosition == pDepend))
249 {
250 // if object being removed is the current or next object in an
251 // iterator, advance this iterator
252 rIter.m_pPosition = pR;
253 }
254 }
255 }
256 pDepend->m_pLeft = nullptr;
257 pDepend->m_pRight = nullptr;
258 pDepend->m_pRegisteredIn = nullptr;
259 return pDepend;
260}
261
263 : m_rToTell(rToTell)
264{}
265
267{}
268
270{
271 EndListening(nullptr);
272 m_vDepends.emplace_back(&m_rToTell, pDepend);
273}
274
275
276bool sw::WriterMultiListener::IsListeningTo(const SwModify* const pBroadcaster) const
277{
278 return std::any_of(m_vDepends.begin(), m_vDepends.end(),
279 [&pBroadcaster](const ListenerEntry& aListener)
280 {
281 return aListener.GetRegisteredIn() == pBroadcaster;
282 });
283}
284
286{
287 m_vDepends.erase(
288 std::remove_if( m_vDepends.begin(), m_vDepends.end(),
289 [&pBroadcaster](const ListenerEntry& aListener)
290 {
291 return aListener.GetRegisteredIn() == nullptr || aListener.GetRegisteredIn() == pBroadcaster;
292 }),
293 m_vDepends.end());
294}
295
297{
298 m_vDepends.clear();
299}
300
302
303void SwModify::SwClientNotify(const SwModify&, const SfxHint& rHint)
304{
305 if (rHint.GetId() != SfxHintId::SwLegacyModify)
306 return;
307
309 if(IsModifyLocked())
310 return;
311
312 LockModify();
313 CallSwClientNotify(rHint);
314 UnlockModify();
315}
316
317void SwModify::CallSwClientNotify( const SfxHint& rHint ) const
318{
321 for(SwClient* pClient = aIter.First(); pClient; pClient = aIter.Next())
322 pClient->SwClientNotify( *this, rHint );
323}
324
325void sw::BroadcastingModify::CallSwClientNotify(const SfxHint& rHint) const
326{
328 const_cast<BroadcastingModify*>(this)->GetNotifier().Broadcast(rHint);
329}
330
331void sw::ClientNotifyAttrChg(SwModify& rModify, const SwAttrSet& aSet, SwAttrSet& aOld, SwAttrSet& aNew)
332{
333 const SwAttrSetChg aChgOld(aSet, aOld);
334 const SwAttrSetChg aChgNew(aSet, aNew);
335 const sw::LegacyModifyHint aHint(&aChgOld, &aChgNew);
336 rModify.SwClientNotify(rModify, aHint);
337}
338/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
SfxHintId GetId() const
sal_uInt16 Which() const
const SwModify * GetRegisteredIn() const
Definition: calbck.hxx:166
void EndListeningAll()
Definition: calbck.cxx:136
SwModify * m_pRegisteredIn
event source
Definition: calbck.hxx:141
virtual void SwClientNotify(const SwModify &, const SfxHint &rHint) override
Definition: calbck.cxx:120
virtual bool GetInfo(SfxPoolItem &) const
Definition: calbck.hxx:173
void StartListeningToSameModifyAs(const SwClient &)
Definition: calbck.cxx:128
std::optional< sw::ModifyChangedHint > CheckRegistration(const SfxPoolItem *pOldValue)
Definition: calbck.cxx:77
SwClient()
Definition: calbck.hxx:154
void CheckRegistrationFormat(SwFormat &rOld)
Definition: calbck.cxx:107
virtual ~SwClient() override
Definition: calbck.cxx:68
Base class for various Writer styles.
Definition: format.hxx:47
SwFormat * DerivedFrom() const
Definition: format.hxx:128
TElementType * Next()
Definition: calbck.hxx:380
TElementType * First()
Definition: calbck.hxx:372
void Add(SwClient *pDepend)
Definition: calbck.cxx:172
virtual void SwClientNotify(const SwModify &, const SfxHint &rHint) override
Definition: calbck.cxx:303
virtual void CallSwClientNotify(const SfxHint &rHint) const
Definition: calbck.cxx:317
virtual ~SwModify() override
Definition: calbck.cxx:142
SwClient * Remove(SwClient *pDepend)
Definition: calbck.cxx:225
virtual bool GetInfo(SfxPoolItem &) const override
Definition: calbck.cxx:161
void * pObject
Definition: hints.hxx:61
static SW_DLLPUBLIC ClientIteratorBase * s_pClientIters
Definition: calbck.hxx:289
virtual void SwClientNotify(const SwModify &rModify, const SfxHint &rHint) override
Definition: calbck.cxx:37
SwClient * m_pToTell
Definition: calbck.hxx:237
virtual bool GetInfo(SfxPoolItem &rInfo) const override
get Client information
Definition: calbck.cxx:35
refactoring out the same of the more sane SwClient functionality
Definition: calbck.hxx:106
WriterListener * m_pLeft
Definition: calbck.hxx:110
WriterListener * m_pRight
double-linked list of other clients
Definition: calbck.hxx:111
void EndListening(SwModify *pDepend)
Definition: calbck.cxx:285
bool IsListeningTo(const SwModify *const pDepend) const
Definition: calbck.cxx:276
void StartListening(SwModify *pDepend)
Definition: calbck.cxx:269
WriterMultiListener(SwClient &rToTell)
Definition: calbck.cxx:262
#define DBG_TESTSOLARMUTEX()
constexpr TypedWhichId< SwPtrMsgPoolItem > RES_OBJECTDYING(RES_FORMAT_MSG_BEGIN)
#define SAL_WARN_IF(condition, area, stream)
#define SAL_WARN(area, stream)
#define SAL_INFO(area, stream)
Dialog to specify the properties of date form field.
void ClientNotifyAttrChg(SwModify &rModify, const SwAttrSet &aSet, SwAttrSet &aOld, SwAttrSet &aNew)
Definition: calbck.cxx:331
virtual ~LegacyModifyHint() override
Definition: calbck.cxx:56