Component and Dialog

From Apache OpenOffice Wiki
Revision as of 12:08, 28 March 2010 by B michaelsen (Talk | contribs)

Jump to: navigation, search

Template:Documentation/Tip Event-driven programming use two different handlers :

  • callback
  • listener

We have already tackled the listener in Dialog here and we want to examine the callback problem now.

The goal of this chapter is to use a dialog to make our counter working. Because it's very easy to construct a dialog with OOoBasic we will use the OOoBasic Dialog editor and not the Dialog at runtime. As we will see along this chapter, two methods are now designed to facilitate the call of a component method with a dialog event. To put it differently, we want transform methods of a component as callback functions executed in reponse to events that occur on the dialog. But before going further we will first examine a method which consists in wrapping C++ with OOoBasic.

Adding a Dialog to the Counter

For your information, we recall that our counter has four methods :

  1. increment
  2. decrement
  3. setCount
  4. getCount

We then design a Dialog with one button by method (then four buttons) and two text fields :

  1. a text field to set a value in the counter (then associated with "setCount" button)
  2. a text field to get the value of the counter (then associated with "getCount" button)

Here is the corresponding dialog

Our Counter dialog

Template:Documentation/Note To make the complete example working, you can simply use the following OOoBasic code :

'Listing 1
REM  *****  BASIC  *****
	Dim oSimpleComponent
	Dim oDialog
Sub demonstrateSimpleComponent
	oSimpleComponent = CreateUnoService( "foo.Counter" )
	'oInspector = createUnoService("org.openoffice.InstanceInspector")
	'oInspector.inspect(oSimpleComponent, "MyCounter")
	'XRay oSimpleComponent
	oDialog=CreateUnoDialog(DialogLibraries.Standard.Dialog1)
	oDialog.Execute()
	oDialog.dispose()
End Sub
 
Sub increment
  oSimpleComponent.increment()
End Sub
 
Sub decrement
   oSimpleComponent.decrement()
End Sub
 
Sub getCount
   Dim oTextField
   oTextField = oDialog.getControl("TextField2")
   oTextField.setText( oSimpleComponent.getCount())  
End Sub
 
Sub setCount
	Dim oTextField
	oTextField = oDialog.getControl("TextField1")
	'implicit conversion String to Integer
	oSimpleComponent.setCount(oTextField.getText())
End Sub

Because these procedures are created in OpenOffice.org Basic, you can assign them to an event using the property window of the dialog editor. In the program of listing 1, we have left comments on different introspection uses in our counter but this is not important. Template:Documentation/Note

Bounding the methods with Introspection Service

As mentioned in Developer's Guide, since 2.0.4 version it is possible to bound methods of a component with events of a control in a Dialog. To put it differently, wrappers, as in the OOoBasic code below :

' Listing 2
REM  *****  BASIC  *****
Sub increment
  oSimpleComponent.increment()
End Sub

could be avoided now. But, for that, you have a price to pay : we have eventually to change our C++ code of our component. To begin with the more easy task, we will modify our OOoBasic program, our Dialog but not our counter.

Triggering the Counter Methods without changing its C++ code

We have to change the previous OOobasic program to use the new com.sun.star.awt.DialogProvider2 service which provide the very interesting "createDialogWithHandler" method. Here is the corresponding code :

' Listing 3
REM  *****  BASIC  *****
	Dim oSimpleComponent
	Dim oDialog
Sub demonstrateSimpleComponent
	oSimpleComponent = CreateUnoService( "foo.Counter" )
	oCreateDialog2=CreateUnoService("com.sun.star.awt.DialogProvider2")
	'Thank you ms777 for the line below (see http://www.oooforum.org/forum/viewtopic.phtml?t=84168)
	oCreateDialog2.initialize(Array(ThisComponent))
	oDialog=oCreateDialog2.createDialogWithHandler("vnd.sun.star.script:Standard.Dialog1?location=document", _
		oSimpleComponent )
	oDialog.Execute()
	oDialog.dispose()
End Sub
 
'Sub increment
'  oSimpleComponent.increment()
'End Sub
 
Sub decrement
   oSimpleComponent.decrement()
End Sub
 
Sub getCount
   Dim oTextField
   oTextField = oDialog.getControl("TextField2")
   oTextField.setText( oSimpleComponent.getCount())  
End Sub
 
Sub setCount
	Dim oTextField
	oTextField = oDialog.getControl("TextField1")
	'implicit conversion String to Integer
	oSimpleComponent.setCount(oTextField.getText())
End Sub
Documentation caution.png The initialize method in OOobasic seems to be called with a createInstanceWithArgumentsAndContext() method in C++. I have noted this fact when encountering an error message telling about initialize although I never call explicitly this method. See an use of createInstanceWithArgumentsAndContext later. Here are the limits of my skills in OOobasic.

As you can see, the OOoBasic Sub "increment" is now in comment and then cannot work. We have also to modify the the "increment" button property in the dialog, which has to fire a method instead of a macro. This operation is explained in Developer's Guide. Template:Documentation/Note We can go further with the "decrement" method of the counter but not with the two last "setCount" and "getCount". The DialogProvider uses the com.sun.star.beans.Introspection service to detect if the method is provided by one of the interfaces supported by the component, but this only works if the corresponding method has one of both prototype below :

void [MethodName](void);

or

  void [MethodName] 
  ( 
      [in] com::sun::star::awt::XDialog xDialog, 
      [in] any aEvent 
  );

This is the case for our "increment" and "decrement" methods but not for the setCount and getCount methods.

Documentation caution.png Because up to then I have not changed nor IDL file nor cxx file, the prototype of increment is in fact "long increment()" instead of "void increment()". However, this example seems to work in contrary of what is explained in Developer's Guide.

Template:Documentation/Tip

Triggering all the Counter Methods with Dialog Events

We plan in this section to modify the C++ counter code to make it working with direct call of all its methods when using buttons of the dialog. As mentioned in the end of previous section, that means both methods "setCount" and "getCount" have no more parameters. They have also to be able to write or read in text controls of the dialog. This will change deeply our counter code because we have to get a Service Manager (com.sun.star.lang.XMultiComponentFactory) and then get an com.sun.star.awt.XControlContainer interface allowing an access in the interesting controls. The Developer's Guide states this is done with a context. This is our first problem to solve.

Obtaining a context

We can get a context with only four modifications of our source code counter.cxx.

Adding a field

To memorize the context we add a type com.sun.star.uno.XComponentContext field like this :

Reference< XComponentContext > m_xContext;

in the corresponding counter class.

Replacement of initialisation function

The old function (which calls the counter constructor) has to be changed. We had

// old function to replace
Reference< XInterface > SAL_CALL MyCounterImpl_create(
    Reference< XComponentContext > const & xContext )
    SAL_THROW( () )
{
    return static_cast< XTypeProvider * >( new MyCounterImpl() );
}

and we want now

// Our new function
Reference< XInterface > SAL_CALL MyCounterImpl_createInstance(
    Reference< XComponentContext > const & xContext )
    SAL_THROW( () )
{
    return static_cast< ::cppu::OWeakObject * >( new MyCounterImpl( xContext ) );
}

The name of this function is not important because a static ::cppu::ImplementationEntry array s_component_entries[], which contains a number of function pointers will be changed accordingly as examined now.

::cppu::ImplementationEntry array Change

We can see in the below code the old function name replaced by our new function name :

static struct ::cppu::ImplementationEntry s_component_entries [] =
{
    { // MyCounterImpl_create replaced by MyCounterImpl_createInstance 19/05/09
        MyCounterImpl_createInstance, getImplementationName_MyCounterImpl,
        getSupportedServiceNames_MyCounterImpl, ::cppu::createSingleComponentFactory,
        0, 0
    },
    { 0, 0, 0, 0, 0, 0 }
};

Changing our Class Constructor

The class constructor has to be changed because a new com.sun.star.uno.XComponentContext parameter is present now.

inline MyCounterImpl( Reference< XComponentContext > const & xContext)
        : m_xContext( xContext ) {
	}

These four change make the counter working. We can memorize the context but we don't get our precious service manager at the moment ! Please, be patient.

Documentation caution.png I have tried an other constructor like
// doesn't work
Reference< XInterface > SAL_CALL MyCounterImpl_createInstance( const Reference< XMultiServiceFactory > & rSMgr)
	throw( Exception )
{
	return (cppu::OWeakObject*) new MyCounterImpl( rSMgr );
}

to directly obtain a service Manager (as in AddOns) but without success. To see later.

Obtaining a Service Manager

We have encounter many Service Manager but starting from a context, it is straightforward to get a com.sun.star.lang.XMultiComponentFactory interface. Again XMultiComponentFactory interface could be memorized with adding a corresponding field :

Reference< XMultiComponentFactory > m_xMCF;

into our counter class. We then have to change our class constructor to set correctly this field with the corresponding Service Manager :

inline MyCounterImpl( Reference< XComponentContext > const & xContext)
        : m_xContext( xContext ) {
	m_xMCF=m_xContext->getServiceManager();
	}

Don't forget to add the corresponding include directive

#ifndef _COM_SUN_STAR_LANG_XMULTICOMPONENTFACTORY_HPP_
#include <com/sun/star/lang/XMultiComponentFactory.hpp>
#endif

The Makefile could not change because if you have a look in CppComponent.uno.xml file, you can see the com.sun.star.lang.XMultiComponentFactory interface and then the com/sun/star/lang/XMultiComponentFactory.hpp file is already created for you.

Ready to go further

We are now ready to construct our counter with its dialog working without OOoBasic sub. To avoid reader irritation we stop showing every step of change but we give complete code.

Counter.idl File

I have hesitate for a long time how to make things working. There is an example in the Developer's Guide but in Java and I think at first to translate it in C++ but I never carry out the translation of the line below

// great translation problem with "this" !!!
XDialog xDialog = xDialogProvider.createDialogWithHandler( DialogURL, this );

What is characeristic of Developer's Guide example is that the component is able to print out its dialog alone. But I am only able to use OOoBasic to print out the dialog. I will keep this OOoBasic part, but because my C++ methods have to read/write a text control, I have to pass the com.sun.star.awt.XDialog interface to my counter component. Our corresponding new IDL file has to manage this constraint and really respect the (callback) method prototype (for an automatic call). It then becomes :

// IDL File
// Listing 4
#include <com/sun/star/uno/XInterface.idl>
#include <com/sun/star/awt/XDialog.idl>
 
module foo
{
       interface XCountable : com::sun::star::uno::XInterface
	{
		void setDialog( [in] ::com::sun::star::awt::XDialog xDialog);
		void getCount();
		void setCount();
		void increment();
		void decrement();
	};	
	service Counter : XCountable ;
};

where we add a "setDialog" method. The goal of this new method is to pass the XDialog interface to the counter and also to show how to manage evoluated types as parameters. It's time to examine the corresponding counter code.

Template:Documentation/Note

counter.cxx File

Documentation caution.png I was stuck here for a long time because of a not clear error message : unable to invoke the constructor because the method "setDialog" was abstract. The problem was in fact in the "setDialog" prototype in C++. Have a look more closer in the IDL-C++ correspondance : the "const" and "&" have to be set in the parameters.

The source code is long but not complicated. Have a look in the corresponding interfaces : com.sun.star.awt.XDialog, com.sun.star.awt.XTextComponent, com.sun.star.awt.XControl, com.sun.star.awt.XControlContainer, com.sun.star.lang.XMultiComponentFactory, com.sun.star.lang.XServiceInfo and com.sun.star.registry.XRegistryKey.

// cxx File
// Listing 5
/***************************************************************************************************
 ***************************************************************************************************
 *
 * service implementation:	 foo.Counter
 * exported interfaces:		 foo.XCounter
 *
 * simple example component implementing a counter
 *
 ***************************************************************************************************
 **************************************************************************************************/
 
#include <rtl/ustring.hxx>
#include <rtl/string.hxx>
#include <cppuhelper/queryinterface.hxx> // helper for queryInterface() impl
#include <cppuhelper/factory.hxx> // helper for component factory
// New
#include <cppuhelper/implbase2.hxx> // "2" implementing three interfaces
#include <cppuhelper/implementationentry.hxx>
#include <com/sun/star/awt/XDialog.hpp>
#include <com/sun/star/awt/XTextComponent.hpp>
#include <com/sun/star/awt/XControl.hpp>
#include <com/sun/star/awt/XControlContainer.hpp>
// generated c++ interfaces
#include <com/sun/star/lang/XSingleServiceFactory.hpp>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/lang/XMultiComponentFactory.hpp>
#include <com/sun/star/lang/XServiceInfo.hpp>
#include <com/sun/star/registry/XRegistryKey.hpp>
#include <foo/XCountable.hpp>
 
 
#define SERVICENAME "foo.Counter"
#define IMPLNAME "com.sun.star.comp.example.cpp.Counter"
 
using namespace ::rtl;
using namespace ::osl;
using namespace ::cppu;
using namespace ::com::sun::star::awt;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::registry;
//using namespace ::com::sun::star::frame;
using namespace ::foo;
//==================================================================================================
namespace my_sc_impl
{
static Sequence< OUString > getSupportedServiceNames_MyCounterImpl()
{
	static Sequence < OUString > *pNames = 0;
	if( ! pNames )
	{
//		MutexGuard guard( Mutex::getGlobalMutex() );
		if( !pNames )
		{
			static Sequence< OUString > seqNames(1);
			seqNames.getArray()[0] = OUString(RTL_CONSTASCII_USTRINGPARAM(SERVICENAME));
			pNames = &seqNames;
		}
	}
	return *pNames;
}
 
static OUString getImplementationName_MyCounterImpl()
{
	static OUString *pImplName = 0;
	if( ! pImplName )
	{
//		MutexGuard guard( Mutex::getGlobalMutex() );
		if( ! pImplName )
		{
			static OUString implName( RTL_CONSTASCII_USTRINGPARAM(IMPLNAME) );
			pImplName = &implName;
		}
	}
	return *pImplName;
}
 
// New
class MyCounterImpl : public ::cppu::WeakImplHelper2<
      XCountable, XServiceInfo >
{
	// to obtain other services if needed
	Reference< XMultiComponentFactory > m_xMCF;
	Reference< XComponentContext > m_xContext;
	Reference< XDialog > m_xDialog;
	sal_Int32 m_nCount;
 
public:
// added this constructor 19/05/09
    inline MyCounterImpl( Reference< XComponentContext > const & xContext) throw ()
        : m_xContext( xContext ) {
	m_xMCF=m_xContext->getServiceManager();
	}
    virtual ~MyCounterImpl() {}
 
    // XServiceInfo specification
    virtual OUString SAL_CALL getImplementationName(  ) throw(RuntimeException);
    virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) throw(RuntimeException);
    virtual Sequence< OUString > SAL_CALL getSupportedServiceNames(  ) throw(RuntimeException);
    static Sequence< OUString > SAL_CALL getSupportedServiceNames_Static(  );
 
    // XCountable specification
    virtual void SAL_CALL getCount() throw (RuntimeException);
    virtual void SAL_CALL setCount() throw (RuntimeException);
    virtual void SAL_CALL increment() throw (RuntimeException);
    virtual void SAL_CALL decrement() throw (RuntimeException);
//    virtual OUString createDialog( const OUString & DialogURL/*, Reference< XModel > const & xModel, 
//					Reference< XFrame > const & xFrame*/ )throw (RuntimeException);
    virtual void setDialog( Reference< XDialog > const & xDialog )throw (RuntimeException);
};
// XCountable implementation
//*************************************************************************
void SAL_CALL MyCounterImpl::setDialog(Reference< XDialog > const & xDialog) throw (RuntimeException)
{
	m_xDialog=xDialog;
}
void SAL_CALL MyCounterImpl::getCount() throw (RuntimeException){
	Reference< XControlContainer > xControlContainer(m_xDialog,UNO_QUERY);
	Reference< XControl > xTextControl=xControlContainer->getControl(OUString::createFromAscii("TextField2"));
	Reference< XTextComponent > xTextComponent(xTextControl,UNO_QUERY);
	xTextComponent->setText(OUString::valueOf((sal_Int32)m_nCount));  
}
void SAL_CALL MyCounterImpl::setCount() throw (RuntimeException){
	Reference< XControlContainer > xControlContainer(m_xDialog,UNO_QUERY);
	Reference< XControl > xTextControl=xControlContainer->getControl(OUString::createFromAscii("TextField1"));
	Reference< XTextComponent > xTextComponent(xTextControl,UNO_QUERY);
	m_nCount=(xTextComponent->getText()).toInt32();  
}
 
void SAL_CALL MyCounterImpl::increment() throw (RuntimeException)
		{ ++m_nCount; }
void SAL_CALL MyCounterImpl::decrement() throw (RuntimeException)
		{ --m_nCount; }
// XServiceInfo	implementation
//*************************************************************************
OUString SAL_CALL MyCounterImpl::getImplementationName(  )
	throw(RuntimeException)
{
	return OUString( RTL_CONSTASCII_USTRINGPARAM(IMPLNAME) );
}
 
//*************************************************************************
sal_Bool SAL_CALL MyCounterImpl::supportsService( const OUString& ServiceName )
	throw(RuntimeException)
{
	return ServiceName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM(SERVICENAME) );
}	
 
//*************************************************************************
Sequence<OUString> SAL_CALL MyCounterImpl::getSupportedServiceNames(  )
	throw(RuntimeException)
{
	return getSupportedServiceNames_Static();
}
 
//*************************************************************************
Sequence<OUString> SAL_CALL MyCounterImpl::getSupportedServiceNames_Static(  )
{
	Sequence<OUString> names(1);
    names[0] = OUString(RTL_CONSTASCII_USTRINGPARAM(SERVICENAME));
    return names;
}
 
//*****New vesion with context management
Reference< XInterface > SAL_CALL MyCounterImpl_createInstance(
    Reference< XComponentContext > const & xContext )
    SAL_THROW( () )
{
    return static_cast< ::cppu::OWeakObject * >( new MyCounterImpl( xContext ) );
//	return static_cast< XTypeProvider * >( new MyCounterImpl( xContext) );
//	return static_cast< XTypeProvider * >( new MyCounterImpl() );
}
}
 
//##################################################################################################
//#### EXPORTED ####################################################################################
//##################################################################################################
 
/* New ****/
/* shared lib exports implemented without helpers in service_impl1.cxx */
 
namespace my_sc_impl
{
static struct ::cppu::ImplementationEntry s_component_entries [] =
{
    { // MyCounterImpl_create replaced by MyCounterImpl_createInstance 19/05/09
        MyCounterImpl_createInstance, getImplementationName_MyCounterImpl,
        getSupportedServiceNames_MyCounterImpl, ::cppu::createSingleComponentFactory,
        0, 0
    },
    { 0, 0, 0, 0, 0, 0 }
};
}
 
extern "C"
{
void SAL_CALL component_getImplementationEnvironment(
    sal_Char const ** ppEnvTypeName, uno_Environment ** ppEnv )
{
    *ppEnvTypeName = CPPU_CURRENT_LANGUAGE_BINDING_NAME;
}
sal_Bool SAL_CALL component_writeInfo(
    XMultiServiceFactory * xMgr, XRegistryKey * xRegistry )
{
    return ::cppu::component_writeInfoHelper(
        xMgr, xRegistry, ::my_sc_impl::s_component_entries );
}
void * SAL_CALL component_getFactory(
    sal_Char const * implName, XMultiServiceFactory * xMgr,
    XRegistryKey * xRegistry )
{
    return ::cppu::component_getFactoryHelper(
        implName, xMgr, xRegistry, ::my_sc_impl::s_component_entries );
}
}

Template:Documentation/Note

The OOoBasic part

The OOoBasic program has to change. All the old sub are removed and we call the "setDialog" method before starting the dialog execution. Here is our new program :

'Listing 6
REM  *****  BASIC  *****
 
Sub demonstrateSimpleComponent
    Dim oSimpleComponent
	Dim oDialog
	oSimpleComponent = CreateUnoService( "foo.Counter" )
	oCreateDialog2=CreateUnoService("com.sun.star.awt.DialogProvider2")
	'Thank you ms777 for the line below (http://www.oooforum.org/forum/viewtopic.phtml?t=84168)
	oCreateDialog2.initialize(Array(ThisComponent))
	oDialog=oCreateDialog2.createDialogWithHandler("vnd.sun.star.script:Standard.Dialog1?location=document", _
		oSimpleComponent )
	oSimpleComponent.setDialog(oDialog)	
	oDialog.Execute()
	oDialog.dispose()
End Sub
 
'Sub increment
'  oSimpleComponent.increment()
'End Sub
 
'Sub decrement
'   oSimpleComponent.decrement()
'End Sub
 
'Sub getCount
'   Dim oTextField
'   oTextField = oDialog.getControl("TextField2")
'   oTextField.setText( oSimpleComponent.getCount())  
'End Sub
 
'Sub setCount
'	Dim oTextField
'	oTextField = oDialog.getControl("TextField1")
	'implicit conversion String to Integer
'	oSimpleComponent.setCount(oTextField.getText())
'End Sub

All the old sub are in comment now. Don't forget to modify your dialog because its callback are not OOoBasic code but methods.

An other way is described in Developer's Guide to bind composant methods with a dialog. Such a component has to implement an com.sun.star.awt.XDialogEventHandler interface. I don't know what of both method is more efficient.

Bounding the Component Methods with XDialogEventHandler Interface

The com.sun.star.awt.XDialogEventHandler interface is the key if you plan to bind component methods with dialog events.

More on XDialogEventHandler Interface

As usual in this document, we have a look at the corresponding IDL file to see what is handled by XDialogEventHandler Interface. We can see two methods :

// IDL File
// Listing 7
module com { module sun { module star { module awt {
 
//============================================================================= 
 
/** Handles events fired by dialogs represented by a
    <type scope="com::sun::star::awt">XDialog</type> interface.
 */
interface XDialogEventHandler : ::com::sun::star::uno::XInterface {
 
    //------------------------------------------------------------------------- 
 
    /** Handles an event generated by a dialog.
 
        The implementation must be aware that the EventObject argument contains types
        which it is not prepared to handle. Similarly this applies for the MethodName
        argument. In this case the method should simply return false.
 
        @param xDialog
            the dialog instance that generated the event. This is the same dialog instance
			that was returned by the <type scope="com::sun::star::awt">XDialogProvider2</type>
			createDialogWithHandler method when passing the XDialogEventHandler instance
			receiving the event as handler parameter.
 
        @param EventObject
            an object describing the event which occurred in the dialog or anything else that
            provides additional information for the event.
            If the event was caused by the dialog or any of the controls which it contains
            then the any should contain an object derived from
            <type scope="com::sun::star::lang">EventObject</type>. Typically this would be one
            of the several com::sun::star::awt::*Event types.
 
        @param MethodName
            the name of the function which is to be called.
 
        @returns
            true if the event was handled, otherwise false.
 
        @throws com::sun::star::lang::WrappedTargetException  
			if the implementation of the method, which is determined by the argument MethodName,
			throws an exception. This exception is then wrapped into a
			<type scope="com::sun::star::lang">WrappedTargetException</type>.
 
     */
	boolean callHandlerMethod(
		[in] com::sun::star::awt::XDialog xDialog,
		[in] any EventObject,
		[in] string MethodName)
        raises(com::sun::star::lang::WrappedTargetException); 
 
 
    /** returns a sequence of supported method names
 
        @returns
            all method names that will be accepted in calls to callHandlerMethod.
     */
	sequence<string> getSupportedMethodNames();
};
 
//============================================================================= 
 
}; }; }; };

"callHandlerMethod" and "getSupportedMethodNames()" which we have to implement.

Our starting IDL File

As mentioned above we start with Counter IDL file. That means we are not particularly interested in methods prototypes. We recall for the nth time the corresponding IDL file :

// IDL File
// Listing 8
#include <com/sun/star/uno/XInterface.idl>
 
module foo
{
       interface XCountable : com::sun::star::uno::XInterface
	{
		long getCount();
		void setCount( [in] long nCount );
		long increment();
		long decrement();
	};	
	service Counter : XCountable ;
};

Source File

You can have a look at com.sun.star.awt.XDialog, com.sun.star.awt.XTextComponent, com.sun.star.awt.XControl, com.sun.star.awt.XControlContainer, com.sun.star.awt.XDialogEventHandler,com.sun.star.lang.XMultiComponentFactory, com.sun.star.lang.XServiceInfo and com.sun.star.registry.XRegistryKey interfaces before going further.

The complete cpp source file is given now :

// Listing 9
/***************************************************************************************************
 ***************************************************************************************************
 *
 * service implementation:	 foo.Counter
 * exported interfaces:		 foo.XCounter
 *
 * simple example component implementing a counter
 *
 ***************************************************************************************************
 **************************************************************************************************/
 
#include <rtl/ustring.hxx>
#include <rtl/string.hxx>
#include <cppuhelper/queryinterface.hxx> // helper for queryInterface() impl
#include <cppuhelper/factory.hxx> // helper for component factory
// New
#include <cppuhelper/implbase3.hxx> // "3" implementing three interfaces
#include <cppuhelper/implementationentry.hxx>
#include <com/sun/star/awt/XDialog.hpp>
#include <com/sun/star/awt/XTextComponent.hpp>
#include <com/sun/star/awt/XControl.hpp>
#include <com/sun/star/awt/XControlContainer.hpp>
#include <com/sun/star/awt/XDialogEventHandler.hpp>
// generated c++ interfaces
#include <com/sun/star/lang/XMultiComponentFactory.hpp>
#include <com/sun/star/lang/XServiceInfo.hpp>
//#include <com/sun/star/lang/WrappedTargetException.hpp>
#include <com/sun/star/registry/XRegistryKey.hpp>
#include <com/sun/star/uno/Any.hxx>
#include <foo/XCountable.hpp>
 
 
#define SERVICENAME "foo.Counter"
#define IMPLNAME "com.sun.star.comp.example.cpp.Counter"
 
using namespace ::rtl;
using namespace ::osl;
using namespace ::cppu;
using namespace ::com::sun::star::awt;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::registry;
//using namespace ::com::sun::star::frame;
using namespace ::foo;
//==================================================================================================
namespace my_sc_impl
{
static Sequence< OUString > getSupportedServiceNames_MyCounterImpl()
{
	static Sequence < OUString > *pNames = 0;
	if( ! pNames )
	{
//		MutexGuard guard( Mutex::getGlobalMutex() );
		if( !pNames )
		{
			static Sequence< OUString > seqNames(1);
			seqNames.getArray()[0] = OUString(RTL_CONSTASCII_USTRINGPARAM(SERVICENAME));
			pNames = &seqNames;
		}
	}
	return *pNames;
}
 
static OUString getImplementationName_MyCounterImpl()
{
	static OUString *pImplName = 0;
	if( ! pImplName )
	{
//		MutexGuard guard( Mutex::getGlobalMutex() );
		if( ! pImplName )
		{
			static OUString implName( RTL_CONSTASCII_USTRINGPARAM(IMPLNAME) );
			pImplName = &implName;
		}
	}
	return *pImplName;
}
 
// New
class MyCounterImpl : public ::cppu::WeakImplHelper3<
      XCountable, XServiceInfo, XDialogEventHandler >
{
	// to obtain other services if needed
	Reference< XMultiComponentFactory > m_xMCF;
	Reference< XComponentContext > m_xContext;
	sal_Int32 m_nCount;	
public:
// added this constructor 19/05/09
    inline MyCounterImpl( Reference< XComponentContext > const & xContext) throw ()
        : m_xContext( xContext ) {
	m_xMCF=m_xContext->getServiceManager();
	}
    virtual ~MyCounterImpl() {}
 
    // XServiceInfo specification
    virtual OUString SAL_CALL getImplementationName(  ) throw(RuntimeException);
    virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) throw(RuntimeException);
    virtual Sequence< OUString > SAL_CALL getSupportedServiceNames(  ) throw(RuntimeException);
    static Sequence< OUString > SAL_CALL getSupportedServiceNames_Static(  );
 
    // XCountable specification
    virtual sal_Int32 SAL_CALL getCount() throw (RuntimeException);
    virtual void SAL_CALL setCount( sal_Int32 nCount) throw (RuntimeException);
    virtual sal_Int32 SAL_CALL increment() throw (RuntimeException);
    virtual sal_Int32 SAL_CALL decrement() throw (RuntimeException);
// XDialogEventHandler specification
    virtual sal_Bool SAL_CALL callHandlerMethod(const Reference< XDialog >& xDialog,const Any& EventObject,const OUString& MethodName )
throw(WrappedTargetException, RuntimeException);
    virtual Sequence< OUString > SAL_CALL getSupportedMethodNames() throw (RuntimeException);
};
// XCountable implementation
//*************************************************************************
sal_Int32 SAL_CALL MyCounterImpl::getCount() throw (RuntimeException){
	return m_nCount;   
}
void SAL_CALL MyCounterImpl::setCount(sal_Int32 nCount) throw (RuntimeException){
	m_nCount = nCount;
}
sal_Int32 SAL_CALL MyCounterImpl::increment() throw (RuntimeException)
		{ return ++m_nCount; }
sal_Int32 SAL_CALL MyCounterImpl::decrement() throw (RuntimeException)
		{ return --m_nCount; }
// XDialogEventHandler implementation
sal_Bool SAL_CALL MyCounterImpl::callHandlerMethod(const Reference< XDialog >& xDialog,const Any& EventObject,const OUString & MethodName ) throw(WrappedTargetException, RuntimeException ){
 if (MethodName.equalsAscii("foo1")){//increment
	increment();
	return sal_True;
  }
  if (MethodName.equalsAscii("foo2")){//decrement
	decrement();
	return sal_True;
  }
  if (MethodName.equalsAscii("foo3")){ //setCount
	Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY);
	Reference< XControl > xTextControl=xControlContainer->getControl(OUString::createFromAscii("TextField1"));
	Reference< XTextComponent > xTextComponent(xTextControl,UNO_QUERY);
	setCount((xTextComponent->getText()).toInt32());
	return sal_True;  
  } 
  if (MethodName.equalsAscii("foo4")){ //getCount
	Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY);
	Reference< XControl > xTextControl=xControlContainer->getControl(OUString::createFromAscii("TextField2"));
	Reference< XTextComponent > xTextComponent(xTextControl,UNO_QUERY);
	xTextComponent->setText(OUString::valueOf((sal_Int32)getCount()));
	return sal_True;
   }
  return sal_False;
}
Sequence< OUString > SAL_CALL MyCounterImpl::getSupportedMethodNames() throw (RuntimeException){
  Sequence< OUString > SeqOUStr(4);
  SeqOUStr[0]=OUString::createFromAscii("foo1");
  SeqOUStr[1]=OUString::createFromAscii("foo2");
  SeqOUStr[2]=OUString::createFromAscii("foo3");
  SeqOUStr[3]=OUString::createFromAscii("foo4");
  return SeqOUStr;
}
// XServiceInfo	implementation
//*************************************************************************
OUString SAL_CALL MyCounterImpl::getImplementationName(  )
	throw(RuntimeException)
{
	return OUString( RTL_CONSTASCII_USTRINGPARAM(IMPLNAME) );
}
 
//*************************************************************************
sal_Bool SAL_CALL MyCounterImpl::supportsService( const OUString& ServiceName )
	throw(RuntimeException)
{
	return ServiceName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM(SERVICENAME) );
}	
 
//*************************************************************************
Sequence<OUString> SAL_CALL MyCounterImpl::getSupportedServiceNames(  )
	throw(RuntimeException)
{
	return getSupportedServiceNames_Static();
}
 
//*************************************************************************
Sequence<OUString> SAL_CALL MyCounterImpl::getSupportedServiceNames_Static(  )
{
	Sequence<OUString> names(1);
    names[0] = OUString(RTL_CONSTASCII_USTRINGPARAM(SERVICENAME));
    return names;
}
 
//*****New vesion with context management
Reference< XInterface > SAL_CALL MyCounterImpl_createInstance(
    Reference< XComponentContext > const & xContext )
    SAL_THROW( () )
{
    return static_cast< ::cppu::OWeakObject * >( new MyCounterImpl( xContext ) );
}
}
 
//##################################################################################################
//#### EXPORTED ####################################################################################
//##################################################################################################
 
/* New ****/
/* shared lib exports implemented without helpers in service_impl1.cxx */
 
namespace my_sc_impl
{
static struct ::cppu::ImplementationEntry s_component_entries [] =
{
    { // MyCounterImpl_create replaced by MyCounterImpl_createInstance 19/05/09
        MyCounterImpl_createInstance, getImplementationName_MyCounterImpl,
        getSupportedServiceNames_MyCounterImpl, ::cppu::createSingleComponentFactory,
        0, 0
    },
    { 0, 0, 0, 0, 0, 0 }
};
}
 
extern "C"
{
void SAL_CALL component_getImplementationEnvironment(
    sal_Char const ** ppEnvTypeName, uno_Environment ** ppEnv )
{
    *ppEnvTypeName = CPPU_CURRENT_LANGUAGE_BINDING_NAME;
}
sal_Bool SAL_CALL component_writeInfo(
    XMultiServiceFactory * xMgr, XRegistryKey * xRegistry )
{
    return ::cppu::component_writeInfoHelper(
        xMgr, xRegistry, ::my_sc_impl::s_component_entries );
}
void * SAL_CALL component_getFactory(
    sal_Char const * implName, XMultiServiceFactory * xMgr,
    XRegistryKey * xRegistry )
{
    return ::cppu::component_getFactoryHelper(
        implName, xMgr, xRegistry, ::my_sc_impl::s_component_entries );
}
}

What we want to discuss now is the com.sun.star.awt.XDialogEventHandler interface implementation. This is done in the code above with two methods as shown below :

// Listing 10
// XDialogEventHandler implementation
sal_Bool SAL_CALL MyCounterImpl::callHandlerMethod(const Reference< XDialog >& xDialog,const Any& EventObject,const OUString & MethodName ) throw(WrappedTargetException, RuntimeException ){
  if (MethodName.equalsAscii("foo1")){//increment
	increment();
	return sal_True;
  }
  if (MethodName.equalsAscii("foo2")){//decrement
	decrement();
	return sal_True;
  }
  if (MethodName.equalsAscii("foo3")){ //setCount
	Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY);
	Reference< XControl > xTextControl=xControlContainer->getControl(OUString::createFromAscii("TextField1"));
	Reference< XTextComponent > xTextComponent(xTextControl,UNO_QUERY);
	setCount((xTextComponent->getText()).toInt32());
	return sal_True;  
  } 
  if (MethodName.equalsAscii("foo4")){ //getCount
	Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY);
	Reference< XControl > xTextControl=xControlContainer->getControl(OUString::createFromAscii("TextField2"));
	Reference< XTextComponent > xTextComponent(xTextControl,UNO_QUERY);
	xTextComponent->setText(OUString::valueOf((sal_Int32)getCount()));
	return sal_True;
   }
  return sal_False;
}
Sequence< OUString > SAL_CALL MyCounterImpl::getSupportedMethodNames() throw (RuntimeException){
  Sequence< OUString > SeqOUStr(4);
  SeqOUStr[0]=OUString::createFromAscii("foo1");
  SeqOUStr[1]=OUString::createFromAscii("foo2");
  SeqOUStr[2]=OUString::createFromAscii("foo3");
  SeqOUStr[3]=OUString::createFromAscii("foo4");
  return SeqOUStr;
}

Template:Documentation/Note The not-real methods are to be given as a property of each button of our dialog : for instance for our "increment" button we choose "foo1" as method name. As can be seen, if callHandlerMethod is fired with "foo1" in MethodName argument, the "increment()" method is called.

OOoBasic File

As usual you have to use the code below in your "SimpleComponent.odt" file :

'Listing 11
REM  *****  BASIC  *****
 
Sub demonstrateSimpleComponent
    Dim oSimpleComponent
	Dim oDialog
	oSimpleComponent = CreateUnoService( "foo.Counter" )
	oCreateDialog2=CreateUnoService("com.sun.star.awt.DialogProvider2")
	'Thank you ms777 for the line below (http://www.oooforum.org/forum/viewtopic.phtml?t=84168)
	oCreateDialog2.initialize(Array(ThisComponent))
	oDialog=oCreateDialog2.createDialogWithHandler("vnd.sun.star.script:Standard.Dialog1?location=document", _
		oSimpleComponent )
	'oSimpleComponent.setDialog(oDialog)	
	oDialog.Execute()
	oDialog.dispose()
End Sub

Our old "setDialog" sub is in comment now : it no longer belongs to the counter interface.

Replacing OOoBasic with C++

The goal of this section is to remove completely or partially our OOoBasic code to make our counter and its correponding Dialog working. We begin with replacing completely the OOoBasic code with a binary executable (in c++).

Launching our Counter with a Binary Executable

Because we always start a project with copying the direcory of the CppComonent example of the SDK, we have a file TestCppComponent.cxx that we never use. In fact we have previously removed all the content of the main() function to allow simple compilation with Makefile. We will be interested with this file in this section with a new role : lanching the Dialog as OOoBasic make it in prevous sections. Here is the C++ program, but before have a loock at com.sun.star.bridge.XUnoUrlResolver, com.sun.star.uno.XInterface, com.sun.star.frame.XDesktop and com.sun.star.awt.XDialogProvider2 interfaces :

// Listing 12
#include <iostream>
#include <cppuhelper/bootstrap.hxx>
#include <com/sun/star/bridge/XUnoUrlResolver.hpp>
#include <com/sun/star/uno/Any.hxx>
#include <com/sun/star/uno/Sequence.hxx>
#include <com/sun/star/uno/XInterface.hpp>
#include <com/sun/star/frame/XDesktop.hpp>
#include <com/sun/star/awt/XDialogProvider2.hpp>
#include <foo/XCountable.hpp>
 
using namespace std;
using namespace rtl;
using namespace com::sun::star::uno;
//namespace cssuno = ::com::sun::star::uno;
using namespace com::sun::star::lang;
using namespace com::sun::star::frame;
using namespace com::sun::star::awt;
 
int SAL_CALL main( int, char ** )
{
    try
    {
        // get the remote office component context
        Reference< XComponentContext > xContext( ::cppu::bootstrap() ); 
        cout << "\nconnected to a running office...\n";
        Reference< XMultiComponentFactory > xMCF = xContext->getServiceManager();
// changed
        Reference< XInterface > CounterService(xMCF->createInstanceWithContext( 
            						OUString( RTL_CONSTASCII_USTRINGPARAM( "foo.Counter" ) ),xContext));
	Reference<foo::XCountable> xCountable ( CounterService, UNO_QUERY_THROW );
	//query the XDesktop Interface
	Reference< XDesktop > xDesktop(
            xMCF->createInstanceWithContext( 
            OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.frame.Desktop" ) ),
            xContext ), UNO_QUERY_THROW );
	Reference< XComponent > xcomponent = xDesktop->getCurrentComponent(); 
	Sequence< Any> Args(1);
	Args[0] <<= xcomponent;
	Reference< XDialogProvider2 > xDialog2(xMCF->createInstanceWithArgumentsAndContext( 
            					OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.awt.DialogProvider2" ) ),
						Args,
						xContext),
						UNO_QUERY_THROW );
	Reference< XDialog > xDialog=xDialog2->createDialogWithHandler(
		OUString::createFromAscii( "vnd.sun.star.script:Standard.Dialog1?location=document" ),
		CounterService);
	xDialog->execute();
    }
    catch ( ::cppu::BootstrapException & e )
    {
        cerr << "\ncaught BootstrapException: "
             << OUStringToOString( e.getMessage(), RTL_TEXTENCODING_ASCII_US ).getStr()
             << '\n';
        return 1;
    }
    catch ( Exception & e )
    {
        cerr << "\ncaught UNO exception: "
             << OUStringToOString( e.Message, RTL_TEXTENCODING_ASCII_US ).getStr()
             << '\n';
        return 1;
    }
 
    return 0;
}

Three parts could be seen in this code :

  1. a part to get the service and interface of our counter
  2. an other part to get the current document in OpenOffice
  3. and the last part to launch the dialog.
Documentation caution.png This example is only working if you have launched OpenOffice before with a valid default document with our Dialog. (SimpleComponent.oxt is OK if you put the dialog in its Standard library with the name "Dialog1").

If you want to be independant of a document you can put your dialog in a OpenOffice Library (Library1 with name Dialog1 for instance) and replace "vnd.sun.star.script:Standard.Dialog1?location=document" with "vnd.sun.star.script:Library1.Dialog1?location=application".

I want to tackkle a last example now : realize a component which is able to launch its dialog itself.

Launching the Dialog in the component

Up to then, the dialog URL is every time known by a program which doesn't belong to the component. At first it was the OOoBasic program which has to invoke the dialog with the correct URL and in the previous section it was a binary executable C++ program which invokes the same dialog. We want realize a component which launch itself the dialog. That means our OOobasic program becomes very simple now. We begin first with the OOoBasic program I want to make working :

'Listing 13
REM  *****  BASIC  *****
 
Sub demonstrateSimpleComponent
    Dim oSimpleComponent
	'Dim oDialog
	oSimpleComponent = CreateUnoService( "foo.Counter" )
	'oCreateDialog2=CreateUnoService("com.sun.star.awt.DialogProvider2")
	'Thank you ms777 for the line below (http://www.oooforum.org/forum/viewtopic.phtml?t=84168)
	'oCreateDialog2.initialize(Array(ThisComponent))
	'oDialog=oCreateDialog2.createDialogWithHandler("vnd.sun.star.script:Standard.Dialog1?location=document", _
	'	oSimpleComponent )
	'oSimpleComponent.setDialog(oDialog)	
	'oDialog.Execute()
	'oDialog.dispose()
End Sub

where you see almost everything is in comment. That means if I want to see a dialog my C++ constructor has to be changed. All is shown in the listing below :

// Listing 14
// modified this constructor 6/06/2009 
    inline MyCounterImpl( Reference< XComponentContext > const & xContext) throw ()
        : m_xContext( xContext ) {
	m_xMCF=m_xContext->getServiceManager();
	Reference< XDesktop > xDesktop(
            m_xMCF->createInstanceWithContext( 
            OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.frame.Desktop" ) ),
            xContext ), UNO_QUERY_THROW );
	Reference< XComponent > xcomponent = xDesktop->getCurrentComponent();
	Sequence< Any> Args(1);
	Args[0] <<= xcomponent;
	Reference< XDialogProvider2 > xDialog2(m_xMCF->createInstanceWithArgumentsAndContext( 
            					OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.awt.DialogProvider2" ) ),
						Args,
						xContext),
						UNO_QUERY_THROW );
	Reference< XDialog > xDialog=xDialog2->createDialogWithHandler(
		OUString::createFromAscii( "vnd.sun.star.script:Library1.Dialog1?location=application" ),
		(XCountable *)this);
	xDialog->execute();
	}

Template:Documentation/Note After a month, I am able to remove the document. In the previous Listing 14, the dialog has the Writer Document windows as a parent windows even if this dialog is an application dialog (see its corresponding URL). To put it an other way, saying the dialog is an application dialog only has to do with its URL, not with its parent windows.

We can remove the document with a new constructor which only uses createInstanceWithContext instead of createInstanceWithArgumentsAndContext as explained in the Developer's Guide. The new simpler constructor is as follows :

// Listing 15
// Changed this constructor 10/07/2009 
    inline MyCounterImpl( Reference< XComponentContext > const & xContext) throw ()
        : m_xContext( xContext ) {
	m_xMCF=m_xContext->getServiceManager();
	Reference< XDialogProvider2 > xDialog2(m_xMCF->createInstanceWithContext( 
            					OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.awt.DialogProvider2" ) ),
						xContext),
						UNO_QUERY_THROW );
	Reference< XDialog > xDialog=xDialog2->createDialogWithHandler(
		OUString::createFromAscii( "vnd.sun.star.script:Library1.Dialog1?location=application" ),
		(XCountable *)this);
	xDialog->execute();
	}
Documentation caution.png In this example and in all the examples of the next chapter, I will use the component constructor to print out the dialog. I am sure of its educational quality, but not sure it's a good choice for professional components. I plan to write a chapter on writing professional components but it would be better with a lot of practice that I haven't at the moment.

The next step is of course to remove completely the OOoBasic program. This means we invoke the component with the OpenOffice menu : this is what is called an add-on in the OpenOffice terminology and will be tackled in an other chapter. We will however go further with Dialog and component in the next chapter where more detailed examples will be adressed.

To do

Documentation caution.png Documentation on the "create()" method available in some.idl file is not completed because I have no skill on the subject at the moment. I don't see how it is working because I don't find the corresponding code in service1_impl.cxx or service2_impl.cxx.

This Document Home Page

HomePageCpp.png Return to Document Home page

See also

Personal tools