/**************************************************************
 * 
 * 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
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_dbaccess.hxx"
#ifndef DBAUI_GENERICCONTROLLER_HXX
#include "genericcontroller.hxx"
#endif
#ifndef _COMPHELPER_UNO3_HXX_
#include <comphelper/uno3.hxx>
#endif
#ifndef _TOOLKIT_AWT_VCLXWINDOW_HXX_
#include <toolkit/awt/vclxwindow.hxx>
#endif
#ifndef DBACCESS_UI_BROWSER_ID_HXX
#include "browserids.hxx"
#endif
#ifndef _SV_SVAPP_HXX //autogen
#include <vcl/svapp.hxx>
#endif
#ifndef _TOOLKIT_HELPER_VCLUNOHELPER_HXX_
#include <toolkit/helper/vclunohelper.hxx>
#endif
#ifndef DBAUI_DATAVIEW_HXX
#include "dataview.hxx"
#endif
#ifndef _TOOLS_DEBUG_HXX
#include <tools/debug.hxx>
#endif
#ifndef TOOLS_DIAGNOSE_EX_H
#include <tools/diagnose_ex.h>
#endif
#ifndef DBACCESS_SHARED_DBUSTRINGS_HRC
#include "dbustrings.hrc"
#endif
#ifndef _VCL_STDTEXT_HXX
#include <vcl/stdtext.hxx>
#endif
#ifndef _CPPUHELPER_TYPEPROVIDER_HXX_
#include <cppuhelper/typeprovider.hxx>
#endif
#include <framework/titlehelper.hxx>
#ifndef _COMPHELPER_SEQUENCE_HXX_
#include <comphelper/sequence.hxx>
#endif
#ifndef _COMPHELPER_EXTRACT_HXX_
#include <comphelper/extract.hxx>
#endif
#ifndef _COM_SUN_STAR_SDBC_XDATASOURCE_HPP_
#include <com/sun/star/sdbc/XDataSource.hpp>
#endif
#ifndef _COM_SUN_STAR_SDB_SQLCONTEXT_HPP_
#include <com/sun/star/sdb/SQLContext.hpp>
#endif
#ifndef _COM_SUN_STAR_SDB_XCOMPLETEDCONNECTION_HPP_
#include <com/sun/star/sdb/XCompletedConnection.hpp>
#endif
#ifndef _COM_SUN_STAR_BEANS_XPROPERTYSET_HPP_
#include <com/sun/star/beans/XPropertySet.hpp>
#endif
#ifndef _COM_SUN_STAR_TASK_XINTERACTIONHANDLER_HPP_
#include <com/sun/star/task/XInteractionHandler.hpp>
#endif
#ifndef _COM_SUN_STAR_UTIL_XCLOSEABLE_HPP_
#include <com/sun/star/util/XCloseable.hpp>
#endif
#ifndef DBAUI_TOOLS_HXX
#include "UITools.hxx"
#endif
#ifndef _DBAUI_COMMON_TYPES_HXX_
#include "commontypes.hxx"
#endif

#ifndef _SV_WAITOBJ_HXX
#include <vcl/waitobj.hxx>
#endif
#ifndef _URLOBJ_HXX
#include <tools/urlobj.hxx>
#endif
#ifndef SVTOOLS_URIHELPER_HXX
#include <svl/urihelper.hxx>
#endif
#ifndef _DBAUI_DATASOURCECONNECTOR_HXX_
#include "datasourceconnector.hxx"
#endif
#ifndef INCLUDED_SVTOOLS_MODULEOPTIONS_HXX
#include <unotools/moduleoptions.hxx>
#endif
#ifndef _COM_SUN_STAR_FRAME_FRAMESEARCHFLAG_HPP_
#include <com/sun/star/frame/FrameSearchFlag.hpp>
#endif
#ifndef _COM_SUN_STAR_FRAME_STATUS_VISIBILITY_HPP_
#include <com/sun/star/frame/status/Visibility.hpp>
#endif
#ifndef _COM_SUN_STAR_UTIL_XMODIFIABLE_HPP_
#include <com/sun/star/util/XModifiable.hpp>
#endif
#ifndef _RTL_USTRING_HXX_
#include <rtl/ustring.hxx>
#endif
#ifndef _RTL_LOGFILE_HXX_
#include <rtl/logfile.hxx>
#endif
#include <algorithm>
#include <hash_map>
#include <cppuhelper/implbase1.hxx>
#include <limits>

using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::frame;
using namespace ::com::sun::star::frame::status;
using namespace ::com::sun::star::util;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::container;
using namespace ::com::sun::star::sdbc;
using namespace ::com::sun::star::sdb;
using namespace ::com::sun::star::task;
using namespace ::com::sun::star::awt;
using namespace ::com::sun::star;
using namespace ::dbtools;
using namespace ::comphelper;

// -------------------------------------------------------------------------
#define ALL_FEATURES	            -1
#define FIRST_USER_DEFINED_FEATURE  ( ::std::numeric_limits< sal_uInt16 >::max() - 1000 )
#define LAST_USER_DEFINED_FEATURE   ( ::std::numeric_limits< sal_uInt16 >::max()        )

// -------------------------------------------------------------------------
typedef ::std::hash_map< sal_Int16, sal_Int16 > CommandHashMap;
typedef ::std::list< DispatchInformation > DispatchInfoList;


// -------------------------------------------------------------------------
const ::rtl::OUString& getConfirmDeletionURL()
{
	static const ::rtl::OUString sConfirmDeletionURL( RTL_CONSTASCII_USTRINGPARAM( ".uno:FormSlots/ConfirmDeletion" ) );
	return sConfirmDeletionURL;
}

namespace dbaui
{

//==========================================================================
//= UserDefinedFeatures
//==========================================================================
class UserDefinedFeatures
{
public:
    UserDefinedFeatures( const Reference< XController >& _rxController );

    FeatureState    getState( const URL& _rFeatureURL );
    void            execute( const URL& _rFeatureURL, const Sequence< PropertyValue>& _rArgs );

private:
    ::com::sun::star::uno::WeakReference< XController > m_aController;
};

//--------------------------------------------------------------------------
UserDefinedFeatures::UserDefinedFeatures( const Reference< XController >& _rxController )
    :m_aController( _rxController )
{
}

//--------------------------------------------------------------------------
FeatureState UserDefinedFeatures::getState( const URL& /*_rFeatureURL*/ )
{
    // for now, enable all the time
    // TODO: we should ask the dispatcher. However, this is laborious, since you cannot ask a dispatcher
    // directly, but need to add a status listener.
    FeatureState aState;
    aState.bEnabled = sal_True;
    return aState;
}

//--------------------------------------------------------------------------
void UserDefinedFeatures::execute( const URL& _rFeatureURL, const Sequence< PropertyValue>& _rArgs )
{
    try
    {
        Reference< XController > xController( (Reference< XController >)m_aController, UNO_SET_THROW );
        Reference< XDispatchProvider > xDispatchProvider( xController->getFrame(), UNO_QUERY_THROW );
        Reference< XDispatch > xDispatch( xDispatchProvider->queryDispatch(
            _rFeatureURL,
            ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "_self" ) ),
            FrameSearchFlag::AUTO
        ) );

        if ( xDispatch == xController )
        {
            OSL_ENSURE( false, "UserDefinedFeatures::execute: the controller shouldn't be the dispatcher here!" );
            xDispatch.clear();
        }

        if ( xDispatch.is() )
            xDispatch->dispatch( _rFeatureURL, _rArgs );
    }
    catch( const Exception& )
    {
    	DBG_UNHANDLED_EXCEPTION();
    }
}

//==========================================================================
//= OGenericUnoController_Data
//==========================================================================
struct OGenericUnoController_Data
{
    ::sfx2::UserInputInterception   m_aUserInputInterception;
    UserDefinedFeatures             m_aUserDefinedFeatures;

    OGenericUnoController_Data( OGenericUnoController& _rController, ::osl::Mutex& _rMutex )
        :m_aUserInputInterception( _rController, _rMutex )
        ,m_aUserDefinedFeatures( _rController.getXController() )
    {
    }
};

//==========================================================================
//= OGenericUnoController
//==========================================================================
DBG_NAME(OGenericUnoController)
// -------------------------------------------------------------------------
OGenericUnoController::OGenericUnoController(const Reference< XMultiServiceFactory >& _rM)
	:OGenericUnoController_Base( getMutex() )
	,m_pView(NULL)
#ifdef DBG_UTIL
    ,m_bDescribingSupportedFeatures( false )
#endif
	,m_aAsyncInvalidateAll(LINK(this, OGenericUnoController, OnAsyncInvalidateAll))
	,m_aAsyncCloseTask(LINK(this, OGenericUnoController, OnAsyncCloseTask))
	,m_xServiceFactory(_rM)
    ,m_aCurrentFrame( *this )
	,m_bPreview(sal_False)
	,m_bReadOnly(sal_False)
	,m_bCurrentlyModified(sal_False)
    ,m_bExternalTitle(sal_False)
{
    osl_incrementInterlockedCount( &m_refCount );
    {
        m_pData.reset( new OGenericUnoController_Data( *this, getMutex() ) );
    }
    osl_decrementInterlockedCount( &m_refCount );

    DBG_CTOR(OGenericUnoController,NULL);

	try
	{
		m_xUrlTransformer = Reference< XURLTransformer > (_rM->createInstance(::rtl::OUString::createFromAscii("com.sun.star.util.URLTransformer")), UNO_QUERY);
	}
	catch(Exception&)
	{
        DBG_UNHANDLED_EXCEPTION();
	}
}

#ifdef WNT
// -----------------------------------------------------------------------------
OGenericUnoController::OGenericUnoController()
	:OGenericUnoController_Base( getMutex() )
	,m_pView(NULL)
#ifdef DBG_UTIL
    ,m_bDescribingSupportedFeatures( false )
#endif
	,m_aAsyncInvalidateAll(LINK(this, OGenericUnoController, OnAsyncInvalidateAll))
	,m_aAsyncCloseTask(LINK(this, OGenericUnoController, OnAsyncCloseTask))
    ,m_aCurrentFrame( *this )
	,m_bPreview(sal_False)
	,m_bReadOnly(sal_False)
	,m_bCurrentlyModified(sal_False)
{
    OSL_ENSURE( false, "OGenericUnoController::OGenericUnoController: illegal call!" );
    // This ctor only exists because the MSVC compiler complained about an unresolved external
    // symbol. It should not be used at all. Since using it yields strange runtime problems,
    // we simply abort here.
    abort();
}
#endif

// -----------------------------------------------------------------------------
OGenericUnoController::~OGenericUnoController()
{

    DBG_DTOR(OGenericUnoController,NULL);
}

// -----------------------------------------------------------------------------
sal_Bool OGenericUnoController::Construct(Window* /*pParent*/)
{
	OSL_ENSURE( getView(), "the view is NULL!" );

	if ( getView() )
	{
		getView()->Construct();
		getView()->Show();
	}

    m_aSupportedFeatures.clear();
	fillSupportedFeatures();

	// create the database context
	DBG_ASSERT(getORB().is(), "OGenericUnoController::Construct need a service factory!");
	try
	{
		m_xDatabaseContext = Reference< XNameAccess >(getORB()->createInstance(SERVICE_SDB_DATABASECONTEXT), UNO_QUERY);
	}
	catch(Exception&)
	{
		DBG_ERROR("OGenericUnoController::Construct: could not create (or start listening at) the database context!");
	}

	if (!m_xDatabaseContext.is())
	{		// at least notify the user. Though the whole component does not make any sense without the database context ...
		ShowServiceNotAvailableError(getView(), String(SERVICE_SDB_DATABASECONTEXT), sal_True);
	}
	return sal_True;
}
//------------------------------------------------------------------------------
IMPL_LINK(OGenericUnoController, OnAsyncInvalidateAll, void*, EMPTYARG)
{
	if ( !OGenericUnoController_Base::rBHelper.bInDispose && !OGenericUnoController_Base::rBHelper.bDisposed )
		InvalidateFeature_Impl();
	return 0L;
}
// -----------------------------------------------------------------------------
void OGenericUnoController::impl_initialize()
{
}
// -------------------------------------------------------------------------
void SAL_CALL OGenericUnoController::initialize( const Sequence< Any >& aArguments ) throw(Exception, RuntimeException)
{
	vos::OGuard aSolarGuard( Application::GetSolarMutex() );
	::osl::MutexGuard aGuard( getMutex() );

	Reference< XWindow >		xParent;
	Reference< XFrame > xFrame;

	PropertyValue aValue;
	const Any* pIter	= aArguments.getConstArray();
	const Any* pEnd 	= pIter + aArguments.getLength();

	for ( ; pIter != pEnd; ++pIter )
	{
		if ( ( *pIter >>= aValue ) && ( 0 == aValue.Name.compareToAscii( "Frame" ) ) )
		{
            xFrame.set(aValue.Value,UNO_QUERY_THROW);
		}
        /* #i42316#
		else if ( ( *pIter >>= aValue ) && ( 0 == aValue.Name.compareToAscii( "ReadOnly" ) ) )
		{
			aValue.Value >>= m_bReadOnly;
		}
        */
		else if ( ( *pIter >>= aValue ) && ( 0 == aValue.Name.compareToAscii( "Preview" ) ) )
		{
			aValue.Value >>= m_bPreview;
            m_bReadOnly = sal_True;
		}
	}
	try
	{
		if ( !xFrame.is() )
            throw IllegalArgumentException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "need a frame" ) ), *this, 1 );

        xParent = xFrame->getContainerWindow();
		VCLXWindow* pParentComponent = VCLXWindow::GetImplementation(xParent);
		Window* pParentWin = pParentComponent ? pParentComponent->GetWindow() : NULL;
		if (!pParentWin)
		{
			throw IllegalArgumentException( ::rtl::OUString::createFromAscii( "Parent window is null" ), *this, 1 );
		}

        m_aInitParameters.assign( aArguments );
		Construct( pParentWin );

        ODataView* pView = getView();
        if ( !pView )
            throw RuntimeException( ::rtl::OUString::createFromAscii( "unable to create a view" ), *this );

		if ( m_bReadOnly || m_bPreview )
			pView->EnableInput( sal_False );

        impl_initialize();
	}
	catch(Exception& e)
	{
		// no one clears my view if I won't
		::std::auto_ptr<Window> aTemp(m_pView);
		m_pView = NULL;
		throw;
	}
}

//------------------------------------------------------------------------------
void SAL_CALL OGenericUnoController::acquire(  ) throw ()
{
	OGenericUnoController_Base::acquire();
}

//------------------------------------------------------------------------------
void SAL_CALL OGenericUnoController::release(  ) throw ()
{
	OGenericUnoController_Base::release();
}

// -------------------------------------------------------------------------
void OGenericUnoController::startFrameListening( const Reference< XFrame >& _rxFrame )
{
    if ( _rxFrame.is() )
	    _rxFrame->addFrameActionListener( this );
}

// -------------------------------------------------------------------------
void OGenericUnoController::stopFrameListening( const Reference< XFrame >& _rxFrame )
{
	if ( _rxFrame.is() )
		_rxFrame->removeFrameActionListener( this );
}

// -------------------------------------------------------------------------
void OGenericUnoController::disposing(const EventObject& Source) throw( RuntimeException )
{
	// our frame ?
	if ( Source.Source == getFrame() )
		stopFrameListening( getFrame() );
}
//------------------------------------------------------------------------
void OGenericUnoController::modified(const EventObject& aEvent) throw( RuntimeException )
{
	::osl::MutexGuard aGuard( getMutex() );
	if ( !isDataSourceReadOnly() )
	{
		Reference<XModifiable> xModi(aEvent.Source,UNO_QUERY);
		if ( xModi.is() )
			m_bCurrentlyModified = xModi->isModified(); // can only be reset by save
		else
			m_bCurrentlyModified = sal_True;
	}
	InvalidateFeature(ID_BROWSER_SAVEDOC);
	InvalidateFeature(ID_BROWSER_UNDO);
}
// -----------------------------------------------------------------------
Reference< XWindow > SAL_CALL OGenericUnoController::getComponentWindow() throw (RuntimeException)
{
	return VCLUnoHelper::GetInterface( getView() );
}

// -----------------------------------------------------------------------
::rtl::OUString SAL_CALL OGenericUnoController::getViewControllerName() throw (::com::sun::star::uno::RuntimeException)
{
    return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Default" ) );
}

// -----------------------------------------------------------------------
Sequence< PropertyValue > SAL_CALL OGenericUnoController::getCreationArguments() throw (RuntimeException)
{
    // currently we do not support any creation args, so anything passed to XModel2::createViewController would be
    // lost, so we can equally return an empty sequence here
    return Sequence< PropertyValue >();
}

// -----------------------------------------------------------------------
void OGenericUnoController::attachFrame( const Reference< XFrame >& _rxFrame ) throw( RuntimeException )
{
    vos::OGuard aSolarGuard( Application::GetSolarMutex() );
	::osl::MutexGuard aGuard( getMutex() );

    stopFrameListening( m_aCurrentFrame.getFrame() );
	Reference< XFrame > xFrame = m_aCurrentFrame.attachFrame( _rxFrame );
	startFrameListening( xFrame );

	loadMenu( xFrame );

	if ( getView() )
		getView()->attachFrame( xFrame );
}

// -----------------------------------------------------------------------------
struct CommandCollector : public ::std::unary_function< SupportedFeatures::value_type, void>
{
	sal_uInt16  m_nFeature;
	StringBag&  m_rFeatureCommands;
	CommandCollector( sal_uInt16 _nFeature, StringBag& _rFeatureCommands )
        :m_nFeature        ( _nFeature         )
        ,m_rFeatureCommands( _rFeatureCommands )
    {
    }

	void operator() ( const SupportedFeatures::value_type& lhs )
	{
		if ( lhs.second.nFeatureId == m_nFeature )
			m_rFeatureCommands.insert( lhs.first );
	}
};

// -----------------------------------------------------------------------
namespace
{
    typedef ::std::vector< Any >    States;

    // ...................................................................
    void    lcl_notifyMultipleStates( XStatusListener& _rListener, FeatureStateEvent& _rEvent, const States& _rStates )
    {
        for (   States::const_iterator state = _rStates.begin();
                state != _rStates.end();
                ++state
            )
        {
            _rEvent.State = *state;
            _rListener.statusChanged( _rEvent );
        }
    }

    // ...................................................................
    void    lcl_collectStates( const FeatureState& _rFeatureState, States& _out_rStates )
    {
        // order matters, due to a bug in framework which resets the check state when any non-boolean event
        // arrives
        // #i68215# is the bug to (re-)introduce this "ordered" notification here
        // #i67882# is the bug which was caused by the real fix which we did in framework
        // #i68216# is the bug which requests to fix the code in Draw which relies on
        //          framework's implementation details
        // 2006-08-07 / frank.schoenheit@sun.com
        if ( !!_rFeatureState.sTitle )
            _out_rStates.push_back( makeAny( *_rFeatureState.sTitle ) );
        if ( !!_rFeatureState.bChecked )
            _out_rStates.push_back( makeAny( (sal_Bool)*_rFeatureState.bChecked ) );
        if ( !!_rFeatureState.bInvisible )
            _out_rStates.push_back( makeAny( Visibility( !*_rFeatureState.bInvisible ) ) );
        if ( _rFeatureState.aValue.hasValue() )
            _out_rStates.push_back( _rFeatureState.aValue );
        if ( _out_rStates.empty() )
            _out_rStates.push_back( Any() );
    }
}

// -----------------------------------------------------------------------
void OGenericUnoController::ImplBroadcastFeatureState(const ::rtl::OUString& _rFeature, const Reference< XStatusListener > & xListener, sal_Bool _bIgnoreCache)
{
	sal_uInt16 nFeat = m_aSupportedFeatures[ _rFeature ].nFeatureId;
	FeatureState aFeatState( GetState( nFeat ) );

	FeatureState& rCachedState = m_aStateCache[nFeat];	// creates if neccessary
	if ( !_bIgnoreCache )
	{
		// check if we really need to notify the listeners : this method may be called much more often than needed, so check
		// the cached state of the feature
		sal_Bool bAlreadyCached = ( m_aStateCache.find(nFeat) != m_aStateCache.end() );
		if ( bAlreadyCached )
            if  (   ( rCachedState.bEnabled == aFeatState.bEnabled )
                &&  ( rCachedState.bChecked == aFeatState.bChecked )
                &&  ( rCachedState.bInvisible == aFeatState.bInvisible )
                &&  ( rCachedState.sTitle == aFeatState.sTitle )
                )
            return;
	}
	rCachedState = aFeatState;

	FeatureStateEvent aEvent;
	aEvent.FeatureURL.Complete = _rFeature;
	if (m_xUrlTransformer.is())
		m_xUrlTransformer->parseStrict(aEvent.FeatureURL);
	aEvent.Source		= (XDispatch*)this;
	aEvent.IsEnabled	= aFeatState.bEnabled;

    // collect all states to be notified
    States aStates;
    lcl_collectStates( aFeatState, aStates );

	// a special listener ?
	if ( xListener.is() )
        lcl_notifyMultipleStates( *xListener.get(), aEvent, aStates );
	else
	{	// no -> iterate through all listeners responsible for the URL
        StringBag aFeatureCommands;
		::std::for_each(
            m_aSupportedFeatures.begin(),
            m_aSupportedFeatures.end(),
            CommandCollector( nFeat, aFeatureCommands )
        );

		// it is possible that listeners are registered or revoked while
		// we are notifying them, so we must use a copy of m_arrStatusListener, not
		// m_arrStatusListener itself
		// #121276# / 2005-05-19 / frank.schoenheit@sun.com
		Dispatch aNotifyLoop( m_arrStatusListener );
		DispatchIterator iterSearch = aNotifyLoop.begin();
		DispatchIterator iterEnd = aNotifyLoop.end();

		while (iterSearch != iterEnd)
		{
			DispatchTarget& rCurrent = *iterSearch;
			if ( aFeatureCommands.find( rCurrent.aURL.Complete ) != aFeatureCommands.end() )
			{
				aEvent.FeatureURL = rCurrent.aURL;
                lcl_notifyMultipleStates( *rCurrent.xListener.get(), aEvent, aStates );
			}
			++iterSearch;
		}
	}

}

//------------------------------------------------------------------------------
sal_Bool OGenericUnoController::isFeatureSupported( sal_Int32 _nId )
{
	SupportedFeatures::iterator aFeaturePos = ::std::find_if(
		m_aSupportedFeatures.begin(),
		m_aSupportedFeatures.end(),
		::std::bind2nd( CompareFeatureById(), _nId )
	);

    return ( m_aSupportedFeatures.end() != aFeaturePos && aFeaturePos->first.getLength());
}

// -----------------------------------------------------------------------
void OGenericUnoController::InvalidateFeature(const ::rtl::OUString& _rURLPath, const Reference< XStatusListener > & _xListener, sal_Bool _bForceBroadcast)
{
	ImplInvalidateFeature( m_aSupportedFeatures[ _rURLPath ].nFeatureId, _xListener, _bForceBroadcast );
}

// -----------------------------------------------------------------------------
void OGenericUnoController::InvalidateFeature_Impl()
{
#ifdef DBG_UTIL
	static sal_Int32 s_nRecursions = 0;
	++s_nRecursions;
#endif

	sal_Bool bEmpty = sal_True;
	FeatureListener aNextFeature;
	{
		::osl::MutexGuard aGuard( m_aFeatureMutex);
		bEmpty = m_aFeaturesToInvalidate.empty();
		if (!bEmpty)
			aNextFeature = m_aFeaturesToInvalidate.front();
	}
	while(!bEmpty)
	{
		if ( ALL_FEATURES == aNextFeature.nId )
		{
			InvalidateAll_Impl();
			break;
		}
		else
		{
			SupportedFeatures::iterator aFeaturePos = ::std::find_if(
				m_aSupportedFeatures.begin(),
				m_aSupportedFeatures.end(),
				::std::bind2nd( CompareFeatureById(), aNextFeature.nId )
			);

#if OSL_DEBUG_LEVEL > 0
            if ( m_aSupportedFeatures.end() == aFeaturePos )
            {
                ::rtl::OString sMessage( "OGenericUnoController::InvalidateFeature_Impl: feature id " );
                sMessage += ::rtl::OString::valueOf( aNextFeature.nId );
                sMessage += ::rtl::OString( " has been invalidated, but is not supported!" );
                OSL_ENSURE( false, sMessage.getStr() );
            }
#endif
			if ( m_aSupportedFeatures.end() != aFeaturePos )
				// we really know this feature
				ImplBroadcastFeatureState( aFeaturePos->first, aNextFeature.xListener, aNextFeature.bForceBroadcast );
		}

		::osl::MutexGuard aGuard( m_aFeatureMutex);
		m_aFeaturesToInvalidate.pop_front();
		bEmpty = m_aFeaturesToInvalidate.empty();
		if (!bEmpty)
			aNextFeature = m_aFeaturesToInvalidate.front();
	}

#ifdef DBG_UTIL
	--s_nRecursions;
#endif
}

// -----------------------------------------------------------------------
void OGenericUnoController::ImplInvalidateFeature( sal_Int32 _nId, const Reference< XStatusListener >& _xListener, sal_Bool _bForceBroadcast )
{
#if OSL_DEBUG_LEVEL > 0
    if ( _nId != -1 )
    {
	    SupportedFeatures::iterator aFeaturePos = ::std::find_if(
		    m_aSupportedFeatures.begin(),
		    m_aSupportedFeatures.end(),
		    ::std::bind2nd( CompareFeatureById(), _nId )
	    );
        OSL_ENSURE( aFeaturePos != m_aSupportedFeatures.end(), "OGenericUnoController::ImplInvalidateFeature: invalidating an unsupported feature is suspicious, at least!" );
    }
#endif

	FeatureListener aListener;
	aListener.nId               = _nId;
	aListener.xListener         = _xListener;
	aListener.bForceBroadcast   = _bForceBroadcast;

	sal_Bool bWasEmpty;
	{
		::osl::MutexGuard aGuard( m_aFeatureMutex );
		bWasEmpty = m_aFeaturesToInvalidate.empty();
		m_aFeaturesToInvalidate.push_back( aListener );
	}

	if ( bWasEmpty )
		m_aAsyncInvalidateAll.Call();
}

// -----------------------------------------------------------------------
void OGenericUnoController::InvalidateFeature(sal_uInt16 _nId, const Reference< XStatusListener > & _xListener, sal_Bool _bForceBroadcast)
{
	ImplInvalidateFeature( _nId, _xListener, _bForceBroadcast );
}

// -----------------------------------------------------------------------
void OGenericUnoController::InvalidateAll()
{
	ImplInvalidateFeature( ALL_FEATURES, NULL, sal_True );
}

// -----------------------------------------------------------------------------
void OGenericUnoController::InvalidateAll_Impl()
{
	// ---------------------------------
	// invalidate all aupported features

	for (   SupportedFeatures::const_iterator aIter = m_aSupportedFeatures.begin();
            aIter != m_aSupportedFeatures.end();
            ++aIter
        )
		ImplBroadcastFeatureState( aIter->first, NULL, sal_True );

	{
		::osl::MutexGuard aGuard( m_aFeatureMutex);
		DBG_ASSERT(m_aFeaturesToInvalidate.size(), "OGenericUnoController::InvalidateAll_Impl: to be called from within InvalidateFeature_Impl only!");
		m_aFeaturesToInvalidate.pop_front();
		if(!m_aFeaturesToInvalidate.empty())
			m_aAsyncInvalidateAll.Call();
	}
}

// -----------------------------------------------------------------------
Reference< XDispatch >	OGenericUnoController::queryDispatch(const URL& aURL, const ::rtl::OUString& aTargetFrameName, sal_Int32 nSearchFlags) throw( RuntimeException )
{
	Reference< XDispatch > xReturn;

    OSL_PRECOND( !m_aSupportedFeatures.empty(), "OGenericUnoController::queryDispatch: shouldn't this be filled at construction time?" );
    if ( m_aSupportedFeatures.empty() )
        fillSupportedFeatures();

	// URL's we can handle ourself?
	if  (   aURL.Complete.equals( getConfirmDeletionURL() )
        ||  (   ( m_aSupportedFeatures.find( aURL.Complete ) != m_aSupportedFeatures.end() )
            &&  !isUserDefinedFeature( aURL.Complete )
            )
        )
	{
		xReturn = this;
	}
	// no? -> ask the slave dispatcher
	else if ( m_xSlaveDispatcher.is() )
	{
		xReturn = m_xSlaveDispatcher->queryDispatch(aURL, aTargetFrameName, nSearchFlags);
	}

	// outta here
	return xReturn;
}

// -----------------------------------------------------------------------
Sequence< Reference< XDispatch > > OGenericUnoController::queryDispatches(const Sequence< DispatchDescriptor >& aDescripts) throw( RuntimeException )
{
	Sequence< Reference< XDispatch > > aReturn;
	sal_Int32 nLen = aDescripts.getLength();
	if ( nLen )
	{
		aReturn.realloc( nLen );
				Reference< XDispatch >* pReturn 	= aReturn.getArray();
		const	Reference< XDispatch >* pReturnEnd	= aReturn.getArray() + nLen;
		const	DispatchDescriptor* 	pDescripts	= aDescripts.getConstArray();

		for ( ; pReturn != pReturnEnd; ++ pReturn, ++pDescripts )
		{
			*pReturn = queryDispatch( pDescripts->FeatureURL, pDescripts->FrameName, pDescripts->SearchFlags );
		}
	}

	return aReturn;
}

// -----------------------------------------------------------------------
Reference< XDispatchProvider >	OGenericUnoController::getSlaveDispatchProvider(void) throw( RuntimeException )
{
	return m_xSlaveDispatcher;
}

// -----------------------------------------------------------------------
void OGenericUnoController::setSlaveDispatchProvider(const Reference< XDispatchProvider > & _xNewProvider) throw( RuntimeException )
{
	m_xSlaveDispatcher = _xNewProvider;
}

// -----------------------------------------------------------------------
Reference< XDispatchProvider >	OGenericUnoController::getMasterDispatchProvider(void) throw( RuntimeException )
{
	return m_xMasterDispatcher;
}

// -----------------------------------------------------------------------
void OGenericUnoController::setMasterDispatchProvider(const Reference< XDispatchProvider > & _xNewProvider) throw( RuntimeException )
{
	m_xMasterDispatcher = _xNewProvider;
}

// -----------------------------------------------------------------------
void OGenericUnoController::dispatch(const URL& _aURL, const Sequence< PropertyValue >& aArgs) throw(RuntimeException)
{
    ::vos::OGuard aSolarGuard( Application::GetSolarMutex() );
    // Since the fix for #123967#, the SolarMutex is not locked anymore when the framework calls into
    // here. So, lock it ourself. The real solution would be to lock it only in the places
    // where it's needed, but a) this might turn out difficult, since we then also need to care
    // for locking in the proper order (SolarMutex and m_aMutex), and b) this would be too many places
    // for the time frame of the fix.
    // #i52602# / frank.schoenheit@sun.com / 2005-07-29

#ifdef TIMELOG
    ::rtl::OString sLog( "OGenericUnoController::dispatch( '" );
    sLog += ::rtl::OString( _aURL.Main.getStr(), _aURL.Main.getLength(), osl_getThreadTextEncoding() );
    sLog += ::rtl::OString( "' )" );
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "dbaccess", "frank.schoenheit@sun.com", sLog.getStr() );
#endif

    executeChecked(_aURL,aArgs);
}

// -----------------------------------------------------------------------
void OGenericUnoController::addStatusListener(const Reference< XStatusListener > & aListener, const URL& _rURL) throw(RuntimeException)
{
    // parse the ULR now and here, this saves later parsing in each notification round
    URL aParsedURL( _rURL );
	if ( m_xUrlTransformer.is() )
		m_xUrlTransformer->parseStrict( aParsedURL );

    // remeber the listener together with the URL
	m_arrStatusListener.insert( m_arrStatusListener.end(), DispatchTarget( aParsedURL, aListener ) );

    // initially broadcast the state
	ImplBroadcastFeatureState( aParsedURL.Complete, aListener, sal_True );
		// force the new state to be broadcasted to the new listener
}

// -----------------------------------------------------------------------
void OGenericUnoController::removeStatusListener(const Reference< XStatusListener > & aListener, const URL& _rURL) throw(RuntimeException)
{
	DispatchIterator iterSearch = m_arrStatusListener.begin();

	sal_Bool bRemoveForAll = (_rURL.Complete.getLength() == 0);
	while ( iterSearch != m_arrStatusListener.end() )
	{
		DispatchTarget& rCurrent = *iterSearch;
		if	(	(rCurrent.xListener == aListener)
			&&	(	bRemoveForAll
				||	(rCurrent.aURL.Complete.equals(_rURL.Complete))
				)
			)
		{
			m_arrStatusListener.erase( iterSearch );
			if (!bRemoveForAll)
				// remove the listener only for the given URL, so we can exit the loop after deletion
				break;
		}
		else
			++iterSearch;
	}

    OSL_PRECOND( !m_aSupportedFeatures.empty(), "OGenericUnoController::removeStatusListener: shouldn't this be filled at construction time?" );
    if ( m_aSupportedFeatures.empty() )
        fillSupportedFeatures();

	SupportedFeatures::const_iterator aIter = m_aSupportedFeatures.find(_rURL.Complete);
	if (aIter != m_aSupportedFeatures.end())
	{	// clear the cache for that feature
		StateCacheIterator aCachePos = m_aStateCache.find( aIter->second.nFeatureId );
		if ( aCachePos != m_aStateCache.end() )
			m_aStateCache.erase( aCachePos );
	}

	// now remove the listener from the deque
	::osl::MutexGuard aGuard( m_aFeatureMutex );
	m_aFeaturesToInvalidate.erase(
		::std::remove_if(	m_aFeaturesToInvalidate.begin(),
							m_aFeaturesToInvalidate.end(),
							::std::bind2nd(FindFeatureListener(),aListener))
		,m_aFeaturesToInvalidate.end());
}
// -----------------------------------------------------------------------------
void OGenericUnoController::releaseNumberForComponent()
{
    try
    {
        Reference< XUntitledNumbers > xUntitledProvider(getPrivateModel(), UNO_QUERY      );
        if ( xUntitledProvider.is() )
            xUntitledProvider->releaseNumberForComponent(static_cast<XWeak*>(this));
    }
    catch( const Exception& )
	{
        // NII
	}
}
// -----------------------------------------------------------------------
void OGenericUnoController::disposing()
{
	{
		EventObject aDisposeEvent;
		aDisposeEvent.Source = static_cast<XWeak*>(this);
		Dispatch aStatusListener = m_arrStatusListener;
		Dispatch::iterator aEnd = aStatusListener.end();
		for (Dispatch::iterator aIter = aStatusListener.begin(); aIter != aEnd; ++aIter)
		{
			aIter->xListener->disposing(aDisposeEvent);
		}
		m_arrStatusListener.clear();
	}

	m_xDatabaseContext = NULL;
	{
		::osl::MutexGuard aGuard( m_aFeatureMutex);
		m_aAsyncInvalidateAll.CancelCall();
		m_aFeaturesToInvalidate.clear();
	}

    releaseNumberForComponent();

	// check out from all the objects we are listening
	// the frame
	stopFrameListening( m_aCurrentFrame.getFrame() );
    m_aCurrentFrame.attachFrame( NULL );

    m_xMasterDispatcher = NULL;
    m_xSlaveDispatcher = NULL;
    m_xServiceFactory = NULL;
	m_xTitleHelper.clear();
    m_xUrlTransformer.clear();
    m_aInitParameters.clear();
}

// -----------------------------------------------------------------------------
void SAL_CALL OGenericUnoController::addEventListener( const Reference< XEventListener >& xListener ) throw (RuntimeException)
{
    // disambiguate
    OGenericUnoController_Base::WeakComponentImplHelperBase::addEventListener( xListener );
}

// -----------------------------------------------------------------------------
void SAL_CALL OGenericUnoController::removeEventListener( const Reference< XEventListener >& xListener ) throw (RuntimeException)
{
    // disambiguate
    OGenericUnoController_Base::WeakComponentImplHelperBase::removeEventListener( xListener );
}

//------------------------------------------------------------------------------
void OGenericUnoController::frameAction(const FrameActionEvent& aEvent) throw( RuntimeException )
{
    ::osl::MutexGuard aGuard( getMutex() );
    if ( aEvent.Frame == m_aCurrentFrame.getFrame() )
        m_aCurrentFrame.frameAction( aEvent.Action );
}

//------------------------------------------------------------------------------
void OGenericUnoController::implDescribeSupportedFeature( const sal_Char* _pAsciiCommandURL,
        sal_uInt16 _nFeatureId, sal_Int16 _nCommandGroup )
{
#ifdef DBG_UTIL
    DBG_ASSERT( m_bDescribingSupportedFeatures, "OGenericUnoController::implDescribeSupportedFeature: bad timing for this call!" );
#endif
    OSL_PRECOND( _nFeatureId < FIRST_USER_DEFINED_FEATURE, "OGenericUnoController::implDescribeSupportedFeature: invalid feature id!" );

    ControllerFeature aFeature;
    aFeature.Command = ::rtl::OUString::createFromAscii( _pAsciiCommandURL );
    aFeature.nFeatureId = _nFeatureId;
    aFeature.GroupId = _nCommandGroup;

#if OSL_DEBUG_LEVEL > 0
    OSL_ENSURE( m_aSupportedFeatures.find( aFeature.Command ) == m_aSupportedFeatures.end(),
        "OGenericUnoController::implDescribeSupportedFeature: this feature is already there!" );
#endif
    m_aSupportedFeatures[ aFeature.Command ] = aFeature;
}

//------------------------------------------------------------------------------
void OGenericUnoController::describeSupportedFeatures()
{
    // add all supported features
    implDescribeSupportedFeature( ".uno:Copy", ID_BROWSER_COPY, CommandGroup::EDIT );
	implDescribeSupportedFeature( ".uno:Cut", ID_BROWSER_CUT, CommandGroup::EDIT );
	implDescribeSupportedFeature( ".uno:Paste", ID_BROWSER_PASTE, CommandGroup::EDIT );
	implDescribeSupportedFeature( ".uno:ClipboardFormatItems", ID_BROWSER_CLIPBOARD_FORMAT_ITEMS );
	implDescribeSupportedFeature( ".uno:DSBEditDoc", ID_BROWSER_EDITDOC, CommandGroup::DOCUMENT );
}

//------------------------------------------------------------------------------
FeatureState OGenericUnoController::GetState( sal_uInt16 _nId ) const
{
	FeatureState aReturn;
		// (disabled automatically)

	switch ( _nId )
	{
		case ID_BROWSER_UNDO:
		case ID_BROWSER_SAVEDOC:
			aReturn.bEnabled = sal_True;
			break;
        default:
            aReturn = m_pData->m_aUserDefinedFeatures.getState( getURLForId( _nId ) );
            break;
	}

	return aReturn;
}

//------------------------------------------------------------------------------
void OGenericUnoController::Execute( sal_uInt16 _nId, const Sequence< PropertyValue>& _rArgs )
{
    OSL_ENSURE( isUserDefinedFeature( _nId ),
        "OGenericUnoController::Execute: responsible for user defined features only!" );

    // user defined features can be handled by dispatch interceptors resp. protocol handlers only.
    // So, we need to do a queryDispatch, and dispatch the URL
    m_pData->m_aUserDefinedFeatures.execute( getURLForId( _nId ), _rArgs );
}

//------------------------------------------------------------------------------
URL OGenericUnoController::getURLForId(sal_Int32 _nId) const
{
	URL aReturn;
	if ( m_xUrlTransformer.is() )
	{
		SupportedFeatures::const_iterator aIter = ::std::find_if(
			m_aSupportedFeatures.begin(),
			m_aSupportedFeatures.end(),
			::std::bind2nd( CompareFeatureById(), _nId )
		);

		if ( m_aSupportedFeatures.end() != aIter && aIter->first.getLength() )
		{
			aReturn.Complete = aIter->first;
			m_xUrlTransformer->parseStrict( aReturn );
		}
	}
	return aReturn;
}

//-------------------------------------------------------------------------
bool OGenericUnoController::isUserDefinedFeature( const sal_uInt16 _nFeatureId ) const
{
    return ( _nFeatureId >= FIRST_USER_DEFINED_FEATURE ) && ( _nFeatureId < LAST_USER_DEFINED_FEATURE );
}

//-------------------------------------------------------------------------
bool OGenericUnoController::isUserDefinedFeature( const ::rtl::OUString& _rFeatureURL ) const
{
    SupportedFeatures::const_iterator pos = m_aSupportedFeatures.find( _rFeatureURL );
    OSL_PRECOND( pos != m_aSupportedFeatures.end(),
        "OGenericUnoController::isUserDefinedFeature: this is no supported feature at all!" );

    return ( pos != m_aSupportedFeatures.end() ) ? isUserDefinedFeature( pos->second.nFeatureId ) : false;
}

//-------------------------------------------------------------------------
sal_Bool SAL_CALL OGenericUnoController::supportsService(const ::rtl::OUString& ServiceName) throw(RuntimeException)
{
	Sequence< ::rtl::OUString > aSupported(getSupportedServiceNames());

	const ::rtl::OUString* pArray = aSupported.getConstArray();
	const ::rtl::OUString* pArrayEnd = aSupported.getConstArray() + aSupported.getLength();

	for ( ;( pArray != pArrayEnd ) && !pArray->equals( ServiceName ); ++pArray )
		;
	return pArray != pArrayEnd;
}

// -----------------------------------------------------------------------------
void OGenericUnoController::startConnectionListening(const Reference< XConnection >& _rxConnection)
{
	// we have to remove ourself before dispoing the connection
	Reference< XComponent >  xComponent(_rxConnection, UNO_QUERY);
	if (xComponent.is())
		xComponent->addEventListener(static_cast<XFrameActionListener*>(this));
}

// -----------------------------------------------------------------------------
void OGenericUnoController::stopConnectionListening(const Reference< XConnection >& _rxConnection)
{
	// we have to remove ourself before dispoing the connection
	Reference< XComponent >  xComponent(_rxConnection, UNO_QUERY);
	if (xComponent.is())
		xComponent->removeEventListener(static_cast<XFrameActionListener*>(this));
}
// -----------------------------------------------------------------------------
Reference< XConnection > OGenericUnoController::connect( const Reference< XDataSource>& _xDataSource,
    ::dbtools::SQLExceptionInfo* _pErrorInfo )
{
	WaitObject aWaitCursor( getView() );

	ODatasourceConnector aConnector( getORB(), getView(), ::rtl::OUString() );
	Reference< XConnection > xConnection = aConnector.connect( _xDataSource, _pErrorInfo );
	startConnectionListening( xConnection );

	return xConnection;
}
// -----------------------------------------------------------------------------
Reference< XConnection > OGenericUnoController::connect( const ::rtl::OUString& _rDataSourceName,
    const ::rtl::OUString& _rContextInformation, ::dbtools::SQLExceptionInfo* _pErrorInfo )
{
	WaitObject aWaitCursor( getView() );

	ODatasourceConnector aConnector( getORB(), getView(), _rContextInformation );
	Reference<XConnection> xConnection = aConnector.connect( _rDataSourceName, _pErrorInfo );
	startConnectionListening( xConnection );

	return xConnection;
}

// -----------------------------------------------------------------------------
void OGenericUnoController::showError(const SQLExceptionInfo& _rInfo)
{
	::dbaui::showError(_rInfo,getView(),getORB());
}
// -----------------------------------------------------------------------------
Reference< XLayoutManager > OGenericUnoController::getLayoutManager(const Reference< XFrame >& _xFrame) const
{
	Reference< XPropertySet > xPropSet( _xFrame, UNO_QUERY );
    Reference< XLayoutManager > xLayoutManager;
	if ( xPropSet.is() )
    {
        try
        {
			xLayoutManager.set(xPropSet->getPropertyValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "LayoutManager" ))),UNO_QUERY);
        }
        catch ( Exception& )
        {
        }
    }
	return xLayoutManager;
}
// -----------------------------------------------------------------------------
void OGenericUnoController::loadMenu(const Reference< XFrame >& _xFrame)
{
    Reference< XLayoutManager > xLayoutManager = getLayoutManager(_xFrame);
    if ( xLayoutManager.is() )
	{
		xLayoutManager->lock();
        xLayoutManager->createElement( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "private:resource/menubar/menubar" )));
		xLayoutManager->createElement( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "private:resource/toolbar/toolbar" )));
		xLayoutManager->unlock();
		xLayoutManager->doLayout();
	}

    onLoadedMenu( xLayoutManager );
}

// -----------------------------------------------------------------------------
void OGenericUnoController::onLoadedMenu(const Reference< XLayoutManager >& /*_xLayoutManager*/)
{
    // not interested in
}

// -----------------------------------------------------------------------------
void OGenericUnoController::closeTask()
{
	m_aAsyncCloseTask.Call();
}
// -----------------------------------------------------------------------------
IMPL_LINK(OGenericUnoController, OnAsyncCloseTask, void*, EMPTYARG)
{
	if ( !OGenericUnoController_Base::rBHelper.bInDispose )
	{
        try
        {
            Reference< util::XCloseable > xCloseable( m_aCurrentFrame.getFrame(), UNO_QUERY_THROW );
            xCloseable->close( sal_False ); // false - holds the owner ship for this frame inside this object!
        }
        catch( const Exception& )
        {
            DBG_UNHANDLED_EXCEPTION();
        }
	}
	return 0L;
}
// -----------------------------------------------------------------------------
Any SAL_CALL OGenericUnoController::getViewData(void) throw( RuntimeException )
{
	return Any();
}
// -----------------------------------------------------------------------------
void SAL_CALL OGenericUnoController::restoreViewData(const Any& /*Data*/) throw( RuntimeException )
{
}

// -----------------------------------------------------------------------------
Reference< XModel > SAL_CALL OGenericUnoController::getModel(void) throw( RuntimeException )
{
    return Reference< XModel >();
}

// -----------------------------------------------------------------------------
Reference< XFrame > SAL_CALL OGenericUnoController::getFrame(void) throw( RuntimeException )
{
    ::osl::MutexGuard aGuard( getMutex() );
    return m_aCurrentFrame.getFrame();
}

// -----------------------------------------------------------------------------
sal_Bool SAL_CALL OGenericUnoController::attachModel(const Reference< XModel > & /*xModel*/) throw( RuntimeException )
{
    OSL_ENSURE( false, "OGenericUnoController::attachModel: not supported!" );
    return sal_False;
}

// -----------------------------------------------------------------------------
void OGenericUnoController::executeUnChecked(sal_uInt16 _nCommandId, const Sequence< PropertyValue >& aArgs)
{
	Execute(_nCommandId, aArgs);
}
// -----------------------------------------------------------------------------
void OGenericUnoController::executeUnChecked(const util::URL& _rCommand, const Sequence< PropertyValue >& aArgs)
{
    OSL_PRECOND( !m_aSupportedFeatures.empty(), "OGenericUnoController::executeUnChecked: shouldn't this be filled at construction time?" );
    if ( m_aSupportedFeatures.empty() )
        fillSupportedFeatures();

    SupportedFeatures::const_iterator aIter = m_aSupportedFeatures.find( _rCommand.Complete );
	if (aIter != m_aSupportedFeatures.end())
		Execute( aIter->second.nFeatureId, aArgs );
}
// -----------------------------------------------------------------------------
void OGenericUnoController::executeChecked(const util::URL& _rCommand, const Sequence< PropertyValue >& aArgs)
{
    OSL_PRECOND( !m_aSupportedFeatures.empty(), "OGenericUnoController::executeChecked: shouldn't this be filled at construction time?" );
    if ( m_aSupportedFeatures.empty() )
        fillSupportedFeatures();

    SupportedFeatures::const_iterator aIter = m_aSupportedFeatures.find( _rCommand.Complete );
	if ( aIter != m_aSupportedFeatures.end() )
	{
		sal_uInt16 nFeatureId = aIter->second.nFeatureId;
		if ( GetState( nFeatureId ).bEnabled )
			Execute( nFeatureId, aArgs );
	}
}
// -----------------------------------------------------------------------------
//------------------------------------------------------------------------------
namespace
{
	::rtl::OUString lcl_getModuleHelpModuleName( const Reference< XFrame >& _rxFrame )
	{
		const sal_Char* pReturn = NULL;

		try
		{
			// get the model of the document in the given frame
			Reference< XController > xController;
			if ( _rxFrame.is() )
				xController = _rxFrame->getController();
			Reference< XModel > xModel;
			if ( xController.is() )
				xModel = xController->getModel();
			Reference< XServiceInfo > xSI( xModel, UNO_QUERY );

			if ( !xSI.is() )
			{	// try to go up the frame hierarchy

				Reference< XFrame > xParentFrame;
				if ( _rxFrame.is() )
					xParentFrame = xParentFrame.query( _rxFrame->getCreator() );
				// did we find a parent frame? Which is no top-level frame?
				if ( xParentFrame.is() && !_rxFrame->isTop() )
					// TODO: to prevent framework assertions, re-insert this "isTop" once 98303 is fixed
					return lcl_getModuleHelpModuleName( xParentFrame );
			}
			else
			{
#if OSL_DEBUG_LEVEL > 0
				Sequence< ::rtl::OUString > sServiceNames = xSI->getSupportedServiceNames();
				const ::rtl::OUString* pLoop = sServiceNames.getConstArray();
				for ( sal_Int32 i=0; i<sServiceNames.getLength(); ++i, ++pLoop )
				{
					sal_Int32 nDummy = 0;
                    (void)nDummy;
				}
#endif

				// check which service we know ....
				static const sal_Char* pTransTable[] = {
					"com.sun.star.sdb.OfficeDatabaseDocument","sdatabase",
                    "com.sun.star.report.ReportDefinition","sdatabase",
					"com.sun.star.text.TextDocument",	"swriter",
					"com.sun.star.sheet.SpreadsheetDocument", "scalc",
					"com.sun.star.presentation.PresentationDocument", "simpress",
					"com.sun.star.drawing.DrawingDocument", "sdraw",
					"com.sun.star.formula.FormularProperties", "smath",
					"com.sun.star.chart.ChartDocument", "schart"
				};
				OSL_ENSURE( ( sizeof( pTransTable ) / sizeof( pTransTable[0] ) ) % 2 == 0,
					"lcl_getModuleHelpModuleName: odd size of translation table!" );

				// loop through the table
				sal_Int32 nTableEntries = ( sizeof( pTransTable ) / sizeof( pTransTable[0] ) ) / 2;
				const sal_Char** pDocumentService = pTransTable;
				const sal_Char** pHelpModuleName = pTransTable + 1;
				for ( sal_Int32 j=0; j<nTableEntries; ++j )
				{
					if ( xSI->supportsService( ::rtl::OUString::createFromAscii( *pDocumentService ) ) )
					{	// found a table entry which matches the model's services
						pReturn = *pHelpModuleName;
						break;
					}

					++pDocumentService; ++pDocumentService;
					++pHelpModuleName; ++pHelpModuleName;
				}
			}

			if ( !pReturn )
			{
				// could not determine the document type we're living in
				// ->fallback
				SvtModuleOptions aModOpt;
				if ( aModOpt.IsModuleInstalled( SvtModuleOptions::E_SWRITER ) )
					pReturn = "swriter";
				else if ( aModOpt.IsModuleInstalled( SvtModuleOptions::E_SDATABASE ) )
					pReturn = "sdatabase";
				else if ( aModOpt.IsModuleInstalled( SvtModuleOptions::E_SCALC ) )
					pReturn = "scalc";
				else if ( aModOpt.IsModuleInstalled( SvtModuleOptions::E_SIMPRESS ) )
					pReturn = "simpress";
				else if ( aModOpt.IsModuleInstalled( SvtModuleOptions::E_SDRAW ) )
					pReturn = "sdraw";
				else if ( aModOpt.IsModuleInstalled( SvtModuleOptions::E_SMATH ) )
					pReturn = "smath";
				else if ( aModOpt.IsModuleInstalled( SvtModuleOptions::E_SCHART ) )
					pReturn = "schart";
				else if ( aModOpt.IsModuleInstalled( SvtModuleOptions::E_SBASIC ) )
					pReturn = "sbasic";
				else
				{
					OSL_ENSURE( sal_False, "lcl_getModuleHelpModuleName: no installed module found" );
				}
			}
		}
        catch( const Exception& )
        {
            DBG_UNHANDLED_EXCEPTION();
        }

		if ( !pReturn )
			pReturn = "swriter";

		return ::rtl::OUString::createFromAscii( pReturn );
	}
}

// -----------------------------------------------------------------------------

void OGenericUnoController::openHelpAgent(rtl::OUString const& _suHelpStringURL )
{
    rtl::OUString suURL(_suHelpStringURL);
    rtl::OUString sLanguage = rtl::OUString::createFromAscii("Language=");
    if (suURL.indexOf(sLanguage) == -1)
    {
        AppendConfigToken(suURL, sal_False /* sal_False := add '&' */ );
    }
    URL aURL;
    aURL.Complete = suURL;

    openHelpAgent( aURL );
}

void OGenericUnoController::openHelpAgent(const rtl::OString& _sHelpId)
{
	openHelpAgent( createHelpAgentURL( lcl_getModuleHelpModuleName( getFrame() ), _sHelpId ) );
}

void OGenericUnoController::openHelpAgent( const URL& _rURL )
{
	try
	{
        URL aURL( _rURL );

        if ( m_xUrlTransformer.is() )
            m_xUrlTransformer->parseStrict(aURL);

		Reference< XDispatchProvider > xDispProv( m_aCurrentFrame.getFrame(), UNO_QUERY );
		Reference< XDispatch > xHelpDispatch;
		if ( xDispProv.is() )
			xHelpDispatch = xDispProv->queryDispatch(aURL, ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("_helpagent")), FrameSearchFlag::PARENT | FrameSearchFlag::SELF);
		OSL_ENSURE(xHelpDispatch.is(), "SbaTableQueryBrowser::openHelpAgent: could not get a dispatcher!");
		if (xHelpDispatch.is())
		{
			xHelpDispatch->dispatch(aURL, Sequence< PropertyValue >());
		}
	}
    catch( const Exception& )
    {
        DBG_UNHANDLED_EXCEPTION();
    }
}
// -----------------------------------------------------------------------------
Reference< awt::XWindow> OGenericUnoController::getTopMostContainerWindow() const
{
	Reference< ::com::sun::star::awt::XWindow> xWindow;

    // get the top most window
    Reference< XFrame > xFrame( m_aCurrentFrame.getFrame() );
	if ( xFrame.is() )
	{
		xWindow = xFrame->getContainerWindow();

        while ( xFrame.is() && !xFrame->isTop() )
		{
			xFrame.set( xFrame->getCreator(), UNO_QUERY );
		}
		if ( xFrame.is() )
			xWindow = xFrame->getContainerWindow();
	}
	return xWindow;
}
// -----------------------------------------------------------------------------
Reference< XTitle > OGenericUnoController::impl_getTitleHelper_throw()
{
    ::vos::OGuard aSolarGuard( Application::GetSolarMutex() );
    ::osl::MutexGuard aGuard( getMutex() );

    if ( ! m_xTitleHelper.is ())
    {
        Reference< XUntitledNumbers > xUntitledProvider(getPrivateModel(), UNO_QUERY      );
        Reference< XController >      xThis(static_cast< XController* >(this), UNO_QUERY_THROW);

        ::framework::TitleHelper* pHelper = new ::framework::TitleHelper(m_xServiceFactory);
        m_xTitleHelper.set( static_cast< ::cppu::OWeakObject* >(pHelper), UNO_QUERY_THROW);

        pHelper->setOwner                   (xThis            );
        pHelper->connectWithUntitledNumbers (xUntitledProvider);
    }

    return m_xTitleHelper;
}

//=============================================================================
// XTitle
::rtl::OUString SAL_CALL OGenericUnoController::getTitle()
    throw (RuntimeException)
{
    ::osl::MutexGuard aGuard( getMutex() );
    if ( m_bExternalTitle )
        return impl_getTitleHelper_throw()->getTitle ();
    return getPrivateTitle() + impl_getTitleHelper_throw()->getTitle ();
}

//=============================================================================
// XTitle
void SAL_CALL OGenericUnoController::setTitle(const ::rtl::OUString& sTitle)
    throw (RuntimeException)
{
    vos::OGuard aSolarGuard( Application::GetSolarMutex() );
	::osl::MutexGuard aGuard( getMutex() );
    m_bExternalTitle = sal_True;
    impl_getTitleHelper_throw()->setTitle (sTitle);
}

//=============================================================================
// XTitleChangeBroadcaster
void SAL_CALL OGenericUnoController::addTitleChangeListener(const Reference< XTitleChangeListener >& xListener)
    throw (RuntimeException)
{
    Reference< XTitleChangeBroadcaster > xBroadcaster(impl_getTitleHelper_throw(), UNO_QUERY);
    if (xBroadcaster.is ())
        xBroadcaster->addTitleChangeListener (xListener);
}

// -----------------------------------------------------------------------------
void SAL_CALL OGenericUnoController::removeTitleChangeListener(const Reference< XTitleChangeListener >& xListener)
    throw (RuntimeException)
{
    Reference< XTitleChangeBroadcaster > xBroadcaster(impl_getTitleHelper_throw(), UNO_QUERY);
    if (xBroadcaster.is ())
        xBroadcaster->removeTitleChangeListener (xListener);
}

// =============================================================================
// XUserInputInterception
// -----------------------------------------------------------------------------
void SAL_CALL OGenericUnoController::addKeyHandler( const Reference< XKeyHandler >& _rxHandler ) throw (RuntimeException)
{
    if ( _rxHandler.is() )
        m_pData->m_aUserInputInterception.addKeyHandler( _rxHandler );
}

// -----------------------------------------------------------------------------
void SAL_CALL OGenericUnoController::removeKeyHandler( const Reference< XKeyHandler >& _rxHandler ) throw (RuntimeException)
{
    m_pData->m_aUserInputInterception.removeKeyHandler( _rxHandler );
}

// -----------------------------------------------------------------------------
void SAL_CALL OGenericUnoController::addMouseClickHandler( const Reference< XMouseClickHandler >& _rxHandler ) throw (RuntimeException)
{
    if ( _rxHandler.is() )
        m_pData->m_aUserInputInterception.addMouseClickHandler( _rxHandler );
}

// -----------------------------------------------------------------------------
void SAL_CALL OGenericUnoController::removeMouseClickHandler( const Reference< XMouseClickHandler >& _rxHandler ) throw (RuntimeException)
{
    m_pData->m_aUserInputInterception.removeMouseClickHandler( _rxHandler );
}

// =============================================================================
// -----------------------------------------------------------------------------
void OGenericUnoController::executeChecked(sal_uInt16 _nCommandId, const Sequence< PropertyValue >& aArgs)
{
	if ( isCommandEnabled(_nCommandId) )
		Execute(_nCommandId, aArgs);
}

// -----------------------------------------------------------------------------
sal_Bool OGenericUnoController::isCommandEnabled(sal_uInt16 _nCommandId) const
{
	return GetState( _nCommandId ).bEnabled;
}

// -----------------------------------------------------------------------------
sal_uInt16 OGenericUnoController::registerCommandURL( const ::rtl::OUString& _rCompleteCommandURL )
{
    if ( !_rCompleteCommandURL.getLength() )
        return 0;

    SupportedFeatures::const_iterator aIter = m_aSupportedFeatures.find( _rCompleteCommandURL );
	if ( aIter != m_aSupportedFeatures.end() )
		return aIter->second.nFeatureId;

    // this is a previously unkwnon command
    sal_uInt16 nFeatureId = FIRST_USER_DEFINED_FEATURE;
    while ( isFeatureSupported( nFeatureId ) && ( nFeatureId < LAST_USER_DEFINED_FEATURE ) )
        ++nFeatureId;
    if ( nFeatureId == LAST_USER_DEFINED_FEATURE )
    {
        OSL_ENSURE( false, "OGenericUnoController::registerCommandURL: no more space for user defined features!" );
        return 0L;
    }

    ControllerFeature aFeature;
    aFeature.Command = _rCompleteCommandURL;
    aFeature.nFeatureId = nFeatureId;
    aFeature.GroupId = CommandGroup::INTERNAL;
    m_aSupportedFeatures[ aFeature.Command ] = aFeature;

    return nFeatureId;
}

// -----------------------------------------------------------------------------
void OGenericUnoController::notifyHiContrastChanged()
{
}

// -----------------------------------------------------------------------------
sal_Bool OGenericUnoController::isDataSourceReadOnly() const
{
    return sal_False;
}

// -----------------------------------------------------------------------------
Reference< XController > OGenericUnoController::getXController() throw( RuntimeException )
{
    return this;
}

// -----------------------------------------------------------------------------
bool OGenericUnoController::interceptUserInput( const NotifyEvent& _rEvent )
{
    return m_pData->m_aUserInputInterception.handleNotifyEvent( _rEvent );
}

// -----------------------------------------------------------------------------
sal_Bool OGenericUnoController::isCommandChecked(sal_uInt16 _nCommandId) const
{
    FeatureState aState = GetState( _nCommandId );

	return aState.bChecked && (sal_Bool)*aState.bChecked;
}
// -----------------------------------------------------------------------------
sal_Bool OGenericUnoController::isCommandEnabled( const ::rtl::OUString& _rCompleteCommandURL ) const
{
    OSL_ENSURE( _rCompleteCommandURL.getLength(), "OGenericUnoController::isCommandEnabled: Empty command url!" );

	sal_Bool bIsEnabled = sal_False;
    SupportedFeatures::const_iterator aIter = m_aSupportedFeatures.find( _rCompleteCommandURL );
	if ( aIter != m_aSupportedFeatures.end() )
		bIsEnabled = isCommandEnabled( aIter->second.nFeatureId );

	return bIsEnabled;
}

// -----------------------------------------------------------------------------
Sequence< ::sal_Int16 > SAL_CALL OGenericUnoController::getSupportedCommandGroups() throw (RuntimeException)
{
    CommandHashMap aCmdHashMap;
	for (   SupportedFeatures::const_iterator aIter = m_aSupportedFeatures.begin();
            aIter != m_aSupportedFeatures.end();
            ++aIter
        )
        if ( aIter->second.GroupId != CommandGroup::INTERNAL )
            aCmdHashMap.insert( CommandHashMap::value_type( aIter->second.GroupId, 0 ));

    Sequence< sal_Int16 > aCommandGroups( aCmdHashMap.size() );
    ::std::transform( aCmdHashMap.begin(),
        aCmdHashMap.end(),
        aCommandGroups.getArray(),
        ::std::select1st< CommandHashMap::value_type >()
    );

    return aCommandGroups;
}

// -----------------------------------------------------------------------------
Sequence< DispatchInformation > SAL_CALL OGenericUnoController::getConfigurableDispatchInformation( ::sal_Int16 CommandGroup ) throw (RuntimeException)
{
    DispatchInfoList    aInformationList;
    DispatchInformation aDispatchInfo;
    for (   SupportedFeatures::const_iterator aIter = m_aSupportedFeatures.begin();
            aIter != m_aSupportedFeatures.end();
            ++aIter
        )
    {
        if ( sal_Int16( aIter->second.GroupId ) == CommandGroup )
        {
            aDispatchInfo = aIter->second;
            aInformationList.push_back( aDispatchInfo );
        }
    }

    Sequence< DispatchInformation > aInformation( aInformationList.size() );
    ::std::transform( aInformationList.begin(),
        aInformationList.end(),
        aInformation.getArray(),
        ::std::identity< DispatchInformation >()
    );

    return aInformation;
}
// -----------------------------------------------------------------------------
void OGenericUnoController::fillSupportedFeatures()
{
#ifdef DBG_UTIL
    m_bDescribingSupportedFeatures = true;
#endif
	describeSupportedFeatures();
// -----------------------------------------------------------------------------
#ifdef DBG_UTIL
    m_bDescribingSupportedFeatures = false;
#endif
}


void SAL_CALL OGenericUnoController::dispose() throw(::com::sun::star::uno::RuntimeException)
{
	::vos::OGuard aSolarGuard(Application::GetSolarMutex());
	OGenericUnoController_Base::dispose();
}
}   // namespace dbaui

