sc: factor out some more code
[LibreOffice.git] / sw / source / core / attr / calbck.cxx
blob8eeae58276eccdfd93cfed519305ea6849ec801c
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 .
21 #include <algorithm>
22 #include <format.hxx>
23 #include <frmfmt.hxx>
24 #include <frame.hxx>
25 #include <hintids.hxx>
26 #include <hints.hxx>
27 #include <osl/diagnose.h>
28 #include <sal/log.hxx>
29 #include <tools/debug.hxx>
31 #ifdef DBG_UTIL
32 #include <sal/backtrace.hxx>
33 #endif
35 namespace sw
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);
45 if (pModifyChanged)
46 m_pToTell->SwClientNotify(rModify, *pModifyChanged);
48 else if (m_pToTell)
49 m_pToTell->SwClientNotify(rModify, rHint);
53 sw::LegacyModifyHint::~LegacyModifyHint() {}
55 template<typename T>
56 sw::ClientBase<T>::ClientBase(sw::ClientBase<T>&& o) noexcept
57 : m_pRegisteredIn(nullptr)
59 if(o.m_pRegisteredIn)
61 o.m_pRegisteredIn->Add(*this);
62 o.EndListeningAll();
66 template<typename T>
67 sw::ClientBase<T>::~ClientBase()
69 if(GetRegisteredIn())
70 DBG_TESTSOLARMUTEX();
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);
76 template<typename T>
77 std::optional<sw::ModifyChangedHint> sw::ClientBase<T>::CheckRegistration( const sw::ObjectDyingHint& rHint )
79 DBG_TESTSOLARMUTEX();
81 if(rHint.m_pDying != m_pRegisteredIn)
83 // we should only care received death notes from objects we are following
84 return {};
86 // I've got a notification from the object I know
87 SwModify* pAbove = m_pRegisteredIn->GetRegisteredIn();
88 if(pAbove)
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
92 pAbove->Add(*this);
94 else
96 // destroy connection
97 EndListeningAll();
99 return sw::ModifyChangedHint(pAbove);
102 template<typename T>
103 void sw::ClientBase<T>::SwClientNotify(const SwModify&, const SfxHint& rHint)
105 if (rHint.GetId() != SfxHintId::SwObjectDying)
106 return;
107 auto pDyingHint = static_cast<const sw::ObjectDyingHint*>(&rHint);
108 CheckRegistration(*pDyingHint);
111 template<typename T>
112 void sw::ClientBase<T>::StartListeningToSameModifyAs(const sw::ClientBase<T>& other)
114 if(other.m_pRegisteredIn)
115 other.m_pRegisteredIn->Add(*this);
116 else
117 EndListeningAll();
120 template<typename T>
121 void sw::ClientBase<T>::EndListeningAll()
123 if(m_pRegisteredIn)
124 m_pRegisteredIn->Remove(*this);
127 template<typename T>
128 void sw::ClientBase<T>::RegisterIn(SwModify* pModify)
130 if(pModify)
131 pModify->Add(*this);
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);
155 namespace {
156 class WriterListenerIterator final : private sw::ClientIteratorBase
158 public:
159 WriterListenerIterator(const SwModify& rSrc) : sw::ClientIteratorBase(rSrc) {}
160 sw::WriterListener* First()
161 { return GoStart(); }
162 sw::WriterListener* Next()
164 if(!IsChanged())
165 m_pPosition = GetRightOfPos();
166 return Sync();
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);
185 template<typename T>
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))
190 return false;
191 return true;
194 void SwModify::RemoveAllWriterListeners()
196 while(m_pWriterListeners)
197 m_pWriterListeners->RegisterIn(nullptr);
200 bool SwModify::GetInfo( SwFindNearestNode& rInfo ) const
202 if(!m_pWriterListeners)
203 return true;
204 auto aIter = WriterListenerIterator(*this);
205 for(auto pListener = aIter.First(); pListener; pListener = aIter.Next())
206 if(!pListener->GetInfo( rInfo ))
207 return false;
208 return true;
212 sw::WriterMultiListener::WriterMultiListener(SwClient& rToTell)
213 : m_rToTell(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)
237 std::erase_if(
238 m_vDepends,
239 [&pBroadcaster](const ListenerEntry& aListener)
241 return aListener.GetRegisteredIn() == nullptr || aListener.GetRegisteredIn() == pBroadcaster;
245 void sw::WriterMultiListener::EndListeningAll()
247 m_vDepends.clear();
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)
259 return;
261 DBG_TESTSOLARMUTEX();
262 if(IsModifyLocked())
263 return;
265 LockModify();
266 CallSwClientNotify(rHint);
267 UnlockModify();
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()
280 #ifdef DBG_UTIL
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
284 // SwModify).
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()));
291 #endif
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: */