/* -*- 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;
const SwPtrMsgPoolItem* pDead = static_cast<const SwPtrMsgPoolItem*>(pOld);
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
{
// 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 = static_cast<SwClient*>(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 );
SAL_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)
{
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;
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V560 A part of conditional expression is always false: !pDead.