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 )
 {
 }

Reply via email to