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