Component and Dialog
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 Daialog 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. But befor going further we will first examine a method which consists to encapsulate C++ with OOoBasic.
Contents
- 1 Adding a Dialog to the Counter
- 2 Direct Calls of methods with Introspection Service
- 3 This Document Home Page
- 4 See also
Adding a Dialog to the Counter
For your information, we recall that our counter has four methods :
- increment
- decrement
- setCount
- getCount
We then design a Dialog with one button by method (then four buttons) and two text fields : O
- a text field to set a value in the counter (then associated with "setCount" button)
- a text field to get the value of the counter (then associated with "getCount" button)
Here is the corresponding dialog
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 required using the property window of the dialog editor. In this program we have left comment on different introspection uses in our counter but this is not important. Template:Documentation/Note
Direct Calls of 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.
Direct Call of 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
As you can see, the OOoBasic Sub "increment" is now in comment and then cannot work. We have also to modify the Dialog for the "increment" button which have to call 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.
Direct calls of all counter methods
We plan in this section to modify the C++ counter code to make it working with direct call of its methods when using buttons of the dialog. As mentioned in the end of previous section, it means that 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) to get com.sun.star.awt.XControlContainer interface and access our 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 it has a parameter com.sun.star.uno.XComponentContext now.
inline MyCounterImpl( Reference< XComponentContext > const & xContext) : m_xContext( xContext ) { //m_xMCF=m_xContext->getServiceManager(); }
These four change works, we can memorize the context but we have not our service manager !
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 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 be not changed 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 going 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 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 signature. It then becomes :
#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. L'objectif de cette méthode est de passer notre interface XDialog au composant et par la même occasion de voir un peu comment on gère les types de paramètres un peu évolués. Examinons maintenant le code correspondant du compteur.
Fichier counter.cxx
Le code source est un peu long et fait appel aux 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 et com.sun.star.registry.XRegistryKey.
/*************************************************************************************************** *************************************************************************************************** * * 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 ); } }
La partie OOoBasic
Le programme OOoBasic doit changer. On retire toutes les anciens sous-programmes et surtout on apelle le setDialog au bon moment. Voici donc le nouveau programme :
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, StarDesktop.getActiveFrame() ) 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
où vous voyez le "setDialog" juste avant l'exécution de la boîte de dialogue. Tous les anciens sous-programmes sont en commentaires maintenant. N'oubliez pas non plus de modifier votre boîte de dialogue pour qu'elle n'appelle plus des sous-programmes OOoBasic mais des méthodes.
Il existe une autre méthode pour appeler les méthodes d'un composant. Il s'agit d'implanter une interface com.sun.star.awt.XDialogEventHandler dans notre composant. Je ne sais pas si cette méthode est plus efficace que l'utilisation de l'introspection.
This Document Home Page
See also
- French version of this chapter
- Constructing Components
- The corresponding paragraph in Developer's Guide
- OOoBasic Guide
- Managing Dialogs in OOoBasic.
- Contextual Objects in OOoBasic