// SOActiveX.cpp : Implementation of CSOActiveX

#pragma warning (disable:4505)
    // permanently suppress "unreferenced local function has been removed" warning

#pragma warning (push,1)
#pragma warning (disable:4265)

#include "stdafx2.h"
#include "so_activex.h"
#include "SOActiveX.h"
#include "SOComWindowPeer.h"
#include "SODispatchInterceptor.h"
#include "SOActionsApproval.h"

#pragma warning (pop)

#define STAROFFICE_WINDOWCLASS "SOParentWindow"


/////////////////////////////////////////////////////////////////////////////

void OutputError_Impl( HWND hw, HRESULT ErrorCode )
{
	void* sMessage;
	FormatMessageA(
		FORMAT_MESSAGE_ALLOCATE_BUFFER |
		FORMAT_MESSAGE_FROM_SYSTEM,
		NULL,
		ErrorCode,
		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
		(LPTSTR) &sMessage,
		0,
		NULL
	);
	::MessageBoxA( hw, (LPCTSTR)sMessage, NULL, MB_OK | MB_ICONINFORMATION );
	LocalFree( sMessage );
}

HRESULT ExecuteFunc( IDispatch* idispUnoObject,
					 OLECHAR* sFuncName,
					 CComVariant* params,
					 unsigned int count,
					 CComVariant* pResult )
{
	if( !idispUnoObject )
		return E_FAIL;

	DISPID id;
	HRESULT hr = idispUnoObject->GetIDsOfNames( IID_NULL, &sFuncName, 1, LOCALE_USER_DEFAULT, &id);
	if( !SUCCEEDED( hr ) ) return hr;

	DISPPARAMS dispparams= { params, 0, count, 0};

	// DEBUG
	EXCEPINFO myInfo;
	hr = idispUnoObject->Invoke( id, IID_NULL,LOCALE_USER_DEFAULT, DISPATCH_METHOD,
                    &dispparams, pResult, &myInfo, 0);

	// for debugging purposes
	// USES_CONVERSION;
	// if ( !SUCCEEDED( hr ) )
	//	::MessageBox( NULL, OLE2A( myInfo.bstrDescription ), OLE2A( myInfo.bstrSource ), MB_OK | MB_ICONINFORMATION );

	return hr;
}

HRESULT GetIDispByFunc( IDispatch* idispUnoObject,
					  	OLECHAR* sFuncName,
					  	CComVariant* params,
					  	unsigned int count,
					  	CComPtr<IDispatch>& pdispResult )
{
	if( !idispUnoObject )
		return E_FAIL;

	CComVariant result;
	HRESULT hr = ExecuteFunc( idispUnoObject, sFuncName, params, count, &result );
	if( !SUCCEEDED( hr ) ) return hr;

	if( result.vt != VT_DISPATCH || result.pdispVal == NULL )
		return E_FAIL;

	pdispResult = CComPtr<IDispatch>( result.pdispVal );

	return S_OK;
}

HRESULT PutPropertiesToIDisp( IDispatch* pdispObject,
							  OLECHAR** sMemberNames,
							  CComVariant* pVariant,
							  unsigned int count )
{
	for( unsigned int ind = 0; ind < count; ind++ )
	{
		DISPID id;
		HRESULT hr = pdispObject->GetIDsOfNames( IID_NULL, &sMemberNames[ind], 1, LOCALE_USER_DEFAULT, &id );
		if( !SUCCEEDED( hr ) ) return hr;

		hr = CComDispatchDriver::PutProperty( pdispObject, id, &pVariant[ind] );
		if( !SUCCEEDED( hr ) ) return hr;
	}

	return S_OK;
}

HRESULT GetPropertiesFromIDisp( IDispatch* pdispObject,
							    OLECHAR** sMemberNames,
							    CComVariant* pVariant,
							    unsigned int count )
{
	for( unsigned int ind = 0; ind < count; ind++ )
	{
		DISPID id;
		HRESULT hr = pdispObject->GetIDsOfNames( IID_NULL, &sMemberNames[ind], 1, LOCALE_USER_DEFAULT, &id );
		if( !SUCCEEDED( hr ) ) return hr;

		hr = CComDispatchDriver::GetProperty( pdispObject, id, &pVariant[ind] );
		if( !SUCCEEDED( hr ) ) return hr;
	}

	return S_OK;
}
/////////////////////////////////////////////////////////////////////////////
// CSOActiveX

CSOActiveX::CSOActiveX()
: mCookie(0)
, mCurFileUrl( L"private:factory/swriter" )
, mbLoad( FALSE )
, mParentWin( NULL )
, mOffWin( NULL )
, mbViewOnly( TRUE )
, mpDispatchInterceptor( NULL )
, mnVersion( SO_NOT_DETECTED )
, mbReadyForActivation( FALSE )
, mbDrawLocked( FALSE )
{
	CLSID clsFactory = {0x82154420,0x0FBF,0x11d4,{0x83, 0x13,0x00,0x50,0x04,0x52,0x6A,0xB4}};
	HRESULT hr = CoCreateInstance( clsFactory, NULL, CLSCTX_ALL, __uuidof(IDispatch), (void**)&mpDispFactory);
	if( !SUCCEEDED( hr ) )
		OutputError_Impl( NULL, hr );

    mPWinClass.style			= CS_HREDRAW|CS_VREDRAW;
    mPWinClass.lpfnWndProc		= ::DefWindowProc;
    mPWinClass.cbClsExtra		= 0;
    mPWinClass.cbWndExtra		= 0;
    mPWinClass.hInstance		= (HINSTANCE) GetModuleHandle(NULL); //myInstance;
    mPWinClass.hIcon			= NULL;
    mPWinClass.hCursor			= NULL;
    mPWinClass.hbrBackground	= (HBRUSH) COLOR_BACKGROUND;
    mPWinClass.lpszMenuName	    = NULL;
    mPWinClass.lpszClassName	= STAROFFICE_WINDOWCLASS;

	RegisterClass(&mPWinClass);
}

CSOActiveX::~CSOActiveX()
{
	Cleanup();

}

HRESULT CSOActiveX::Cleanup()
{
	CComVariant dummyResult;

	if( mpDispatchInterceptor )
	{
		if( mpDispFrame )
		{
			// remove dispatch interceptor
			CComQIPtr< IDispatch, &IID_IDispatch > pIDispDispInter( mpDispatchInterceptor );
		        CComVariant aVariant( pIDispDispInter );
			ExecuteFunc( mpDispFrame,
						 L"releaseDispatchProviderInterceptor",
						 &aVariant,
						 1,
						 &dummyResult );
		}

		mpDispatchInterceptor->ClearParent();
        mpDispatchInterceptor->Release();
        mpDispatchInterceptor = NULL;
	}

	mpDispTempFile = CComPtr< IDispatch >();
	mbReadyForActivation = FALSE;

	if( mpInstanceLocker )
	{
		ExecuteFunc( mpInstanceLocker, L"dispose", NULL, 0, &dummyResult );
		mpInstanceLocker = CComPtr< IDispatch >();
	}

	if( mpDispFrame )
	{
		BOOL bCloserActivated = FALSE;

		CComPtr<IDispatch> pDispDocumentCloser;
        CComVariant aDocCloser( L"com.sun.star.embed.DocumentCloser" );
		HRESULT hr = GetIDispByFunc( mpDispFactory,
					 				L"createInstance",
					 				&aDocCloser,
					 				1,
					 				pDispDocumentCloser );
		if ( SUCCEEDED( hr ) && pDispDocumentCloser )
		{
			SAFEARRAY FAR* pInitFrame = SafeArrayCreateVector( VT_VARIANT, 0, 1 );
			long nInitInd = 0;
			CComVariant pFrameVariant( mpDispFrame );
			SafeArrayPutElement( pInitFrame, &nInitInd, &pFrameVariant );
			CComVariant aVarInitFrame;
			aVarInitFrame.vt = VT_ARRAY | VT_VARIANT; aVarInitFrame.parray = pInitFrame;
			hr = ExecuteFunc( pDispDocumentCloser, L"initialize", &aVarInitFrame, 1, &dummyResult );
			if( SUCCEEDED( hr ) )
			{
				// the following call will let the closing happen
				hr = ExecuteFunc( pDispDocumentCloser, L"dispose", NULL, 0, &dummyResult );
				bCloserActivated = SUCCEEDED( hr );
			}
		}

		if ( !bCloserActivated )
		{
			CComVariant aPropVar;
			aPropVar.vt = VT_BOOL; aPropVar.boolVal = VARIANT_TRUE;
			if ( !SUCCEEDED( ExecuteFunc( mpDispFrame, L"close", &aPropVar, 1, &dummyResult ) ) )
				ExecuteFunc( mpDispFrame, L"dispose", NULL, 0, &dummyResult );
		}

		mpDispFrame = CComPtr< IDispatch >();
	}

	if( ::IsWindow( mOffWin ) )
		::DestroyWindow( mOffWin );

    TerminateOffice();

	return S_OK;
}

HRESULT CSOActiveX::TerminateOffice()
{
    // create desktop
	CComPtr<IDispatch> pdispDesktop;
    CComVariant aDesktopServiceName( L"com.sun.star.frame.Desktop" );

    HRESULT hr = GetIDispByFunc( mpDispFactory, L"createInstance", &aDesktopServiceName, 1, pdispDesktop );
	if( !pdispDesktop || !SUCCEEDED( hr ) ) return hr;

	// create tree of frames
	CComPtr<IDispatch> pdispChildren;
	hr = GetIDispByFunc( pdispDesktop, L"getFrames", NULL, 0, pdispChildren );
	if( !pdispChildren || !SUCCEEDED( hr ) ) return hr;

	CComVariant aFrames;
    CComVariant nFlag( 4 );
	hr = ExecuteFunc( pdispChildren, L"queryFrames", &nFlag, 1, &aFrames );
    if ( SUCCEEDED( hr ) )
    {
		if ( ( aFrames.vt == ( VT_ARRAY | VT_DISPATCH ) || aFrames.vt == ( VT_ARRAY | VT_VARIANT ) ) 
          && ( !aFrames.parray || aFrames.parray->cDims == 1 && aFrames.parray->rgsabound[0].cElements == 0 ) )
        {
            // there is no frames open
            // TODO: check whether the frames are hidden if they are open?
            CComVariant dummyResult;
	        hr = ExecuteFunc( pdispDesktop, L"terminate", NULL, 0, &dummyResult );
        }
    }
    
    return hr;
}

STDMETHODIMP CSOActiveX::InitNew ()
{
	mnVersion = GetVersionConnected();
	mbLoad = TRUE;
    return S_OK;
}

STDMETHODIMP CSOActiveX::Load ( LPSTREAM /*pStm*/ )
{
	mnVersion = GetVersionConnected();
	mbLoad = TRUE;

	// may be later?
	// for now just ignore

    return S_OK;
}

STDMETHODIMP CSOActiveX::Load( LPPROPERTYBAG pPropBag, LPERRORLOG /*pErrorLog*/ )
{
	mnVersion = GetVersionConnected();

    IPropertyBag2* pPropBag2;
    HRESULT hr = pPropBag->QueryInterface( IID_IPropertyBag2, (void**)&pPropBag2 );
    //ATLASSERT( hr >= 0 );

    if( !SUCCEEDED( hr ) )
        return hr;

    unsigned long aNum;
    hr = pPropBag2->CountProperties( &aNum );
    //ATLASSERT( hr >= 0 );
    if( !SUCCEEDED( hr ) )
        return hr;

    PROPBAG2* aPropNames = new PROPBAG2[aNum];
    unsigned long aReaded;

    hr = pPropBag2->GetPropertyInfo( 0,
                                     aNum,
                                     aPropNames,
                                     &aReaded );
    //ATLASSERT( hr >= 0 );
    if( !SUCCEEDED( hr ) )
    {
        delete[] aPropNames;
        return hr;
    }

	CComVariant* aVal = new CComVariant[aNum];
    HRESULT*     hvs = new HRESULT[aNum];
	hr = pPropBag2->Read( aNum,
                          aPropNames,
                          NULL,
                          aVal,
                          hvs );
    //ATLASSERT( hr >= 0 );
    if( !SUCCEEDED( hr ) )
    {
        delete[] hvs;
        delete[] aVal;
        delete[] aPropNames;
        return hr;
    }

	USES_CONVERSION;
    for( unsigned long ind = 0; ind < aNum; ind++ )
    {
		// all information from the 'object' tag is in strings
		if( aVal[ind].vt == VT_BSTR && !strcmp( OLE2T( aPropNames[ind].pstrName ), "src" ) )
		{
            mCurFileUrl = wcsdup( aVal[ind].bstrVal );
		}
		else if( aVal[ind].vt == VT_BSTR
				&& !strcmp( OLE2T( aPropNames[ind].pstrName ), "readonly" ) )
		{
			if( !strcmp( OLE2T( aVal[ind].bstrVal ), "true" ) )
			{
				// the default value
				mbViewOnly = TRUE;
			}
			else
			{
				mbViewOnly = FALSE;
			}
		}
    }

    delete[] hvs;
    delete[] aVal;
    delete[] aPropNames;

	if( !mpDispFactory )
		return hr;

	mbReadyForActivation = FALSE;
	hr = CBindStatusCallback<CSOActiveX>::Download( this, &CSOActiveX::CallbackCreateXInputStream, mCurFileUrl, m_spClientSite, FALSE );
	if ( hr == MK_S_ASYNCHRONOUS )
		hr = S_OK;
	
	if ( !SUCCEEDED( hr ) )
	{
		// trigger initialization without stream
		mbLoad = TRUE;

    	Invalidate();
    	UpdateWindow();
	}

	return hr;
}

HRESULT CSOActiveX::GetUnoStruct( OLECHAR* sStructName, CComPtr<IDispatch>& pdispResult )
{
    CComVariant aComStruct( sStructName );
	return GetIDispByFunc( mpDispFactory, L"Bridge_GetStruct", &aComStruct, 1, pdispResult );
}

HRESULT CSOActiveX::GetUrlStruct( OLECHAR* sUrl, CComPtr<IDispatch>& pdispUrl )
{
	HRESULT hr = GetUnoStruct( L"com.sun.star.util.URL", pdispUrl );
	if( !SUCCEEDED( hr ) ) return hr;

	OLECHAR* sURLMemberName = L"Complete";
	DISPID nURLID;
	hr = pdispUrl->GetIDsOfNames( IID_NULL, &sURLMemberName, 1, LOCALE_USER_DEFAULT, &nURLID );
	if( !SUCCEEDED( hr ) ) return hr;
    CComVariant aComUrl( sUrl );
	hr = CComDispatchDriver::PutProperty( pdispUrl, nURLID, &aComUrl );
	if( !SUCCEEDED( hr ) ) return hr;

	CComPtr<IDispatch> pdispTransformer;
    CComVariant aServiceName( L"com.sun.star.util.URLTransformer" );
	hr = GetIDispByFunc( mpDispFactory,
						 L"createInstance",
						 &aServiceName,
						 1,
						 pdispTransformer );
	if( !SUCCEEDED( hr ) ) return hr;

	CComVariant dummyResult;
	CComVariant aParam[2];
	aParam[1].ppdispVal = &pdispUrl;
	aParam[1].vt = VT_DISPATCH | VT_BYREF;
	aParam[0] = CComVariant( L"file:///" );

	hr = ExecuteFunc( pdispTransformer, L"parseSmart", aParam, 2, &dummyResult );
	if( !SUCCEEDED( hr ) || dummyResult.vt != VT_BOOL || !dummyResult.boolVal ) return hr;

	return S_OK;
}

HRESULT CSOActiveX::SetLayoutManagerProps()
{
	if ( !mpDispFrame )
		return E_FAIL;

	CComVariant pVarLayoutMgr;
	OLECHAR* sLMPropName = L"LayoutManager";
	HRESULT hr = GetPropertiesFromIDisp( mpDispFrame, &sLMPropName, &pVarLayoutMgr, 1 );
	if( pVarLayoutMgr.vt != VT_DISPATCH || pVarLayoutMgr.pdispVal == NULL )
		return E_FAIL;

  	CComPtr<IDispatch> pdispLM( pVarLayoutMgr.pdispVal );


	if( !SUCCEEDED( hr ) || !pdispLM )
		return E_FAIL;

	OLECHAR* sATName = L"AutomaticToolbars";
	CComVariant pATProp;
	pATProp.vt = VT_BOOL; pATProp.boolVal = VARIANT_FALSE ;
	hr = PutPropertiesToIDisp( pdispLM, &sATName, &pATProp, 1 );

	return hr;
}

HRESULT CSOActiveX::CreateFrameOldWay( HWND hwnd, int width, int height )
{
	if( !mpDispFactory )
		return E_FAIL;

	// create window handle holder
	CComPtr< CComObject< SOComWindowPeer > > pPeerToSend = new CComObject<SOComWindowPeer>();
	pPeerToSend->SetHWNDInternally( hwnd );
	CComQIPtr< IDispatch, &IID_IDispatch > pIDispToSend( pPeerToSend );

	// create rectangle structure
	CComPtr<IDispatch> pdispRectangle;
	HRESULT hr = GetUnoStruct( L"com.sun.star.awt.Rectangle", pdispRectangle );
	if( !SUCCEEDED( hr ) ) return hr;

	OLECHAR* sRectMemberNames[4] = { L"X",
								 	 L"Y",
								 	 L"Width",
								 	 L"Height" };
	CComVariant pRectVariant[4];
	pRectVariant[0] = pRectVariant[1] = pRectVariant[2] = pRectVariant[3] = CComVariant( 0 );

	hr = PutPropertiesToIDisp( pdispRectangle, sRectMemberNames, pRectVariant, 4 );
	if( !SUCCEEDED( hr ) ) return hr;

	// create WindowDescriptor structure
	CComPtr<IDispatch> pdispWinDescr;
	hr = GetUnoStruct( L"com.sun.star.awt.WindowDescriptor", pdispWinDescr );
	if( !SUCCEEDED( hr ) ) return hr;

	// fill in descriptor with info
	OLECHAR* sDescriptorMemberNames[6] = { L"Type",
								 L"WindowServiceName",
								 L"ParentIndex",
								 L"Parent",
								 L"Bounds",
								 L"WindowAttributes" };
	CComVariant pDescriptorVar[6];
	pDescriptorVar[0] = CComVariant( 0 );
	pDescriptorVar[1] = CComVariant( L"workwindow" );
	pDescriptorVar[2] = CComVariant( 1 );
	pDescriptorVar[3] = CComVariant( pIDispToSend );
	pDescriptorVar[4] = CComVariant( pdispRectangle );
	pDescriptorVar[5] = CComVariant( 33 );
	hr = PutPropertiesToIDisp( pdispWinDescr, sDescriptorMemberNames, pDescriptorVar, 6 );
	if( !SUCCEEDED( hr ) ) return hr;

	// create XToolkit instance
	CComPtr<IDispatch> pdispToolkit;
    CComVariant aServiceName( L"com.sun.star.awt.Toolkit" );
	hr = GetIDispByFunc( mpDispFactory, L"createInstance", &aServiceName, 1, pdispToolkit );
	if( !SUCCEEDED( hr ) ) return hr;

	// create window with toolkit
    CComVariant aWinDescr( pdispWinDescr );
	hr = GetIDispByFunc( pdispToolkit, L"createWindow", &aWinDescr, 1, mpDispWin );
	if( !SUCCEEDED( hr ) ) return hr;

	// create frame
    aServiceName = CComVariant( L"com.sun.star.frame.Task" );
	hr = GetIDispByFunc( mpDispFactory, L"createInstance", &aServiceName, 1, mpDispFrame );
	if( !SUCCEEDED( hr ) || !mpDispFrame )
	{
		// the interface com.sun.star.frame.Task is removed in 6.1
		// but the interface com.sun.star.frame.Frame has some bugs in 6.0
        aServiceName = CComVariant( L"com.sun.star.frame.Frame" );
		hr = GetIDispByFunc( mpDispFactory, L"createInstance", &aServiceName, 1, mpDispFrame );
		if( !SUCCEEDED( hr ) ) return hr;
	}

	// initialize frame
	CComVariant dummyResult;
    CComVariant aDispWin( mpDispWin );
	hr = ExecuteFunc( mpDispFrame, L"initialize", &aDispWin, 1, &dummyResult );
	if( !SUCCEEDED( hr ) ) return hr;

	// set some properties to the layout manager, ignore errors for now
	SetLayoutManagerProps();

	// create desktop
	CComPtr<IDispatch> pdispDesktop;
    aServiceName = CComVariant( L"com.sun.star.frame.Desktop" );
	hr = GetIDispByFunc( mpDispFactory, L"createInstance", &aServiceName, 1, pdispDesktop );
	if( !SUCCEEDED( hr ) ) return hr;

	// create tree of frames
	CComPtr<IDispatch> pdispChildren;
	hr = GetIDispByFunc( pdispDesktop, L"getFrames", NULL, 0, pdispChildren );
	if( !SUCCEEDED( hr ) ) return hr;

	// insert new frame into desctop hierarchy
    CComVariant aDispFrame( mpDispFrame );
	hr = ExecuteFunc( pdispChildren, L"append", &aDispFrame, 1, &dummyResult );
	if( !SUCCEEDED( hr ) ) return hr;

	// initialize window
    CComVariant aTransparent( (long)0xFFFFFFFF );
	hr = ExecuteFunc( mpDispWin, L"setBackground", &aTransparent, 1, &dummyResult );
	if( !SUCCEEDED( hr ) ) return hr;

    CComVariant aTrue( TRUE );
	hr = ExecuteFunc( mpDispWin, L"setVisible", &aTrue, 1, &dummyResult );
	if( !SUCCEEDED( hr ) ) return hr;

	CComVariant aPosArgs[5];
	aPosArgs[4] = CComVariant( 0 );
	aPosArgs[3] = CComVariant( 0 );
	aPosArgs[2] = CComVariant( width );
	aPosArgs[1] = CComVariant( height );
	aPosArgs[0] = CComVariant( 12 );
	hr = ExecuteFunc( mpDispWin, L"setPosSize", aPosArgs, 5, &dummyResult );
	if( !SUCCEEDED( hr ) ) return hr;

	// create frame locker if there is such service
    aServiceName = CComVariant( L"com.sun.star.embed.InstanceLocker" );
	hr = GetIDispByFunc( mpDispFactory, L"createInstance", &aServiceName, 1, mpInstanceLocker );
	if( SUCCEEDED( hr ) && mpInstanceLocker )
	{
		SAFEARRAY FAR* pInitVals = SafeArrayCreateVector( VT_VARIANT, 0, 3 );

		// the first sequence element
		long nInitInd = 0;
		CComVariant pFrameVariant( mpDispFrame );
		SafeArrayPutElement( pInitVals, &nInitInd, &pFrameVariant );
		
		// the second sequence element
		nInitInd = 1;
		CComVariant pStrArr( 1L );
		SafeArrayPutElement( pInitVals, &nInitInd, &pStrArr );

		// the third sequence element
		nInitInd = 2;
		CComPtr<IDispatch> pdispValueObj;
		hr = GetIDispByFunc( mpDispFactory, L"Bridge_GetValueObject", NULL, 0, pdispValueObj );
		if( !SUCCEEDED( hr ) || !pdispValueObj ) return hr;

		CComVariant aValueArgs[2];
		aValueArgs[1] = CComVariant( L"com.sun.star.embed.XActionsApproval" );
		CComPtr< CComObject< SOActionsApproval > > pApproval( new CComObject<SOActionsApproval>() );
		aValueArgs[0] = CComVariant ( pApproval );

		hr = ExecuteFunc( pdispValueObj, L"Set", aValueArgs, 2, &dummyResult );
		if( !SUCCEEDED( hr ) ) return hr;

        CComVariant aValueObj( pdispValueObj );
		SafeArrayPutElement( pInitVals, &nInitInd, &aValueObj );

		// execute initialize()
		CComVariant aVarInitVals;
		aVarInitVals.vt = VT_ARRAY | VT_VARIANT; aVarInitVals.parray = pInitVals;
		hr = ExecuteFunc( mpInstanceLocker, L"initialize", &aVarInitVals, 1, &dummyResult );
		if( !SUCCEEDED( hr ) ) return hr;
	}

	return S_OK;
}

HRESULT CSOActiveX::CallLoadComponentFromURL1PBool( OLECHAR* sUrl, OLECHAR* sArgName, BOOL sArgVal )
{
	SAFEARRAY FAR* pPropVals = SafeArrayCreateVector( VT_DISPATCH, 0, 1 );
	long ix = 0;
	CComPtr<IDispatch> pdispPropVal;
	HRESULT hr = GetUnoStruct( L"com.sun.star.beans.PropertyValue", pdispPropVal );
	if( !SUCCEEDED( hr ) ) return hr;

	OLECHAR* 	sPropMemberNames[2] = { L"Name", L"Value" };
	CComVariant pPropVar[2];
	pPropVar[0] = CComVariant( sArgName );
	pPropVar[1].vt = VT_BOOL; pPropVar[1].boolVal = sArgVal ? VARIANT_TRUE : VARIANT_FALSE ;
	hr = PutPropertiesToIDisp( pdispPropVal, sPropMemberNames, pPropVar, 2 );
	if( !SUCCEEDED( hr ) ) return hr;

	SafeArrayPutElement( pPropVals, &ix, pdispPropVal );

	CComVariant aDispArgs[4];
	aDispArgs[3] = CComVariant( sUrl );
	aDispArgs[2] = CComVariant( L"_self" );
	aDispArgs[1] = CComVariant( 0 );
	// aDispArgs[0] = CComVariant( pPropVals ); such constructor is not defined ??!
	aDispArgs[0].vt = VT_ARRAY | VT_DISPATCH; aDispArgs[0].parray = pPropVals;

	CComVariant dummyResult;
	hr = ExecuteFunc( mpDispFrame, L"loadComponentFromURL", aDispArgs, 4, &dummyResult );
	if( !SUCCEEDED( hr ) ) return hr;

	return S_OK;
}

HRESULT CSOActiveX::CallDispatchMethod( OLECHAR* sUrl,
									    CComVariant* aArgNames,
									    CComVariant* aArgVals,
									    unsigned int count )
{
	CComPtr<IDispatch> pdispURL;
	HRESULT hr = GetUrlStruct( sUrl, pdispURL );
	if( !SUCCEEDED( hr ) ) return hr;

	CComPtr<IDispatch> pdispXDispatch;
	CComVariant aArgs[3];
	aArgs[2] = CComVariant( pdispURL );
	aArgs[1] = CComVariant( L"" );
	aArgs[0] = CComVariant( (int)0 );
	hr = GetIDispByFunc( mpDispFrame,
						 L"queryDispatch",
						 aArgs,
						 3,
						 pdispXDispatch );
	if( !SUCCEEDED( hr ) ) return hr;

	SAFEARRAY FAR* pPropVals = SafeArrayCreateVector( VT_DISPATCH, 0, count );
	for( long ix = 0; ix < (long)count; ix ++ )
	{
		CComPtr<IDispatch> pdispPropVal;
		hr = GetUnoStruct( L"com.sun.star.beans.PropertyValue", pdispPropVal );
		if( !SUCCEEDED( hr ) ) return hr;

		OLECHAR* 	sPropMemberNames[2] = { L"Name", L"Value" };
		CComVariant pPropVar[2];
		pPropVar[0] = aArgNames[ix];
		pPropVar[1] = aArgVals[ix];
		hr = PutPropertiesToIDisp( pdispPropVal, sPropMemberNames, pPropVar, 2 );
		if( !SUCCEEDED( hr ) ) return hr;

		SafeArrayPutElement( pPropVals, &ix, pdispPropVal );
	}

	CComVariant aDispArgs[2];
	aDispArgs[1] = CComVariant( pdispURL );
	// aDispArgs[0] = CComVariant( pPropVals ); such constructor is not defined ??!
	aDispArgs[0].vt = VT_ARRAY | VT_DISPATCH; aDispArgs[0].parray = pPropVals;

	CComVariant dummyResult;
	hr = ExecuteFunc( pdispXDispatch, L"dispatch", aDispArgs, 2, &dummyResult );
	if( !SUCCEEDED( hr ) ) return hr;

	return S_OK;
}

void CSOActiveX::CallbackCreateXInputStream( CBindStatusCallback<CSOActiveX>* /*pbsc*/, BYTE* pBytes, DWORD dwSize )
{
	if ( mbReadyForActivation )
		return;

	BOOL bSuccess = FALSE;
	BOOL bFinishDownload = FALSE;
	if ( !pBytes )
	{
		// means the download is finished, dwSize contains hresult
		bFinishDownload = TRUE;
		if ( SUCCEEDED( dwSize ) )
			bSuccess = TRUE;
	}
	else
	{
		HRESULT hr = S_OK;

		if ( !mpDispTempFile )
		{
            CComVariant aServiceName( L"com.sun.star.io.TempFile" );
			hr = GetIDispByFunc( mpDispFactory,
						 		L"createInstance",
						 		&aServiceName,
						 		1,
						 		mpDispTempFile );
		}

		if( SUCCEEDED( hr ) && mpDispTempFile )
		{
			SAFEARRAY FAR* pDataArray = SafeArrayCreateVector( VT_I1, 0, dwSize );
	
			if ( pDataArray )
			{
				hr = SafeArrayLock( pDataArray );
				if ( SUCCEEDED( hr ) )
				{
					for( DWORD ix = 0; ix < dwSize; ix++ )
						((BYTE*)(pDataArray->pvData))[ix] = pBytes[ix];
					hr = SafeArrayUnlock( pDataArray );
					if ( SUCCEEDED( hr ) )
					{
						CComVariant aArgs[1];
						aArgs[0].vt = VT_ARRAY | VT_I1; aArgs[0].parray = pDataArray;
						CComVariant dummyResult;
						hr = ExecuteFunc( mpDispTempFile, L"writeBytes", aArgs, 1, &dummyResult );
						if( SUCCEEDED( hr ) )
							bSuccess = TRUE;
					}
				}
			}
		}
	}

	if ( !bSuccess )
	{
		// the download failed, let StarOffice download
		bFinishDownload = TRUE;
		mpDispTempFile = CComPtr< IDispatch >();
	}

	if ( bFinishDownload )
	{
		// trigger the loading now
		mbLoad = TRUE;
		mbReadyForActivation = TRUE;

    	Invalidate();
    	UpdateWindow();
	}
}

HRESULT CSOActiveX::LoadURLToFrame( )
{
	CComVariant aArgNames[4] = { L"ReadOnly", L"ViewOnly", L"AsTemplate", L"InputStream" };
	CComVariant aArgVals[4];
	unsigned int nCount = 3; // the 4-th argument is used only if the stream can be retrieved

	aArgVals[0].vt = VT_BOOL; aArgVals[0].boolVal = mbViewOnly ? VARIANT_TRUE : VARIANT_FALSE;
	aArgVals[1].vt = VT_BOOL; aArgVals[1].boolVal = mbViewOnly ? VARIANT_TRUE : VARIANT_FALSE;
	aArgVals[2].vt = VT_BOOL; aArgVals[2].boolVal = VARIANT_FALSE;

	if ( mpDispTempFile )
	{
		aArgVals[3] = CComVariant( mpDispTempFile );
		nCount = 4;
	}

	HRESULT hr = CallDispatchMethod( mCurFileUrl, aArgNames, aArgVals, nCount );
	if( !SUCCEEDED( hr ) ) return hr;

	CComVariant aBarName( L"MenuBarVisible" );
	CComVariant aBarVis;
	aBarVis.vt = VT_BOOL; aBarVis.boolVal = VARIANT_FALSE;
	hr = CallDispatchMethod( L"slot:6661", &aBarName, &aBarVis, 1 );
	// does not work for some documents, but it is no error
	// if( !SUCCEEDED( hr ) ) return hr;

    // try to get the model and set the presetation specific property, the setting will fail for other document formats
    CComPtr<IDispatch> pdispController;
    hr = GetIDispByFunc( mpDispFrame, L"getController", NULL, 0, pdispController );
    if ( SUCCEEDED( hr ) && pdispController )
    {
        CComPtr<IDispatch> pdispModel;
        hr = GetIDispByFunc( pdispController, L"getModel", NULL, 0, pdispModel );
        if ( SUCCEEDED( hr ) && pdispModel )
        {
            CComPtr<IDispatch> pdispPres;
            hr = GetIDispByFunc( pdispModel, L"getPresentation", NULL, 0, pdispPres );
            if ( SUCCEEDED( hr ) && pdispPres )
            {
                // this is a presentation
                // let the slide show be shown in the document window
                OLECHAR* pPropName = L"IsFullScreen";
                CComVariant pPresProp;
                pPresProp.vt = VT_BOOL; pPresProp.boolVal = VARIANT_FALSE ;
                hr = PutPropertiesToIDisp( pdispPres, &pPropName, &pPresProp, 1 );

                // start the slide show
                if ( SUCCEEDED( hr ) )
                {
                    CComVariant dummyResult;
                    ExecuteFunc( pdispPres, L"Start", NULL, 0, &dummyResult );
                }
            }
        }
    }

	// create dispatch interceptor
	mpDispatchInterceptor = new CComObject< SODispatchInterceptor >();
	mpDispatchInterceptor->AddRef();
	mpDispatchInterceptor->SetParent( this );
	CComQIPtr< IDispatch, &IID_IDispatch > pIDispDispInter( mpDispatchInterceptor );

	// register dispatch interceptor in the frame
        CComVariant aDispVariant( pIDispDispInter );
	CComVariant dummyResult;
	hr = ExecuteFunc( mpDispFrame,
					  L"registerDispatchProviderInterceptor",
                                          &aDispVariant,
					  1,
					  &dummyResult );

	if( !SUCCEEDED( hr ) ) return hr;

	return S_OK;
}

SOVersion CSOActiveX::GetVersionConnected()
{
	SOVersion bResult = SO_NOT_DETECTED;
	if( mpDispFactory )
	{
		// create ConfigurationProvider instance
		CComPtr<IDispatch> pdispConfProv;
        CComVariant aServiceName( L"com.sun.star.configuration.ConfigurationProvider" );
		HRESULT hr = GetIDispByFunc( mpDispFactory,
							 L"createInstance",
							 &aServiceName,
							 1,
							 pdispConfProv );

		if( SUCCEEDED( hr ) && pdispConfProv )
		{
			CComPtr<IDispatch> pdispConfAccess;

			SAFEARRAY* pInitParams = SafeArrayCreateVector( VT_VARIANT, 0, 1 );

			if( pInitParams )
			{
				long ix = 0;
				CComVariant aConfPath( L"org.openoffice.Setup" );
				SafeArrayPutElement( pInitParams, &ix, &aConfPath );

				CComVariant aArgs[2];
				aArgs[1] = CComVariant( L"com.sun.star.configuration.ConfigurationAccess" );
				aArgs[0].vt = VT_ARRAY | VT_VARIANT; aArgs[0].parray = pInitParams;

				hr = GetIDispByFunc( pdispConfProv,
								 	L"createInstanceWithArguments",
									aArgs,
								 	2,
								 	pdispConfAccess );

				if( SUCCEEDED( hr ) && pdispConfAccess )
				{
					CComVariant aOfficeName;

                    CComVariant aProductName( L"Product/ooName" );
					hr = ExecuteFunc( pdispConfAccess,
										L"getByHierarchicalName",
										&aProductName,
										1,
										&aOfficeName );

					if( SUCCEEDED( hr ) && aOfficeName.vt == VT_BSTR )
					{
						CComVariant aOfficeVersion;

                        CComVariant aProductVersion( L"Product/ooSetupVersion" );
						hr = ExecuteFunc( pdispConfAccess,
											L"getByHierarchicalName",
											&aProductVersion,
											1,
											&aOfficeVersion );

						if( SUCCEEDED( hr ) && aOfficeVersion.vt == VT_BSTR )
						{
							USES_CONVERSION;
							if( !strcmp( OLE2T( aOfficeName.bstrVal ), "StarOffice" ) )
							{
								if( !strncmp( OLE2T( aOfficeVersion.bstrVal ), "6.1", 3 ) )
									bResult = SO_61;
								else if( !strncmp( OLE2T( aOfficeVersion.bstrVal ), "6.0", 3 ) )
									bResult = SO_60;
								else if( !strncmp( OLE2T( aOfficeVersion.bstrVal ), "5.2", 3 ) )
									bResult = SO_52;
								else
									bResult = SO_UNKNOWN;
							}
							else // OpenOffice
							{
								if( !strncmp( OLE2T( aOfficeVersion.bstrVal ), "1.1", 3 ) )
									bResult = OO_11;
								else if( !strncmp( OLE2T( aOfficeVersion.bstrVal ), "1.0", 3 ) )
									bResult = OO_10;
								else
									bResult = OO_UNKNOWN;
							}
						}
					}
				}
			}
		}
	}

	return bResult;
}

class LockingGuard
{
	BOOL& mbLocked;
public:
	LockingGuard( BOOL& bLocked )
	: mbLocked( bLocked )
	{
		mbLocked = TRUE;
	}

	~LockingGuard()
	{
		mbLocked = FALSE;
	}
};

HRESULT CSOActiveX::OnDrawAdvanced( ATL_DRAWINFO& di )
{
	// This method is called only in main thread, no need to lock it

	// Get read of reentrance problems
	if ( mbDrawLocked )
		return S_OK;
	LockingGuard aGuard( mbDrawLocked );
	
    if( m_spInPlaceSite && mCurFileUrl && mbReadyForActivation )
    {
    	HWND hwnd;
	    HRESULT hr = m_spInPlaceSite->GetWindow( &hwnd );
		if( !SUCCEEDED( hr ) ) return hr;

        if( mParentWin != hwnd || !mOffWin )
        {
			if( mpDispFrame )
			{
				CComVariant dummyResult;
				CComVariant aPropVar;
				aPropVar.vt = VT_BOOL; aPropVar.boolVal = VARIANT_FALSE;
				HRESULT hr = ExecuteFunc( mpDispFrame, L"close", &aPropVar, 1, &dummyResult );
                (void)hr;
				mpDispFrame = CComPtr<IDispatch>();
			}

            mParentWin = hwnd;
            mOffWin = CreateWindow(
								STAROFFICE_WINDOWCLASS,
								"OfficeContainer",
								WS_CHILD | WS_CLIPCHILDREN | WS_BORDER,
								di.prcBounds->left,
								di.prcBounds->top,
								di.prcBounds->right - di.prcBounds->left,
								di.prcBounds->bottom - di.prcBounds->top,
								mParentWin,
								NULL,
								NULL,
								NULL );

			::ShowWindow( mOffWin, SW_SHOW );
        }
        else
        {
            RECT aRect;
            ::GetWindowRect( mOffWin, &aRect );

            if( aRect.left !=  di.prcBounds->left || aRect.top != di.prcBounds->top
             || aRect.right != di.prcBounds->right || aRect.bottom != di.prcBounds->bottom )
			{
				// on this state the office window should exist already
                ::SetWindowPos( mOffWin,
                              HWND_TOP,
							  di.prcBounds->left,
							  di.prcBounds->top,
							  di.prcBounds->right - di.prcBounds->left,
							  di.prcBounds->bottom - di.prcBounds->top,
                              SWP_NOZORDER );

				CComVariant aPosArgs[5];
				aPosArgs[4] = CComVariant( 0 );
				aPosArgs[3] = CComVariant( 0 );
				aPosArgs[2] = CComVariant( int(di.prcBounds->right - di.prcBounds->left) );
				aPosArgs[1] = CComVariant( int(di.prcBounds->bottom - di.prcBounds->top) );
				aPosArgs[0] = CComVariant( 12 );
				CComVariant dummyResult;
				hr = ExecuteFunc( mpDispWin, L"setPosSize", aPosArgs, 5, &dummyResult );
				if( !SUCCEEDED( hr ) ) return hr;
			}
        }

		if( !mnVersion )
		{
			OutputError_Impl( mOffWin, CS_E_INVALID_VERSION );
			return E_FAIL;
		}

		if( ! mpDispFrame )
		{
			hr = CreateFrameOldWay( mOffWin,
							di.prcBounds->right - di.prcBounds->left,
							di.prcBounds->bottom - di.prcBounds->top );

			if( !SUCCEEDED( hr ) )
			{
				// if the frame can not be opened do not try any more
				mbReadyForActivation = FALSE;
				OutputError_Impl( mOffWin, STG_E_ABNORMALAPIEXIT );
				return hr;
			}
		}

		if( mbLoad )
		{
			hr = LoadURLToFrame();
			mbLoad = FALSE;

			if( !SUCCEEDED( hr ) )
			{
				// if the document can not be opened do not try any more
				mbReadyForActivation = FALSE;

				OutputError_Impl( mOffWin, STG_E_ABNORMALAPIEXIT );

				return hr;
			}
		}
	}
	else
	{
		// activate the fallback
		CComControl<CSOActiveX>::OnDrawAdvanced( di );
	}

	return S_OK;
}

HRESULT CSOActiveX::OnDraw( ATL_DRAWINFO& di )
{
	// fallback that is activated by the parent class
	if ( di.hdcDraw )
		FillRect( di.hdcDraw, (RECT*)di.prcBounds, (HBRUSH)COLOR_BACKGROUND );

	return S_OK;
}

STDMETHODIMP CSOActiveX::SetClientSite( IOleClientSite* aClientSite )
{
	HRESULT hr = IOleObjectImpl<CSOActiveX>::SetClientSite( aClientSite );

	if( !aClientSite )
	{
		//ATLASSERT( mWebBrowser2 );
		if( mWebBrowser2 )
			AtlUnadvise( mWebBrowser2, DIID_DWebBrowserEvents2, mCookie );
		return hr;
	}

	CComPtr<IOleContainer> aContainer;
	m_spClientSite->GetContainer( &aContainer );
//	ATLASSERT( aContainer );

	if( SUCCEEDED( hr )  && aContainer )
	{
		CComQIPtr<IServiceProvider, &IID_IServiceProvider> aServiceProvider( aContainer );
		//ATLASSERT( aServiceProvider );

		if( aServiceProvider )
		{
			aServiceProvider->QueryService( SID_SInternetExplorer,
											IID_IWebBrowser,
											(void**)&mWebBrowser2 );
//			ATLASSERT( mWebBrowser2 );
			if( mWebBrowser2 )
				AtlAdvise( mWebBrowser2, GetUnknown(), DIID_DWebBrowserEvents2, &mCookie );
		}
	}

	return hr;
}

STDMETHODIMP CSOActiveX::Invoke(DISPID dispidMember,
							    REFIID riid,
							    LCID lcid,
                                WORD wFlags,
							    DISPPARAMS* pDispParams,
                                VARIANT* pvarResult,
							    EXCEPINFO* pExcepInfo,
                                UINT* puArgErr)
{
    if (riid != IID_NULL)
        return DISP_E_UNKNOWNINTERFACE;

    if (!pDispParams)
        return DISP_E_PARAMNOTOPTIONAL;

	if ( dispidMember == DISPID_ONQUIT )
		Cleanup();

    IDispatchImpl<ISOActiveX, &IID_ISOActiveX,
                  &LIBID_SO_ACTIVEXLib>::Invoke(
             dispidMember, riid, lcid, wFlags, pDispParams,
             pvarResult, pExcepInfo, puArgErr);

    return S_OK;
}

HRESULT CSOActiveX::GetURL( const OLECHAR* url,
                              const OLECHAR* target )
{
    CComVariant aEmpty1, aEmpty2, aEmpty3;
    CComVariant aUrl( url );
    CComVariant aTarget;
    if ( target )
        aTarget = CComVariant( target );

    return mWebBrowser2->Navigate2( &aUrl,
                                  &aEmpty1,
                                  &aTarget,
                                  &aEmpty2,
                                  &aEmpty3 );
}


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

