sw/inc/calbck.hxx | 193 ++++++++++++++++++++++++++++----------- sw/inc/fmthdft.hxx | 18 ++- sw/inc/ring.hxx | 1 sw/source/core/attr/calbck.cxx | 190 ++++++++++++++++++-------------------- sw/source/core/attr/format.cxx | 4 sw/source/core/doc/docdesc.cxx | 2 sw/source/core/doc/docfmt.cxx | 5 - sw/source/core/layout/atrfrm.cxx | 30 ++---- 8 files changed, 259 insertions(+), 184 deletions(-)
New commits: commit e146f49704ef535c9ddd74338783e7c38390bf59 Author: Bjoern Michaelsen <bjoern.michael...@libreoffice.org> AuthorDate: Sat Jan 11 08:35:10 2025 +0100 Commit: Stephan Bergmann <stephan.bergm...@allotropia.de> CommitDate: Sun Jan 12 21:29:13 2025 +0100 stronger typing for SwClient::GetRegisteredIn" and fix SwIterator cast * and fixes UBSAN by iterating over WriterListeners in GetInfo/SwClientNotify for safe casting Change-Id: If564111ac9529dc18daf1b5a30ac355023331b0c Reviewed-on: https://gerrit.libreoffice.org/c/core/+/179663 Reviewed-by: Stephan Bergmann <stephan.bergm...@allotropia.de> Tested-by: Jenkins diff --git a/sw/inc/calbck.hxx b/sw/inc/calbck.hxx index 58a336bbc92b..8979661456e2 100644 --- a/sw/inc/calbck.hxx +++ b/sw/inc/calbck.hxx @@ -24,6 +24,7 @@ #include <svl/hint.hxx> #include <svl/broadcast.hxx> #include <svl/poolitem.hxx> +#include <tools/debug.hxx> #include "swdllapi.h" #include "ring.hxx" #include <type_traits> @@ -32,6 +33,7 @@ class SwModify; class SwFormat; +class SwFormatChangeHint; class SfxPoolItem; class SwAttrSet; class SwCellFrame; @@ -64,10 +66,18 @@ class SwFindNearestNode; This is still subject to refactoring. */ + namespace sw { class ClientIteratorBase; class ListenerEntry; + enum class IteratorMode { Exact, UnwrapMulti }; +} + +template<typename TElementType, typename TSource, sw::IteratorMode eMode> class SwIterator; + +namespace sw +{ void ClientNotifyAttrChg(SwModify& rModify, const SwAttrSet& aSet, SwAttrSet& aOld, SwAttrSet& aNew); struct SAL_DLLPUBLIC_RTTI LegacyModifyHint final: SfxHint { @@ -112,7 +122,8 @@ namespace sw WriterListener(WriterListener const&) = delete; WriterListener& operator=(WriterListener const&) = delete; - + // Helpers for SwModify + virtual void RegisterIn(SwModify*) { assert(false); }; // should only be called with a ClientBase<> protected: WriterListener() : m_pLeft(nullptr), m_pRight(nullptr) @@ -125,53 +136,57 @@ namespace sw virtual const SwTabFrame* DynCastTabFrame() const { return nullptr; } virtual const SwRowFrame* DynCastRowFrame() const { return nullptr; } virtual const SwTable* DynCastTable() const { return nullptr; } + // get information about attribute + virtual bool GetInfo( SwFindNearestNode& ) const { return true; } }; - enum class IteratorMode { Exact, UnwrapMulti }; -} -// SwClient -class SW_DLLPUBLIC SwClient : public ::sw::WriterListener -{ - // avoids making the details of the linked list and the callback method public - friend class SwModify; - friend class sw::ClientIteratorBase; - friend class sw::ListenerEntry; - template<typename E, typename S, sw::IteratorMode> friend class SwIterator; + template<typename T> + class SW_DLLPUBLIC ClientBase : public ::sw::WriterListener + { + // avoids making the details of the linked list and the callback method public + friend class ::SwModify; + friend class sw::ClientIteratorBase; + friend class sw::ListenerEntry; + template<typename E, typename S, sw::IteratorMode> friend class ::SwIterator; - SwModify *m_pRegisteredIn; ///< event source + T* m_pRegisteredIn; ///< event source + virtual void RegisterIn(SwModify* pModify) override; -protected: - // single argument ctors shall be explicit. - inline explicit SwClient( SwModify* pToRegisterIn ); + protected: + // single argument ctors shall be explicit. + inline explicit ClientBase( T* pToRegisterIn ) + : m_pRegisteredIn( nullptr ) + { + if(pToRegisterIn) + pToRegisterIn->Add(*this); + } - // write access to pRegisteredIn shall be granted only to the object itself (protected access) - SwModify* GetRegisteredInNonConst() const { return m_pRegisteredIn; } + // write access to pRegisteredIn shall be granted only to the object itself (protected access) + T* GetRegisteredInNonConst() const { return m_pRegisteredIn; } - // when overriding this, you MUST call SwClient::SwClientNotify() in the override! - virtual void SwClientNotify(const SwModify&, const SfxHint& rHint) override; + // when overriding this, you MUST call SwClient::SwClientNotify() in the override! + virtual void SwClientNotify(const SwModify&, const SfxHint& rHint) override; -public: - SwClient() : m_pRegisteredIn(nullptr) {} - SwClient(SwClient&&) noexcept; - virtual ~SwClient() override; + public: + ClientBase() : m_pRegisteredIn(nullptr) {} + ClientBase(ClientBase&&) noexcept; + virtual ~ClientBase() override; - // in case an SwModify object is destroyed that itself is registered in another SwModify, - // its SwClient objects can decide to get registered to the latter instead by calling this method - std::optional<sw::ModifyChangedHint> CheckRegistration( const SfxPoolItem* pOldValue ); - // SwFormat wants to die different than the rest: It wants to reparent every client to its parent - // and then send a SwFormatChg hint. - void CheckRegistrationFormat(SwFormat& rOld); + // in case an SwModify object is destroyed that itself is registered in another SwModify, + // its SwClient objects can decide to get registered to the latter instead by calling this method + std::optional<sw::ModifyChangedHint> CheckRegistration( const SfxPoolItem* pOldValue ); - const SwModify* GetRegisteredIn() const { return m_pRegisteredIn; } - SwModify* GetRegisteredIn() { return m_pRegisteredIn; } - void EndListeningAll(); - void StartListeningToSameModifyAs(const SwClient&); + const T* GetRegisteredIn() const { return m_pRegisteredIn; } + T* GetRegisteredIn() { return m_pRegisteredIn; } + void EndListeningAll(); + void StartListeningToSameModifyAs(const ClientBase&); - // get information about attribute - virtual bool GetInfo( SwFindNearestNode& ) const { return true; } -}; + }; +} + +typedef sw::ClientBase<SwModify> SwClient; // SwModify @@ -181,14 +196,18 @@ class SW_DLLPUBLIC SwModify: public SwClient { friend class sw::ClientIteratorBase; friend void sw::ClientNotifyAttrChg(SwModify&, const SwAttrSet&, SwAttrSet&, SwAttrSet&); - template<typename E, typename S, sw::IteratorMode> friend class SwIterator; + template<typename E, typename S, sw::IteratorMode> friend class ::SwIterator; sw::WriterListener* m_pWriterListeners; // the start of the linked list of clients bool m_bModifyLocked; // don't broadcast changes now SwModify(SwModify const &) = delete; SwModify &operator =(const SwModify&) = delete; + void EnsureBroadcasting(); protected: virtual void SwClientNotify(const SwModify&, const SfxHint& rHint) override; + // SwFormat wants to die different than the rest: It wants to reparent every client to its parent + // and then send a SwFormatChg hint. + void PrepareFormatDeath(const SwFormatChangeHint&); public: SwModify() : SwClient(), m_pWriterListeners(nullptr), m_bModifyLocked(false) @@ -199,9 +218,11 @@ public: virtual ~SwModify() override; - void Add(SwClient& rDepend); - void Remove(SwClient& rDepend); + template<typename T> void Add(sw::ClientBase<T>& rDepend); + template<typename T> void Remove(sw::ClientBase<T>& rDepend); + void RemoveAllWriterListeners(); bool HasWriterListeners() const { return m_pWriterListeners; } + template<typename T> bool HasOnlySpecificWriterListeners() const; bool HasOnlyOneListener() const { return m_pWriterListeners && m_pWriterListeners->IsLast(); } // get information about attribute @@ -212,7 +233,6 @@ public: bool IsModifyLocked() const { return m_bModifyLocked; } }; -template<typename TElementType, typename TSource, sw::IteratorMode eMode> class SwIterator; namespace sw { @@ -276,8 +296,7 @@ namespace sw }; class ClientIteratorBase : public sw::Ring< ::sw::ClientIteratorBase > { - friend void SwModify::Remove(SwClient&); - friend void SwModify::Add(SwClient&); + friend class ::SwModify; protected: const SwModify& m_rRoot; // the current object in an iteration @@ -405,27 +424,99 @@ public: using sw::ClientIteratorBase::IsChanged; }; -template< typename TSource > class SwIterator<SwClient, TSource> final : private sw::ClientIteratorBase +template< typename T, typename TSource > class SwIterator<sw::ClientBase<T>, TSource> final : private sw::ClientIteratorBase { static_assert(std::is_base_of<SwModify,TSource>::value, "TSource needs to be derived from SwModify"); public: SwIterator( const TSource& rSrc ) : sw::ClientIteratorBase(rSrc) {} - SwClient* First() - { return static_cast<SwClient*>(GoStart()); } - SwClient* Next() + sw::ClientBase<T>* First() + { + auto pListener = GoStart(); + assert(dynamic_cast<sw::ClientBase<T>*>(pListener)); + return static_cast<sw::ClientBase<T>*>(pListener); + } + sw::ClientBase<T>* Next() { if(!IsChanged()) m_pPosition = GetRightOfPos(); - return static_cast<SwClient*>(Sync()); + auto pNext = Sync(); + assert(dynamic_cast<sw::ClientBase<T>*>(pNext)); + return static_cast<sw::ClientBase<T>*>(pNext); } using sw::ClientIteratorBase::IsChanged; }; -SwClient::SwClient( SwModify* pToRegisterIn ) - : m_pRegisteredIn( nullptr ) +template<typename T> +void SwModify::Add(sw::ClientBase<T>& rDepend) { - if(pToRegisterIn) - pToRegisterIn->Add(*this); + DBG_TESTSOLARMUTEX(); +#ifdef DBG_UTIL + EnsureBroadcasting(); + assert(dynamic_cast<T*>(this)); +#endif + + if (rDepend.GetRegisteredIn() == this) + return; + + // deregister new client in case it is already registered elsewhere + if( rDepend.GetRegisteredIn() != nullptr ) + rDepend.m_pRegisteredIn->Remove(rDepend); + + if( !m_pWriterListeners ) + { + // first client added + m_pWriterListeners = &rDepend; + m_pWriterListeners->m_pLeft = nullptr; + m_pWriterListeners->m_pRight = nullptr; + } + else + { + // append client + rDepend.m_pRight = m_pWriterListeners->m_pRight; + m_pWriterListeners->m_pRight = &rDepend; + rDepend.m_pLeft = m_pWriterListeners; + if( rDepend.m_pRight ) + rDepend.m_pRight->m_pLeft = &rDepend; + } + + // connect client to me + rDepend.m_pRegisteredIn = static_cast<T*>(this); } +template<typename T> +void SwModify::Remove(sw::ClientBase<T>& rDepend) +{ + DBG_TESTSOLARMUTEX(); + assert(rDepend.m_pRegisteredIn == this); + + // SwClient is my listener + // remove it from my list + ::sw::WriterListener* pR = rDepend.m_pRight; + ::sw::WriterListener* pL = rDepend.m_pLeft; + if( m_pWriterListeners == &rDepend ) + m_pWriterListeners = pL ? pL : pR; + + if( pL ) + pL->m_pRight = pR; + if( pR ) + pR->m_pLeft = pL; + + // update ClientIterators + if(sw::ClientIteratorBase::s_pClientIters) + { + for(auto& rIter : sw::ClientIteratorBase::s_pClientIters->GetRingContainer()) + { + if (&rIter.m_rRoot == this && + (rIter.m_pCurrent == &rDepend || rIter.m_pPosition == &rDepend)) + { + // if object being removed is the current or next object in an + // iterator, advance this iterator + rIter.m_pPosition = pR; + } + } + } + rDepend.m_pLeft = nullptr; + rDepend.m_pRight = nullptr; + rDepend.m_pRegisteredIn = nullptr; +} /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/inc/fmthdft.hxx b/sw/inc/fmthdft.hxx index 9a9b4c58e588..ab11891c54ec 100644 --- a/sw/inc/fmthdft.hxx +++ b/sw/inc/fmthdft.hxx @@ -25,12 +25,16 @@ #include "calbck.hxx" #include "frmfmt.hxx" +namespace sw { + typedef ClientBase<::SwFrameFormat> FrameFormatClient; +} + class IntlWrapper; /** Header, for PageFormats Client of FrameFormat describing the header. */ -class SW_DLLPUBLIC SwFormatHeader final : public SfxPoolItem, public SwClient +class SW_DLLPUBLIC SwFormatHeader final : public SfxPoolItem, public sw::FrameFormatClient { bool m_bActive; ///< Only for controlling (creation of content). @@ -52,10 +56,10 @@ public: OUString &rText, const IntlWrapper& rIntl ) const override; - const SwFrameFormat *GetHeaderFormat() const { return static_cast<const SwFrameFormat*>(GetRegisteredIn()); } - SwFrameFormat *GetHeaderFormat() { return static_cast<SwFrameFormat*>(GetRegisteredIn()); } + const SwFrameFormat *GetHeaderFormat() const { return GetRegisteredIn(); } + SwFrameFormat *GetHeaderFormat() { return GetRegisteredIn(); } - void RegisterToFormat( SwFormat& rFormat ); + void RegisterToFormat( SwFrameFormat& rFormat ); bool IsActive() const { return m_bActive; } void dumpAsXml(xmlTextWriterPtr pWriter) const override; }; @@ -63,7 +67,7 @@ public: /**Footer, for pageformats Client of FrameFormat describing the footer */ -class SW_DLLPUBLIC SwFormatFooter final : public SfxPoolItem, public SwClient +class SW_DLLPUBLIC SwFormatFooter final : public SfxPoolItem, public sw::FrameFormatClient { bool m_bActive; // Only for controlling (creation of content). @@ -85,8 +89,8 @@ public: OUString &rText, const IntlWrapper& rIntl ) const override; - const SwFrameFormat *GetFooterFormat() const { return static_cast<const SwFrameFormat*>(GetRegisteredIn()); } - SwFrameFormat *GetFooterFormat() { return static_cast<SwFrameFormat*>(GetRegisteredIn()); } + const SwFrameFormat *GetFooterFormat() const { return GetRegisteredIn(); } + SwFrameFormat *GetFooterFormat() { return GetRegisteredIn(); } void RegisterToFormat( SwFormat& rFormat ); bool IsActive() const { return m_bActive; } diff --git a/sw/inc/ring.hxx b/sw/inc/ring.hxx index 194ed47db10b..f9ee213fc6e8 100644 --- a/sw/inc/ring.hxx +++ b/sw/inc/ring.hxx @@ -19,6 +19,7 @@ #ifndef INCLUDED_SW_INC_RING_HXX #define INCLUDED_SW_INC_RING_HXX +#include <cassert> #include <utility> #include <sal/types.h> #include <iterator> diff --git a/sw/source/core/attr/calbck.cxx b/sw/source/core/attr/calbck.cxx index e4a1f437cef7..e7ce3bcb3c29 100644 --- a/sw/source/core/attr/calbck.cxx +++ b/sw/source/core/attr/calbck.cxx @@ -20,6 +20,8 @@ #include <algorithm> #include <format.hxx> +#include <frmfmt.hxx> +#include <frame.hxx> #include <hintids.hxx> #include <hints.hxx> #include <osl/diagnose.h> @@ -55,7 +57,8 @@ namespace sw sw::LegacyModifyHint::~LegacyModifyHint() {} -SwClient::SwClient(SwClient&& o) noexcept +template<typename T> +sw::ClientBase<T>::ClientBase(sw::ClientBase<T>&& o) noexcept : m_pRegisteredIn(nullptr) { if(o.m_pRegisteredIn) @@ -65,7 +68,8 @@ SwClient::SwClient(SwClient&& o) noexcept } } -SwClient::~SwClient() +template<typename T> +sw::ClientBase<T>::~ClientBase() { if(GetRegisteredIn()) DBG_TESTSOLARMUTEX(); @@ -74,7 +78,8 @@ SwClient::~SwClient() m_pRegisteredIn->Remove(*this); } -std::optional<sw::ModifyChangedHint> SwClient::CheckRegistration( const SfxPoolItem* pOld ) +template<typename T> +std::optional<sw::ModifyChangedHint> sw::ClientBase<T>::CheckRegistration( const SfxPoolItem* pOld ) { DBG_TESTSOLARMUTEX(); // this method only handles notification about dying SwModify objects @@ -104,18 +109,8 @@ std::optional<sw::ModifyChangedHint> SwClient::CheckRegistration( const SfxPoolI return sw::ModifyChangedHint(pAbove); } -void SwClient::CheckRegistrationFormat(SwFormat& rOld) -{ - assert(GetRegisteredIn() == &rOld); - auto pNew = rOld.DerivedFrom(); - SAL_INFO("sw.core", "reparenting " << typeid(*this).name() << " at " << this << " from " << typeid(rOld).name() << " at " << &rOld << " to " << typeid(*pNew).name() << " at " << pNew); - assert(pNew); - pNew->Add(*this); - const SwFormatChangeHint aHint(&rOld, pNew); - SwClientNotify(rOld, aHint); -} - -void SwClient::SwClientNotify(const SwModify&, const SfxHint& rHint) +template<typename T> +void sw::ClientBase<T>::SwClientNotify(const SwModify&, const SfxHint& rHint) { if (rHint.GetId() != SfxHintId::SwLegacyModify) return; @@ -123,7 +118,8 @@ void SwClient::SwClientNotify(const SwModify&, const SfxHint& rHint) CheckRegistration(pLegacyHint->m_pOld); }; -void SwClient::StartListeningToSameModifyAs(const SwClient& other) +template<typename T> +void sw::ClientBase<T>::StartListeningToSameModifyAs(const sw::ClientBase<T>& other) { if(other.m_pRegisteredIn) other.m_pRegisteredIn->Add(*this); @@ -131,12 +127,22 @@ void SwClient::StartListeningToSameModifyAs(const SwClient& other) EndListeningAll(); } -void SwClient::EndListeningAll() +template<typename T> +void sw::ClientBase<T>::EndListeningAll() { if(m_pRegisteredIn) m_pRegisteredIn->Remove(*this); } +template<typename T> +void sw::ClientBase<T>::RegisterIn(SwModify* pModify) +{ + if(pModify) + pModify->Add(*this); + else if(m_pRegisteredIn) + m_pRegisteredIn->Remove(*this); +} + SwModify::~SwModify() { DBG_TESTSOLARMUTEX(); @@ -156,97 +162,63 @@ SwModify::~SwModify() assert(!hasListenersOnDeath); } -bool SwModify::GetInfo( SwFindNearestNode& rInfo ) const -{ - if(!m_pWriterListeners) - return true; - SwIterator<SwClient,SwModify> aIter(*this); - for(SwClient* pClient = aIter.First(); pClient; pClient = aIter.Next()) - if(!pClient->GetInfo( rInfo )) - return false; - return true; +namespace { + class WriterListenerIterator final : private sw::ClientIteratorBase + { + public: + WriterListenerIterator(const SwModify& rSrc) : sw::ClientIteratorBase(rSrc) {} + sw::WriterListener* First() + { return GoStart(); } + sw::WriterListener* Next() + { + if(!IsChanged()) + m_pPosition = GetRightOfPos(); + return Sync(); + } + using sw::ClientIteratorBase::IsChanged; + }; } -void SwModify::Add(SwClient& rDepend) +void SwModify::PrepareFormatDeath(const SwFormatChangeHint& rHint) { - DBG_TESTSOLARMUTEX(); -#ifdef DBG_UTIL - // You should not EVER use SwModify directly in new code: - // - Preexisting SwModifys should only ever be used via sw::BroadcastingModify. - // This includes sw::BroadcastMixin, which is the long-term target (without - // SwModify). - // - New classes should use sw::BroadcastMixin alone. - if(!dynamic_cast<sw::BroadcastingModify*>(this)) + assert(rHint.m_pOldFormat == this); + assert(rHint.m_pNewFormat); + auto aIter = WriterListenerIterator(*this); + for(auto pListener = aIter.First(); pListener; pListener = aIter.Next()) { - auto pBT = sal::backtrace_get(20); - SAL_WARN("sw.core", "Modify that is not broadcasting used! " << sal::backtrace_to_string(pBT.get())); + 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); + pListener->RegisterIn(rHint.m_pNewFormat); + pListener->SwClientNotify(*this, rHint); } -#endif - - if (rDepend.m_pRegisteredIn == this) - return; - - // deregister new client in case it is already registered elsewhere - if( rDepend.m_pRegisteredIn != nullptr ) - rDepend.m_pRegisteredIn->Remove(rDepend); +} - if( !m_pWriterListeners ) - { - // first client added - m_pWriterListeners = &rDepend; - m_pWriterListeners->m_pLeft = nullptr; - m_pWriterListeners->m_pRight = nullptr; - } - else - { - // append client - rDepend.m_pRight = m_pWriterListeners->m_pRight; - m_pWriterListeners->m_pRight = &rDepend; - rDepend.m_pLeft = m_pWriterListeners; - if( rDepend.m_pRight ) - rDepend.m_pRight->m_pLeft = &rDepend; - } +template<typename T> +bool SwModify::HasOnlySpecificWriterListeners() const { + auto aIter = WriterListenerIterator(*this); + for(auto pListener = aIter.First(); pListener; pListener = aIter.Next()) + if(!dynamic_cast<T*>(pListener)) + return false; + return true; +} - // connect client to me - rDepend.m_pRegisteredIn = this; +void SwModify::RemoveAllWriterListeners() +{ + while(m_pWriterListeners) + m_pWriterListeners->RegisterIn(nullptr); } -void SwModify::Remove(SwClient& rDepend) +bool SwModify::GetInfo( SwFindNearestNode& rInfo ) const { - DBG_TESTSOLARMUTEX(); - assert(rDepend.m_pRegisteredIn == this); - - // SwClient is my listener - // remove it from my list - ::sw::WriterListener* pR = rDepend.m_pRight; - ::sw::WriterListener* pL = rDepend.m_pLeft; - if( m_pWriterListeners == &rDepend ) - m_pWriterListeners = pL ? pL : pR; - - if( pL ) - pL->m_pRight = pR; - if( pR ) - pR->m_pLeft = pL; - - // update ClientIterators - if(sw::ClientIteratorBase::s_pClientIters) - { - for(auto& rIter : sw::ClientIteratorBase::s_pClientIters->GetRingContainer()) - { - if (&rIter.m_rRoot == this && - (rIter.m_pCurrent == &rDepend || rIter.m_pPosition == &rDepend)) - { - // if object being removed is the current or next object in an - // iterator, advance this iterator - rIter.m_pPosition = pR; - } - } - } - rDepend.m_pLeft = nullptr; - rDepend.m_pRight = nullptr; - rDepend.m_pRegisteredIn = nullptr; + if(!m_pWriterListeners) + return true; + auto aIter = WriterListenerIterator(*this); + for(auto pListener = aIter.First(); pListener; pListener = aIter.Next()) + if(!pListener->GetInfo( rInfo )) + return false; + return true; } + sw::WriterMultiListener::WriterMultiListener(SwClient& rToTell) : m_rToTell(rToTell) {} @@ -304,9 +276,25 @@ void SwModify::SwClientNotify(const SwModify&, const SfxHint& rHint) void SwModify::CallSwClientNotify( const SfxHint& rHint ) const { DBG_TESTSOLARMUTEX(); - SwIterator<SwClient,SwModify> aIter(*this); - for(SwClient* pClient = aIter.First(); pClient; pClient = aIter.Next()) - pClient->SwClientNotify( *this, rHint ); + auto aIter = WriterListenerIterator(*this); + for(auto pListener = aIter.First(); pListener; pListener = aIter.Next()) + pListener->SwClientNotify(*this, rHint); +} + +void SwModify::EnsureBroadcasting() +{ +#ifdef DBG_UTIL + // You should not EVER use SwModify directly in new code: + // - Preexisting SwModifys should only ever be used via sw::BroadcastingModify. + // This includes sw::BroadcastMixin, which is the long-term target (without + // SwModify). + // - New classes should use sw::BroadcastMixin alone. + if(!dynamic_cast<sw::BroadcastingModify*>(this)) + { + auto pBT = sal::backtrace_get(20); + SAL_WARN("sw.core", "Modify that is not broadcasting used! " << sal::backtrace_to_string(pBT.get())); + } +#endif } void sw::BroadcastingModify::CallSwClientNotify(const SfxHint& rHint) const @@ -322,4 +310,8 @@ void sw::ClientNotifyAttrChg(SwModify& rModify, const SwAttrSet& aSet, SwAttrSet const sw::LegacyModifyHint aHint(&aChgOld, &aChgNew); rModify.SwClientNotify(rModify, aHint); } + +template class sw::ClientBase<SwModify>; +template class sw::ClientBase<SwFrameFormat>; +template bool SwModify::HasOnlySpecificWriterListeners<SwFrame>() const; /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/attr/format.cxx b/sw/source/core/attr/format.cxx index 5c4683a07897..d844b616fc43 100644 --- a/sw/source/core/attr/format.cxx +++ b/sw/source/core/attr/format.cxx @@ -196,9 +196,7 @@ void SwFormat::Destr() << GetName()); return; } - SwIterator<SwClient, SwFormat> aIter(*this); - for (SwClient* pClient = aIter.First(); pClient; pClient = aIter.Next()) - pClient->CheckRegistrationFormat(*this); + PrepareFormatDeath(SwFormatChangeHint(this, DerivedFrom())); assert(!HasWriterListeners()); } diff --git a/sw/source/core/doc/docdesc.cxx b/sw/source/core/doc/docdesc.cxx index b03a3558baca..04c318cdbff1 100644 --- a/sw/source/core/doc/docdesc.cxx +++ b/sw/source/core/doc/docdesc.cxx @@ -458,7 +458,7 @@ void SwDoc::ChgPageDesc( size_t i, const SwPageDesc &rChged ) rDesc.GetLeft().ResetFormatAttr(RES_FOOTER); rDesc.GetFirstLeft().ResetFormatAttr(RES_FOOTER); - auto lDelHFFormat = [this](SwClient* pToRemove, SwFrameFormat* pFormat) + auto lDelHFFormat = [this](sw::ClientBase<SwFrameFormat>* pToRemove, SwFrameFormat* pFormat) { // Code taken from lcl_DelHFFormat pFormat->Remove(*pToRemove); diff --git a/sw/source/core/doc/docfmt.cxx b/sw/source/core/doc/docfmt.cxx index 4945c9f948e8..3a885ef40270 100644 --- a/sw/source/core/doc/docfmt.cxx +++ b/sw/source/core/doc/docfmt.cxx @@ -666,10 +666,7 @@ void SwDoc::SetDefault( const SfxItemSet& rSet ) } // remove the default formats from the object again - SwIterator<SwClient, sw::BroadcastingModify> aClientIter(aCallMod); - for(SwClient* pClient = aClientIter.First(); pClient; pClient = aClientIter.Next()) - aCallMod.Remove(*pClient); - + aCallMod.RemoveAllWriterListeners(); getIDocumentState().SetModified(); } diff --git a/sw/source/core/layout/atrfrm.cxx b/sw/source/core/layout/atrfrm.cxx index c8c10a73661a..7cdc5f51b643 100644 --- a/sw/source/core/layout/atrfrm.cxx +++ b/sw/source/core/layout/atrfrm.cxx @@ -134,8 +134,10 @@ bool GetAtPageRelOrientation(sal_Int16 & rOrientation, bool const isIgnorePrintA } } + } // namespace sw + SfxPoolItem* SwFormatLineNumber::CreateDefault() { return new SwFormatLineNumber; } static sal_Int16 lcl_IntToRelation(const uno::Any& rVal) @@ -146,7 +148,7 @@ static sal_Int16 lcl_IntToRelation(const uno::Any& rVal) return nVal; } -static void lcl_DelHFFormat( SwClient *pToRemove, SwFrameFormat *pFormat ) +static void lcl_DelHFFormat( sw::FrameFormatClient *pToRemove, SwFrameFormat *pFormat ) { //If the client is the last one who uses this format, then we have to delete //it - before this is done, we may need to delete the content-section. @@ -159,17 +161,7 @@ static void lcl_DelHFFormat( SwClient *pToRemove, SwFrameFormat *pFormat ) } // Anything other than frames registered? - bool bDel = true; - { - // nested scope because DTOR of SwClientIter resets the flag bTreeChg. - // It's suboptimal if the format is deleted beforehand. - SwIterator<SwClient,SwFrameFormat> aIter(*pFormat); - for(SwClient* pLast = aIter.First(); bDel && pLast; pLast = aIter.Next()) - if (dynamic_cast<const SwFrame*>(pLast) == nullptr) - bDel = false; - } - - if ( !bDel ) + if(!pFormat->HasOnlySpecificWriterListeners<SwFrame>()) return; // If there is a Cursor registered in one of the nodes, we need to call the @@ -509,21 +501,21 @@ sal_uInt16 SwFormatFillOrder::GetValueCount() const // Partially implemented inline in hxx SwFormatHeader::SwFormatHeader( SwFrameFormat *pHeaderFormat ) : SfxPoolItem( RES_HEADER ), - SwClient( pHeaderFormat ), + sw::FrameFormatClient( pHeaderFormat ), m_bActive( pHeaderFormat ) { } SwFormatHeader::SwFormatHeader( const SwFormatHeader &rCpy ) : SfxPoolItem( RES_HEADER ), - SwClient( const_cast<sw::BroadcastingModify*>(static_cast<const sw::BroadcastingModify*>(rCpy.GetRegisteredIn())) ), + sw::FrameFormatClient( const_cast<SwFrameFormat*>(rCpy.GetRegisteredIn()) ), m_bActive( rCpy.IsActive() ) { } SwFormatHeader::SwFormatHeader( bool bOn ) : SfxPoolItem( RES_HEADER ), - SwClient( nullptr ), + sw::FrameFormatClient( nullptr ), m_bActive( bOn ) { } @@ -546,7 +538,7 @@ SwFormatHeader* SwFormatHeader::Clone( SfxItemPool* ) const return new SwFormatHeader( *this ); } -void SwFormatHeader::RegisterToFormat( SwFormat& rFormat ) +void SwFormatHeader::RegisterToFormat( SwFrameFormat& rFormat ) { rFormat.Add(*this); } @@ -568,21 +560,21 @@ void SwFormatHeader::dumpAsXml(xmlTextWriterPtr pWriter) const // Partially implemented inline in hxx SwFormatFooter::SwFormatFooter( SwFrameFormat *pFooterFormat ) : SfxPoolItem( RES_FOOTER ), - SwClient( pFooterFormat ), + sw::FrameFormatClient( pFooterFormat ), m_bActive( pFooterFormat ) { } SwFormatFooter::SwFormatFooter( const SwFormatFooter &rCpy ) : SfxPoolItem( RES_FOOTER ), - SwClient( const_cast<sw::BroadcastingModify*>(static_cast<const sw::BroadcastingModify*>(rCpy.GetRegisteredIn())) ), + sw::FrameFormatClient( const_cast<SwFrameFormat*>(rCpy.GetRegisteredIn()) ), m_bActive( rCpy.IsActive() ) { } SwFormatFooter::SwFormatFooter( bool bOn ) : SfxPoolItem( RES_FOOTER ), - SwClient( nullptr ), + sw::FrameFormatClient( nullptr ), m_bActive( bOn ) { }