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 .
25 #include <hintids.hxx>
27 #include <osl/diagnose.h>
28 #include <sal/log.hxx>
29 #include <tools/debug.hxx>
32 #include <sal/backtrace.hxx>
37 bool ListenerEntry::GetInfo(SwFindNearestNode
& rInfo
) const
38 { return m_pToTell
== nullptr || m_pToTell
->GetInfo( rInfo
); }
39 void ListenerEntry::SwClientNotify(const SwModify
& rModify
, const SfxHint
& rHint
)
41 if (rHint
.GetId() == SfxHintId::SwObjectDying
)
43 auto pDyingHint
= static_cast<const sw::ObjectDyingHint
*>(&rHint
);
44 auto pModifyChanged
= CheckRegistration(*pDyingHint
);
46 m_pToTell
->SwClientNotify(rModify
, *pModifyChanged
);
49 m_pToTell
->SwClientNotify(rModify
, rHint
);
53 sw::LegacyModifyHint::~LegacyModifyHint() {}
56 sw::ClientBase
<T
>::ClientBase(sw::ClientBase
<T
>&& o
) noexcept
57 : m_pRegisteredIn(nullptr)
61 o
.m_pRegisteredIn
->Add(*this);
67 sw::ClientBase
<T
>::~ClientBase()
71 OSL_ENSURE( !m_pRegisteredIn
|| m_pRegisteredIn
->HasWriterListeners(), "SwModify still known, but Client already disconnected!" );
72 if( m_pRegisteredIn
&& m_pRegisteredIn
->HasWriterListeners() )
73 m_pRegisteredIn
->Remove(*this);
77 std::optional
<sw::ModifyChangedHint
> sw::ClientBase
<T
>::CheckRegistration( const sw::ObjectDyingHint
& rHint
)
81 if(rHint
.m_pDying
!= m_pRegisteredIn
)
83 // we should only care received death notes from objects we are following
86 // I've got a notification from the object I know
87 SwModify
* pAbove
= m_pRegisteredIn
->GetRegisteredIn();
90 // if the dying object itself was listening at an SwModify, I take over
91 // adding myself to pAbove will automatically remove me from my current pRegisteredIn
99 return sw::ModifyChangedHint(pAbove
);
103 void sw::ClientBase
<T
>::SwClientNotify(const SwModify
&, const SfxHint
& rHint
)
105 if (rHint
.GetId() != SfxHintId::SwObjectDying
)
107 auto pDyingHint
= static_cast<const sw::ObjectDyingHint
*>(&rHint
);
108 CheckRegistration(*pDyingHint
);
112 void sw::ClientBase
<T
>::StartListeningToSameModifyAs(const sw::ClientBase
<T
>& other
)
114 if(other
.m_pRegisteredIn
)
115 other
.m_pRegisteredIn
->Add(*this);
121 void sw::ClientBase
<T
>::EndListeningAll()
124 m_pRegisteredIn
->Remove(*this);
128 void sw::ClientBase
<T
>::RegisterIn(SwModify
* pModify
)
132 else if(m_pRegisteredIn
)
133 m_pRegisteredIn
->Remove(*this);
136 SwModify::~SwModify()
138 DBG_TESTSOLARMUTEX();
139 OSL_ENSURE( !IsModifyLocked(), "Modify destroyed but locked." );
141 // notify all clients that they shall remove themselves
142 sw::ObjectDyingHint
aDyObject( this );
143 SwModify::SwClientNotify(*this, aDyObject
);
145 const bool hasListenersOnDeath
= m_pWriterListeners
;
146 (void)hasListenersOnDeath
;
147 while(m_pWriterListeners
)
149 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 << ".");
150 static_cast<SwClient
*>(m_pWriterListeners
)->CheckRegistration(aDyObject
);
152 assert(!hasListenersOnDeath
);
156 class WriterListenerIterator final
: private sw::ClientIteratorBase
159 WriterListenerIterator(const SwModify
& rSrc
) : sw::ClientIteratorBase(rSrc
) {}
160 sw::WriterListener
* First()
161 { return GoStart(); }
162 sw::WriterListener
* Next()
165 m_pPosition
= GetRightOfPos();
168 using sw::ClientIteratorBase::IsChanged
;
172 void SwModify::PrepareFormatDeath(const SwFormatChangeHint
& rHint
)
174 assert(rHint
.m_pOldFormat
== this);
175 assert(rHint
.m_pNewFormat
);
176 auto aIter
= WriterListenerIterator(*this);
177 for(auto pListener
= aIter
.First(); pListener
; pListener
= aIter
.Next())
179 SAL_INFO("sw.core", "reparenting " << typeid(*pListener
).name() << " at " << pListener
<< " from " << typeid(this).name() << " at " << this << " to " << typeid(rHint
.m_pNewFormat
).name() << " at " << rHint
.m_pNewFormat
);
180 pListener
->RegisterIn(rHint
.m_pNewFormat
);
181 pListener
->SwClientNotify(*this, rHint
);
186 bool SwModify::HasOnlySpecificWriterListeners() const {
187 auto aIter
= WriterListenerIterator(*this);
188 for(auto pListener
= aIter
.First(); pListener
; pListener
= aIter
.Next())
189 if(!dynamic_cast<T
*>(pListener
))
194 void SwModify::RemoveAllWriterListeners()
196 while(m_pWriterListeners
)
197 m_pWriterListeners
->RegisterIn(nullptr);
200 bool SwModify::GetInfo( SwFindNearestNode
& rInfo
) const
202 if(!m_pWriterListeners
)
204 auto aIter
= WriterListenerIterator(*this);
205 for(auto pListener
= aIter
.First(); pListener
; pListener
= aIter
.Next())
206 if(!pListener
->GetInfo( rInfo
))
212 sw::WriterMultiListener::WriterMultiListener(SwClient
& rToTell
)
216 sw::WriterMultiListener::~WriterMultiListener()
219 void sw::WriterMultiListener::StartListening(SwModify
* pDepend
)
221 EndListening(nullptr);
222 m_vDepends
.emplace_back(&m_rToTell
, pDepend
);
226 bool sw::WriterMultiListener::IsListeningTo(const SwModify
* const pBroadcaster
) const
228 return std::any_of(m_vDepends
.begin(), m_vDepends
.end(),
229 [&pBroadcaster
](const ListenerEntry
& aListener
)
231 return aListener
.GetRegisteredIn() == pBroadcaster
;
235 void sw::WriterMultiListener::EndListening(SwModify
* pBroadcaster
)
239 [&pBroadcaster
](const ListenerEntry
& aListener
)
241 return aListener
.GetRegisteredIn() == nullptr || aListener
.GetRegisteredIn() == pBroadcaster
;
245 void sw::WriterMultiListener::EndListeningAll()
250 sw::ClientIteratorBase
* sw::ClientIteratorBase::s_pClientIters
= nullptr;
252 void SwModify::SwClientNotify(const SwModify
&, const SfxHint
& rHint
)
254 if (rHint
.GetId() != SfxHintId::SwLegacyModify
255 && rHint
.GetId() != SfxHintId::SwRemoveUnoObject
256 && rHint
.GetId() != SfxHintId::SwAttrSetChange
257 && rHint
.GetId() != SfxHintId::SwObjectDying
258 && rHint
.GetId() != SfxHintId::SwUpdateAttr
)
261 DBG_TESTSOLARMUTEX();
266 CallSwClientNotify(rHint
);
270 void SwModify::CallSwClientNotify( const SfxHint
& rHint
) const
272 DBG_TESTSOLARMUTEX();
273 auto aIter
= WriterListenerIterator(*this);
274 for(auto pListener
= aIter
.First(); pListener
; pListener
= aIter
.Next())
275 pListener
->SwClientNotify(*this, rHint
);
278 void SwModify::EnsureBroadcasting()
281 // You should not EVER use SwModify directly in new code:
282 // - Preexisting SwModifys should only ever be used via sw::BroadcastingModify.
283 // This includes sw::BroadcastMixin, which is the long-term target (without
285 // - New classes should use sw::BroadcastMixin alone.
286 if(!dynamic_cast<sw::BroadcastingModify
*>(this))
288 auto pBT
= sal::backtrace_get(20);
289 SAL_WARN("sw.core", "Modify that is not broadcasting used!\n" << sal::backtrace_to_string(pBT
.get()));
294 void sw::BroadcastingModify::CallSwClientNotify(const SfxHint
& rHint
) const
296 SwModify::CallSwClientNotify(rHint
);
297 const_cast<BroadcastingModify
*>(this)->GetNotifier().Broadcast(rHint
);
300 void sw::ClientNotifyAttrChg(SwModify
& rModify
, const SwAttrSet
& aSet
, SwAttrSet
& aOld
, SwAttrSet
& aNew
)
302 const SwAttrSetChg
aChgOld(aSet
, aOld
);
303 const SwAttrSetChg
aChgNew(aSet
, aNew
);
304 const sw::AttrSetChangeHint
aHint(&aChgOld
, &aChgNew
);
305 rModify
.SwClientNotify(rModify
, aHint
);
308 template class sw::ClientBase
<SwModify
>;
309 template class sw::ClientBase
<SwFrameFormat
>;
310 template bool SwModify::HasOnlySpecificWriterListeners
<SwFrame
>() const;
311 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */