Skip to content

Commit

Permalink
stronger typing for SwClient::GetRegisteredIn
Browse files Browse the repository at this point in the history
- introduce sw::ClientBase<> which knows what kind of SwModify it is a
  client of
  * unfortunately, while this is implemented as a statically typed
    template class, it is only ensured at runtime by an assertion
    in SwModify::Add()
  * still I think it is better than not trying to have any typing at all
    between SwClient and SwModify
- thus make SwClient a typedef of sw::ClientBase<SwModify>: a generic
  client of any kind of SwModify
- introducing sw::FrameFormatClient as an SwClient only listing to
  SwFrameFormats as a first example use in headers and footers.

Change-Id: Id2dc6d6acb34854be779c8bc4252c836db91bb6a
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/179307
Tested-by: Jenkins
Reviewed-by: Bjoern Michaelsen <[email protected]>
  • Loading branch information
bjoernmichaelsen committed Dec 27, 2024
1 parent 4911fc7 commit 687080c
Show file tree
Hide file tree
Showing 6 changed files with 182 additions and 147 deletions.
170 changes: 125 additions & 45 deletions sw/inc/calbck.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -64,10 +65,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
{
Expand Down Expand Up @@ -126,52 +135,58 @@ namespace sw
virtual const SwRowFrame* DynCastRowFrame() const { return nullptr; }
virtual const SwTable* DynCastTable() const { return nullptr; }
};
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

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 );
// 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);

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; }
};
// get information about attribute
virtual bool GetInfo( SwFindNearestNode& ) const { return true; }
};
}

typedef sw::ClientBase<SwModify> SwClient;


// SwModify
Expand All @@ -181,12 +196,13 @@ 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;
public:
Expand All @@ -199,8 +215,8 @@ 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);
bool HasWriterListeners() const { return m_pWriterListeners; }
bool HasOnlyOneListener() const { return m_pWriterListeners && m_pWriterListeners->IsLast(); }

Expand All @@ -212,7 +228,6 @@ public:
bool IsModifyLocked() const { return m_bModifyLocked; }
};

template<typename TElementType, typename TSource, sw::IteratorMode eMode> class SwIterator;

namespace sw
{
Expand Down Expand Up @@ -276,8 +291,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
Expand Down Expand Up @@ -421,11 +435,77 @@ public:
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: */
18 changes: 11 additions & 7 deletions sw/inc/fmthdft.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -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).

Expand All @@ -51,18 +55,18 @@ 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;
};

/**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).

Expand All @@ -83,8 +87,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; }
Expand Down
1 change: 1 addition & 0 deletions sw/inc/ring.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
Loading

0 comments on commit 687080c

Please sign in to comment.