1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
23 #include <hintids.hxx>
25 #include <osl/diagnose.h>
26 #include <sal/log.hxx>
27 #include <tools/debug.hxx>
30 #include <sal/backtrace.hxx>
35 bool ListenerEntry::GetInfo(SfxPoolItem
& rInfo
) const
36 { return m_pToTell
== nullptr || m_pToTell
->GetInfo( rInfo
); }
37 void ListenerEntry::SwClientNotify(const SwModify
& rModify
, const SfxHint
& rHint
)
39 if (rHint
.GetId() == SfxHintId::SwLegacyModify
)
41 auto pLegacyHint
= static_cast<const sw::LegacyModifyHint
*>(&rHint
);
42 if (pLegacyHint
->m_pNew
&& pLegacyHint
->m_pNew
->Which() == RES_OBJECTDYING
)
44 auto pModifyChanged
= CheckRegistration(pLegacyHint
->m_pOld
);
46 m_pToTell
->SwClientNotify(rModify
, *pModifyChanged
);
49 m_pToTell
->SwClientNotify(rModify
, rHint
);
52 m_pToTell
->SwClientNotify(rModify
, rHint
);
56 sw::LegacyModifyHint::~LegacyModifyHint() {}
58 SwClient::SwClient(SwClient
&& o
) noexcept
59 : m_pRegisteredIn(nullptr)
63 o
.m_pRegisteredIn
->Add(this);
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 );
77 std::optional
<sw::ModifyChangedHint
> SwClient::CheckRegistration( const SfxPoolItem
* pOld
)
80 // this method only handles notification about dying SwModify objects
81 if( !pOld
|| pOld
->Which() != RES_OBJECTDYING
)
84 assert(dynamic_cast<const SwPtrMsgPoolItem
*>(pOld
));
85 const SwPtrMsgPoolItem
* pDead
= static_cast<const SwPtrMsgPoolItem
*>(pOld
);
86 if(pDead
->pObject
!= m_pRegisteredIn
)
88 // we should only care received death notes from objects we are following
91 // I've got a notification from the object I know
92 SwModify
* pAbove
= m_pRegisteredIn
->GetRegisteredIn();
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
101 // destroy connection
104 return sw::ModifyChangedHint(pAbove
);
107 void SwClient::CheckRegistrationFormat(SwFormat
& rOld
)
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
);
114 const SwFormatChg
aOldFormat(&rOld
);
115 const SwFormatChg
aNewFormat(pNew
);
116 const sw::LegacyModifyHint
aHint(&aOldFormat
, &aNewFormat
);
117 SwClientNotify(rOld
, aHint
);
120 void SwClient::SwClientNotify(const SwModify
&, const SfxHint
& rHint
)
122 if (rHint
.GetId() != SfxHintId::SwLegacyModify
)
124 auto pLegacyHint
= static_cast<const sw::LegacyModifyHint
*>(&rHint
);
125 CheckRegistration(pLegacyHint
->m_pOld
);
128 void SwClient::StartListeningToSameModifyAs(const SwClient
& other
)
130 if(other
.m_pRegisteredIn
)
131 other
.m_pRegisteredIn
->Add(this);
136 void SwClient::EndListeningAll()
139 m_pRegisteredIn
->Remove(this);
142 SwModify::~SwModify()
144 DBG_TESTSOLARMUTEX();
145 OSL_ENSURE( !IsModifyLocked(), "Modify destroyed but locked." );
147 // notify all clients that they shall remove themselves
148 SwPtrMsgPoolItem
aDyObject( RES_OBJECTDYING
, this );
149 SwModify::SwClientNotify(*this, sw::LegacyModifyHint(&aDyObject
, &aDyObject
));
151 const bool hasListenersOnDeath
= m_pWriterListeners
;
152 (void)hasListenersOnDeath
;
153 while(m_pWriterListeners
)
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
);
158 assert(!hasListenersOnDeath
);
161 bool SwModify::GetInfo( SfxPoolItem
& rInfo
) const
163 if(!m_pWriterListeners
)
165 SwIterator
<SwClient
,SwModify
> aIter(*this);
166 for(SwClient
* pClient
= aIter
.First(); pClient
; pClient
= aIter
.Next())
167 if(!pClient
->GetInfo( rInfo
))
172 void SwModify::Add( SwClient
* pDepend
)
174 DBG_TESTSOLARMUTEX();
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
180 // - New classes should use sw::BroadcastMixin alone.
181 if(!dynamic_cast<sw::BroadcastingModify
*>(this))
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()));
188 if(pDepend
->m_pRegisteredIn
== this)
191 #if OSL_DEBUG_LEVEL > 0
192 if(sw::ClientIteratorBase::s_pClientIters
)
194 for(auto& rIter
: sw::ClientIteratorBase::s_pClientIters
->GetRingContainer())
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.");
200 // deregister new client in case it is already registered elsewhere
201 if( pDepend
->m_pRegisteredIn
!= nullptr )
202 pDepend
->m_pRegisteredIn
->Remove( pDepend
);
204 if( !m_pWriterListeners
)
206 // first client added
207 m_pWriterListeners
= pDepend
;
208 m_pWriterListeners
->m_pLeft
= nullptr;
209 m_pWriterListeners
->m_pRight
= nullptr;
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
;
221 // connect client to me
222 pDepend
->m_pRegisteredIn
= this;
225 SwClient
* SwModify::Remove( SwClient
* pDepend
)
227 DBG_TESTSOLARMUTEX();
228 assert(pDepend
->m_pRegisteredIn
== this);
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
;
242 // update ClientIterators
243 if(sw::ClientIteratorBase::s_pClientIters
)
245 for(auto& rIter
: sw::ClientIteratorBase::s_pClientIters
->GetRingContainer())
247 if (&rIter
.m_rRoot
== this &&
248 (rIter
.m_pCurrent
== pDepend
|| rIter
.m_pPosition
== pDepend
))
250 // if object being removed is the current or next object in an
251 // iterator, advance this iterator
252 rIter
.m_pPosition
= pR
;
256 pDepend
->m_pLeft
= nullptr;
257 pDepend
->m_pRight
= nullptr;
258 pDepend
->m_pRegisteredIn
= nullptr;
262 sw::WriterMultiListener::WriterMultiListener(SwClient
& rToTell
)
266 sw::WriterMultiListener::~WriterMultiListener()
269 void sw::WriterMultiListener::StartListening(SwModify
* pDepend
)
271 EndListening(nullptr);
272 m_vDepends
.emplace_back(&m_rToTell
, pDepend
);
276 bool sw::WriterMultiListener::IsListeningTo(const SwModify
* const pBroadcaster
) const
278 return std::any_of(m_vDepends
.begin(), m_vDepends
.end(),
279 [&pBroadcaster
](const ListenerEntry
& aListener
)
281 return aListener
.GetRegisteredIn() == pBroadcaster
;
285 void sw::WriterMultiListener::EndListening(SwModify
* pBroadcaster
)
288 std::remove_if( m_vDepends
.begin(), m_vDepends
.end(),
289 [&pBroadcaster
](const ListenerEntry
& aListener
)
291 return aListener
.GetRegisteredIn() == nullptr || aListener
.GetRegisteredIn() == pBroadcaster
;
296 void sw::WriterMultiListener::EndListeningAll()
301 sw::ClientIteratorBase
* sw::ClientIteratorBase::s_pClientIters
= nullptr;
303 void SwModify::SwClientNotify(const SwModify
&, const SfxHint
& rHint
)
305 if (rHint
.GetId() != SfxHintId::SwLegacyModify
)
308 DBG_TESTSOLARMUTEX();
313 CallSwClientNotify(rHint
);
317 void SwModify::CallSwClientNotify( const SfxHint
& rHint
) const
319 DBG_TESTSOLARMUTEX();
320 SwIterator
<SwClient
,SwModify
> aIter(*this);
321 for(SwClient
* pClient
= aIter
.First(); pClient
; pClient
= aIter
.Next())
322 pClient
->SwClientNotify( *this, rHint
);
325 void sw::BroadcastingModify::CallSwClientNotify(const SfxHint
& rHint
) const
327 SwModify::CallSwClientNotify(rHint
);
328 const_cast<BroadcastingModify
*>(this)->GetNotifier().Broadcast(rHint
);
331 void sw::ClientNotifyAttrChg(SwModify
& rModify
, const SwAttrSet
& aSet
, SwAttrSet
& aOld
, SwAttrSet
& aNew
)
333 const SwAttrSetChg
aChgOld(aSet
, aOld
);
334 const SwAttrSetChg
aChgNew(aSet
, aNew
);
335 const sw::LegacyModifyHint
aHint(&aChgOld
, &aChgNew
);
336 rModify
.SwClientNotify(rModify
, aHint
);
338 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */