/* -*- 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 <sfx2/viewfrm.hxx>
#include <svx/dataaccessdescriptor.hxx>
#include <svl/hint.hxx>
#include <vcl/svapp.hxx>
 
#include <com/sun/star/frame/XDispatchProviderInterception.hpp>
#include <com/sun/star/view/XSelectionSupplier.hpp>
#include <com/sun/star/sdb/CommandType.hpp>
 
#include <dispuno.hxx>
#include <tabvwsh.hxx>
#include <dbdocfun.hxx>
#include <dbdata.hxx>
 
using namespace com::sun::star;
 
static const char cURLInsertColumns[] = ".uno:DataSourceBrowser/InsertColumns"; //data into text
static const char cURLDocDataSource[] = ".uno:DataSourceBrowser/DocumentDataSource";
 
static uno::Reference<view::XSelectionSupplier> lcl_GetSelectionSupplier( const SfxViewShell* pViewShell )
{
    if ( pViewShell )
    {
        SfxViewFrame* pViewFrame = pViewShell->GetViewFrame();
        if (pViewFrame)
        {
            return uno::Reference<view::XSelectionSupplier>( pViewFrame->GetFrame().GetController(), uno::UNO_QUERY );
        }
    }
    return uno::Reference<view::XSelectionSupplier>();
}
 
ScDispatchProviderInterceptor::ScDispatchProviderInterceptor(ScTabViewShell* pViewSh) :
    pViewShell( pViewSh )
{
    if ( pViewShell )
    {
        m_xIntercepted.set(uno::Reference<frame::XDispatchProviderInterception>(pViewShell->GetViewFrame()->GetFrame().GetFrameInterface(), uno::UNO_QUERY));
        if (m_xIntercepted.is())
        {
            osl_atomic_increment( &m_refCount );
 
            m_xIntercepted->registerDispatchProviderInterceptor(
                        static_cast<frame::XDispatchProviderInterceptor*>(this));
            // this should make us the top-level dispatch-provider for the component, via a call to our
            // setDispatchProvider we should have got an fallback for requests we (i.e. our master) cannot fulfill
            uno::Reference<lang::XComponent> xInterceptedComponent(m_xIntercepted, uno::UNO_QUERY);
            if (xInterceptedComponent.is())
                xInterceptedComponent->addEventListener(static_cast<lang::XEventListener*>(this));
 
            osl_atomic_decrement( &m_refCount );
        }
 
        StartListening(*pViewShell);
    }
}
 
ScDispatchProviderInterceptor::~ScDispatchProviderInterceptor()
{
    if (pViewShell)
        EndListening(*pViewShell);
}
 
void ScDispatchProviderInterceptor::Notify( SfxBroadcaster&, const SfxHint& rHint )
{
    if ( rHint.GetId() == SfxHintId::Dying )
        pViewShell = nullptr;
}
 
// XDispatchProvider
 
uno::Reference<frame::XDispatch> SAL_CALL ScDispatchProviderInterceptor::queryDispatch(
                        const util::URL& aURL, const OUString& aTargetFrameName,
                        sal_Int32 nSearchFlags )
{
    SolarMutexGuard aGuard;
 
    uno::Reference<frame::XDispatch> xResult;
    // create some dispatch ...
    if ( pViewShell && (
        aURL.Complete == cURLInsertColumns ||
        aURL.Complete == cURLDocDataSource ) )
    {
        if (!m_xMyDispatch.is())
            m_xMyDispatch = new ScDispatch( pViewShell );
        xResult = m_xMyDispatch;
    }
 
    // ask our slave provider
    if (!xResult.is() && m_xSlaveDispatcher.is())
        xResult = m_xSlaveDispatcher->queryDispatch(aURL, aTargetFrameName, nSearchFlags);
 
    return xResult;
}
 
uno::Sequence< uno::Reference<frame::XDispatch> > SAL_CALL
                        ScDispatchProviderInterceptor::queryDispatches(
                        const uno::Sequence<frame::DispatchDescriptor>& aDescripts )
{
    SolarMutexGuard aGuard;
 
    uno::Sequence< uno::Reference< frame::XDispatch> > aReturn(aDescripts.getLength());
    uno::Reference< frame::XDispatch>* pReturn = aReturn.getArray();
    const frame::DispatchDescriptor* pDescripts = aDescripts.getConstArray();
    for (sal_Int32 i=0; i<aDescripts.getLength(); ++i, ++pReturn, ++pDescripts)
    {
        *pReturn = queryDispatch(pDescripts->FeatureURL,
                pDescripts->FrameName, pDescripts->SearchFlags);
    }
    return aReturn;
}
 
// XDispatchProviderInterceptor
 
uno::Reference<frame::XDispatchProvider> SAL_CALL
                        ScDispatchProviderInterceptor::getSlaveDispatchProvider()
{
    SolarMutexGuard aGuard;
    return m_xSlaveDispatcher;
}
 
void SAL_CALL ScDispatchProviderInterceptor::setSlaveDispatchProvider(
                        const uno::Reference<frame::XDispatchProvider>& xNewDispatchProvider )
{
    SolarMutexGuard aGuard;
    m_xSlaveDispatcher.set(xNewDispatchProvider);
}
 
uno::Reference<frame::XDispatchProvider> SAL_CALL
                        ScDispatchProviderInterceptor::getMasterDispatchProvider()
{
    SolarMutexGuard aGuard;
    return m_xMasterDispatcher;
}
 
void SAL_CALL ScDispatchProviderInterceptor::setMasterDispatchProvider(
                        const uno::Reference<frame::XDispatchProvider>& xNewSupplier )
{
    SolarMutexGuard aGuard;
    m_xMasterDispatcher.set(xNewSupplier);
}
 
// XEventListener
 
void SAL_CALL ScDispatchProviderInterceptor::disposing( const lang::EventObject& /* Source */ )
{
    SolarMutexGuard aGuard;
 
    if (m_xIntercepted.is())
    {
        m_xIntercepted->releaseDispatchProviderInterceptor(
                static_cast<frame::XDispatchProviderInterceptor*>(this));
        uno::Reference<lang::XComponent> xInterceptedComponent(m_xIntercepted, uno::UNO_QUERY);
        if (xInterceptedComponent.is())
            xInterceptedComponent->removeEventListener(static_cast<lang::XEventListener*>(this));
 
        m_xMyDispatch = nullptr;
    }
    m_xIntercepted = nullptr;
}
 
ScDispatch::ScDispatch(ScTabViewShell* pViewSh) :
    pViewShell( pViewSh ),
    bListeningToView( false )
{
    if (pViewShell)
        StartListening(*pViewShell);
}
 
ScDispatch::~ScDispatch()
{
    if (pViewShell)
        EndListening(*pViewShell);
 
    if (bListeningToView && pViewShell)
    {
        uno::Reference<view::XSelectionSupplier> xSupplier(lcl_GetSelectionSupplier( pViewShell ));
        if ( xSupplier.is() )
            xSupplier->removeSelectionChangeListener(this);
    }
}
 
void ScDispatch::Notify( SfxBroadcaster&, const SfxHint& rHint )
{
    if ( rHint.GetId() == SfxHintId::Dying )
        pViewShell = nullptr;
}
 
// XDispatch
 
void SAL_CALL ScDispatch::dispatch( const util::URL& aURL,
                                const uno::Sequence<beans::PropertyValue>& aArgs )
{
    SolarMutexGuard aGuard;
 
    bool bDone = false;
    if ( pViewShell && aURL.Complete == cURLInsertColumns )
    {
        ScViewData& rViewData = pViewShell->GetViewData();
        ScAddress aPos( rViewData.GetCurX(), rViewData.GetCurY(), rViewData.GetTabNo() );
 
        ScDBDocFunc aFunc( *rViewData.GetDocShell() );
        aFunc.DoImportUno( aPos, aArgs );
        bDone = true;
    }
    // cURLDocDataSource is never dispatched
 
    if (!bDone)
        throw uno::RuntimeException();
}
 
static void lcl_FillDataSource( frame::FeatureStateEvent& rEvent, const ScImportParam& rParam )
{
    rEvent.IsEnabled = rParam.bImport;
 
    svx::ODataAccessDescriptor aDescriptor;
    if ( rParam.bImport )
    {
        sal_Int32 nType = rParam.bSql ? sdb::CommandType::COMMAND :
                    ( (rParam.nType == ScDbQuery) ? sdb::CommandType::QUERY :
                                                    sdb::CommandType::TABLE );
 
        aDescriptor.setDataSource(rParam.aDBName);
        aDescriptor[svx::DataAccessDescriptorProperty::Command]     <<= rParam.aStatement;
        aDescriptor[svx::DataAccessDescriptorProperty::CommandType] <<= nType;
    }
    else
    {
        //  descriptor has to be complete anyway
 
        aDescriptor[svx::DataAccessDescriptorProperty::DataSource]  <<= OUString();
        aDescriptor[svx::DataAccessDescriptorProperty::Command]     <<= OUString();
        aDescriptor[svx::DataAccessDescriptorProperty::CommandType] <<= sal_Int32(sdb::CommandType::TABLE);
    }
    rEvent.State <<= aDescriptor.createPropertyValueSequence();
}
 
void SAL_CALL ScDispatch::addStatusListener(
    const uno::Reference<frame::XStatusListener>& xListener,
    const util::URL& aURL)
{
    SolarMutexGuard aGuard;
 
    if (!pViewShell)
        throw uno::RuntimeException();
 
    //  initial state
    frame::FeatureStateEvent aEvent;
    aEvent.IsEnabled = true;
    aEvent.Source.set(static_cast<cppu::OWeakObject*>(this));
    aEvent.FeatureURL = aURL;
 
    if ( aURL.Complete == cURLDocDataSource )
    {
        aDataSourceListeners.emplace_back( xListener );
 
        if (!bListeningToView)
        {
            uno::Reference<view::XSelectionSupplier> xSupplier(lcl_GetSelectionSupplier( pViewShell ));
            if ( xSupplier.is() )
                xSupplier->addSelectionChangeListener(this);
            bListeningToView = true;
        }
 
        ScDBData* pDBData = pViewShell->GetDBData(false,SC_DB_OLD);
        if ( pDBData )
            pDBData->GetImportParam( aLastImport );
        lcl_FillDataSource( aEvent, aLastImport );          // modifies State, IsEnabled
    }
    //! else add to listener for "enabled" changes?
 
    xListener->statusChanged( aEvent );
}
 
void SAL_CALL ScDispatch::removeStatusListener(
                                const uno::Reference<frame::XStatusListener>& xListener,
                                const util::URL& aURL )
{
    SolarMutexGuard aGuard;
 
    if ( aURL.Complete == cURLDocDataSource )
    {
        sal_uInt16 nCount = aDataSourceListeners.size();
        for ( sal_uInt16 n=nCount; n--; )
        {
            uno::Reference<frame::XStatusListener>& rObj = aDataSourceListeners[n];
            if ( rObj == xListener )
            {
                aDataSourceListeners.erase( aDataSourceListeners.begin() + n );
                break;
            }
        }
 
        if ( aDataSourceListeners.empty() && pViewShell )
        {
            uno::Reference<view::XSelectionSupplier> xSupplier(lcl_GetSelectionSupplier( pViewShell ));
            if ( xSupplier.is() )
                xSupplier->removeSelectionChangeListener(this);
            bListeningToView = false;
        }
    }
}
 
// XSelectionChangeListener
 
void SAL_CALL ScDispatch::selectionChanged( const css::lang::EventObject& /* aEvent */ )
{
    //  currently only called for URL cURLDocDataSource
 
    if ( pViewShell )
    {
        ScImportParam aNewImport;
        ScDBData* pDBData = pViewShell->GetDBData(false,SC_DB_OLD);
        if ( pDBData )
            pDBData->GetImportParam( aNewImport );
 
        //  notify listeners only if data source has changed
        if ( aNewImport.bImport    != aLastImport.bImport ||
             aNewImport.aDBName    != aLastImport.aDBName ||
             aNewImport.aStatement != aLastImport.aStatement ||
             aNewImport.bSql       != aLastImport.bSql ||
             aNewImport.nType      != aLastImport.nType )
        {
            frame::FeatureStateEvent aEvent;
            aEvent.Source.set(static_cast<cppu::OWeakObject*>(this));
            aEvent.FeatureURL.Complete = cURLDocDataSource;
 
            lcl_FillDataSource( aEvent, aNewImport );       // modifies State, IsEnabled
 
            for (uno::Reference<frame::XStatusListener> & xDataSourceListener : aDataSourceListeners)
                xDataSourceListener->statusChanged( aEvent );
 
            aLastImport = aNewImport;
        }
    }
}
 
// XEventListener
 
void SAL_CALL ScDispatch::disposing( const css::lang::EventObject& rSource )
{
    uno::Reference<view::XSelectionSupplier> xSupplier(rSource.Source, uno::UNO_QUERY);
    xSupplier->removeSelectionChangeListener(this);
    bListeningToView = false;
 
    lang::EventObject aEvent;
    aEvent.Source.set(static_cast<cppu::OWeakObject*>(this));
    for (uno::Reference<frame::XStatusListener> & xDataSourceListener : aDataSourceListeners)
        xDataSourceListener->disposing( aEvent );
 
    pViewShell = nullptr;
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V1029 Numeric Truncation Error. Result of the 'size' function is written to the 16-bit variable.