diff options
-rw-r--r-- | sw/inc/calbck.hxx | 56 | ||||
-rw-r--r-- | sw/qa/core/uwriter.cxx | 112 | ||||
-rw-r--r-- | sw/source/core/attr/calbck.cxx | 70 |
3 files changed, 180 insertions, 58 deletions
diff --git a/sw/inc/calbck.hxx b/sw/inc/calbck.hxx index d42f1e95dbd0..7b903c2c9638 100644 --- a/sw/inc/calbck.hxx +++ b/sw/inc/calbck.hxx @@ -26,6 +26,8 @@ #include "ring.hxx" #include "hintids.hxx" #include <type_traits> +#include <list> +#include <memory> class SwModify; @@ -69,6 +71,12 @@ namespace sw const SfxPoolItem* m_pOld; const SfxPoolItem* m_pNew; }; + struct SW_DLLPUBLIC ModifyChangedHint final: SfxHint + { + ModifyChangedHint(const SwModify* pNew) : m_pNew(pNew) {}; + virtual ~ModifyChangedHint() override; + const SwModify* m_pNew; + }; /// refactoring out the some of the more sane SwClient functionality class SW_DLLPUBLIC WriterListener { @@ -104,6 +112,7 @@ class SW_DLLPUBLIC SwClient : public ::sw::WriterListener protected: // single argument ctors shall be explicit. inline explicit SwClient( SwModify* pToRegisterIn ); + SwClient(SwClient&&); // write access to pRegisteredIn shall be granted only to the object itself (protected access) SwModify* GetRegisteredInNonConst() const { return m_pRegisteredIn; } @@ -122,7 +131,7 @@ public: // 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 - void CheckRegistration( const SfxPoolItem *pOldValue ); + std::unique_ptr<sw::ModifyChangedHint> CheckRegistration( const SfxPoolItem* pOldValue ); // controlled access to Modify method // mba: this is still considered a hack and it should be fixed; the name makes grep-ing easier @@ -212,6 +221,12 @@ class SW_DLLPUBLIC SwDepend final : public SwClient public: SwDepend(SwClient *pTellHim, SwModify *pDepend) : SwClient(pDepend), m_pToTell(pTellHim) {} + SwDepend(SwDepend&) = delete; + SwDepend(SwDepend&& o) + : SwClient(std::move(o)), m_pToTell(o.m_pToTell) + { + o.m_pToTell = nullptr; + } SwClient* GetToTell() { return m_pToTell; } @@ -221,17 +236,46 @@ public: private: virtual void Modify( const SfxPoolItem* pOldValue, const SfxPoolItem *pNewValue ) override { - if( pNewValue && pNewValue->Which() == RES_OBJECTDYING ) - CheckRegistration(pOldValue); - else if( m_pToTell ) - m_pToTell->ModifyNotification(pOldValue, pNewValue); + SwClientNotify(*GetRegisteredIn(), sw::LegacyModifyHint(pOldValue, pNewValue)); } virtual void SwClientNotify( const SwModify& rModify, const SfxHint& rHint ) override - { if(m_pToTell) m_pToTell->SwClientNotifyCall(rModify, rHint); } + { + if (auto pLegacyHint = dynamic_cast<const sw::LegacyModifyHint*>(&rHint)) + { + if( pLegacyHint->m_pNew && pLegacyHint->m_pNew->Which() == RES_OBJECTDYING ) + { + auto pModifyChanged = CheckRegistration(pLegacyHint->m_pOld); + if(pModifyChanged) + m_pToTell->SwClientNotify(rModify, *pModifyChanged); + } + else if( m_pToTell ) + m_pToTell->ModifyNotification(pLegacyHint->m_pOld, pLegacyHint->m_pNew); + } + else if(m_pToTell) + m_pToTell->SwClientNotifyCall(rModify, rHint); + } }; + namespace sw { + class SW_DLLPUBLIC WriterMultiListener final + { + #ifdef WNT + typedef std::shared_ptr<SwDepend> pointer_t; + #else + typedef std::unique_ptr<SwDepend> pointer_t; + #endif + SwClient& m_rToTell; + std::list<pointer_t> m_vDepends; + public: + WriterMultiListener(SwClient& rToTell) + : m_rToTell(rToTell) {} + void StartListening(SwModify* pDepend); + void EndListening(SwModify* pDepend); + bool IsListeningTo(const SwModify* const pDepend); + void EndListeningAll(); + }; class ClientIteratorBase : public sw::Ring< ::sw::ClientIteratorBase > { friend SwClient* SwModify::Remove(SwClient*); diff --git a/sw/qa/core/uwriter.cxx b/sw/qa/core/uwriter.cxx index 6bc47bdc3df1..f642735c33c1 100644 --- a/sw/qa/core/uwriter.cxx +++ b/sw/qa/core/uwriter.cxx @@ -116,6 +116,7 @@ public: void testFormulas(); void testIntrusiveRing(); void testClientModify(); + void testWriterMultiListener(); void test64kPageDescs(); void testTdf92308(); void testTableCellComparison(); @@ -152,6 +153,7 @@ public: CPPUNIT_TEST(testFormulas); CPPUNIT_TEST(testIntrusiveRing); CPPUNIT_TEST(testClientModify); + CPPUNIT_TEST(testWriterMultiListener); CPPUNIT_TEST(test64kPageDescs); CPPUNIT_TEST(testTdf92308); CPPUNIT_TEST(testTableCellComparison); @@ -1775,24 +1777,30 @@ namespace { int m_nModifyCount; int m_nNotifyCount; - TestClient() : m_nModifyCount(0), m_nNotifyCount(0) {}; + int m_nModifyChangedCount; + const SwModify* m_pLastChangedModify; + TestClient() : m_nModifyCount(0), m_nNotifyCount(0), m_nModifyChangedCount(0), m_pLastChangedModify(nullptr) {}; virtual void Modify( const SfxPoolItem*, const SfxPoolItem*) override - { ++m_nModifyCount; } - virtual void SwClientNotify(const SwModify& rModify, const SfxHint& rHint) override + { assert(false); } + virtual void SwClientNotify(const SwModify&, const SfxHint& rHint) override { if(typeid(TestHint) == typeid(rHint)) ++m_nNotifyCount; - else - SwClient::SwClientNotify(rModify, rHint); + else if(dynamic_cast<const sw::LegacyModifyHint*>(&rHint)) + ++m_nModifyCount; + else if(auto pModifyChangedHint = dynamic_cast<const sw::ModifyChangedHint*>(&rHint)) + { + ++m_nModifyChangedCount; + m_pLastChangedModify = pModifyChangedHint->m_pNew; + } } }; - // sad copypasta as tools/rtti.hxx little brain can't cope with templates struct OtherTestClient : SwClient { int m_nModifyCount; OtherTestClient() : m_nModifyCount(0) {}; virtual void Modify( const SfxPoolItem*, const SfxPoolItem*) override - { ++m_nModifyCount; } + { ++m_nModifyCount; } }; } void SwDocTest::testClientModify() @@ -1804,36 +1812,36 @@ void SwDocTest::testClientModify() // test client registration CPPUNIT_ASSERT(!aMod.HasWriterListeners()); CPPUNIT_ASSERT(!aMod.HasOnlyOneListener()); - CPPUNIT_ASSERT_EQUAL(aClient1.GetRegisteredIn(),static_cast<SwModify*>(nullptr)); - CPPUNIT_ASSERT_EQUAL(aClient2.GetRegisteredIn(),static_cast<SwModify*>(nullptr)); - CPPUNIT_ASSERT_EQUAL(aClient2.GetRegisteredIn(),static_cast<SwModify*>(nullptr)); + CPPUNIT_ASSERT_EQUAL(static_cast<SwModify*>(nullptr),aClient1.GetRegisteredIn()); + CPPUNIT_ASSERT_EQUAL(static_cast<SwModify*>(nullptr),aClient2.GetRegisteredIn()); + CPPUNIT_ASSERT_EQUAL(static_cast<SwModify*>(nullptr),aClient2.GetRegisteredIn()); aMod.Add(&aClient1); CPPUNIT_ASSERT(aMod.HasWriterListeners()); CPPUNIT_ASSERT(aMod.HasOnlyOneListener()); aMod.Add(&aClient2); - CPPUNIT_ASSERT_EQUAL(aClient1.GetRegisteredIn(),static_cast<SwModify*>(&aMod)); - CPPUNIT_ASSERT_EQUAL(aClient2.GetRegisteredIn(),static_cast<SwModify*>(&aMod)); + CPPUNIT_ASSERT_EQUAL(static_cast<SwModify*>(&aMod),aClient1.GetRegisteredIn()); + CPPUNIT_ASSERT_EQUAL(static_cast<SwModify*>(&aMod), aClient2.GetRegisteredIn()); CPPUNIT_ASSERT(aMod.HasWriterListeners()); CPPUNIT_ASSERT(!aMod.HasOnlyOneListener()); // test broadcast aMod.ModifyBroadcast(nullptr, nullptr); - CPPUNIT_ASSERT_EQUAL(aClient1.m_nModifyCount,1); - CPPUNIT_ASSERT_EQUAL(aClient2.m_nModifyCount,1); - CPPUNIT_ASSERT_EQUAL(aClient1.m_nNotifyCount,0); - CPPUNIT_ASSERT_EQUAL(aClient2.m_nNotifyCount,0); + CPPUNIT_ASSERT_EQUAL(1,aClient1.m_nModifyCount); + CPPUNIT_ASSERT_EQUAL(1,aClient2.m_nModifyCount); + CPPUNIT_ASSERT_EQUAL(0,aClient1.m_nNotifyCount); + CPPUNIT_ASSERT_EQUAL(0,aClient2.m_nNotifyCount); aMod.ModifyBroadcast(nullptr, nullptr); - CPPUNIT_ASSERT_EQUAL(aClient1.m_nModifyCount,2); - CPPUNIT_ASSERT_EQUAL(aClient2.m_nModifyCount,2); - CPPUNIT_ASSERT_EQUAL(aClient1.m_nNotifyCount,0); - CPPUNIT_ASSERT_EQUAL(aClient2.m_nNotifyCount,0); + CPPUNIT_ASSERT_EQUAL(2,aClient1.m_nModifyCount); + CPPUNIT_ASSERT_EQUAL(2,aClient2.m_nModifyCount); + CPPUNIT_ASSERT_EQUAL(0,aClient1.m_nNotifyCount); + CPPUNIT_ASSERT_EQUAL(0,aClient2.m_nNotifyCount); // test notify { TestHint aHint; aMod.CallSwClientNotify(aHint); - CPPUNIT_ASSERT_EQUAL(aClient1.m_nModifyCount,2); - CPPUNIT_ASSERT_EQUAL(aClient2.m_nModifyCount,2); - CPPUNIT_ASSERT_EQUAL(aClient1.m_nNotifyCount,1); - CPPUNIT_ASSERT_EQUAL(aClient2.m_nNotifyCount,1); + CPPUNIT_ASSERT_EQUAL(2,aClient1.m_nModifyCount); + CPPUNIT_ASSERT_EQUAL(2,aClient2.m_nModifyCount); + CPPUNIT_ASSERT_EQUAL(1,aClient1.m_nNotifyCount); + CPPUNIT_ASSERT_EQUAL(1,aClient2.m_nNotifyCount); } // test typed iteration CPPUNIT_ASSERT(typeid(aClient1) != typeid(OtherTestClient)); @@ -1847,28 +1855,28 @@ void SwDocTest::testClientModify() SwIterator<TestClient,SwModify> aIter(aMod); for(TestClient* pClient = aIter.First(); pClient ; pClient = aIter.Next()) { - CPPUNIT_ASSERT_EQUAL(pClient->m_nModifyCount,2); + CPPUNIT_ASSERT_EQUAL(2,pClient->m_nModifyCount); ++nCount; } - CPPUNIT_ASSERT_EQUAL(nCount,2); + CPPUNIT_ASSERT_EQUAL(2,nCount); } aMod.Add(&aOtherClient1); - CPPUNIT_ASSERT_EQUAL(aOtherClient1.m_nModifyCount,0); + CPPUNIT_ASSERT_EQUAL(0,aOtherClient1.m_nModifyCount); { int nCount = 0; SwIterator<TestClient,SwModify> aIter(aMod); for(TestClient* pClient = aIter.First(); pClient ; pClient = aIter.Next()) { - CPPUNIT_ASSERT_EQUAL(pClient->m_nModifyCount,2); + CPPUNIT_ASSERT_EQUAL(2,pClient->m_nModifyCount); ++nCount; } - CPPUNIT_ASSERT_EQUAL(nCount,2); + CPPUNIT_ASSERT_EQUAL(2,nCount); } - CPPUNIT_ASSERT_EQUAL(aOtherClient1.m_nModifyCount,0); + CPPUNIT_ASSERT_EQUAL(0,aOtherClient1.m_nModifyCount); aMod.Remove(&aOtherClient1); - CPPUNIT_ASSERT_EQUAL(aClient1.GetRegisteredIn(),static_cast<SwModify*>(&aMod)); - CPPUNIT_ASSERT_EQUAL(aClient2.GetRegisteredIn(),static_cast<SwModify*>(&aMod)); - CPPUNIT_ASSERT_EQUAL(aOtherClient1.GetRegisteredIn(),static_cast<SwModify*>(nullptr)); + CPPUNIT_ASSERT_EQUAL(static_cast<SwModify*>(&aMod),aClient1.GetRegisteredIn()); + CPPUNIT_ASSERT_EQUAL(static_cast<SwModify*>(&aMod),aClient2.GetRegisteredIn()); + CPPUNIT_ASSERT_EQUAL(static_cast<SwModify*>(nullptr),aOtherClient1.GetRegisteredIn()); // test client self-deregistration during iteration { int nCount = 0; @@ -1878,10 +1886,10 @@ void SwDocTest::testClientModify() aMod.Remove(pClient); ++nCount; } - CPPUNIT_ASSERT_EQUAL(nCount,2); + CPPUNIT_ASSERT_EQUAL(2,nCount); } - CPPUNIT_ASSERT_EQUAL(aClient1.GetRegisteredIn(), static_cast<SwModify*>(nullptr)); - CPPUNIT_ASSERT_EQUAL(aClient2.GetRegisteredIn(), static_cast<SwModify*>(nullptr)); + CPPUNIT_ASSERT_EQUAL(static_cast<SwModify*>(nullptr),aClient1.GetRegisteredIn()); + CPPUNIT_ASSERT_EQUAL(static_cast<SwModify*>(nullptr),aClient2.GetRegisteredIn()); { SwIterator<TestClient,SwModify> aIter(aMod); for(TestClient* pClient = aIter.First(); pClient ; pClient = aIter.Next()) @@ -1890,10 +1898,34 @@ void SwDocTest::testClientModify() } } aMod.ModifyBroadcast(nullptr, nullptr); - CPPUNIT_ASSERT_EQUAL(aClient1.m_nModifyCount,2); - CPPUNIT_ASSERT_EQUAL(aClient2.m_nModifyCount,2); - CPPUNIT_ASSERT_EQUAL(aClient1.m_nNotifyCount,1); - CPPUNIT_ASSERT_EQUAL(aClient2.m_nNotifyCount,1); + CPPUNIT_ASSERT_EQUAL(2,aClient1.m_nModifyCount); + CPPUNIT_ASSERT_EQUAL(2,aClient2.m_nModifyCount); + CPPUNIT_ASSERT_EQUAL(1,aClient1.m_nNotifyCount); + CPPUNIT_ASSERT_EQUAL(1,aClient2.m_nNotifyCount); +} +void SwDocTest::testWriterMultiListener() +{ + TestModify aMod; + TestClient aClient; + sw::WriterMultiListener aMulti(aClient); + CPPUNIT_ASSERT(!aMulti.IsListeningTo(&aMod)); + aMulti.StartListening(&aMod); + CPPUNIT_ASSERT(aMulti.IsListeningTo(&aMod)); + aMulti.EndListeningAll(); + CPPUNIT_ASSERT(!aMulti.IsListeningTo(&aMod)); + aMulti.StartListening(&aMod); + aMulti.EndListening(&aMod); + CPPUNIT_ASSERT(!aMulti.IsListeningTo(&aMod)); + int nPreDeathChangedCount; + { + TestModify aTempMod; + aMod.Add(&aTempMod); + aMulti.StartListening(&aTempMod); + nPreDeathChangedCount = aClient.m_nModifyChangedCount; + } + CPPUNIT_ASSERT(aMulti.IsListeningTo(&aMod)); + CPPUNIT_ASSERT_EQUAL(nPreDeathChangedCount+1, aClient.m_nModifyChangedCount); + CPPUNIT_ASSERT_EQUAL(static_cast<const SwModify*>(&aMod),aClient.m_pLastChangedModify); } void SwDocTest::test64kPageDescs() diff --git a/sw/source/core/attr/calbck.cxx b/sw/source/core/attr/calbck.cxx index 98376e649a93..a0f82b381841 100644 --- a/sw/source/core/attr/calbck.cxx +++ b/sw/source/core/attr/calbck.cxx @@ -23,8 +23,20 @@ #include <swcache.hxx> #include <swfntcch.hxx> #include <tools/debug.hxx> +#include <algorithm> sw::LegacyModifyHint::~LegacyModifyHint() {} +sw::ModifyChangedHint::~ModifyChangedHint() {} + + +SwClient::SwClient(SwClient&& o) +{ + if(o.m_pRegisteredIn) + { + o.m_pRegisteredIn->Add(this); + o.EndListeningAll(); + } +} SwClient::~SwClient() @@ -36,28 +48,33 @@ SwClient::~SwClient() m_pRegisteredIn->Remove( this ); } -void SwClient::CheckRegistration( const SfxPoolItem* pOld ) +std::unique_ptr<sw::ModifyChangedHint> SwClient::CheckRegistration( const SfxPoolItem* pOld ) { DBG_TESTSOLARMUTEX(); // this method only handles notification about dying SwModify objects if( !pOld || pOld->Which() != RES_OBJECTDYING ) - return; + return nullptr; const SwPtrMsgPoolItem* pDead = static_cast<const SwPtrMsgPoolItem*>(pOld); - if(pDead && pDead->pObject == m_pRegisteredIn) + if(!pDead || pDead->pObject != m_pRegisteredIn) + { + // we should only care received death notes from objects we are following + return nullptr; + } + // I've got a notification from the object I know + SwModify* pAbove = m_pRegisteredIn->GetRegisteredIn(); + if(pAbove) + { + // if the dying object itself was listening at an SwModify, I take over + // adding myself to pAbove will automatically remove me from my current pRegisteredIn + pAbove->Add(this); + } + else { - // I've got a notification from the object I know - SwModify* pAbove = m_pRegisteredIn->GetRegisteredIn(); - if(pAbove) - { - // if the dying object itself was listening at an SwModify, I take over - // adding myself to pAbove will automatically remove me from my current pRegisteredIn - pAbove->Add(this); - return; - } // destroy connection EndListeningAll(); } + return std::unique_ptr<sw::ModifyChangedHint>(new sw::ModifyChangedHint(pAbove)); } void SwClient::SwClientNotify(const SwModify&, const SfxHint& rHint) @@ -280,5 +297,34 @@ void SwModify::CheckCaching( const sal_uInt16 nWhich ) } } +void sw::WriterMultiListener::StartListening(SwModify* pDepend) +{ + EndListening(nullptr); + m_vDepends.emplace_back(pointer_t( new SwDepend(&m_rToTell, pDepend))); +} + + +bool sw::WriterMultiListener::IsListeningTo(const SwModify* const pBroadcaster) +{ + return std::any_of(m_vDepends.begin(), m_vDepends.end(), + [&pBroadcaster](const pointer_t& pListener) + { + return pListener->GetRegisteredIn() == pBroadcaster; + }); +} + +void sw::WriterMultiListener::EndListening(SwModify* pBroadcaster) +{ + m_vDepends.remove_if( [&pBroadcaster](const pointer_t& pListener) + { + return pListener->GetRegisteredIn() == nullptr || pListener->GetRegisteredIn() == pBroadcaster; + }); +} + +void sw::WriterMultiListener::EndListeningAll() +{ + m_vDepends.clear(); +} + sw::ClientIteratorBase* sw::ClientIteratorBase::our_pClientIters = nullptr; /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |