/* -*- 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 <com/sun/star/embed/VerbDescriptor.hpp>
#include <com/sun/star/embed/VerbAttributes.hpp>
#include <com/sun/star/lang/XInitialization.hpp>
#include <officecfg/Office/Common.hxx>
#include <rtl/ustring.hxx>
#include <sal/log.hxx>
#include <osl/diagnose.h>
#include <svl/itempool.hxx>
#include <svl/setitem.hxx>
#include <svl/voiditem.hxx>
#include <svl/undo.hxx>
#include <svtools/asynclink.hxx>
#include <unotools/configmgr.hxx>
#include <comphelper/lok.hxx>
#include <comphelper/propertysequence.hxx>
#include <sfx2/shell.hxx>
#include <sfx2/bindings.hxx>
#include <sfx2/dispatch.hxx>
#include <sfx2/viewfrm.hxx>
#include <sfx2/objface.hxx>
#include <sfx2/objsh.hxx>
#include <sfx2/viewsh.hxx>
#include <sfx2/request.hxx>
#include <sfx2/sfxsids.hrc>
#include <statcach.hxx>
#include <sidebar/ContextChangeBroadcaster.hxx>
#include <com/sun/star/ui/dialogs/XSLTFilterDialog.hpp>
#include <toolkit/helper/vclunohelper.hxx>
#include <tools/debug.hxx>

#include <memory>
#include <vector>
#include <map>

#include <desktop/crashreport.hxx>

using namespace com::sun::star;

struct SfxShell_Impl: public SfxBroadcaster
{
    OUString                    aObjectName;   // Name of Sbx-Objects
    // Maps the Which() field to a pointer to a SfxPoolItem
    std::map<sal_uInt16, std::unique_ptr<SfxPoolItem>>
                                m_Items;       // Data exchange on Item level
    SfxViewShell*               pViewSh;       // SfxViewShell if Shell is
                                               // ViewFrame/ViewShell/SubShell list
    SfxViewFrame*               pFrame;        // Frame, if  <UI-active>
    SfxRepeatTarget*            pRepeatTarget; // SbxObjectRef xParent;
    bool                        bActive;
    SfxDisableFlags             nDisableFlags;
    std::unique_ptr<svtools::AsynchronLink> pExecuter;
    std::unique_ptr<svtools::AsynchronLink> pUpdater;
    std::vector<std::unique_ptr<SfxSlot> >  aSlotArr;

    css::uno::Sequence < css::embed::VerbDescriptor > aVerbList;
    ::sfx2::sidebar::ContextChangeBroadcaster maContextChangeBroadcaster;

    SfxShell_Impl()
        : pViewSh(nullptr)
        , pFrame(nullptr)
        , pRepeatTarget(nullptr)
        , bActive(false)
        , nDisableFlags(SfxDisableFlags::NONE)
    {
    }

    virtual ~SfxShell_Impl() override { pExecuter.reset(); pUpdater.reset();}
};


void SfxShell::EmptyExecStub(SfxShell *, SfxRequest &)
{
}

void SfxShell::EmptyStateStub(SfxShell *, SfxItemSet &)
{
}

SfxShell::SfxShell()
:   pImpl(new SfxShell_Impl),
    pPool(nullptr),
    pUndoMgr(nullptr)
{
}

SfxShell::SfxShell( SfxViewShell *pViewSh )
:   pImpl(new SfxShell_Impl),
    pPool(nullptr),
    pUndoMgr(nullptr)
{
    pImpl->pViewSh = pViewSh;
}

SfxShell::~SfxShell()
{
}

void SfxShell::SetName( const OUString &rName )
{
    pImpl->aObjectName = rName;
}

const OUString& SfxShell::GetName() const
{
    return pImpl->aObjectName;
}

SfxDispatcher* SfxShell::GetDispatcher() const
{
    return pImpl->pFrame ? pImpl->pFrame->GetDispatcher() : nullptr;
}

SfxViewShell* SfxShell::GetViewShell() const
{
    return pImpl->pViewSh;
}

SfxViewFrame* SfxShell::GetFrame() const
{
    if ( pImpl->pFrame )
        return pImpl->pFrame;
    if ( pImpl->pViewSh )
        return &pImpl->pViewSh->GetViewFrame();
    return nullptr;
}

const SfxPoolItem* SfxShell::GetItem
(
    sal_uInt16  nSlotId         // Slot-Id of the querying <SfxPoolItem>s
)   const
{
    auto const it = pImpl->m_Items.find( nSlotId );
    if (it != pImpl->m_Items.end())
        return it->second.get();
    return nullptr;
}

void SfxShell::PutItem
(
    const SfxPoolItem&  rItem  /* Instance, of which a copy is created,
                                  which is stored in the SfxShell in a list. */
)
{
    DBG_ASSERT( !rItem.isSetItem(), "SetItems aren't allowed here" );
    DBG_ASSERT( SfxItemPool::IsSlot( rItem.Which() ),
                "items with Which-Ids aren't allowed here" );

    // MSC made a mess here of WNT/W95, beware of changes
    SfxPoolItem *pItem = rItem.Clone();
    SfxPoolItemHint aItemHint( pItem );
    sal_uInt16 nWhich = rItem.Which();

    auto const it = pImpl->m_Items.find(nWhich);
    if (it != pImpl->m_Items.end())
    {
        // Replace Item
        it->second = std::unique_ptr<SfxPoolItem>(pItem);

        // if active, notify Bindings
        SfxDispatcher *pDispat = GetDispatcher();
        if ( pDispat )
        {
            SfxBindings* pBindings = pDispat->GetBindings();
            pBindings->Broadcast( aItemHint );
            sal_uInt16 nSlotId = nWhich; //pItem->GetSlotId();
            SfxStateCache* pCache = pBindings->GetStateCache( nSlotId );
            if ( pCache )
            {
                pCache->SetState( SfxItemState::DEFAULT, pItem, true );
                pCache->SetCachedState( true );
            }
        }
        return;
    }
    else
    {
        Broadcast( aItemHint );
        pImpl->m_Items.insert(std::make_pair(nWhich, std::unique_ptr<SfxPoolItem>(pItem)));
    }
}

SfxInterface* SfxShell::GetInterface() const
{
    return GetStaticInterface();
}

SfxUndoManager* SfxShell::GetUndoManager()
{
    return pUndoMgr;
}

void SfxShell::SetUndoManager( SfxUndoManager *pNewUndoMgr )
{
    OSL_ENSURE( ( pUndoMgr == nullptr ) || ( pNewUndoMgr == nullptr ) || ( pUndoMgr == pNewUndoMgr ),
        "SfxShell::SetUndoManager: exchanging one non-NULL manager with another non-NULL manager? Suspicious!" );
    // there's at least one client of our UndoManager - the DocumentUndoManager at the SfxBaseModel - which
    // caches the UndoManager, and registers itself as listener. If exchanging non-NULL UndoManagers is really
    // a supported scenario (/me thinks it is not), then we would need to notify all such clients instances.

    pUndoMgr = pNewUndoMgr;
    if (pUndoMgr && !comphelper::IsFuzzing())
    {
        pUndoMgr->SetMaxUndoActionCount(
            officecfg::Office::Common::Undo::Steps::get());
    }
}

SfxRepeatTarget* SfxShell::GetRepeatTarget() const
{
    return pImpl->pRepeatTarget;
}

void SfxShell::SetRepeatTarget( SfxRepeatTarget *pTarget )
{
    pImpl->pRepeatTarget = pTarget;
}

void SfxShell::Invalidate
(
    sal_uInt16          nId     /* Invalidated Slot-Id or Which-Id.
                               If these are 0 (default), then all
                               by this Shell currently handled Slot-Ids are
                               invalidated. */
)
{
    if ( !GetViewShell() )
    {
        OSL_FAIL( "wrong Invalidate method called!" );
        return;
    }

    Invalidate_Impl( GetViewShell()->GetViewFrame().GetBindings(), nId );
}

void SfxShell::Invalidate_Impl( SfxBindings& rBindings, sal_uInt16 nId )
{
    if ( nId == 0 )
    {
        rBindings.InvalidateShell( *this );
    }
    else
    {
        const SfxInterface *pIF = GetInterface();
        do
        {
            const SfxSlot *pSlot = pIF->GetSlot(nId);
            if ( pSlot )
            {
                // Invalidate the Slot itself
                rBindings.Invalidate( pSlot->GetSlotId() );
                return;
            }

            pIF = pIF->GetGenoType();
        }

        while ( pIF );

        SAL_INFO( "sfx.control", "W3: invalidating slot-id unknown in shell" );
    }
}

void SfxShell::HandleOpenXmlFilterSettings(SfxRequest & rReq)
{
    try
    {
        uno::Reference < ui::dialogs::XExecutableDialog > xDialog = ui::dialogs::XSLTFilterDialog::create( ::comphelper::getProcessComponentContext() );

        // set dialog parent
        css::uno::Reference<com::sun::star::lang::XInitialization> xInit(xDialog,
                                                                         css::uno::UNO_QUERY);
        if (xInit.is())
        {
            if (SfxViewShell* pViewShell = GetViewShell())
            {
                css::uno::Reference<css::awt::XWindow> xDialogParent
                    = VCLUnoHelper::GetInterface(pViewShell->GetWindow());
                css::uno::Sequence<css::uno::Any> aSeq(comphelper::InitAnyPropertySequence(
                    { { "ParentWindow", uno::Any(xDialogParent) } }));
                xInit->initialize(aSeq);
            }
        }

        (void)xDialog->execute();
    }
    catch (const uno::Exception&)
    {
    }
    rReq.Ignore ();
}

void SfxShell::DoActivate_Impl( SfxViewFrame *pFrame, bool bMDI )
{
    SfxObjectShell* pObjectShell = GetObjectShell();
    if ( pObjectShell )
    {
        const OUString sActiveDocName = pObjectShell->GetTitle();
        if( !pImpl->aObjectName.startsWith(sActiveDocName) )
        {
           CrashReporter::setActiveSfxObjectName(pImpl->aObjectName);
        }
    }
    else
    {
        CrashReporter::setActiveSfxObjectName(pImpl->aObjectName);
    }

#ifdef DBG_UTIL
    const SfxInterface *p_IF = GetInterface();
    if ( !p_IF )
        return;
#endif
    SAL_INFO(
        "sfx.control",
        "SfxShell::DoActivate() " << this << "  " << GetInterface()->GetClassName()
            << " bMDI " << (bMDI ? "MDI" : ""));

    if ( bMDI )
    {
        // Remember Frame, in which it was activated
        pImpl->pFrame = pFrame;
        pImpl->bActive = true;
    }

    // Notify Subclass
    Activate(bMDI);
}

void SfxShell::DoDeactivate_Impl( SfxViewFrame const *pFrame, bool bMDI )
{
#ifdef DBG_UTIL
    const SfxInterface *p_IF = GetInterface();
    if ( !p_IF )
        return;
#endif
    SAL_INFO(
        "sfx.control",
        "SfxShell::DoDeactivate()" << this << "  " << GetInterface()->GetClassName()
            << " bMDI " << (bMDI ? "MDI" : ""));

    // Only when it comes from a Frame
    // (not when for instance by popping BASIC-IDE from AppDisp)
    if ( bMDI && pImpl->pFrame == pFrame )
    {
        // deliver
        pImpl->pFrame = nullptr;
        pImpl->bActive = false;
    }

    // Notify Subclass
    Deactivate(bMDI);
}

bool SfxShell::IsActive() const
{
    return pImpl->bActive;
}

void SfxShell::Activate
(
    bool    /*bMDI*/        /*  TRUE
                            the <SfxDispatcher>, on which the SfxShell is
                            located, is activated or the SfxShell instance
                            was pushed on an active SfxDispatcher.
                            (compare with SystemWindow::IsMDIActivate())

                            FALSE
                            the <SfxViewFrame>, on which SfxDispatcher
                            the SfxShell instance is located, was
                            activated. (for example by a closing dialog) */
)
{
    BroadcastContextForActivation(true);
}

void SfxShell::Deactivate
(
    bool    /*bMDI*/        /*  TRUE
                            the <SfxDispatcher>, on which the SfxShell is
                            located, is inactivated or the SfxShell instance
                            was popped on an active SfxDispatcher.
                            (compare with SystemWindow::IsMDIActivate())

                            FALSE
                            the <SfxViewFrame>, on which SfxDispatcher
                            the SfxShell instance is located, was
                            deactivated. (for example by a dialog) */
)
{
    BroadcastContextForActivation(false);
}

bool SfxShell::CanExecuteSlot_Impl( const SfxSlot &rSlot )
{
    // Get Slot status
    SfxItemPool &rPool = GetPool();
    const sal_uInt16 nId = rSlot.GetWhich( rPool );
    SfxItemSet aSet(rPool, nId, nId);
    SfxStateFunc pFunc = rSlot.GetStateFnc();
    (*pFunc)( this, aSet );
    return aSet.GetItemState(nId) != SfxItemState::DISABLED;
}

bool SfxShell::IsConditionalFastCall( const SfxRequest &rReq )
{
    sal_uInt16 nId = rReq.GetSlot();
    bool bRet = false;

    if (nId == SID_UNDO || nId == SID_REDO)
    {
        const SfxItemSet* pArgs = rReq.GetArgs();
        if (pArgs && pArgs->HasItem(SID_REPAIRPACKAGE))
            bRet = true;
    }
    return bRet;
}


static void ShellCall_Impl( void* pObj, void* pArg )
{
    static_cast<SfxShell*>(pObj)->ExecuteSlot( *static_cast<SfxRequest*>(pArg) );
}

void SfxShell::ExecuteSlot( SfxRequest& rReq, bool bAsync )
{
    if( !bAsync )
        ExecuteSlot( rReq );
    else
    {
        if( !pImpl->pExecuter )
            pImpl->pExecuter.reset( new svtools::AsynchronLink(
                LINK_NONMEMBER( this, ShellCall_Impl ) ) );
        pImpl->pExecuter->Call( new SfxRequest( rReq ) );
    }
}

const SfxPoolItemHolder& SfxShell::ExecuteSlot
(
    SfxRequest          &rReq,  // the relayed <SfxRequest>
    const SfxInterface* pIF     // default = 0 means get virtually
)
{
    if ( !pIF )
        pIF = GetInterface();

    sal_uInt16 nSlot = rReq.GetSlot();
    const SfxSlot* pSlot = nullptr;
    if ( nSlot >= SID_VERB_START && nSlot <= SID_VERB_END )
        pSlot = GetVerbSlot_Impl(nSlot);
    if ( !pSlot )
        pSlot = pIF->GetSlot(nSlot);
    assert(pSlot && "slot not supported");

    SfxExecFunc pFunc = pSlot->GetExecFnc();
    if ( pFunc )
        (*pFunc)( this, rReq );

    return rReq.GetReturnValue();
}

SfxPoolItemHolder SfxShell::GetSlotState
(
    sal_uInt16              nSlotId,    // Slot-Id to the Slots in question
    const SfxInterface* pIF,        // default = 0 means get virtually
    SfxItemSet*         pStateSet   // SfxItemSet of the Slot-State method
)
{
    // Get Slot on the given Interface
    if ( !pIF )
        pIF = GetInterface();
    SfxItemState eState(SfxItemState::DEFAULT);
    bool bItemStateSet(false);
    SfxItemPool &rPool = GetPool();

    const SfxSlot* pSlot = nullptr;
    if ( nSlotId >= SID_VERB_START && nSlotId <= SID_VERB_END )
        pSlot = GetVerbSlot_Impl(nSlotId);
    if ( !pSlot )
        pSlot = pIF->GetSlot(nSlotId);
    if ( pSlot )
        // Map on Which-Id if possible
        nSlotId = pSlot->GetWhich( rPool );

    // Get Item and Item status
    const SfxPoolItem *pItem = nullptr;
    SfxItemSet aSet( rPool, nSlotId, nSlotId ); // else pItem dies too soon
    if ( nullptr != pSlot )
    {
        // Call Status method
        SfxStateFunc pFunc = pSlot->GetStateFnc();
        if ( pFunc )
            (*pFunc)( this, aSet );
        eState = aSet.GetItemState( nSlotId, true, &pItem );
        bItemStateSet = true;

        // get default Item if possible
        if ( eState == SfxItemState::DEFAULT )
        {
            if ( SfxItemPool::IsWhich(nSlotId) )
                pItem = &rPool.GetUserOrPoolDefaultItem(nSlotId);
            else
                eState = SfxItemState::INVALID;
        }
    }

    // Evaluate Item and item status and possibly maintain them in pStateSet
    if ( !bItemStateSet || eState <= SfxItemState::DISABLED )
    {
        if ( pStateSet )
            pStateSet->DisableItem(nSlotId);
        return SfxPoolItemHolder();
    }

    if ( bItemStateSet && eState == SfxItemState::INVALID )
    {
        if ( pStateSet )
            pStateSet->ClearItem(nSlotId);
        return SfxPoolItemHolder(rPool, DISABLED_POOL_ITEM);
    }

    // bItemStateSet && eState >= SfxItemState::DEFAULT
    if ( pStateSet && pStateSet->Put( *pItem ) )
    {
        return SfxPoolItemHolder(rPool, &pStateSet->Get(pItem->Which()));
    }

    return SfxPoolItemHolder(rPool, pItem);
}

static SFX_EXEC_STUB(SfxShell, VerbExec)
static void SfxStubSfxShellVerbState(SfxShell *, SfxItemSet& rSet)
{
    SfxShell::VerbState( rSet );
}

void SfxShell::SetVerbs(const css::uno::Sequence < css::embed::VerbDescriptor >& aVerbs)
{
    SfxViewShell *pViewSh = dynamic_cast<SfxViewShell*>( this );

    DBG_ASSERT(pViewSh, "Only call SetVerbs at the ViewShell!");
    if ( !pViewSh )
        return;

    // First make all Statecaches dirty, so that no-one no longer tries to use
    // the Slots
    {
        SfxBindings *pBindings =
            pViewSh->GetViewFrame().GetDispatcher()->GetBindings();
        sal_uInt16 nCount = pImpl->aSlotArr.size();
        for (sal_uInt16 n1=0; n1<nCount ; n1++)
        {
            sal_uInt16 nId = SID_VERB_START + n1;
            pBindings->Invalidate(nId, false, true);
        }
    }

    sal_uInt16 nr=0;
    for (sal_Int32 n=0; n<aVerbs.getLength(); n++)
    {
        sal_uInt16 nSlotId = SID_VERB_START + nr++;
        DBG_ASSERT(nSlotId <= SID_VERB_END, "Too many Verbs!");
        if (nSlotId > SID_VERB_END)
            break;

        SfxSlot* pNewSlot = new SfxSlot{
            nSlotId, SfxGroupId::NONE,
            // Verb slots must be executed asynchronously, so that they can be
            // destroyed while executing.
            SfxSlotMode::ASYNCHRON | SfxSlotMode::CONTAINER,
            0, 0,
            SFX_STUB_PTR(SfxShell, VerbExec), SFX_STUB_PTR(SfxShell, VerbState),
            nullptr, // HACK(SFX_TYPE(SfxVoidItem)) ???
            nullptr, nullptr, 0, SfxDisableFlags::NONE, u""_ustr};

        if (!pImpl->aSlotArr.empty())
        {
            SfxSlot& rSlot = *pImpl->aSlotArr[0];
            pNewSlot->pNextSlot = rSlot.pNextSlot;
            rSlot.pNextSlot = pNewSlot;
        }
        else
            pNewSlot->pNextSlot = pNewSlot;

        pImpl->aSlotArr.insert(pImpl->aSlotArr.begin() + static_cast<sal_uInt16>(n), std::unique_ptr<SfxSlot>(pNewSlot));
    }

    pImpl->aVerbList = aVerbs;

    // The status of SID_OBJECT is collected in the controller directly on
    // the Shell, it is thus enough to encourage a new status update
    SfxBindings* pBindings = pViewSh->GetViewFrame().GetDispatcher()->GetBindings();
    pBindings->Invalidate(SID_OBJECT, true, true);
}

const css::uno::Sequence < css::embed::VerbDescriptor >& SfxShell::GetVerbs() const
{
    return pImpl->aVerbList;
}

void SfxShell::VerbExec(SfxRequest& rReq)
{
    sal_uInt16 nId = rReq.GetSlot();
    SfxViewShell *pViewShell = GetViewShell();
    if ( !pViewShell )
        return;

    bool bReadOnly = pViewShell->GetObjectShell()->IsReadOnly();
    const css::uno::Sequence < css::embed::VerbDescriptor > aList = pViewShell->GetVerbs();
    sal_Int32 nVerb = 0;
    for (const auto& rVerb : aList)
    {
        // check for ReadOnly verbs
        if ( bReadOnly && !(rVerb.VerbAttributes & embed::VerbAttributes::MS_VERBATTR_NEVERDIRTIES) )
            continue;

        // check for verbs that shouldn't appear in the menu
        if ( !(rVerb.VerbAttributes & embed::VerbAttributes::MS_VERBATTR_ONCONTAINERMENU) )
            continue;

        if (nId == SID_VERB_START + nVerb++)
        {
            pViewShell->DoVerb(rVerb.VerbID);
            rReq.Done();
            return;
        }
    }
}

void SfxShell::VerbState(SfxItemSet& )
{
}

const SfxSlot* SfxShell::GetVerbSlot_Impl(sal_uInt16 nId) const
{
    css::uno::Sequence < css::embed::VerbDescriptor > rList = pImpl->aVerbList;

    DBG_ASSERT(nId >= SID_VERB_START && nId <= SID_VERB_END,"Wrong VerbId!");
    sal_uInt16 nIndex = nId - SID_VERB_START;
    DBG_ASSERT(nIndex < rList.getLength(),"Wrong VerbId!");

    if (nIndex < rList.getLength())
        return pImpl->aSlotArr[nIndex].get();
    else
        return nullptr;
}

SfxObjectShell* SfxShell::GetObjectShell()
{
    if ( GetViewShell() )
        return GetViewShell()->GetViewFrame().GetObjectShell();
    else
        return nullptr;
}

bool SfxShell::HasUIFeature(SfxShellFeature) const
{
    return false;
}

static void DispatcherUpdate_Impl( void*, void* pArg )
{
    static_cast<SfxDispatcher*>(pArg)->Update_Impl( true );
    static_cast<SfxDispatcher*>(pArg)->GetBindings()->InvalidateAll(false);
}

void SfxShell::UIFeatureChanged()
{
    SfxViewFrame *pFrame = GetFrame();
    if ( pFrame && pFrame->IsVisible() )
    {
        // Also force an update, if dispatcher is already updated otherwise
        // something may get stuck in the bunkered tools. Asynchronous call to
        // prevent recursion.
        if ( !pImpl->pUpdater )
            pImpl->pUpdater.reset( new svtools::AsynchronLink( LINK_NONMEMBER( this, DispatcherUpdate_Impl ) ) );

        // Multiple views allowed
        pImpl->pUpdater->Call( pFrame->GetDispatcher(), true );
    }
}

void SfxShell::SetDisableFlags( SfxDisableFlags nFlags )
{
    pImpl->nDisableFlags = nFlags;
}

SfxDisableFlags SfxShell::GetDisableFlags() const
{
    return pImpl->nDisableFlags;
}

std::optional<SfxItemSet> SfxShell::CreateItemSet( sal_uInt16 )
{
    return {};
}

void SfxShell::ApplyItemSet( sal_uInt16, const SfxItemSet& )
{
}

void SfxShell::SetContextName (const OUString& rsContextName)
{
    pImpl->maContextChangeBroadcaster.Initialize(rsContextName);
}

void SfxShell::SetViewShell_Impl( SfxViewShell* pView )
{
    pImpl->pViewSh = pView;
}

void SfxShell::BroadcastContextForActivation (const bool bIsActivated)
{
    // Avoids activation and de-activation (can be seen on switching view) from causing
    // the sidebar to re-build. Such switching can happen as we change view to render
    // using LOK for example, and is un-necessary for Online.
    if (comphelper::LibreOfficeKit::isDialogPainting())
        return;

    SfxViewFrame* pViewFrame = GetFrame();
    if (pViewFrame != nullptr)
    {
        if (bIsActivated)
            pImpl->maContextChangeBroadcaster.Activate(pViewFrame->GetFrame().GetFrameInterface());
        else
            pImpl->maContextChangeBroadcaster.Deactivate(pViewFrame->GetFrame().GetFrameInterface());
    }
}

bool SfxShell::SetContextBroadcasterEnabled (const bool bIsEnabled)
{
    return pImpl->maContextChangeBroadcaster.SetBroadcasterEnabled(bIsEnabled);
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
