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 
29 namespace sw
30 {
32  { return m_pToTell == nullptr || m_pToTell->GetInfo( rInfo ); }
33  void ListenerEntry::Modify(const SfxPoolItem *const pOldValue,
34  const SfxPoolItem *const pNewValue)
35  {
36  SwClientNotify(*GetRegisteredIn(), sw::LegacyModifyHint(pOldValue, pNewValue));
37  }
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->SwClientNotifyCall(rModify, rHint);
50  }
51  else if (m_pToTell)
52  m_pToTell->SwClientNotifyCall(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 
108 void SwClient::SwClientNotify(const SwModify&, const SfxHint& rHint)
109 {
110  if (auto pLegacyHint = dynamic_cast<const sw::LegacyModifyHint*>(&rHint))
111  {
112  Modify(pLegacyHint->m_pOld, pLegacyHint->m_pNew);
113  }
114 };
115 
117 {
118  if(other.m_pRegisteredIn)
119  other.m_pRegisteredIn->Add(this);
120  else
121  EndListeningAll();
122 }
123 
125 {
126  if(m_pRegisteredIn)
127  m_pRegisteredIn->Remove(this);
128 }
129 
130 void SwClient::Modify(SfxPoolItem const*const pOldValue, SfxPoolItem const*const /*pNewValue*/)
131 {
132  CheckRegistration( pOldValue );
133 }
134 
136 {
137  // If the document gets destroyed anyway, just tell clients to
138  // forget me so that they don't try to get removed from my list
139  // later when they also get destroyed
140  SwIterator<SwClient,SwModify> aIter(*this);
141  for(SwClient* pClient = aIter.First(); pClient; pClient = aIter.Next())
142  pClient->m_pRegisteredIn = nullptr;
143  m_pWriterListeners = nullptr;
144 }
145 
147 {
149  OSL_ENSURE( !IsModifyLocked(), "Modify destroyed but locked." );
150 
151  if ( IsInCache() )
152  SwFrame::GetCache().Delete( this );
153 
154  if ( IsInSwFntCache() )
155  pSwFontCache->Delete( this );
156 
157  // notify all clients that they shall remove themselves
158  SwPtrMsgPoolItem aDyObject( RES_OBJECTDYING, this );
159  NotifyClients( &aDyObject, &aDyObject );
160 
161  // remove all clients that have not done themselves
162  // mba: possibly a hotfix for forgotten base class calls?!
163  while( m_pWriterListeners )
164  static_cast<SwClient*>(m_pWriterListeners)->CheckRegistration( &aDyObject );
165 }
166 
167 void SwModify::NotifyClients( const SfxPoolItem* pOldValue, const SfxPoolItem* pNewValue )
168 {
170  if ( IsInCache() || IsInSwFntCache() )
171  {
172  const sal_uInt16 nWhich = pOldValue ? pOldValue->Which() :
173  pNewValue ? pNewValue->Which() : 0;
174  CheckCaching( nWhich );
175  }
176 
177  if ( !m_pWriterListeners || IsModifyLocked() )
178  return;
179 
180  LockModify();
181 
182  // mba: WTF?!
183  if( !pOldValue )
184  {
185  m_bLockClientList = true;
186  }
187  else
188  {
189  switch( pOldValue->Which() )
190  {
191  case RES_OBJECTDYING:
193  m_bLockClientList = static_cast<const SwPtrMsgPoolItem*>(pOldValue)->pObject != this;
194  break;
195 
196  default:
197  m_bLockClientList = true;
198  }
199  }
200 
201  ModifyBroadcast( pOldValue, pNewValue );
202  m_bLockClientList = false;
203  UnlockModify();
204 }
205 
206 bool SwModify::GetInfo( SfxPoolItem& rInfo ) const
207 {
208  if(!m_pWriterListeners)
209  return true;
210  SwIterator<SwClient,SwModify> aIter(*this);
211  for(SwClient* pClient = aIter.First(); pClient; pClient = aIter.Next())
212  if(!pClient->GetInfo( rInfo ))
213  return false;
214  return true;
215 }
216 
217 void SwModify::Add( SwClient* pDepend )
218 {
220  OSL_ENSURE( !m_bLockClientList, "Client inserted while in Modify" );
221 
222  if(pDepend->m_pRegisteredIn != this )
223  {
224 #if OSL_DEBUG_LEVEL > 0
226  {
227  for(auto& rIter : sw::ClientIteratorBase::s_pClientIters->GetRingContainer())
228  {
229  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.");
230  }
231  }
232 #endif
233  // deregister new client in case it is already registered elsewhere
234  if( pDepend->m_pRegisteredIn != nullptr )
235  pDepend->m_pRegisteredIn->Remove( pDepend );
236 
237  if( !m_pWriterListeners )
238  {
239  // first client added
240  m_pWriterListeners = pDepend;
241  m_pWriterListeners->m_pLeft = nullptr;
242  m_pWriterListeners->m_pRight = nullptr;
243  }
244  else
245  {
246  // append client
247  pDepend->m_pRight = m_pWriterListeners->m_pRight;
248  m_pWriterListeners->m_pRight = pDepend;
249  pDepend->m_pLeft = m_pWriterListeners;
250  if( pDepend->m_pRight )
251  pDepend->m_pRight->m_pLeft = pDepend;
252  }
253 
254  // connect client to me
255  pDepend->m_pRegisteredIn = this;
256  }
257 }
258 
260 {
262  assert(pDepend->m_pRegisteredIn == this);
263 
264  // SwClient is my listener
265  // remove it from my list
266  ::sw::WriterListener* pR = pDepend->m_pRight;
267  ::sw::WriterListener* pL = pDepend->m_pLeft;
268  if( m_pWriterListeners == pDepend )
269  m_pWriterListeners = pL ? pL : pR;
270 
271  if( pL )
272  pL->m_pRight = pR;
273  if( pR )
274  pR->m_pLeft = pL;
275 
276  // update ClientIterators
278  {
279  for(auto& rIter : sw::ClientIteratorBase::s_pClientIters->GetRingContainer())
280  {
281  if (&rIter.m_rRoot == this &&
282  (rIter.m_pCurrent == pDepend || rIter.m_pPosition == pDepend))
283  {
284  // if object being removed is the current or next object in an
285  // iterator, advance this iterator
286  rIter.m_pPosition = static_cast<SwClient*>(pR);
287  }
288  }
289  }
290  pDepend->m_pLeft = nullptr;
291  pDepend->m_pRight = nullptr;
292  pDepend->m_pRegisteredIn = nullptr;
293  return pDepend;
294 }
295 
296 void SwModify::CheckCaching( const sal_uInt16 nWhich )
297 {
298  if( isCHRATR( nWhich ) )
299  {
300  SetInSwFntCache( false );
301  }
302  else
303  {
304  switch( nWhich )
305  {
306  case RES_OBJECTDYING:
307  case RES_FMT_CHG:
308  case RES_ATTRSET_CHG:
309  SetInSwFntCache( false );
310  [[fallthrough]];
311  case RES_UL_SPACE:
312  case RES_LR_SPACE:
313  case RES_BOX:
314  case RES_SHADOW:
315  case RES_FRM_SIZE:
316  case RES_KEEP:
317  case RES_BREAK:
318  if( IsInCache() )
319  {
320  SwFrame::GetCache().Delete( this );
321  SetInCache( false );
322  }
323  break;
324  }
325  }
326 }
327 
329  : m_rToTell(rToTell)
330 {}
331 
333 {}
334 
336 {
337  EndListening(nullptr);
338  m_vDepends.emplace_back(ListenerEntry(&m_rToTell, pDepend));
339 }
340 
341 
342 bool sw::WriterMultiListener::IsListeningTo(const SwModify* const pBroadcaster) const
343 {
344  return std::any_of(m_vDepends.begin(), m_vDepends.end(),
345  [&pBroadcaster](const ListenerEntry& aListener)
346  {
347  return aListener.GetRegisteredIn() == pBroadcaster;
348  });
349 }
350 
352 {
353  m_vDepends.erase(
354  std::remove_if( m_vDepends.begin(), m_vDepends.end(),
355  [&pBroadcaster](const ListenerEntry& aListener)
356  {
357  return aListener.GetRegisteredIn() == nullptr || aListener.GetRegisteredIn() == pBroadcaster;
358  }),
359  m_vDepends.end());
360 }
361 
363 {
364  m_vDepends.clear();
365 }
366 
368 
369 void SwModify::CallSwClientNotify( const SfxHint& rHint ) const
370 {
371  SwIterator<SwClient,SwModify> aIter(*this);
372  for(SwClient* pClient = aIter.First(); pClient; pClient = aIter.Next())
373  pClient->SwClientNotify( *this, rHint );
374 }
375 
377 {
379  const_cast<BroadcastingModify*>(this)->GetNotifier().Broadcast(rHint);
380 }
381 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
#define RES_FRM_SIZE
Definition: hintids.hxx:194
void Add(SwClient *pDepend)
Definition: calbck.cxx:217
refactoring out the same of the more sane SwClient functionality
Definition: calbck.hxx:96
bool isCHRATR(const sal_uInt16 nWhich)
Definition: hintids.hxx:342
#define RES_ATTRSET_CHG
Definition: hintids.hxx:285
virtual ~LegacyModifyHint() override
Definition: calbck.cxx:56
#define RES_SHADOW
Definition: hintids.hxx:212
static SwCache & GetCache()
Definition: frame.hxx:500
TElementType * Next()
Definition: calbck.hxx:376
Dialog to specify the properties of drop-down form field.
Definition: accframe.hxx:34
WriterListener * m_pLeft
Definition: calbck.hxx:101
virtual void SwClientNotify(const SwModify &, const SfxHint &rHint) override
Definition: calbck.cxx:108
SwClient()
Definition: calbck.hxx:137
SwClient * m_pToTell
Definition: calbck.hxx:248
SwClient * Remove(SwClient *pDepend)
Definition: calbck.cxx:259
#define RES_REMOVE_UNO_OBJECT
Definition: hintids.hxx:304
virtual bool GetInfo(SfxPoolItem &) const override
Definition: calbck.cxx:206
void SwClientNotifyCall(const SwModify &rModify, const SfxHint &rHint)
Definition: calbck.hxx:155
void StartListeningToSameModifyAs(const SwClient &)
Definition: calbck.cxx:116
virtual void Modify(const SfxPoolItem *pOldValue, const SfxPoolItem *pNewValue) override
Definition: calbck.cxx:33
#define RES_UL_SPACE
Definition: hintids.hxx:197
virtual ~SwClient() override
Definition: calbck.cxx:69
void EndListening(SwModify *pDepend)
Definition: calbck.cxx:351
void EndListeningAll()
Definition: calbck.cxx:124
std::unique_ptr< sw::ModifyChangedHint > CheckRegistration(const SfxPoolItem *pOldValue)
Definition: calbck.cxx:78
void CheckCaching(const sal_uInt16 nWhich)
Definition: calbck.cxx:296
bool IsListeningTo(const SwModify *const pDepend) const
Definition: calbck.cxx:342
TElementType * First()
Definition: calbck.hxx:345
static SW_DLLPUBLIC ClientIteratorBase * s_pClientIters
Definition: calbck.hxx:301
virtual void CallSwClientNotify(const SfxHint &rHint) const
Definition: calbck.cxx:369
virtual bool GetInfo(SfxPoolItem &rInfo) const override
get Client information
Definition: calbck.cxx:31
WriterListener * m_pRight
double-linked list of other clients
Definition: calbck.hxx:102
#define RES_FMT_CHG
Definition: hintids.hxx:284
SwFontCache * pSwFontCache
Definition: swfntcch.cxx:33
#define RES_LR_SPACE
Definition: hintids.hxx:196
void NotifyClients(const SfxPoolItem *pOldValue, const SfxPoolItem *pNewValue)
Definition: calbck.cxx:167
void Delete(const void *pOwner, sal_uInt16 nIndex)
void StartListening(SwModify *pDepend)
Definition: calbck.cxx:335
#define SAL_WARN_IF(condition, area, stream)
virtual ~SwModify() override
Definition: calbck.cxx:146
#define RES_KEEP
Definition: hintids.hxx:215
const SwModify * GetRegisteredIn() const
Definition: calbck.hxx:157
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:376
virtual bool GetInfo(SfxPoolItem &) const
Definition: calbck.hxx:164
SwModify * m_pRegisteredIn
event source
Definition: calbck.hxx:127
WriterMultiListener(SwClient &rToTell)
Definition: calbck.cxx:328
#define RES_BOX
Definition: hintids.hxx:211
#define RES_OBJECTDYING
Definition: hintids.hxx:283
void * pObject
Definition: hints.hxx:53
#define DBG_TESTSOLARMUTEX()
#define RES_BREAK
Definition: hintids.hxx:199
virtual ~ModifyChangedHint() override
Definition: calbck.cxx:57
sal_uInt16 Which() const
virtual void Modify(const SfxPoolItem *pOldValue, const SfxPoolItem *pNewValue)
Definition: calbck.cxx:130
void SetInDocDTOR()
Definition: calbck.cxx:135