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 #include <frame.hxx>
21 #include <hintids.hxx>
22 #include <hints.hxx>
23 #include <swcache.hxx>
24 #include <swfntcch.hxx>
25 #include <tools/debug.hxx>
26 #include <sal/log.hxx>
27 #include <algorithm>
28 #ifdef DBG_UTIL
29 #include <sal/backtrace.hxx>
30 #endif
31 
32 namespace sw
33 {
35  { return m_pToTell == nullptr || m_pToTell->GetInfo( rInfo ); }
36  void ListenerEntry::Modify(const SfxPoolItem *const pOldValue,
37  const SfxPoolItem *const pNewValue)
38  {
39  SwClientNotify(*GetRegisteredIn(), sw::LegacyModifyHint(pOldValue, pNewValue));
40  }
41  void ListenerEntry::SwClientNotify(const SwModify& rModify, const SfxHint& rHint)
42  {
43  if (auto pLegacyHint = dynamic_cast<const sw::LegacyModifyHint*>(&rHint))
44  {
45  if (pLegacyHint->m_pNew && pLegacyHint->m_pNew->Which() == RES_OBJECTDYING)
46  {
47  auto pModifyChanged = CheckRegistration(pLegacyHint->m_pOld);
48  if (pModifyChanged)
49  m_pToTell->SwClientNotifyCall(rModify, *pModifyChanged);
50  }
51  else if (m_pToTell)
52  m_pToTell->SwClientNotifyCall(rModify, rHint);
53  }
54  else if (m_pToTell)
55  m_pToTell->SwClientNotifyCall(rModify, rHint);
56  }
57 }
58 
61 
63  : m_pRegisteredIn(nullptr)
64 {
65  if(o.m_pRegisteredIn)
66  {
67  o.m_pRegisteredIn->Add(this);
68  o.EndListeningAll();
69  }
70 }
71 
73 {
74  if(GetRegisteredIn())
76  OSL_ENSURE( !m_pRegisteredIn || m_pRegisteredIn->HasWriterListeners(), "SwModify still known, but Client already disconnected!" );
77  if( m_pRegisteredIn && m_pRegisteredIn->HasWriterListeners() )
78  m_pRegisteredIn->Remove( this );
79 }
80 
81 std::unique_ptr<sw::ModifyChangedHint> SwClient::CheckRegistration( const SfxPoolItem* pOld )
82 {
84  // this method only handles notification about dying SwModify objects
85  if( !pOld || pOld->Which() != RES_OBJECTDYING )
86  return nullptr;
87 
88  assert(dynamic_cast<const SwPtrMsgPoolItem*>(pOld));
89  const SwPtrMsgPoolItem* pDead = static_cast<const SwPtrMsgPoolItem*>(pOld);
90  if(pDead->pObject != m_pRegisteredIn)
91  {
92  // we should only care received death notes from objects we are following
93  return nullptr;
94  }
95  // I've got a notification from the object I know
96  SwModify* pAbove = m_pRegisteredIn->GetRegisteredIn();
97  if(pAbove)
98  {
99  // if the dying object itself was listening at an SwModify, I take over
100  // adding myself to pAbove will automatically remove me from my current pRegisteredIn
101  pAbove->Add(this);
102  }
103  else
104  {
105  // destroy connection
106  EndListeningAll();
107  }
108  return std::unique_ptr<sw::ModifyChangedHint>(new sw::ModifyChangedHint(pAbove));
109 }
110 
111 void SwClient::SwClientNotify(const SwModify&, const SfxHint& rHint)
112 {
113  if (auto pLegacyHint = dynamic_cast<const sw::LegacyModifyHint*>(&rHint))
114  {
115  Modify(pLegacyHint->m_pOld, pLegacyHint->m_pNew);
116  }
117 };
118 
120 {
121  if(other.m_pRegisteredIn)
122  other.m_pRegisteredIn->Add(this);
123  else
124  EndListeningAll();
125 }
126 
128 {
129  if(m_pRegisteredIn)
130  m_pRegisteredIn->Remove(this);
131 }
132 
133 void SwClient::Modify(SfxPoolItem const*const pOldValue, SfxPoolItem const*const /*pNewValue*/)
134 {
135  CheckRegistration( pOldValue );
136 }
137 
139 {
140  // If the document gets destroyed anyway, just tell clients to
141  // forget me so that they don't try to get removed from my list
142  // later when they also get destroyed
143  SwIterator<SwClient,SwModify> aIter(*this);
144  for(SwClient* pClient = aIter.First(); pClient; pClient = aIter.Next())
145  pClient->m_pRegisteredIn = nullptr;
146  m_pWriterListeners = nullptr;
147 }
148 
150 {
152  OSL_ENSURE( !IsModifyLocked(), "Modify destroyed but locked." );
153 
154  if ( IsInCache() )
155  SwFrame::GetCache().Delete( this );
156 
157  if ( IsInSwFntCache() )
158  pSwFontCache->Delete( this );
159 
160  // notify all clients that they shall remove themselves
161  SwPtrMsgPoolItem aDyObject( RES_OBJECTDYING, this );
162  NotifyClients( &aDyObject, &aDyObject );
163 
164  // remove all clients that have not done themselves
165  // mba: possibly a hotfix for forgotten base class calls?!
166  while( m_pWriterListeners )
167  static_cast<SwClient*>(m_pWriterListeners)->CheckRegistration( &aDyObject );
168 }
169 
170 void SwModify::NotifyClients( const SfxPoolItem* pOldValue, const SfxPoolItem* pNewValue )
171 {
173  if ( IsInCache() || IsInSwFntCache() )
174  {
175  const sal_uInt16 nWhich = pOldValue ? pOldValue->Which() :
176  pNewValue ? pNewValue->Which() : 0;
177  CheckCaching( nWhich );
178  }
179 
180  if ( !m_pWriterListeners || IsModifyLocked() )
181  return;
182 
183  LockModify();
184  CallSwClientNotify( sw::LegacyModifyHint{ pOldValue, pNewValue } );
185  UnlockModify();
186 }
187 
188 bool SwModify::GetInfo( SfxPoolItem& rInfo ) const
189 {
190  if(!m_pWriterListeners)
191  return true;
192  SwIterator<SwClient,SwModify> aIter(*this);
193  for(SwClient* pClient = aIter.First(); pClient; pClient = aIter.Next())
194  if(!pClient->GetInfo( rInfo ))
195  return false;
196  return true;
197 }
198 
199 void SwModify::Add( SwClient* pDepend )
200 {
202 #ifdef DBG_UTIL
203  // You should not EVER use SwModify directly in new code:
204  // - Preexisting SwModifys should only ever be used via sw::BroadcastingModify.
205  // This includes sw::BroadcastMixin, which is the long-term target (without
206  // SwModify).
207  // - New classes should use sw::BroadcastMixin alone.
208  if(!dynamic_cast<sw::BroadcastingModify*>(this))
209  {
210  auto pBT = sal::backtrace_get(20);
211  SAL_WARN("sw.core", "Modify that is not broadcasting used!\n" << sal::backtrace_to_string(pBT.get()));
212  }
213 #endif
214 
215  if(pDepend->m_pRegisteredIn == this)
216  return;
217 
218 #if OSL_DEBUG_LEVEL > 0
220  {
221  for(auto& rIter : sw::ClientIteratorBase::s_pClientIters->GetRingContainer())
222  {
223  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.");
224  }
225  }
226 #endif
227  // deregister new client in case it is already registered elsewhere
228  if( pDepend->m_pRegisteredIn != nullptr )
229  pDepend->m_pRegisteredIn->Remove( pDepend );
230 
231  if( !m_pWriterListeners )
232  {
233  // first client added
234  m_pWriterListeners = pDepend;
235  m_pWriterListeners->m_pLeft = nullptr;
236  m_pWriterListeners->m_pRight = nullptr;
237  }
238  else
239  {
240  // append client
241  pDepend->m_pRight = m_pWriterListeners->m_pRight;
242  m_pWriterListeners->m_pRight = pDepend;
243  pDepend->m_pLeft = m_pWriterListeners;
244  if( pDepend->m_pRight )
245  pDepend->m_pRight->m_pLeft = pDepend;
246  }
247 
248  // connect client to me
249  pDepend->m_pRegisteredIn = this;
250 }
251 
253 {
255  assert(pDepend->m_pRegisteredIn == this);
256 
257  // SwClient is my listener
258  // remove it from my list
259  ::sw::WriterListener* pR = pDepend->m_pRight;
260  ::sw::WriterListener* pL = pDepend->m_pLeft;
261  if( m_pWriterListeners == pDepend )
262  m_pWriterListeners = pL ? pL : pR;
263 
264  if( pL )
265  pL->m_pRight = pR;
266  if( pR )
267  pR->m_pLeft = pL;
268 
269  // update ClientIterators
271  {
272  for(auto& rIter : sw::ClientIteratorBase::s_pClientIters->GetRingContainer())
273  {
274  if (&rIter.m_rRoot == this &&
275  (rIter.m_pCurrent == pDepend || rIter.m_pPosition == pDepend))
276  {
277  // if object being removed is the current or next object in an
278  // iterator, advance this iterator
279  rIter.m_pPosition = pR;
280  }
281  }
282  }
283  pDepend->m_pLeft = nullptr;
284  pDepend->m_pRight = nullptr;
285  pDepend->m_pRegisteredIn = nullptr;
286  return pDepend;
287 }
288 
289 void SwModify::CheckCaching( const sal_uInt16 nWhich )
290 {
291  if( isCHRATR( nWhich ) )
292  {
293  SetInSwFntCache( false );
294  }
295  else
296  {
297  switch( nWhich )
298  {
299  case RES_OBJECTDYING:
300  case RES_FMT_CHG:
301  case RES_ATTRSET_CHG:
302  SetInSwFntCache( false );
303  [[fallthrough]];
304  case RES_UL_SPACE:
305  case RES_LR_SPACE:
306  case RES_BOX:
307  case RES_SHADOW:
308  case RES_FRM_SIZE:
309  case RES_KEEP:
310  case RES_BREAK:
311  if( IsInCache() )
312  {
313  SwFrame::GetCache().Delete( this );
314  SetInCache( false );
315  }
316  break;
317  }
318  }
319 }
320 
322  : m_rToTell(rToTell)
323 {}
324 
326 {}
327 
329 {
330  EndListening(nullptr);
331  m_vDepends.emplace_back(ListenerEntry(&m_rToTell, pDepend));
332 }
333 
334 
335 bool sw::WriterMultiListener::IsListeningTo(const SwModify* const pBroadcaster) const
336 {
337  return std::any_of(m_vDepends.begin(), m_vDepends.end(),
338  [&pBroadcaster](const ListenerEntry& aListener)
339  {
340  return aListener.GetRegisteredIn() == pBroadcaster;
341  });
342 }
343 
345 {
346  m_vDepends.erase(
347  std::remove_if( m_vDepends.begin(), m_vDepends.end(),
348  [&pBroadcaster](const ListenerEntry& aListener)
349  {
350  return aListener.GetRegisteredIn() == nullptr || aListener.GetRegisteredIn() == pBroadcaster;
351  }),
352  m_vDepends.end());
353 }
354 
356 {
357  m_vDepends.clear();
358 }
359 
361 
362 void SwModify::CallSwClientNotify( const SfxHint& rHint ) const
363 {
365  SwIterator<SwClient,SwModify> aIter(*this);
366  for(SwClient* pClient = aIter.First(); pClient; pClient = aIter.Next())
367  pClient->SwClientNotify( *this, rHint );
368 }
369 
371 {
373  const_cast<BroadcastingModify*>(this)->GetNotifier().Broadcast(rHint);
374 }
375 
376 void sw::ClientNotifyAttrChg(SwModify& rModify, const SwAttrSet& aSet, SwAttrSet& aOld, SwAttrSet& aNew)
377 {
378  const SwAttrSetChg aChgOld(aSet, aOld);
379  const SwAttrSetChg aChgNew(aSet, aNew);
380  const sw::LegacyModifyHint aHint(&aChgOld, &aChgNew);
381  rModify.SwClientNotifyCall(rModify, aHint);
382 }
383 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
void Add(SwClient *pDepend)
Definition: calbck.cxx:199
refactoring out the same of the more sane SwClient functionality
Definition: calbck.hxx:99
bool isCHRATR(const sal_uInt16 nWhich)
Definition: hintids.hxx:469
virtual ~LegacyModifyHint() override
Definition: calbck.cxx:59
constexpr TypedWhichId< SvxFormatKeepItem > RES_KEEP(110)
constexpr TypedWhichId< SwFormatFrameSize > RES_FRM_SIZE(89)
static SwCache & GetCache()
Definition: frame.hxx:505
TElementType * Next()
Definition: calbck.hxx:354
constexpr TypedWhichId< SvxFormatBreakItem > RES_BREAK(94)
Dialog to specify the properties of date form field.
WriterListener * m_pLeft
Definition: calbck.hxx:104
virtual void SwClientNotify(const SwModify &, const SfxHint &rHint) override
Definition: calbck.cxx:111
SwClient()
Definition: calbck.hxx:143
SwClient * m_pToTell
Definition: calbck.hxx:248
SwClient * Remove(SwClient *pDepend)
Definition: calbck.cxx:252
virtual bool GetInfo(SfxPoolItem &) const override
Definition: calbck.cxx:188
void SwClientNotifyCall(const SwModify &rModify, const SfxHint &rHint)
Definition: calbck.hxx:161
void StartListeningToSameModifyAs(const SwClient &)
Definition: calbck.cxx:119
virtual void Modify(const SfxPoolItem *pOldValue, const SfxPoolItem *pNewValue) override
Definition: calbck.cxx:36
const BorderLinePrimitive2D *pCandidateB assert(pCandidateA)
constexpr TypedWhichId< SwAttrSetChg > RES_ATTRSET_CHG(161)
constexpr TypedWhichId< SwFormatChg > RES_FMT_CHG(160)
virtual ~SwClient() override
Definition: calbck.cxx:72
void EndListening(SwModify *pDepend)
Definition: calbck.cxx:344
void ClientNotifyAttrChg(SwModify &rModify, const SwAttrSet &aSet, SwAttrSet &aOld, SwAttrSet &aNew)
Definition: calbck.cxx:376
void EndListeningAll()
Definition: calbck.cxx:127
std::unique_ptr< sw::ModifyChangedHint > CheckRegistration(const SfxPoolItem *pOldValue)
Definition: calbck.cxx:81
void CheckCaching(const sal_uInt16 nWhich)
Definition: calbck.cxx:289
bool IsListeningTo(const SwModify *const pDepend) const
Definition: calbck.cxx:335
TElementType * First()
Definition: calbck.hxx:346
static SW_DLLPUBLIC ClientIteratorBase * s_pClientIters
Definition: calbck.hxx:301
virtual void CallSwClientNotify(const SfxHint &rHint) const
Definition: calbck.cxx:362
virtual bool GetInfo(SfxPoolItem &rInfo) const override
get Client information
Definition: calbck.cxx:34
WriterListener * m_pRight
double-linked list of other clients
Definition: calbck.hxx:105
SwFontCache * pSwFontCache
Definition: swfntcch.cxx:33
void NotifyClients(const SfxPoolItem *pOldValue, const SfxPoolItem *pNewValue)
Definition: calbck.cxx:170
void Delete(const void *pOwner, sal_uInt16 nIndex)
void StartListening(SwModify *pDepend)
Definition: calbck.cxx:328
virtual void Modify(const SfxPoolItem *, const SfxPoolItem *)
Definition: calbck.cxx:133
#define SAL_WARN_IF(condition, area, stream)
virtual ~SwModify() override
Definition: calbck.cxx:149
const SwModify * GetRegisteredIn() const
Definition: calbck.hxx:163
constexpr TypedWhichId< SvxLRSpaceItem > RES_LR_SPACE(91)
virtual void SwClientNotify(const SwModify &rModify, const SfxHint &rHint) override
Definition: calbck.cxx:41
constexpr TypedWhichId< SvxBoxItem > RES_BOX(106)
virtual void CallSwClientNotify(const SfxHint &rHint) const override
Definition: calbck.cxx:370
virtual bool GetInfo(SfxPoolItem &) const
Definition: calbck.hxx:170
SwModify * m_pRegisteredIn
event source
Definition: calbck.hxx:130
WriterMultiListener(SwClient &rToTell)
Definition: calbck.cxx:321
#define SAL_WARN(area, stream)
void * pObject
Definition: hints.hxx:57
#define DBG_TESTSOLARMUTEX()
virtual ~ModifyChangedHint() override
Definition: calbck.cxx:60
constexpr TypedWhichId< SvxULSpaceItem > RES_UL_SPACE(92)
sal_uInt16 Which() const
constexpr TypedWhichId< SvxShadowItem > RES_SHADOW(107)
void SetInDocDTOR()
Definition: calbck.cxx:138
constexpr TypedWhichId< SwPtrMsgPoolItem > RES_OBJECTDYING(RES_MSG_BEGIN)