/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: eventsupplier.cxx,v $
 *
 *  $Revision: 1.30 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-07 19:19:47 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

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

#ifndef  _COM_SUN_STAR_BEANS_PROPERTYVALUE_HPP_
#include <com/sun/star/beans/PropertyValue.hpp>
#endif

#ifndef  _COM_SUN_STAR_UTL_URL_HPP_
#include <com/sun/star/util/URL.hpp>
#endif

#ifndef  _COM_SUN_STAR_UTL_XURLTRANSFORMER_HPP_
#include <com/sun/star/util/XURLTransformer.hpp>
#endif

#ifndef _URLOBJ_HXX
#include <tools/urlobj.hxx>
#endif

#ifndef _SFXMACITEM_HXX
#include <svtools/macitem.hxx>
#endif

#ifndef _SFX_APPUNO_HXX
#include <appuno.hxx>
#endif
#ifndef _SFX_OBJSH_HXX
#include <objsh.hxx>
#endif
#ifndef _SFX_SFXBASEMODEL_HXX_
#include <sfxbasemodel.hxx>
#endif
#ifndef _SFX_EVENTCONF_HXX
#include <evntconf.hxx>
#endif
#include <svtools/eventcfg.hxx>

#include <svtools/securityoptions.hxx>
#include <comphelper/processfactory.hxx>

#ifndef _SFX_EVENTSUPPLIER_HXX_
#include "eventsupplier.hxx"
#endif

#include "app.hxx"
#include "sfxresid.hxx"

#include "sfxsids.hrc"
#include "sfxlocal.hrc"
#include "docfile.hxx"
#include "viewfrm.hxx"
#include "frame.hxx"

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

#define MACRO_PRFIX         "macro://"
#define MACRO_POSTFIX       "()"

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

#define PROPERTYVALUE       ::com::sun::star::beans::PropertyValue
#define UNO_QUERY           ::com::sun::star::uno::UNO_QUERY

namespace css = ::com::sun::star;

//--------------------------------------------------------------------------------------------------------
    //  --- XNameReplace ---
//--------------------------------------------------------------------------------------------------------
void SAL_CALL SfxEvents_Impl::replaceByName( const OUSTRING & aName, const ANY & rElement )
                                throw( ILLEGALARGUMENTEXCEPTION, NOSUCHELEMENTEXCEPTION,
                                       WRAPPEDTARGETEXCEPTION, RUNTIMEEXCEPTION )
{
    ::osl::MutexGuard aGuard( maMutex );

    // find the event in the list and replace the data
    long nCount = maEventNames.getLength();
    for ( long i=0; i<nCount; i++ )
    {
        if ( maEventNames[i] == aName )
        {
            // check for correct type of the element
            if ( ::getCppuType( (const SEQUENCE < PROPERTYVALUE > *)0 ) == rElement.getValueType() )
            {
                // create Configuration at first, creation might call this method also and that would overwrite everything
                // we might have stored before!
                USHORT nID = (USHORT) SfxEventConfiguration::GetEventId_Impl( aName );
                if ( nID )
                {
                    ANY aValue;
                    BlowUpMacro( rElement, aValue, mpObjShell );

                    // pConfig becomes the owner of the new SvxMacro
                    if ( mpObjShell && !mpObjShell->IsLoading() )
                        mpObjShell->SetModified( TRUE );
                    maEventData[i] = aValue;

                    SEQUENCE < PROPERTYVALUE > aProperties;
                    if ( aValue >>= aProperties )
                    {
                        long nCount = aProperties.getLength();
                        for ( long nIndex = 0; nIndex < nCount; nIndex++ )
                        {
                            if ( aProperties[ nIndex ].Name.compareToAscii( PROP_EVENT_TYPE ) == 0 )
                            {
                                ::rtl::OUString aType;
                                aProperties[ nIndex ].Value >>= aType;
                                break;
                            }
                        }
                    }
                }
            }
            else
                throw ILLEGALARGUMENTEXCEPTION();

            return;
        }
    }

    throw NOSUCHELEMENTEXCEPTION();
}

//--------------------------------------------------------------------------------------------------------
//  --- XNameAccess ---
//--------------------------------------------------------------------------------------------------------
ANY SAL_CALL SfxEvents_Impl::getByName( const OUSTRING& aName )
                                throw( NOSUCHELEMENTEXCEPTION, WRAPPEDTARGETEXCEPTION,
                                       RUNTIMEEXCEPTION )
{
    ::osl::MutexGuard aGuard( maMutex );

    // find the event in the list and return the data

    long nCount = maEventNames.getLength();

    for ( long i=0; i<nCount; i++ )
    {
        if ( maEventNames[i] == aName )
            return maEventData[i];
    }

    throw NOSUCHELEMENTEXCEPTION();

    return ANY();
}

//--------------------------------------------------------------------------------------------------------
SEQUENCE< OUSTRING > SAL_CALL SfxEvents_Impl::getElementNames() throw ( RUNTIMEEXCEPTION )
{
    return maEventNames;
}

//--------------------------------------------------------------------------------------------------------
sal_Bool SAL_CALL SfxEvents_Impl::hasByName( const OUSTRING& aName ) throw ( RUNTIMEEXCEPTION )
{
    ::osl::MutexGuard aGuard( maMutex );

    // find the event in the list and return the data

    long nCount = maEventNames.getLength();

    for ( long i=0; i<nCount; i++ )
    {
        if ( maEventNames[i] == aName )
            return sal_True;
    }

    return sal_False;
}

//--------------------------------------------------------------------------------------------------------
//  --- XElementAccess ( parent of XNameAccess ) ---
//--------------------------------------------------------------------------------------------------------
UNOTYPE SAL_CALL SfxEvents_Impl::getElementType() throw ( RUNTIMEEXCEPTION )
{
    UNOTYPE aElementType = ::getCppuType( (const SEQUENCE < PROPERTYVALUE > *)0 );
    return aElementType;
}

//--------------------------------------------------------------------------------------------------------
sal_Bool SAL_CALL SfxEvents_Impl::hasElements() throw ( RUNTIMEEXCEPTION )
{
    ::osl::MutexGuard aGuard( maMutex );

    if ( maEventNames.getLength() )
        return sal_True;
    else
        return sal_False;
}

static void Execute( ANY& aEventData, SfxObjectShell* pDoc )
{
    SEQUENCE < PROPERTYVALUE > aProperties;
    if ( aEventData >>= aProperties )
    {
        OUSTRING        aPrefix = OUSTRING( RTL_CONSTASCII_USTRINGPARAM( MACRO_PRFIX ) );
        OUSTRING        aType;
        OUSTRING        aScript;
        OUSTRING        aLibrary;
        OUSTRING        aMacroName;

        sal_Int32 nCount = aProperties.getLength();

        if ( !nCount )
            return;

        sal_Int32 nIndex = 0;
        while ( nIndex < nCount )
        {
            if ( aProperties[ nIndex ].Name.compareToAscii( PROP_EVENT_TYPE ) == 0 )
                aProperties[ nIndex ].Value >>= aType;
            else if ( aProperties[ nIndex ].Name.compareToAscii( PROP_SCRIPT ) == 0 )
                aProperties[ nIndex ].Value >>= aScript;
            else if ( aProperties[ nIndex ].Name.compareToAscii( PROP_LIBRARY ) == 0 )
                aProperties[ nIndex ].Value >>= aLibrary;
            else if ( aProperties[ nIndex ].Name.compareToAscii( PROP_MACRO_NAME ) == 0 )
                aProperties[ nIndex ].Value >>= aMacroName;
            else
                DBG_ERROR("Unknown property value!");
            nIndex += 1;
        }

        if ( aType.compareToAscii( STAR_BASIC ) == 0 && aScript.getLength() )
        {
            com::sun::star::uno::Any aAny;
            SfxMacroLoader::loadMacro( aScript, aAny, pDoc );
        }
        else if ( aType.compareToAscii( "Service" ) == 0 ||
                            aType.compareToAscii( "Script" ) == 0 )
        {
            if ( aScript.getLength() )
            {
                SfxViewFrame* pView = pDoc ?
                    SfxViewFrame::GetFirst( pDoc ) :
                    SfxViewFrame::Current();

                ::com::sun::star::uno::Reference
                    < ::com::sun::star::util::XURLTransformer > xTrans(
                        ::comphelper::getProcessServiceFactory()->createInstance(
                            rtl::OUString::createFromAscii(
                                "com.sun.star.util.URLTransformer" ) ),
                        UNO_QUERY );

                ::com::sun::star::util::URL aURL;
                aURL.Complete = aScript;
                xTrans->parseStrict( aURL );

                ::com::sun::star::uno::Reference
                    < ::com::sun::star::frame::XDispatchProvider > xProv;

                if ( pView != NULL )
                {
                    xProv = ::com::sun::star::uno::Reference
                        < ::com::sun::star::frame::XDispatchProvider > (
                            pView->GetFrame()->GetFrameInterface(), UNO_QUERY );
                }
                else
                {
                    xProv = ::com::sun::star::uno::Reference
                        < ::com::sun::star::frame::XDispatchProvider > (
                            ::comphelper::getProcessServiceFactory()->createInstance(
                                rtl::OUString::createFromAscii(
                                    "com.sun.star.frame.Desktop" ) ),
                            UNO_QUERY );
                }

                ::com::sun::star::uno::Reference < ::com::sun::star::frame::XDispatch > xDisp;
                if ( xProv.is() )
                    xDisp = xProv->queryDispatch( aURL, ::rtl::OUString(), 0 );

                if ( xDisp.is() )
                {
                    //::com::sun::star::uno::Sequence < ::com::sun::star::beans::PropertyValue > aArgs(1);
                    //aArgs[0].Name = rtl::OUString::createFromAscii("Referer");
                    //aArs[0].Value <<= ::rtl::OUString( pDoc->GetMedium()->GetName() );
                    //xDisp->dispatch( aURL, aArgs );
                    xDisp->dispatch( aURL, ::com::sun::star::uno::Sequence < ::com::sun::star::beans::PropertyValue >() );
                }
            }
        }
        else
        {
            DBG_ERRORFILE( "notifyEvent(): Unsupported event type" );
        }
    }
}

//--------------------------------------------------------------------------------------------------------
// --- ::document::XEventListener ---
//--------------------------------------------------------------------------------------------------------
void SAL_CALL SfxEvents_Impl::notifyEvent( const DOCEVENTOBJECT& aEvent ) throw( RUNTIMEEXCEPTION )
{
    ::osl::ClearableMutexGuard aGuard( maMutex );

    // get the event name, find the coresponding data, execute the data

    OUSTRING    aName   = aEvent.EventName;
    long        nCount  = maEventNames.getLength();
    long        nIndex  = 0;
    sal_Bool    bFound  = sal_False;

    while ( !bFound && ( nIndex < nCount ) )
    {
        if ( maEventNames[nIndex] == aName )
            bFound = sal_True;
        else
            nIndex += 1;
    }

    if ( !bFound )
        return;

    ANY aEventData = maEventData[ nIndex ];
    aGuard.clear();
    Execute( aEventData, mpObjShell );
}

//--------------------------------------------------------------------------------------------------------
// --- ::lang::XEventListener ---
//--------------------------------------------------------------------------------------------------------
void SAL_CALL SfxEvents_Impl::disposing( const EVENTOBJECT& Source ) throw( RUNTIMEEXCEPTION )
{
    ::osl::MutexGuard aGuard( maMutex );

    if ( mxBroadcaster.is() )
    {
        mxBroadcaster->removeEventListener( this );
        mxBroadcaster = NULL;
    }
}

//--------------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------------
SfxEvents_Impl::SfxEvents_Impl( SfxObjectShell* pShell,
                                REFERENCE< XEVENTBROADCASTER > xBroadcaster )
{
    // get the list of supported events and store it
    if ( pShell )
        maEventNames = pShell->GetEventNames();
    else
        maEventNames = SfxObjectShell::GetEventNames_Impl();

    maEventData = SEQUENCE < ANY > ( maEventNames.getLength() );

    mpObjShell      = pShell;
    mxBroadcaster   = xBroadcaster;

    if ( mxBroadcaster.is() )
        mxBroadcaster->addEventListener( this );
}

//--------------------------------------------------------------------------------------------------------
SfxEvents_Impl::~SfxEvents_Impl()
{
}

//--------------------------------------------------------------------------------------------------------
SvxMacro* SfxEvents_Impl::ConvertToMacro( const ANY& rElement, SfxObjectShell* pObjShell, BOOL bBlowUp )
{
    SvxMacro* pMacro = NULL;
    SEQUENCE < PROPERTYVALUE > aProperties;
    ANY aAny;
    if ( bBlowUp )
        BlowUpMacro( rElement, aAny, pObjShell );
    else
        aAny = rElement;

    if ( aAny >>= aProperties )
    {
        OUSTRING        aType;
        OUSTRING        aScriptURL;
        OUSTRING        aLibrary;
        OUSTRING        aMacroName;

        long nCount = aProperties.getLength();
        long nIndex = 0;

        if ( !nCount )
            return pMacro;

        while ( nIndex < nCount )
        {
            if ( aProperties[ nIndex ].Name.compareToAscii( PROP_EVENT_TYPE ) == 0 )
                aProperties[ nIndex ].Value >>= aType;
            else if ( aProperties[ nIndex ].Name.compareToAscii( PROP_SCRIPT ) == 0 )
                aProperties[ nIndex ].Value >>= aScriptURL;
            else if ( aProperties[ nIndex ].Name.compareToAscii( PROP_LIBRARY ) == 0 )
                aProperties[ nIndex ].Value >>= aLibrary;
            else if ( aProperties[ nIndex ].Name.compareToAscii( PROP_MACRO_NAME ) == 0 )
                aProperties[ nIndex ].Value >>= aMacroName;
            else
                DBG_ERROR("Unknown propery value!");
            nIndex += 1;
        }

        // Get the type
        ScriptType  eType( STARBASIC );
        if ( aType.compareToAscii( STAR_BASIC ) == COMPARE_EQUAL )
            eType = STARBASIC;
        else if ( aType.compareToAscii( "Script" ) == COMPARE_EQUAL && aScriptURL.getLength() )
            eType = EXTENDED_STYPE;
        else if ( aType.compareToAscii( SVX_MACRO_LANGUAGE_JAVASCRIPT ) == COMPARE_EQUAL )
            eType = JAVASCRIPT;
        else
            DBG_ERRORFILE( "ConvertToMacro: Unknown macro type" );

        if ( aMacroName.getLength() )
        {
            if ( aLibrary.compareToAscii("application") == 0 )
                aLibrary = SFX_APP()->GetName();
            else
                aLibrary = ::rtl::OUString();
            pMacro = new SvxMacro( aMacroName, aLibrary, eType );
        }
        else if ( eType == EXTENDED_STYPE )
            pMacro = new SvxMacro( aScriptURL, aType );
    }

    return pMacro;
}

void SfxEvents_Impl::BlowUpMacro( const ANY& rEvent, ANY& rRet, SfxObjectShell* pDoc )
{
    if ( !pDoc )
        pDoc = SfxObjectShell::Current();

    SEQUENCE < PROPERTYVALUE > aInProps;
    SEQUENCE < PROPERTYVALUE > aOutProps(2);

    if ( !( rEvent >>= aInProps ) )
        return;

    sal_Int32 nCount = aInProps.getLength();

    if ( !nCount )
        return;

    OUSTRING aType;
    OUSTRING aScript;
    OUSTRING aLibrary;
    OUSTRING aMacroName;

    sal_Int32 nIndex = 0;

    while ( nIndex < nCount )
    {
        if ( aInProps[ nIndex ].Name.compareToAscii( PROP_EVENT_TYPE ) == 0 )
        {
            aInProps[nIndex].Value >>= aType;
            aOutProps[0] = aInProps[nIndex];
        }
        else if ( aInProps[ nIndex ].Name.compareToAscii( PROP_SCRIPT ) == 0 )
        {
            aInProps[nIndex].Value >>= aScript;
            aOutProps[1] = aInProps[nIndex];
        }
        else if ( aInProps[ nIndex ].Name.compareToAscii( PROP_LIBRARY ) == 0 )
        {
            aInProps[ nIndex ].Value >>= aLibrary;
        }
        else if ( aInProps[ nIndex ].Name.compareToAscii( PROP_MACRO_NAME ) == 0 )
        {
            aInProps[ nIndex ].Value >>= aMacroName;
        }
        nIndex += 1;
    }

    if ( aType.compareToAscii( STAR_BASIC ) == 0 )
    {
        aOutProps.realloc(4);
        if ( aScript.getLength() )
        {
            if( ! aMacroName.getLength() || ! aLibrary.getLength() )
            {
                sal_Int32 nHashPos = aScript.indexOf( '/', 8 );
                sal_Int32 nArgsPos = aScript.indexOf( '(' );
                if ( ( nHashPos != STRING_NOTFOUND ) && ( nHashPos < nArgsPos ) )
                {
                    OUSTRING aBasMgrName( INetURLObject::decode( aScript.copy( 8, nHashPos-8 ), INET_HEX_ESCAPE, INetURLObject::DECODE_WITH_CHARSET ) );
                    if ( aBasMgrName.compareToAscii(".") == 0 )
                        aLibrary = pDoc->GetTitle();
/*
                    else if ( aBasMgrName.getLength() )
                        aLibrary = aBasMgrName;
 */
                    else
                        aLibrary = SFX_APP()->GetName();

                    // Get the macro name
                    aMacroName = aScript.copy( nHashPos+1, nArgsPos - nHashPos - 1 );
                }
                else
                {
                    DBG_ERRORFILE( "ConvertToMacro: Unknown macro url format" );
                }
            }
        }
        else if ( aMacroName.getLength() )
        {
            aScript = OUSTRING( RTL_CONSTASCII_USTRINGPARAM( MACRO_PRFIX ) );
            if ( aLibrary.compareTo( SFX_APP()->GetName() ) != 0 && aLibrary.compareToAscii("StarDesktop") != 0 && aLibrary.compareToAscii("application") != 0 )
                aScript += String('.');

            aScript += String('/');
            aScript += aMacroName;
            aScript += OUSTRING( RTL_CONSTASCII_USTRINGPARAM( MACRO_POSTFIX ) );
        }
        else
            // wrong properties
            return;

        if ( aLibrary.compareToAscii("document") != 0 )
        {
            if ( !aLibrary.getLength() || pDoc && ( String(aLibrary) == pDoc->GetTitle( SFX_TITLE_APINAME ) || String(aLibrary) == pDoc->GetTitle() ) )
                aLibrary = String::CreateFromAscii("document");
            else
                aLibrary = String::CreateFromAscii("application");
        }

        aOutProps[1].Name = OUSTRING::createFromAscii( PROP_SCRIPT );
        aOutProps[1].Value <<= aScript;
        aOutProps[2].Name = OUSTRING::createFromAscii( PROP_LIBRARY );
        aOutProps[2].Value <<= aLibrary;
        aOutProps[3].Name = OUSTRING::createFromAscii( PROP_MACRO_NAME );
        aOutProps[3].Value <<= aMacroName;
        rRet <<= aOutProps;
    }
    else if ( aType.compareToAscii( SVX_MACRO_LANGUAGE_JAVASCRIPT ) == 0 )
    {
        aOutProps[1] = aInProps[1];
        rRet <<= aOutProps;
    }
    else
    {
        rRet <<= aOutProps;
    }
}

ModelCollectionEnumeration::ModelCollectionEnumeration(const css::uno::Reference< css::lang::XMultiServiceFactory >& xSMGR)
    : ModelCollectionMutexBase(                 )
    , m_xSMGR                 (xSMGR            )
    , m_pEnumerationIt        (m_lModels.begin())
{
}

ModelCollectionEnumeration::~ModelCollectionEnumeration()
{
}

void ModelCollectionEnumeration::setModelList(const TModelList& rList)
{
    // SAFE ->
    ::osl::ResettableMutexGuard aLock(m_aLock);
    m_lModels        = rList;
    m_pEnumerationIt = m_lModels.begin();
    aLock.clear();
    // <- SAFE
}

sal_Bool SAL_CALL ModelCollectionEnumeration::hasMoreElements()
    throw(css::uno::RuntimeException)
{
    // SAFE ->
    ::osl::ResettableMutexGuard aLock(m_aLock);
    return (m_pEnumerationIt != m_lModels.end());
    // <- SAFE
}

css::uno::Any SAL_CALL ModelCollectionEnumeration::nextElement()
    throw(css::container::NoSuchElementException,
          css::lang::WrappedTargetException     ,
          css::uno::RuntimeException            )
{
    // SAFE ->
    ::osl::ResettableMutexGuard aLock(m_aLock);
    if (m_pEnumerationIt == m_lModels.end())
        throw css::container::NoSuchElementException(
                    ::rtl::OUString::createFromAscii("End of model enumeration reached."),
                    static_cast< css::container::XEnumeration* >(this));
    css::uno::Reference< css::frame::XModel > xModel(*m_pEnumerationIt, UNO_QUERY);
    ++m_pEnumerationIt;
    aLock.clear();
    // <- SAFE

    return css::uno::makeAny(xModel);
}

SFX_IMPL_XSERVICEINFO( SfxGlobalEvents_Impl, "com.sun.star.frame.GlobalEventBroadcaster", "com.sun.star.comp.sfx2.GlobalEventBroadcaster" )
SFX_IMPL_ONEINSTANCEFACTORY( SfxGlobalEvents_Impl );

//-----------------------------------------------------------------------------
SfxGlobalEvents_Impl::SfxGlobalEvents_Impl( const com::sun::star::uno::Reference < ::com::sun::star::lang::XMultiServiceFactory >& xSMGR)
    : ModelCollectionMutexBase(       )
    , m_xSMGR                 (xSMGR  )
    , m_aInterfaceContainer   (m_aLock)
    , pImp                    (0      )
{
    m_refCount++;
    SFX_APP();
    pImp           = new GlobalEventConfig();
    m_xEvents      = pImp;
    m_xJobsBinding = css::uno::Reference< css::task::XJobExecutor >(
                        xSMGR->createInstance(::rtl::OUString::createFromAscii("com.sun.star.task.JobExecutor")),
                        UNO_QUERY);
    m_refCount--;
}

//-----------------------------------------------------------------------------
SfxGlobalEvents_Impl::~SfxGlobalEvents_Impl()
{
}

//-----------------------------------------------------------------------------
void SfxGlobalEvents_Impl::Notify( SfxBroadcaster& aBC, const SfxHint& aHint )
{
    SfxEventHint* pNamedHint = PTR_CAST(SfxEventHint, &aHint);
    if (!pNamedHint)
        return;

    css::uno::Reference< css::document::XEventsSupplier > xSup;

    ::rtl::OUString sName  = SfxEventConfiguration::GetEventName_Impl(pNamedHint->GetEventId());
    SfxObjectShell* pShell = pNamedHint->GetObjShell();
    if (pShell)
        xSup = css::uno::Reference< css::document::XEventsSupplier >(pShell->GetModel(), UNO_QUERY);

    css::document::EventObject aEvent(xSup, sName);
    notifyEvent(aEvent);
}

//-----------------------------------------------------------------------------
css::uno::Reference< css::container::XNameReplace > SAL_CALL SfxGlobalEvents_Impl::getEvents()
    throw(css::uno::RuntimeException)
{
    // SAFE ->
    ::osl::ResettableMutexGuard aLock(m_aLock);
    return m_xEvents;
    // <- SAFE
}

//-----------------------------------------------------------------------------
void SAL_CALL SfxGlobalEvents_Impl::addEventListener(const css::uno::Reference< css::document::XEventListener >& xListener)
    throw(css::uno::RuntimeException)
{
    // container is threadsafe
    m_aInterfaceContainer.addInterface(xListener);
}

//-----------------------------------------------------------------------------
void SAL_CALL SfxGlobalEvents_Impl::removeEventListener(const css::uno::Reference< css::document::XEventListener >& xListener)
    throw(css::uno::RuntimeException)
{
    // container is threadsafe
    m_aInterfaceContainer.removeInterface(xListener);
}

//-----------------------------------------------------------------------------
void SAL_CALL SfxGlobalEvents_Impl::notifyEvent(const css::document::EventObject& aEvent)
    throw(css::uno::RuntimeException)
{
    implts_notifyJobExecution(aEvent);
    implts_checkAndExecuteEventBindings(aEvent);
    implts_notifyListener(aEvent);
}

//-----------------------------------------------------------------------------
void SAL_CALL SfxGlobalEvents_Impl::disposing(const css::lang::EventObject& aEvent)
    throw(css::uno::RuntimeException)
{
    css::uno::Reference< css::frame::XModel > xDoc(aEvent.Source, UNO_QUERY);

    // SAFE ->
    ::osl::ResettableMutexGuard aLock(m_aLock);
    TModelList::iterator pIt = impl_searchDoc(xDoc);
    if (pIt != m_lModels.end())
        m_lModels.erase(pIt);
    aLock.clear();
    // <- SAFE
}

//-----------------------------------------------------------------------------
sal_Bool SAL_CALL SfxGlobalEvents_Impl::has(const css::uno::Any& aElement)
    throw (css::uno::RuntimeException)
{
    css::uno::Reference< css::frame::XModel > xDoc;
    aElement >>= xDoc;

    sal_Bool bHas = sal_False;

    // SAFE ->
    ::osl::ResettableMutexGuard aLock(m_aLock);
    TModelList::iterator pIt = impl_searchDoc(xDoc);
    if (pIt != m_lModels.end())
        bHas = sal_True;
    aLock.clear();
    // <- SAFE

    return bHas;
}

//-----------------------------------------------------------------------------
void SAL_CALL SfxGlobalEvents_Impl::insert( const css::uno::Any& aElement )
    throw (css::lang::IllegalArgumentException  ,
           css::container::ElementExistException,
           css::uno::RuntimeException           )
{
    css::uno::Reference< css::frame::XModel > xDoc;
    aElement >>= xDoc;
    if (!xDoc.is())
        throw css::lang::IllegalArgumentException(
                ::rtl::OUString::createFromAscii("Cant locate at least the model parameter."),
                static_cast< css::container::XSet* >(this),
                0);

    // SAFE ->
    ::osl::ResettableMutexGuard aLock(m_aLock);
    TModelList::iterator pIt = impl_searchDoc(xDoc);
    if (pIt != m_lModels.end())
        throw css::container::ElementExistException(
                ::rtl::OUString(),
                static_cast< css::container::XSet* >(this));
    m_lModels.push_back(xDoc);
    aLock.clear();
    // <- SAFE

    css::uno::Reference< css::document::XEventBroadcaster > xDocBroadcast(xDoc, UNO_QUERY);
    if (xDocBroadcast.is())
        xDocBroadcast->addEventListener(static_cast< css::document::XEventListener* >(this));
}

//-----------------------------------------------------------------------------
void SAL_CALL SfxGlobalEvents_Impl::remove( const css::uno::Any& aElement )
    throw (css::lang::IllegalArgumentException   ,
           css::container::NoSuchElementException,
           css::uno::RuntimeException            )
{
    css::uno::Reference< css::frame::XModel > xDoc;
    aElement >>= xDoc;
    if (!xDoc.is())
        throw css::lang::IllegalArgumentException(
                ::rtl::OUString::createFromAscii("Cant locate at least the model parameter."),
                static_cast< css::container::XSet* >(this),
                0);

    // SAFE ->
    ::osl::ResettableMutexGuard aLock(m_aLock);
    TModelList::iterator pIt = impl_searchDoc(xDoc);
    if (pIt == m_lModels.end())
        throw css::container::NoSuchElementException(
                ::rtl::OUString(),
                static_cast< css::container::XSet* >(this));
    m_lModels.erase(pIt);
    aLock.clear();
    // <- SAFE

    css::uno::Reference< css::document::XEventBroadcaster > xDocBroadcast(xDoc, UNO_QUERY);
    if (xDocBroadcast.is())
        xDocBroadcast->removeEventListener(static_cast< css::document::XEventListener* >(this));
}

//-----------------------------------------------------------------------------
css::uno::Reference< css::container::XEnumeration > SAL_CALL SfxGlobalEvents_Impl::createEnumeration()
    throw (css::uno::RuntimeException)
{
    // SAFE ->
    ::osl::ResettableMutexGuard aLock(m_aLock);
    ModelCollectionEnumeration* pEnum = new ModelCollectionEnumeration(m_xSMGR);
    pEnum->setModelList(m_lModels);
    css::uno::Reference< css::container::XEnumeration > xEnum(
        static_cast< css::container::XEnumeration* >(pEnum),
        UNO_QUERY);
    aLock.clear();
    // <- SAFE

    return xEnum;
}

//-----------------------------------------------------------------------------
css::uno::Type SAL_CALL SfxGlobalEvents_Impl::getElementType()
    throw (css::uno::RuntimeException)
{
    return ::getCppuType(static_cast< css::uno::Reference< css::frame::XModel >* >(NULL));
}

//-----------------------------------------------------------------------------
sal_Bool SAL_CALL SfxGlobalEvents_Impl::hasElements()
    throw (css::uno::RuntimeException)
{
    // SAFE ->
    ::osl::ResettableMutexGuard aLock(m_aLock);
    return (m_lModels.size()>0);
    // <- SAFE
}

//-----------------------------------------------------------------------------
void SfxGlobalEvents_Impl::implts_notifyJobExecution(const css::document::EventObject& aEvent)
{
    try
    {
        // SAFE ->
        ::osl::ResettableMutexGuard aLock(m_aLock);
        css::uno::Reference< css::document::XEventListener > xJobExecutor(m_xJobsBinding.get(), UNO_QUERY);
        aLock.clear();
        // <- SAFE
        if (xJobExecutor.is())
            xJobExecutor->notifyEvent(aEvent);
    }
    catch(const css::uno::RuntimeException& exRun)
        { throw exRun; }
    catch(const css::uno::Exception&)
        {}
}

//-----------------------------------------------------------------------------
void SfxGlobalEvents_Impl::implts_checkAndExecuteEventBindings(const css::document::EventObject& aEvent)
{
    try
    {
        // SAFE ->
        ::osl::ResettableMutexGuard aLock(m_aLock);
        css::uno::Reference< css::container::XNameReplace > xEvents = m_xEvents;
        aLock.clear();
        // <- SAFE

        css::uno::Any aAny;
        if (xEvents.is())
            aAny = xEvents->getByName(aEvent.EventName);
        Execute(aAny, 0);
    }
    catch(const css::uno::RuntimeException& exRun)
        { throw exRun; }
    catch(const css::uno::Exception&)
        {}
}

//-----------------------------------------------------------------------------
void SfxGlobalEvents_Impl::implts_notifyListener(const css::document::EventObject& aEvent)
{
    // container is threadsafe
    ::cppu::OInterfaceIteratorHelper aIt(m_aInterfaceContainer);
    while (aIt.hasMoreElements())
    {
        try
        {
            ((css::document::XEventListener*)aIt.next())->notifyEvent(aEvent);
        }
        catch(const css::uno::Exception&)
            { aIt.remove(); }
    }
}

//-----------------------------------------------------------------------------
// not threadsafe ... must be locked from outside!
TModelList::iterator SfxGlobalEvents_Impl::impl_searchDoc(const css::uno::Reference< css::frame::XModel >& xModel)
{
    if (!xModel.is())
        return m_lModels.end();

    TModelList::iterator pIt;
    for (  pIt  = m_lModels.begin();
           pIt != m_lModels.end()  ;
         ++pIt                     )
    {
        css::uno::Reference< css::frame::XModel > xContainerDoc(*pIt, UNO_QUERY);
        if (xContainerDoc == xModel)
            break;
    }

    return pIt;
}

