diff options
Diffstat (limited to 'sw/source/core/attr/calbck.cxx')
-rw-r--r-- | sw/source/core/attr/calbck.cxx | 381 |
1 files changed, 381 insertions, 0 deletions
diff --git a/sw/source/core/attr/calbck.cxx b/sw/source/core/attr/calbck.cxx new file mode 100644 index 000000000..cef9b02b0 --- /dev/null +++ b/sw/source/core/attr/calbck.cxx @@ -0,0 +1,381 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <frame.hxx> +#include <hintids.hxx> +#include <hints.hxx> +#include <swcache.hxx> +#include <swfntcch.hxx> +#include <tools/debug.hxx> +#include <sal/log.hxx> +#include <algorithm> + +namespace sw +{ + bool ListenerEntry::GetInfo(SfxPoolItem& rInfo) const + { return m_pToTell == nullptr || m_pToTell->GetInfo( rInfo ); } + void ListenerEntry::Modify(const SfxPoolItem *const pOldValue, + const SfxPoolItem *const pNewValue) + { + SwClientNotify(*GetRegisteredIn(), sw::LegacyModifyHint(pOldValue, pNewValue)); + } + void ListenerEntry::SwClientNotify(const SwModify& rModify, const SfxHint& 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->SwClientNotifyCall(rModify, rHint); + } + else if (m_pToTell) + m_pToTell->SwClientNotifyCall(rModify, rHint); + } +} + +sw::LegacyModifyHint::~LegacyModifyHint() {} +sw::ModifyChangedHint::~ModifyChangedHint() {} + +SwClient::SwClient(SwClient&& o) noexcept + : m_pRegisteredIn(nullptr) +{ + if(o.m_pRegisteredIn) + { + o.m_pRegisteredIn->Add(this); + o.EndListeningAll(); + } +} + +SwClient::~SwClient() +{ + if(GetRegisteredIn()) + DBG_TESTSOLARMUTEX(); + OSL_ENSURE( !m_pRegisteredIn || m_pRegisteredIn->HasWriterListeners(), "SwModify still known, but Client already disconnected!" ); + if( m_pRegisteredIn && m_pRegisteredIn->HasWriterListeners() ) + m_pRegisteredIn->Remove( this ); +} + +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 nullptr; + + assert(dynamic_cast<const SwPtrMsgPoolItem*>(pOld)); + const SwPtrMsgPoolItem* pDead = static_cast<const SwPtrMsgPoolItem*>(pOld); + if(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 + { + // destroy connection + EndListeningAll(); + } + return std::unique_ptr<sw::ModifyChangedHint>(new sw::ModifyChangedHint(pAbove)); +} + +void SwClient::SwClientNotify(const SwModify&, const SfxHint& rHint) +{ + if (auto pLegacyHint = dynamic_cast<const sw::LegacyModifyHint*>(&rHint)) + { + Modify(pLegacyHint->m_pOld, pLegacyHint->m_pNew); + } +}; + +void SwClient::StartListeningToSameModifyAs(const SwClient& other) +{ + if(other.m_pRegisteredIn) + other.m_pRegisteredIn->Add(this); + else + EndListeningAll(); +} + +void SwClient::EndListeningAll() +{ + if(m_pRegisteredIn) + m_pRegisteredIn->Remove(this); +} + +void SwClient::Modify(SfxPoolItem const*const pOldValue, SfxPoolItem const*const /*pNewValue*/) +{ + CheckRegistration( pOldValue ); +} + +void SwModify::SetInDocDTOR() +{ + // If the document gets destroyed anyway, just tell clients to + // forget me so that they don't try to get removed from my list + // later when they also get destroyed + SwIterator<SwClient,SwModify> aIter(*this); + for(SwClient* pClient = aIter.First(); pClient; pClient = aIter.Next()) + pClient->m_pRegisteredIn = nullptr; + m_pWriterListeners = nullptr; +} + +SwModify::~SwModify() +{ + DBG_TESTSOLARMUTEX(); + OSL_ENSURE( !IsModifyLocked(), "Modify destroyed but locked." ); + + if ( IsInCache() ) + SwFrame::GetCache().Delete( this ); + + if ( IsInSwFntCache() ) + pSwFontCache->Delete( this ); + + // notify all clients that they shall remove themselves + SwPtrMsgPoolItem aDyObject( RES_OBJECTDYING, this ); + NotifyClients( &aDyObject, &aDyObject ); + + // remove all clients that have not done themselves + // mba: possibly a hotfix for forgotten base class calls?! + while( m_pWriterListeners ) + static_cast<SwClient*>(m_pWriterListeners)->CheckRegistration( &aDyObject ); +} + +void SwModify::NotifyClients( const SfxPoolItem* pOldValue, const SfxPoolItem* pNewValue ) +{ + DBG_TESTSOLARMUTEX(); + if ( IsInCache() || IsInSwFntCache() ) + { + const sal_uInt16 nWhich = pOldValue ? pOldValue->Which() : + pNewValue ? pNewValue->Which() : 0; + CheckCaching( nWhich ); + } + + if ( !m_pWriterListeners || IsModifyLocked() ) + return; + + LockModify(); + + // mba: WTF?! + if( !pOldValue ) + { + m_bLockClientList = true; + } + else + { + switch( pOldValue->Which() ) + { + case RES_OBJECTDYING: + case RES_REMOVE_UNO_OBJECT: + m_bLockClientList = static_cast<const SwPtrMsgPoolItem*>(pOldValue)->pObject != this; + break; + + default: + m_bLockClientList = true; + } + } + + ModifyBroadcast( pOldValue, pNewValue ); + m_bLockClientList = false; + UnlockModify(); +} + +bool SwModify::GetInfo( SfxPoolItem& 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; +} + +void SwModify::Add( SwClient* pDepend ) +{ + DBG_TESTSOLARMUTEX(); + OSL_ENSURE( !m_bLockClientList, "Client inserted while in Modify" ); + + if(pDepend->m_pRegisteredIn != this ) + { +#if OSL_DEBUG_LEVEL > 0 + if(sw::ClientIteratorBase::s_pClientIters) + { + for(auto& rIter : sw::ClientIteratorBase::s_pClientIters->GetRingContainer()) + { + 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."); + } + } +#endif + // deregister new client in case it is already registered elsewhere + if( pDepend->m_pRegisteredIn != nullptr ) + pDepend->m_pRegisteredIn->Remove( pDepend ); + + if( !m_pWriterListeners ) + { + // first client added + m_pWriterListeners = pDepend; + m_pWriterListeners->m_pLeft = nullptr; + m_pWriterListeners->m_pRight = nullptr; + } + else + { + // append client + pDepend->m_pRight = m_pWriterListeners->m_pRight; + m_pWriterListeners->m_pRight = pDepend; + pDepend->m_pLeft = m_pWriterListeners; + if( pDepend->m_pRight ) + pDepend->m_pRight->m_pLeft = pDepend; + } + + // connect client to me + pDepend->m_pRegisteredIn = this; + } +} + +SwClient* SwModify::Remove( SwClient* pDepend ) +{ + DBG_TESTSOLARMUTEX(); + assert(pDepend->m_pRegisteredIn == this); + + // SwClient is my listener + // remove it from my list + ::sw::WriterListener* pR = pDepend->m_pRight; + ::sw::WriterListener* pL = pDepend->m_pLeft; + if( m_pWriterListeners == pDepend ) + 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 == pDepend || rIter.m_pPosition == pDepend)) + { + // if object being removed is the current or next object in an + // iterator, advance this iterator + rIter.m_pPosition = pR; + } + } + } + pDepend->m_pLeft = nullptr; + pDepend->m_pRight = nullptr; + pDepend->m_pRegisteredIn = nullptr; + return pDepend; +} + +void SwModify::CheckCaching( const sal_uInt16 nWhich ) +{ + if( isCHRATR( nWhich ) ) + { + SetInSwFntCache( false ); + } + else + { + switch( nWhich ) + { + case RES_OBJECTDYING: + case RES_FMT_CHG: + case RES_ATTRSET_CHG: + SetInSwFntCache( false ); + [[fallthrough]]; + case RES_UL_SPACE: + case RES_LR_SPACE: + case RES_BOX: + case RES_SHADOW: + case RES_FRM_SIZE: + case RES_KEEP: + case RES_BREAK: + if( IsInCache() ) + { + SwFrame::GetCache().Delete( this ); + SetInCache( false ); + } + break; + } + } +} + +sw::WriterMultiListener::WriterMultiListener(SwClient& rToTell) + : m_rToTell(rToTell) +{} + +sw::WriterMultiListener::~WriterMultiListener() +{} + +void sw::WriterMultiListener::StartListening(SwModify* pDepend) +{ + EndListening(nullptr); + m_vDepends.emplace_back(ListenerEntry(&m_rToTell, pDepend)); +} + + +bool sw::WriterMultiListener::IsListeningTo(const SwModify* const pBroadcaster) const +{ + return std::any_of(m_vDepends.begin(), m_vDepends.end(), + [&pBroadcaster](const ListenerEntry& aListener) + { + return aListener.GetRegisteredIn() == pBroadcaster; + }); +} + +void sw::WriterMultiListener::EndListening(SwModify* pBroadcaster) +{ + m_vDepends.erase( + std::remove_if( m_vDepends.begin(), m_vDepends.end(), + [&pBroadcaster](const ListenerEntry& aListener) + { + return aListener.GetRegisteredIn() == nullptr || aListener.GetRegisteredIn() == pBroadcaster; + }), + m_vDepends.end()); +} + +void sw::WriterMultiListener::EndListeningAll() +{ + m_vDepends.clear(); +} + +sw::ClientIteratorBase* sw::ClientIteratorBase::s_pClientIters = nullptr; + +void SwModify::CallSwClientNotify( const SfxHint& rHint ) const +{ + SwIterator<SwClient,SwModify> aIter(*this); + for(SwClient* pClient = aIter.First(); pClient; pClient = aIter.Next()) + pClient->SwClientNotify( *this, rHint ); +} + +void sw::BroadcastingModify::CallSwClientNotify(const SfxHint& rHint) const +{ + SwModify::CallSwClientNotify(rHint); + const_cast<BroadcastingModify*>(this)->GetNotifier().Broadcast(rHint); +} +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |