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 (auto pLegacyHint = dynamic_cast<const sw::LegacyModifyHint*>(&rHint))
41  {
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 
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::unique_ptr<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 nullptr;
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 nullptr;
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 std::unique_ptr<sw::ModifyChangedHint>(new 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(auto pLegacyHint = dynamic_cast<const sw::LegacyModifyHint*>(&rHint))
124  CheckRegistration(pLegacyHint->m_pOld);
125 };
126 
128 {
129  if(other.m_pRegisteredIn)
130  other.m_pRegisteredIn->Add(this);
131  else
132  EndListeningAll();
133 }
134 
136 {
137  if(m_pRegisteredIn)
138  m_pRegisteredIn->Remove(this);
139 }
140 
142 {
144  OSL_ENSURE( !IsModifyLocked(), "Modify destroyed but locked." );
145 
146  // notify all clients that they shall remove themselves
147  SwPtrMsgPoolItem aDyObject( RES_OBJECTDYING, this );
148  SwModify::SwClientNotify(*this, sw::LegacyModifyHint(&aDyObject, &aDyObject));
149 
150  const bool hasListenersOnDeath = m_pWriterListeners;
151  (void)hasListenersOnDeath;
152  while(m_pWriterListeners)
153  {
154  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 << ".");
155  static_cast<SwClient*>(m_pWriterListeners)->CheckRegistration(&aDyObject);
156  }
157  assert(!hasListenersOnDeath);
158 }
159 
160 bool SwModify::GetInfo( SfxPoolItem& rInfo ) const
161 {
162  if(!m_pWriterListeners)
163  return true;
164  SwIterator<SwClient,SwModify> aIter(*this);
165  for(SwClient* pClient = aIter.First(); pClient; pClient = aIter.Next())
166  if(!pClient->GetInfo( rInfo ))
167  return false;
168  return true;
169 }
170 
171 void SwModify::Add( SwClient* pDepend )
172 {
174 #ifdef DBG_UTIL
175  // You should not EVER use SwModify directly in new code:
176  // - Preexisting SwModifys should only ever be used via sw::BroadcastingModify.
177  // This includes sw::BroadcastMixin, which is the long-term target (without
178  // SwModify).
179  // - New classes should use sw::BroadcastMixin alone.
180  if(!dynamic_cast<sw::BroadcastingModify*>(this))
181  {
182  auto pBT = sal::backtrace_get(20);
183  SAL_WARN("sw.core", "Modify that is not broadcasting used!\n" << sal::backtrace_to_string(pBT.get()));
184  }
185 #endif
186 
187  if(pDepend->m_pRegisteredIn == this)
188  return;
189 
190 #if OSL_DEBUG_LEVEL > 0
192  {
193  for(auto& rIter : sw::ClientIteratorBase::s_pClientIters->GetRingContainer())
194  {
195  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.");
196  }
197  }
198 #endif
199  // deregister new client in case it is already registered elsewhere
200  if( pDepend->m_pRegisteredIn != nullptr )
201  pDepend->m_pRegisteredIn->Remove( pDepend );
202 
203  if( !m_pWriterListeners )
204  {
205  // first client added
206  m_pWriterListeners = pDepend;
207  m_pWriterListeners->m_pLeft = nullptr;
208  m_pWriterListeners->m_pRight = nullptr;
209  }
210  else
211  {
212  // append client
213  pDepend->m_pRight = m_pWriterListeners->m_pRight;
214  m_pWriterListeners->m_pRight = pDepend;
215  pDepend->m_pLeft = m_pWriterListeners;
216  if( pDepend->m_pRight )
217  pDepend->m_pRight->m_pLeft = pDepend;
218  }
219 
220  // connect client to me
221  pDepend->m_pRegisteredIn = this;
222 }
223 
225 {
227  assert(pDepend->m_pRegisteredIn == this);
228 
229  // SwClient is my listener
230  // remove it from my list
231  ::sw::WriterListener* pR = pDepend->m_pRight;
232  ::sw::WriterListener* pL = pDepend->m_pLeft;
233  if( m_pWriterListeners == pDepend )
234  m_pWriterListeners = pL ? pL : pR;
235 
236  if( pL )
237  pL->m_pRight = pR;
238  if( pR )
239  pR->m_pLeft = pL;
240 
241  // update ClientIterators
243  {
244  for(auto& rIter : sw::ClientIteratorBase::s_pClientIters->GetRingContainer())
245  {
246  if (&rIter.m_rRoot == this &&
247  (rIter.m_pCurrent == pDepend || rIter.m_pPosition == pDepend))
248  {
249  // if object being removed is the current or next object in an
250  // iterator, advance this iterator
251  rIter.m_pPosition = pR;
252  }
253  }
254  }
255  pDepend->m_pLeft = nullptr;
256  pDepend->m_pRight = nullptr;
257  pDepend->m_pRegisteredIn = nullptr;
258  return pDepend;
259 }
260 
262  : m_rToTell(rToTell)
263 {}
264 
266 {}
267 
269 {
270  EndListening(nullptr);
271  m_vDepends.emplace_back(&m_rToTell, pDepend);
272 }
273 
274 
275 bool sw::WriterMultiListener::IsListeningTo(const SwModify* const pBroadcaster) const
276 {
277  return std::any_of(m_vDepends.begin(), m_vDepends.end(),
278  [&pBroadcaster](const ListenerEntry& aListener)
279  {
280  return aListener.GetRegisteredIn() == pBroadcaster;
281  });
282 }
283 
285 {
286  m_vDepends.erase(
287  std::remove_if( m_vDepends.begin(), m_vDepends.end(),
288  [&pBroadcaster](const ListenerEntry& aListener)
289  {
290  return aListener.GetRegisteredIn() == nullptr || aListener.GetRegisteredIn() == pBroadcaster;
291  }),
292  m_vDepends.end());
293 }
294 
296 {
297  m_vDepends.clear();
298 }
299 
301 
302 void SwModify::SwClientNotify(const SwModify&, const SfxHint& rHint)
303 {
304  if(dynamic_cast<const sw::LegacyModifyHint*>(&rHint))
305  {
307  if(IsModifyLocked())
308  return;
309 
310  LockModify();
311  CallSwClientNotify(rHint);
312  UnlockModify();
313  }
314 }
315 
316 void SwModify::CallSwClientNotify( const SfxHint& rHint ) const
317 {
319  SwIterator<SwClient,SwModify> aIter(*this);
320  for(SwClient* pClient = aIter.First(); pClient; pClient = aIter.Next())
321  pClient->SwClientNotify( *this, rHint );
322 }
323 
325 {
327  const_cast<BroadcastingModify*>(this)->GetNotifier().Broadcast(rHint);
328 }
329 
330 void sw::ClientNotifyAttrChg(SwModify& rModify, const SwAttrSet& aSet, SwAttrSet& aOld, SwAttrSet& aNew)
331 {
332  const SwAttrSetChg aChgOld(aSet, aOld);
333  const SwAttrSetChg aChgNew(aSet, aNew);
334  const sw::LegacyModifyHint aHint(&aChgOld, &aChgNew);
335  rModify.SwClientNotify(rModify, aHint);
336 }
337 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
void Add(SwClient *pDepend)
Definition: calbck.cxx:171
refactoring out the same of the more sane SwClient functionality
Definition: calbck.hxx:102
virtual ~LegacyModifyHint() override
Definition: calbck.cxx:56
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:224
virtual bool GetInfo(SfxPoolItem &) const override
Definition: calbck.cxx:160
void StartListeningToSameModifyAs(const SwClient &)
Definition: calbck.cxx:127
const BorderLinePrimitive2D *pCandidateB assert(pCandidateA)
virtual ~SwClient() override
Definition: calbck.cxx:69
void EndListening(SwModify *pDepend)
Definition: calbck.cxx:284
void ClientNotifyAttrChg(SwModify &rModify, const SwAttrSet &aSet, SwAttrSet &aOld, SwAttrSet &aNew)
Definition: calbck.cxx:330
void EndListeningAll()
Definition: calbck.cxx:135
Base class for various Writer styles.
Definition: format.hxx:46
std::unique_ptr< sw::ModifyChangedHint > CheckRegistration(const SfxPoolItem *pOldValue)
Definition: calbck.cxx:78
bool IsListeningTo(const SwModify *const pDepend) const
Definition: calbck.cxx:275
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:316
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:268
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:141
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:324
virtual bool GetInfo(SfxPoolItem &) const
Definition: calbck.hxx:166
SwModify * m_pRegisteredIn
event source
Definition: calbck.hxx:134
WriterMultiListener(SwClient &rToTell)
Definition: calbck.cxx:261
#define SAL_WARN(area, stream)
void * pObject
Definition: hints.hxx:57
virtual void SwClientNotify(const SwModify &, const SfxHint &rHint) override
Definition: calbck.cxx:302
#define DBG_TESTSOLARMUTEX()
virtual ~ModifyChangedHint() override
Definition: calbck.cxx:57
sal_uInt16 Which() const
constexpr TypedWhichId< SwPtrMsgPoolItem > RES_OBJECTDYING(RES_MSG_BEGIN)
typedef void(CALLTYPE *GetFuncDataPtr)(sal_uInt16 &nNo