android: Update app-specific/MIME type icons
[LibreOffice.git] / sw / source / core / attr / calbck.cxx
blob81a249203dc8804f38a248f82ebb1e085483ad3b
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 <hintids.hxx>
24 #include <hints.hxx>
25 #include <osl/diagnose.h>
26 #include <sal/log.hxx>
27 #include <tools/debug.hxx>
29 #ifdef DBG_UTIL
30 #include <sal/backtrace.hxx>
31 #endif
33 namespace sw
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);
45 if (pModifyChanged)
46 m_pToTell->SwClientNotify(rModify, *pModifyChanged);
48 else if (m_pToTell)
49 m_pToTell->SwClientNotify(rModify, rHint);
51 else if (m_pToTell)
52 m_pToTell->SwClientNotify(rModify, rHint);
56 sw::LegacyModifyHint::~LegacyModifyHint() {}
58 SwClient::SwClient(SwClient&& o) noexcept
59 : m_pRegisteredIn(nullptr)
61 if(o.m_pRegisteredIn)
63 o.m_pRegisteredIn->Add(this);
64 o.EndListeningAll();
68 SwClient::~SwClient()
70 if(GetRegisteredIn())
71 DBG_TESTSOLARMUTEX();
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 )
79 DBG_TESTSOLARMUTEX();
80 // this method only handles notification about dying SwModify objects
81 if( !pOld || pOld->Which() != RES_OBJECTDYING )
82 return {};
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
89 return {};
91 // I've got a notification from the object I know
92 SwModify* pAbove = m_pRegisteredIn->GetRegisteredIn();
93 if(pAbove)
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
97 pAbove->Add(this);
99 else
101 // destroy connection
102 EndListeningAll();
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);
112 assert(pNew);
113 pNew->Add(this);
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)
123 return;
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);
132 else
133 EndListeningAll();
136 void SwClient::EndListeningAll()
138 if(m_pRegisteredIn)
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)
164 return true;
165 SwIterator<SwClient,SwModify> aIter(*this);
166 for(SwClient* pClient = aIter.First(); pClient; pClient = aIter.Next())
167 if(!pClient->GetInfo( rInfo ))
168 return false;
169 return true;
172 void SwModify::Add( SwClient* pDepend )
174 DBG_TESTSOLARMUTEX();
175 #ifdef DBG_UTIL
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
179 // SwModify).
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()));
186 #endif
188 if(pDepend->m_pRegisteredIn == this)
189 return;
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.");
199 #endif
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;
211 else
213 // append client
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;
237 if( pL )
238 pL->m_pRight = pR;
239 if( pR )
240 pR->m_pLeft = pL;
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;
259 return pDepend;
262 sw::WriterMultiListener::WriterMultiListener(SwClient& rToTell)
263 : m_rToTell(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)
287 m_vDepends.erase(
288 std::remove_if( m_vDepends.begin(), m_vDepends.end(),
289 [&pBroadcaster](const ListenerEntry& aListener)
291 return aListener.GetRegisteredIn() == nullptr || aListener.GetRegisteredIn() == pBroadcaster;
293 m_vDepends.end());
296 void sw::WriterMultiListener::EndListeningAll()
298 m_vDepends.clear();
301 sw::ClientIteratorBase* sw::ClientIteratorBase::s_pClientIters = nullptr;
303 void SwModify::SwClientNotify(const SwModify&, const SfxHint& rHint)
305 if (rHint.GetId() != SfxHintId::SwLegacyModify)
306 return;
308 DBG_TESTSOLARMUTEX();
309 if(IsModifyLocked())
310 return;
312 LockModify();
313 CallSwClientNotify(rHint);
314 UnlockModify();
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: */