/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 * 
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org 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 version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_fpicker.hxx"

#include "commonpicker.hxx"
#include <com/sun/star/beans/PropertyAttribute.hpp>
#include <com/sun/star/beans/NamedValue.hpp>
#include <vcl/svapp.hxx>
#include <osl/mutex.hxx>
#include <toolkit/helper/vclunohelper.hxx>
#include <comphelper/weakeventlistener.hxx>
#include <comphelper/types.hxx>
#include <vcl/msgbox.hxx>
#include "iodlg.hxx"

//.........................................................................
namespace svt
{
//.........................................................................

#define PROPERTY_ID_HELPURL		1
#define PROPERTY_ID_WINDOW		2

    // using --------------------------------------------------------------

    using namespace     ::com::sun::star::lang;
    using namespace     ::com::sun::star::ui::dialogs;
    using namespace     ::com::sun::star::uno;
    using namespace     ::com::sun::star::beans;
    using namespace     ::comphelper;

    //---------------------------------------------------------------------
    OCommonPicker::OCommonPicker( const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory >& _rxFactory )
        :OCommonPicker_Base( m_aMutex )
        ,OPropertyContainer( GetBroadcastHelper() )
        ,m_xORB( _rxFactory )
        ,m_pDlg( NULL )
        ,m_nCancelEvent( 0 )
        ,m_bExecuting( sal_False )
    {
        // the two properties we have
        registerProperty(
            ::rtl::OUString::createFromAscii( "HelpURL" ), PROPERTY_ID_HELPURL,
            PropertyAttribute::TRANSIENT,
            &m_sHelpURL, ::getCppuType( &m_sHelpURL )
        );

        registerProperty(
            ::rtl::OUString::createFromAscii( "Window" ), PROPERTY_ID_WINDOW,
            PropertyAttribute::TRANSIENT | PropertyAttribute::READONLY,
            &m_xWindow, ::getCppuType( &m_xWindow )
        );
    }

    //---------------------------------------------------------------------
    OCommonPicker::~OCommonPicker()
    {
        if ( !GetBroadcastHelper().bDisposed )
        {
            acquire();
            dispose();
        }
    }

    //---------------------------------------------------------------------
    // disambiguate XInterface
    //---------------------------------------------------------------------
    IMPLEMENT_FORWARD_XINTERFACE2( OCommonPicker, OCommonPicker_Base, OPropertyContainer )

    //---------------------------------------------------------------------
    // disambiguate XTypeProvider
    //---------------------------------------------------------------------
    IMPLEMENT_FORWARD_XTYPEPROVIDER2( OCommonPicker, OCommonPicker_Base, OPropertyContainer )

    //---------------------------------------------------------------------
    // XComponent related methods
    //---------------------------------------------------------------------
    void OCommonPicker::checkAlive() const SAL_THROW( (DisposedException) )
    {
        if ( GetBroadcastHelper().bInDispose || GetBroadcastHelper().bDisposed )
            throw DisposedException();
    }

    void OCommonPicker::prepareDialog()
    {
        if ( !getDialog() )
            createPicker();

        // set the title
        if ( m_aTitle.getLength() > 0 )
            getDialog()->SetText( m_aTitle );
    }

    //---------------------------------------------------------------------
    void SAL_CALL OCommonPicker::disposing()
    {
        SolarMutexGuard aGuard;

        stopWindowListening();

        if ( m_nCancelEvent )
            Application::RemoveUserEvent( m_nCancelEvent );

        {
            ::osl::MutexGuard aOwnGuard( m_aMutex );
            if ( m_bExecuting && m_pDlg )
                m_pDlg->EndDialog( RET_CANCEL );
        }

        delete m_pDlg;
        m_pDlg = NULL;
        m_xWindow = NULL;
        m_xDialogParent = NULL;
    }

    //---------------------------------------------------------------------
    void OCommonPicker::stopWindowListening()
    {
        disposeComponent( m_xWindowListenerAdapter );
        disposeComponent( m_xParentListenerAdapter );
    }

    //---------------------------------------------------------------------
    // XEventListener
    //---------------------------------------------------------------------
    void SAL_CALL OCommonPicker::disposing( const EventObject& _rSource ) throw (RuntimeException)
    {
        SolarMutexGuard aGuard;
        sal_Bool bDialogDying = _rSource.Source == m_xWindow;
        sal_Bool bParentDying = _rSource.Source == m_xDialogParent;

        if ( bDialogDying || bParentDying )
        {
            stopWindowListening();

            if ( !bDialogDying )	// it's the parent which is dying -> delete the dialog
                delete m_pDlg;

            m_pDlg = NULL;
            m_xWindow = NULL;
            m_xDialogParent = NULL;
        }
        else
        {
            DBG_ERROR( "OCommonPicker::disposing: where did this come from?" );
        }
    }

    //---------------------------------------------------------------------
    // property set related methods
    //---------------------------------------------------------------------
    ::cppu::IPropertyArrayHelper* OCommonPicker::createArrayHelper( ) const
    {
        Sequence< Property > aProps;
        describeProperties( aProps );
        return new cppu::OPropertyArrayHelper( aProps );
    }

    //---------------------------------------------------------------------
    ::cppu::IPropertyArrayHelper& SAL_CALL OCommonPicker::getInfoHelper()
    {
        return *const_cast< OCommonPicker* >( this )->getArrayHelper();
    }

    //---------------------------------------------------------------------
    Reference< XPropertySetInfo > SAL_CALL OCommonPicker::getPropertySetInfo(  ) throw(RuntimeException)
    {
        return ::cppu::OPropertySetHelper::createPropertySetInfo( getInfoHelper() );
    }

    //---------------------------------------------------------------------
    void SAL_CALL OCommonPicker::setFastPropertyValue_NoBroadcast( sal_Int32 _nHandle, const Any& _rValue ) throw (Exception)
    {
        OPropertyContainer::setFastPropertyValue_NoBroadcast( _nHandle, _rValue );

        // if the HelpURL changed, forward this to the dialog
        if ( PROPERTY_ID_HELPURL == _nHandle )
            if ( m_pDlg )
                OControlAccess::setHelpURL( m_pDlg, m_sHelpURL, sal_False );
    }


    //---------------------------------------------------------------------
    sal_Bool OCommonPicker::createPicker()
    {
        SolarMutexGuard aGuard;

        if ( !m_pDlg )
        {
            m_pDlg = implCreateDialog( VCLUnoHelper::GetWindow( m_xDialogParent ) );
            DBG_ASSERT( m_pDlg, "OCommonPicker::createPicker: invalid dialog returned!" );

            if ( m_pDlg )
            {
                // synchronize the help id of the dialog with out help URL property
                if ( m_sHelpURL.getLength() )
                {	// somebody already set the help URL while we had no dialog yet
                    OControlAccess::setHelpURL( m_pDlg, m_sHelpURL, sal_False );
                }
                else
                {
                    m_sHelpURL = OControlAccess::getHelpURL( m_pDlg, sal_False );
                }

                m_xWindow = VCLUnoHelper::GetInterface( m_pDlg );

                // add as event listener to the window
                Reference< XComponent > xWindowComp( m_xWindow, UNO_QUERY );
                OSL_ENSURE( xWindowComp.is(), "OCommonPicker::createFileDialog: invalid window component!" );
                if ( xWindowComp.is() )
                {
                    m_xWindowListenerAdapter = new OWeakEventListenerAdapter( this, xWindowComp );
                        // the adapter will add itself as listener, and forward notifications
                }

                // _and_ add as event listener to the parent - in case the parent is destroyed
                // before we are disposed, our disposal would access dead VCL windows then ....
                m_xDialogParent = VCLUnoHelper::GetInterface( m_pDlg->GetParent() );
                xWindowComp = xWindowComp.query( m_xDialogParent );
                OSL_ENSURE( xWindowComp.is() || !m_pDlg->GetParent(), "OCommonPicker::createFileDialog: invalid window component (the parent this time)!" );
                if ( xWindowComp.is() )
                {
                    m_xParentListenerAdapter = new OWeakEventListenerAdapter( this, xWindowComp );
                        // the adapter will add itself as listener, and forward notifications
                }
            }
        }

        return NULL != m_pDlg;
    }

    //---------------------------------------------------------------------
    // XControlAccess functions
    //---------------------------------------------------------------------
    void SAL_CALL OCommonPicker::setControlProperty( const ::rtl::OUString& aControlName, const ::rtl::OUString& aControlProperty, const Any& aValue ) throw (IllegalArgumentException, RuntimeException)
    {
        checkAlive();

        SolarMutexGuard aGuard;
        if ( createPicker() )
        {
            ::svt::OControlAccess aAccess( m_pDlg, m_pDlg->GetView() );
            aAccess.setControlProperty( aControlName, aControlProperty, aValue );
        }
    }

    //---------------------------------------------------------------------
    Any SAL_CALL OCommonPicker::getControlProperty( const ::rtl::OUString& aControlName, const ::rtl::OUString& aControlProperty ) throw (IllegalArgumentException, RuntimeException)
    {
        checkAlive();

        SolarMutexGuard aGuard;
        if ( createPicker() )
        {
            ::svt::OControlAccess aAccess( m_pDlg, m_pDlg->GetView() );
            return aAccess.getControlProperty( aControlName, aControlProperty );
        }

        return Any();
    }

    //---------------------------------------------------------------------
    // XControlInformation functions
    //---------------------------------------------------------------------
    Sequence< ::rtl::OUString > SAL_CALL OCommonPicker::getSupportedControls(  ) throw (RuntimeException)
    {
        checkAlive();

        SolarMutexGuard aGuard;
        if ( createPicker() )
        {
            ::svt::OControlAccess aAccess( m_pDlg, m_pDlg->GetView() );
            return aAccess.getSupportedControls( );
        }

        return Sequence< ::rtl::OUString >();
    }

    //---------------------------------------------------------------------
    sal_Bool SAL_CALL OCommonPicker::isControlSupported( const ::rtl::OUString& aControlName ) throw (RuntimeException)
    {
        checkAlive();

        SolarMutexGuard aGuard;
        if ( createPicker() )
        {
            ::svt::OControlAccess aAccess( m_pDlg, m_pDlg->GetView() );
            return aAccess.isControlSupported( aControlName );
        }

        return sal_False;
    }

    //---------------------------------------------------------------------
    Sequence< ::rtl::OUString > SAL_CALL OCommonPicker::getSupportedControlProperties( const ::rtl::OUString& aControlName ) throw (IllegalArgumentException, RuntimeException)
    {
        checkAlive();

        SolarMutexGuard aGuard;
        if ( createPicker() )
        {
            ::svt::OControlAccess aAccess( m_pDlg, m_pDlg->GetView() );
            return aAccess.getSupportedControlProperties( aControlName );
        }

        return Sequence< ::rtl::OUString >();
    }

    //---------------------------------------------------------------------
    sal_Bool SAL_CALL OCommonPicker::isControlPropertySupported( const ::rtl::OUString& aControlName, const ::rtl::OUString& aControlProperty ) throw (IllegalArgumentException, RuntimeException)
    {
        checkAlive();

        SolarMutexGuard aGuard;
        if ( createPicker() )
        {
            ::svt::OControlAccess aAccess( m_pDlg, m_pDlg->GetView() );
            return aAccess.isControlPropertySupported( aControlName, aControlProperty );
        }

        return sal_False;
    }

    //---------------------------------------------------------------------
    // XExecutableDialog functions
    //---------------------------------------------------------------------
    void SAL_CALL OCommonPicker::setTitle( const rtl::OUString& _rTitle ) throw( RuntimeException )
    {
        SolarMutexGuard aGuard;
        m_aTitle = _rTitle;
    }

    //---------------------------------------------------------------------
    sal_Int16 OCommonPicker::execute() throw (RuntimeException)
    {
        SolarMutexGuard aGuard;

        prepareDialog();

        {
            ::osl::MutexGuard aOwnGuard( m_aMutex );
            m_bExecuting = sal_True;
        }
        sal_Int16 nResult = implExecutePicker();
        {
            ::osl::MutexGuard aOwnGuard( m_aMutex );
            m_bExecuting = sal_False;
        }

        return nResult;
    }

    //---------------------------------------------------------------------
    // XCancellable functions
    //---------------------------------------------------------------------
    void SAL_CALL OCommonPicker::cancel(  ) throw (RuntimeException)
    {
        {
            ::osl::MutexGuard aGuard( m_aMutex );
            if ( m_nCancelEvent )
                // nothing to do - the event for cancelling the dialog is already on the way
                return;
        }

        // The thread which executes our dialog has locked the solar mutex for
        // sure. Cancelling the dialog should be done with a locked solar mutex, too.
        // Thus we post ourself a message for cancelling the dialog. This way, the message
        // is either handled in the thread which opened the dialog (which may even be
        // this thread here), or, if no dialog is open, in the thread doing scheduling
        // currently. Both is okay for us ....
        //
        // Note that we could do check if we are really executing the dialog currently.
        // but the information would be potentially obsolete at the moment our event
        // arrives, so we need to check it there, anyway ...
        m_nCancelEvent = Application::PostUserEvent( LINK( this, OCommonPicker, OnCancelPicker ) );
    }

    //---------------------------------------------------------------------
    IMPL_LINK( OCommonPicker, OnCancelPicker, void*, EMPTYARG )
    {
        // By definition, the solar mutex is locked when we arrive here. Note that this
        // is important, as for instance the consistency of m_pDlg depends on this mutex.
        ::osl::MutexGuard aGuard( m_aMutex );
        m_nCancelEvent = 0;

        if ( !m_bExecuting )
            // nothing to do. This may be because the dialog was cancelled after our cancel method
            // posted this async event, or because somebody called cancel without the dialog
            // being executed at this time.
            return 0;

        OSL_ENSURE( getDialog(), "OCommonPicker::OnCancelPicker: executing, but no dialog!" );
        if ( getDialog() )
            getDialog()->EndDialog( RET_CANCEL );

        return 0L;
    }

    //------------------------------------------------------------------------------------
    // XInitialization functions
    //------------------------------------------------------------------------------------
    void SAL_CALL OCommonPicker::initialize( const Sequence< Any >& _rArguments )
        throw ( Exception, RuntimeException )
    {
        checkAlive();

        ::rtl::OUString sSettingName;
        Any				aSettingValue;

        PropertyValue	aPropArg;
        NamedValue		aPairArg;


        const Any* pArguments		= _rArguments.getConstArray();
        const Any* pArgumentsEnd	= _rArguments.getConstArray() + _rArguments.getLength();
        for	(	const Any* pArgument = pArguments;
                pArgument != pArgumentsEnd;
                ++pArgument
            )
        {
            if ( *pArgument >>= aPropArg )
            {
                if ( aPropArg.Name.getLength() <= 0)
                    continue;

                sSettingName = aPropArg.Name;
                aSettingValue = aPropArg.Value;
            }
            else if ( *pArgument >>= aPairArg )
            {
                if ( aPairArg.Name.getLength() <= 0)
                    continue;

                sSettingName = aPairArg.Name;
                aSettingValue = aPairArg.Value;

                
            }
            else
            {
                DBG_ERROR(
                    (	::rtl::OString( "OCommonPicker::initialize: unknown argument type at position " )
                    +=	::rtl::OString::valueOf( (sal_Int32)( pArguments - _rArguments.getConstArray() ) )
                    ).getStr()
                );
                continue;
            }

#ifdef DBG_UTIL
            sal_Bool bKnownSetting =
#endif
            implHandleInitializationArgument( sSettingName, aSettingValue );
            DBG_ASSERT( bKnownSetting,
                (	::rtl::OString( "OCommonPicker::initialize: unknown argument \"" )
                +=	::rtl::OString( sSettingName.getStr(), sSettingName.getLength(), osl_getThreadTextEncoding() )
                +=	::rtl::OString( "\"!" )
                ).getStr()
            );
        }
    }

    //---------------------------------------------------------------------
    sal_Bool OCommonPicker::implHandleInitializationArgument( const ::rtl::OUString& _rName, const Any& _rValue ) SAL_THROW( ( Exception, RuntimeException ) )
    {
        sal_Bool bKnown = sal_True;
        if ( _rName.equalsAscii( "ParentWindow" ) )
        {
            m_xDialogParent.clear();
            OSL_VERIFY( _rValue >>= m_xDialogParent );
            OSL_ENSURE( VCLUnoHelper::GetWindow( m_xDialogParent ), "OCommonPicker::implHandleInitializationArgument: invalid parent window given!" );
        }
        else
            bKnown = sal_False;
        return bKnown;
    }

//.........................................................................
}	// namespace svt
//.........................................................................

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