tdf#99815 use XNotifyingDispatch in sfx2

- move DispatchHelper somewhere public
- use it from generic dispatcher call sites in sfx2
- return result of dispatcher calls (conveyed via
  XDispatchResultListener) to calling code, instead of faking it

Change-Id: Ie8041133e99dd99e45819f98798829b96532b9e6
Reviewed-on: https://gerrit.libreoffice.org/24953
Tested-by: Jenkins <ci@libreoffice.org>
Reviewed-by: Thorsten Behrens <Thorsten.Behrens@CIB.de>
diff --git a/framework/Library_fwe.mk b/framework/Library_fwe.mk
index 9b10e16..0559236 100644
--- a/framework/Library_fwe.mk
+++ b/framework/Library_fwe.mk
@@ -79,6 +79,7 @@ $(eval $(call gb_Library_add_exception_objects,fwe,\
    framework/source/fwe/xml/toolboxconfiguration \
    framework/source/fwe/xml/toolboxdocumenthandler \
    framework/source/fwe/xml/xmlnamespaces \
    framework/source/services/dispatchhelper \
))

# Runtime dependency for unit-tests
diff --git a/framework/Library_fwl.mk b/framework/Library_fwl.mk
index ea325e0..f3729c4 100644
--- a/framework/Library_fwl.mk
+++ b/framework/Library_fwl.mk
@@ -60,7 +60,6 @@ $(eval $(call gb_Library_add_exception_objects,fwl,\
    framework/source/recording/dispatchrecordersupplier \
    framework/source/register/registertemp \
    framework/source/dispatch/dispatchdisabler \
    framework/source/services/dispatchhelper \
    framework/source/services/mediatypedetectionhelper \
    framework/source/services/uriabbreviation \
    framework/source/uielement/fontmenucontroller \
diff --git a/framework/inc/services/dispatchhelper.hxx b/framework/inc/services/dispatchhelper.hxx
deleted file mode 100644
index de8e972..0000000
--- a/framework/inc/services/dispatchhelper.hxx
+++ /dev/null
@@ -1,104 +0,0 @@
/* -*- 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 .
 */

#ifndef INCLUDED_FRAMEWORK_INC_SERVICES_DISPATCHHELPER_HXX
#define INCLUDED_FRAMEWORK_INC_SERVICES_DISPATCHHELPER_HXX

#include <macros/xinterface.hxx>
#include <macros/xtypeprovider.hxx>
#include <macros/xserviceinfo.hxx>
#include <general.h>

#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/frame/XDispatchHelper.hpp>
#include <com/sun/star/frame/XDispatchResultListener.hpp>
#include <com/sun/star/frame/DispatchResultEvent.hpp>

#include <cppuhelper/implbase.hxx>
#include <osl/conditn.hxx>

namespace framework{

/**
    @short      implements an easy way for dispatches
    @descr      Dispatches are splitted into different parts:
                    - parsing of the URL
                    - searching for a dispatch object
                    - dispatching of the URL
                All these steps are done inside one method call here.
*/

class DispatchHelper : public ::cppu::WeakImplHelper< css::lang::XServiceInfo,css::frame::XDispatchHelper,css::frame::XDispatchResultListener >
{

    // member

    private:

        osl::Mutex m_mutex;

        /** global uno service manager.
            Can be used to create own needed services. */
        css::uno::Reference< css::uno::XComponentContext > m_xContext;

        /** used to wait for asynchronous listener callbacks. */
        ::osl::Condition m_aBlock;

        css::uno::Any m_aResult;

        css::uno::Reference< css::uno::XInterface > m_xBroadcaster;

    // interface

    public:

        // ctor/dtor

                 DispatchHelper( const css::uno::Reference< css::uno::XComponentContext >& xContext );
        virtual ~DispatchHelper(                                                                    );

        // XInterface, XTypeProvider, XServiceInfo

        DECLARE_XSERVICEINFO

        // XDispatchHelper
        virtual css::uno::Any SAL_CALL executeDispatch(
                                        const css::uno::Reference< css::frame::XDispatchProvider >& xDispatchProvider ,
                                        const OUString&                                      sURL              ,
                                        const OUString&                                      sTargetFrameName  ,
                                              sal_Int32                                             nSearchFlags      ,
                                        const css::uno::Sequence< css::beans::PropertyValue >&      lArguments        )
        throw(css::uno::RuntimeException, std::exception) override;

        // XDispatchResultListener
        virtual void SAL_CALL dispatchFinished(
                                const css::frame::DispatchResultEvent& aResult )
        throw(css::uno::RuntimeException, std::exception) override;

        // XEventListener
        virtual void SAL_CALL disposing(
                                const css::lang::EventObject& aEvent )
        throw(css::uno::RuntimeException, std::exception) override;
};

}

#endif

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/register/registertemp.cxx b/framework/source/register/registertemp.cxx
index 393c5bd..f6c28ef 100644
--- a/framework/source/register/registertemp.cxx
+++ b/framework/source/register/registertemp.cxx
@@ -38,7 +38,7 @@
#include <dispatch/popupmenudispatcher.hxx>
#include <dispatch/servicehandler.hxx>
#include <dispatch/dispatchdisabler.hxx>
#include <services/dispatchhelper.hxx>
#include <framework/dispatchhelper.hxx>
#include <recording/dispatchrecorder.hxx>
#include <recording/dispatchrecordersupplier.hxx>
#include <services/uriabbreviation.hxx>
diff --git a/framework/source/services/dispatchhelper.cxx b/framework/source/services/dispatchhelper.cxx
index 5700e2c..9cb6166 100644
--- a/framework/source/services/dispatchhelper.cxx
+++ b/framework/source/services/dispatchhelper.cxx
@@ -17,7 +17,8 @@
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#include <services/dispatchhelper.hxx>
#include <framework/dispatchhelper.hxx>
#include <macros/xserviceinfo.hxx>
#include <services.h>

#include <com/sun/star/util/URLTransformer.hpp>
@@ -103,41 +104,58 @@ css::uno::Any SAL_CALL DispatchHelper::executeDispatch(

    // search dispatcher
    css::uno::Reference< css::frame::XDispatch >          xDispatch       = xDispatchProvider->queryDispatch(aURL, sTargetFrameName, nSearchFlags);
    css::uno::Reference< css::frame::XNotifyingDispatch > xNotifyDispatch (xDispatch, css::uno::UNO_QUERY);

    // make sure that synchronous execution is used (if possible)
    css::uno::Sequence< css::beans::PropertyValue > aArguments( lArguments );
    sal_Int32 nLength = lArguments.getLength();
    aArguments.realloc( nLength + 1 );
    aArguments[ nLength ].Name = "SynchronMode";
    aArguments[ nLength ].Value <<= true;
    return executeDispatch(xDispatch, aURL, true, lArguments);
}

    css::uno::Any aResult;
    if (xNotifyDispatch.is())

css::uno::Any SAL_CALL DispatchHelper::executeDispatch(
                                 const css::uno::Reference< css::frame::XDispatch >&  xDispatch     ,
                                 const  css::util::URL&                                 aURL        ,
                                 bool                                                   SyncronFlag ,
                                 const css::uno::Sequence< css::beans::PropertyValue >& lArguments  )
                                 throw(css::uno::RuntimeException)
{
    css::uno::Reference< css::uno::XInterface > xTHIS(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY);
    m_aResult.clear();

    // check for valid parameters
    if (xDispatch.is() )
    {
        // dispatch it with guaranteed notification
        // Here we can hope for a result ... instead of the normal dispatch.
        css::uno::Reference< css::frame::XDispatchResultListener > xListener(xTHIS, css::uno::UNO_QUERY);
        /* SAFE { */
        osl::ClearableMutexGuard aWriteLock(m_mutex);
        m_xBroadcaster.set(xNotifyDispatch, css::uno::UNO_QUERY);
        m_aResult      = css::uno::Any();
        m_aBlock.reset();
        aWriteLock.clear();
        /* } SAFE */
        css::uno::Reference< css::frame::XNotifyingDispatch > xNotifyDispatch (xDispatch, css::uno::UNO_QUERY);

        // dispatch it and wait for a notification
        // TODO/MBA: waiting in main thread?!
        xNotifyDispatch->dispatchWithNotification(aURL, aArguments, xListener);
        aResult = m_aResult;
    }
    else if (xDispatch.is())
    {
        // dispatch it without any chance to get a result
        xDispatch->dispatch( aURL, aArguments );
        // make sure that synchronous execution is used (if possible)
        css::uno::Sequence< css::beans::PropertyValue > aArguments( lArguments );
        sal_Int32 nLength = lArguments.getLength();
        aArguments.realloc( nLength + 1 );
        aArguments[ nLength ].Name = "SynchronMode";
        aArguments[ nLength ].Value <<= SyncronFlag;

        if (xNotifyDispatch.is())
        {
            // dispatch it with guaranteed notification
            // Here we can hope for a result ... instead of the normal dispatch.
            css::uno::Reference< css::frame::XDispatchResultListener > xListener(xTHIS, css::uno::UNO_QUERY);
            /* SAFE { */
            osl::ClearableMutexGuard aWriteLock(m_mutex);
            m_xBroadcaster.set(xNotifyDispatch, css::uno::UNO_QUERY);
            m_aBlock.reset();
            aWriteLock.clear();
            /* } SAFE */

            // dispatch it and wait for a notification
            // TODO/MBA: waiting in main thread?!
            xNotifyDispatch->dispatchWithNotification(aURL, aArguments, xListener);
            m_aBlock.wait();        // wait for result
        }
        else
        {
            // dispatch it without any chance to get a result
            xDispatch->dispatch( aURL, aArguments );
        }
    }

    return aResult;
    return m_aResult;
}

/** callback for started dispatch with guaranteed notifications.
diff --git a/include/framework/dispatchhelper.hxx b/include/framework/dispatchhelper.hxx
new file mode 100644
index 0000000..2b4c79b
--- /dev/null
+++ b/include/framework/dispatchhelper.hxx
@@ -0,0 +1,123 @@
/* -*- 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 .
 */

#ifndef INCLUDED_FRAMEWORK_INC_SERVICES_DISPATCHHELPER_HXX
#define INCLUDED_FRAMEWORK_INC_SERVICES_DISPATCHHELPER_HXX

#include <com/sun/star/uno/Any.hxx>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/uno/XComponentContext.hpp>
#include <com/sun/star/frame/XDispatchHelper.hpp>
#include <com/sun/star/frame/XDispatchResultListener.hpp>
#include <com/sun/star/lang/XSingleServiceFactory.hpp>
#include <com/sun/star/lang/XServiceInfo.hpp>


#include <cppuhelper/implbase.hxx>
#include <osl/conditn.hxx>
#include <framework/fwedllapi.h>

namespace framework{

/**
    @short      implements an easy way for dispatches
    @descr      Dispatches are splitted into different parts:
                    - parsing of the URL
                    - searching for a dispatch object
                    - dispatching of the URL
                All these steps are done inside one method call here.
*/
class FWE_DLLPUBLIC DispatchHelper : public ::cppu::WeakImplHelper< css::lang::XServiceInfo,css::frame::XDispatchHelper,css::frame::XDispatchResultListener >
{

    // member

    private:

        osl::Mutex m_mutex;

        /** global uno service manager.
            Can be used to create own needed services. */
        css::uno::Reference< css::uno::XComponentContext > m_xContext;

        /** used to wait for asynchronous listener callbacks. */
        ::osl::Condition m_aBlock;

        css::uno::Any m_aResult;

        css::uno::Reference< css::uno::XInterface > m_xBroadcaster;

    // interface

    public:

        // ctor/dtor

                 DispatchHelper( const css::uno::Reference< css::uno::XComponentContext >& xContext );
        virtual ~DispatchHelper(                                                                    );

        // XServiceInfo
        virtual OUString                        SAL_CALL getImplementationName              (                                   ) throw( css::uno::RuntimeException, std::exception ) override;
        virtual sal_Bool                        SAL_CALL supportsService                    ( const OUString&   sServiceName    ) throw( css::uno::RuntimeException, std::exception ) override;
        virtual css::uno::Sequence< OUString >  SAL_CALL getSupportedServiceNames           (                                   ) throw( css::uno::RuntimeException, std::exception ) override;

        static css::uno::Sequence< OUString >   SAL_CALL impl_getStaticSupportedServiceNames(                                   );
        static OUString                         SAL_CALL impl_getStaticImplementationName   (                                   );
        // Helper for initialization of service by using own reference!
        void                                    SAL_CALL impl_initService                   (                                   );

        // Helper for registry
        static css::uno::Reference< css::uno::XInterface >             SAL_CALL impl_createInstance( const css::uno::Reference< css::lang::XMultiServiceFactory >& xServiceManager ) throw( css::uno::Exception );
        static css::uno::Reference< css::lang::XSingleServiceFactory > SAL_CALL impl_createFactory ( const css::uno::Reference< css::lang::XMultiServiceFactory >& xServiceManager );


        // XDispatchHelper
        virtual css::uno::Any SAL_CALL executeDispatch(
                                        const css::uno::Reference< css::frame::XDispatchProvider >& xDispatchProvider ,
                                        const OUString&                                      sURL              ,
                                        const OUString&                                      sTargetFrameName  ,
                                              sal_Int32                                             nSearchFlags      ,
                                        const css::uno::Sequence< css::beans::PropertyValue >&      lArguments        )
        throw(css::uno::RuntimeException, std::exception) override;

        // not a public XDispatchHelper-method, need in sfx2/source/control/statcach.cxx for extentions
        css::uno::Any SAL_CALL executeDispatch(
                                        const css::uno::Reference< css::frame::XDispatch >&  xDispatch      ,
                                        const  css::util::URL&                                  aURL        ,
                                        bool                                                    SyncronFlag ,
                                        const css::uno::Sequence< css::beans::PropertyValue >& lArguments   )
        throw(css::uno::RuntimeException);


        // XDispatchResultListener
        virtual void SAL_CALL dispatchFinished(
                                const css::frame::DispatchResultEvent& aResult )
        throw(css::uno::RuntimeException, std::exception) override;

        // XEventListener
        virtual void SAL_CALL disposing(
                                const css::lang::EventObject& aEvent )
        throw(css::uno::RuntimeException, std::exception) override;
};

}

#endif

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/control/bindings.cxx b/sfx2/source/control/bindings.cxx
index 3bdeb27..cfc899f 100644
--- a/sfx2/source/control/bindings.cxx
+++ b/sfx2/source/control/bindings.cxx
@@ -34,6 +34,7 @@
#include <com/sun/star/frame/XDispatchProviderInterceptor.hpp>
#include <com/sun/star/frame/XDispatch.hpp>
#include <com/sun/star/frame/XDispatchProvider.hpp>
#include <com/sun/star/frame/DispatchResultState.hpp>
#include <com/sun/star/frame/XStatusListener.hpp>
#include <com/sun/star/frame/FrameSearchFlag.hpp>
#include <com/sun/star/frame/XDispatchProviderInterception.hpp>
@@ -1014,10 +1015,15 @@ const SfxPoolItem* SfxBindings::Execute_Impl( sal_uInt16 nId, const SfxPoolItem*
                aReq.AppendItem( **ppItems++ );

        // cache binds to an external dispatch provider
        pCache->Dispatch( aReq.GetArgs(), nCallMode == SfxCallMode::SYNCHRON );
        SfxPoolItem *pVoid = new SfxVoidItem( nId );
        DeleteItemOnIdle( pVoid );
        return pVoid;
        sal_Int16 eRet = pCache->Dispatch( aReq.GetArgs(), nCallMode == SfxCallMode::SYNCHRON );
        SfxPoolItem *pPool;
        if ( eRet == css::frame::DispatchResultState::DONTKNOW )
            pPool = new SfxVoidItem( nId );
        else
            pPool = new SfxBoolItem( nId, eRet == css::frame::DispatchResultState::SUCCESS);

        DeleteItemOnIdle( pPool );
        return pPool;
    }

    // slot is handled internally by SfxDispatcher
diff --git a/sfx2/source/control/statcach.cxx b/sfx2/source/control/statcach.cxx
index d5165c4..4721b05 100644
--- a/sfx2/source/control/statcach.cxx
+++ b/sfx2/source/control/statcach.cxx
@@ -30,6 +30,8 @@
#include <com/sun/star/frame/XFrame.hpp>
#include <com/sun/star/frame/FrameActionEvent.hpp>
#include <com/sun/star/frame/FrameAction.hpp>
#include <framework/dispatchhelper.hxx>
#include <com/sun/star/frame/DispatchResultState.hpp>
#include <com/sun/star/beans/PropertyValue.hpp>
#include <cppuhelper/weak.hxx>
#include <svl/eitem.hxx>
@@ -162,17 +164,22 @@ void BindDispatch_Impl::Release()
}


void BindDispatch_Impl::Dispatch( const uno::Sequence < beans::PropertyValue >& aProps, bool bForceSynchron )
sal_Int16 BindDispatch_Impl::Dispatch( const css::uno::Sequence < css::beans::PropertyValue >& aProps, bool bForceSynchron )
{
    sal_Int16 eRet = css::frame::DispatchResultState::DONTKNOW;

    if ( xDisp.is() && aStatus.IsEnabled )
    {
        sal_Int32 nLength = aProps.getLength();
        uno::Sequence < beans::PropertyValue > aProps2 = aProps;
        aProps2.realloc(nLength+1);
        aProps2[nLength].Name = "SynchronMode";
        aProps2[nLength].Value <<= bForceSynchron ;
        xDisp->dispatch( aURL, aProps2 );
        ::rtl::Reference< ::framework::DispatchHelper > xHelper( new ::framework::DispatchHelper(nullptr));
        css::uno::Any aResult = xHelper->executeDispatch(xDisp, aURL, bForceSynchron, aProps);

        css::frame::DispatchResultEvent aEvent;
        aResult >>= aEvent;

        eRet = aEvent.State;
    }

    return eRet;
}


@@ -479,17 +486,22 @@ css::uno::Reference< css::frame::XDispatch >  SfxStateCache::GetDispatch() const
    return css::uno::Reference< css::frame::XDispatch > ();
}

void SfxStateCache::Dispatch( const SfxItemSet* pSet, bool bForceSynchron )
sal_Int16 SfxStateCache::Dispatch( const SfxItemSet* pSet, bool bForceSynchron )
{
    // protect pDispatch against destruction in the call
    css::uno::Reference < css::frame::XStatusListener > xKeepAlive( pDispatch );
    sal_Int16 eRet = css::frame::DispatchResultState::DONTKNOW;

    if ( pDispatch )
    {
        uno::Sequence < beans::PropertyValue > aArgs;
        if (pSet)
            TransformItems( nId, *pSet, aArgs );
        pDispatch->Dispatch( aArgs, bForceSynchron );

        eRet = pDispatch->Dispatch( aArgs, bForceSynchron );
    }

    return eRet;
}


diff --git a/sfx2/source/control/unoctitm.cxx b/sfx2/source/control/unoctitm.cxx
index 3c00b52..fbbebfe 100644
--- a/sfx2/source/control/unoctitm.cxx
+++ b/sfx2/source/control/unoctitm.cxx
@@ -702,7 +702,14 @@ void SAL_CALL SfxDispatchController_Impl::dispatch( const css::util::URL& aURL,
                        {
                            // execute with arguments - call directly
                            pItem = pDispatcher->Execute(GetId(), nCall, xSet.get(), &aInternalSet, nModifier);
                            bSuccess = (pItem != nullptr);
                            if ( pItem != nullptr )
                            {
                                if ( dynamic_cast< const SfxBoolItem *>( pItem ) !=  nullptr )
                                    bSuccess = dynamic_cast< const SfxBoolItem *>( pItem )->GetValue();
                                else if ( dynamic_cast< const SfxVoidItem *>( pItem ) ==  nullptr )
                                    bSuccess = true;  // all other types are true
                            }
                            // else bSuccess = false look to line 664 it is false
                        }
                        else
                        {
diff --git a/sfx2/source/inc/statcach.hxx b/sfx2/source/inc/statcach.hxx
index c34c089..d55bb2d 100644
--- a/sfx2/source/inc/statcach.hxx
+++ b/sfx2/source/inc/statcach.hxx
@@ -56,7 +56,7 @@ public:

    void                    Release();
    const css::frame::FeatureStateEvent& GetStatus() const { return aStatus;}
    void                    Dispatch( const css::uno::Sequence < css::beans::PropertyValue >& aProps, bool bForceSynchron = false );
    sal_Int16               Dispatch( const css::uno::Sequence < css::beans::PropertyValue >& aProps, bool bForceSynchron = false );
};

class SfxStateCache
@@ -89,7 +89,7 @@ public:
    const SfxSlotServer*    GetSlotServer( SfxDispatcher &rDispat )
                            { return GetSlotServer( rDispat, css::uno::Reference< css::frame::XDispatchProvider > () ); }
    css::uno::Reference< css::frame::XDispatch >          GetDispatch() const;
    void                    Dispatch( const SfxItemSet* pSet, bool bForceSynchron = false );
    sal_Int16               Dispatch( const SfxItemSet* pSet, bool bForceSynchron = false );
    bool                    IsControllerDirty() const
                            { return bCtrlDirty; }
    void                    ClearCache();