FR/Documentation/Composants et boite de dialogue

From Apache OpenOffice Wiki
Jump to: navigation, search
Tip.png Pour faire fonctionner les exemples de ce chapitre relisez "comment faire fonctionner nos exemples".


En programmation événementielle la réponse à des événements se fait :

  • avec des fonctions de callback
  • avec des listener

Nous avons déjà traité les listeners ici nous allons donc nous intéresser dans ce chapitre aux fonctions de callback.

Nous désirons réaliser une boîte de dialogue pour faire fonctionner notre compteur, ce sera le fil conducteur de ce chapitre. L'idée est d'utiliser l'environnement OOoBasic, car il est aisé d'y créer des boîtes de dialogue. Comme on va le voir, il existe plusieurs techniques pour faciliter l'utilisation des méthodes d'un composant à l'aide des boîtes de dialogue. L'objectif de ce chapitre est donc d'examiner comment transformer les méthodes d'un composant en fonction de callback destinées à réagir aux événements qui se produisent dans la boîte de dialogue. La première solution que l'on se propose d'examiner consiste à ne rien changer du code C++ du composant mais à enrober ce code par du OOoBasic et à utiliser ainsi les facilités de la gestion des sous-programmes OOoBasic par des boutons d'une boîte de dialogue.

Ajouter une boîte de dialogue au compteur avec le Basic

Rappelons pour information que notre compteur dispose de quatre méthodes :

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

On va donc chercher à réaliser une boîte de dialogue qui va proposer un bouton par méthode. Elle proposera d'autre part deux champs texte :

  1. un champ texte pour positionner une valeur dans le compteur (donc associé au bouton setCount)
  2. un champ texte pour afficher la valeur du compteur (donc associé au bouton getCount)

Pour se faire une idée de quoi il s'agit, voici une image de la boîte de dialogue

Notre boîte de dialogue
Documentation note.png La boîte de dialogue présentée sera construite, dans une bonne partie de ce chapitre, dans le document "SimpleComponent.odt" de votre répertoire courant et dans la librairie standard (avec le nom "Dialog1"). Ceci détermine l'URL de la boîte de dialogue, URL utilisée par la suite dans les programmes OOoBasic.

Pour faire fonctionner l'ensemble on peut tout simplement utiliser le code OOoBasic suivant :

'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

en prenant soin d'associer aux boutons les sous-programmes OOoBasic correspondants. Dans ce programme on a laissé en commentaire les différentes façons de réaliser l'introspection sur notre compteur, mais ceci n'a pas grand intérêt pour ce qui nous préoccupe.

Documentation note.png Le point important à remarquer dans ce code est ce que j'appelle "enrobage OOoBasic"  : une fonction OOoBasic qui a la même nom qu'une méthode et qui ne fait qu'appeler cette méthode. Il est aussi à noter que parfois l'enrobage comporte plusieurs lignes mais c'est parce que l'on gère les champs de texte soit en tant que paramètre d'entrée, soit en tant que paramètre de sortie.

Déclenchement des méthodes du compteur à l'aide du service d'Introspection

Comme indiqué dans le Developer's Guide, depuis la version 2.0.4 il est possible d'associer directement des méthodes d'un composant à des événements de boutons (ou autres contrôles). Une autre façon de dire les choses, c'est que l'enrobage que l'on a utilisé dans la section précédente du genre :

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

n'est plus nécessaire. Mais ceci a un coût : on ne pourra pas toujours garder le composant sans en modifier le code C++. Pour commencer par le plus simple, nous allons modifier notre programme OOoBasic, notre boîte de dialogue mais pas notre compteur.

Déclenchement d'une méthode du compteur sans changer son code

On reprend en le modifiant le programme OOoBasic (Listing 1) précédent pour qu'il utilise le service com.sun.star.awt.DialogProvider2 qui nous fournit la méthode "createDialogWithHandler" convoitée. Voici le programme correspondant :

'Listing 3
REM  *****  BASIC  *****
	Dim oSimpleComponent
	Dim oDialog
Sub demonstrateSimpleComponent
	oSimpleComponent = CreateUnoService( "foo.Counter" )
	oCreateDialog2=CreateUnoService("com.sun.star.awt.DialogProvider2")
	'Merci ms777 pour la ligne suivante (voir 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 La méthode initialize du OOobasic semble être appelée par un createInstanceWithArgumentsAndContext en C++. Je m'en suis aperçu par hasard en tombant sur un message d'erreur qui me parlait de initialize alors que je ne l'ai jamais appelé explicitement. Voir une utilisation de createInstanceWithArgumentsAndContext plus loin. On arrive ici aux limites de mes connaissances OOoBasic actuelles.

Si vous voulez ne pas être dépendant d'un document mettez votre boîte de dialogue dans une bibliothèque OpenOffice ("Library1" avec le nom "Dialog1" par exemple) et remplacez alors "vnd.sun.star.script:Standard.Dialog1?location=document" par "vnd.sun.star.script:Library1.Dialog1?location=application". Comme vous pouvez le voir dans le listing 3 ci-dessus, j'ai mis en commentaire le sous-programme OOoBasic "increment", il ne peut donc plus fonctionner. En fait j'ai modifié aussi la boîte de dialogue pour que le bouton "increment" n'appelle plus un sous-programme de macro, mais directement une méthode du composant counter. Cette page en anglais du Developer's Guide explique comment on procède pour cela. La partie initialisation s'en trouve légèrement changée.

Documentation note.png Pour cet exemple le code du composant compteur n'a toujours pas été changé. Ce que l'on vient de réaliser fonctionne car notre compteur fournit l'interface com.sun.star.lang.XTypeProvider et bien sûr la méthode "increment()".

Il nous est possible de poursuivre comme cela avec la méthode decrement du compteur, mais impossible de continuer avec les deux autres méthodes "setCount" et "getCount". Pour comprendre pourquoi, il faut se poser la question de savoir comment tout cela fonctionne. En fait dans ce cas précis c'est le service d'introspection com.sun.star.beans.Introspection qui va être utilisé automatiquement pour retrouver la méthode. Mais cette introspection automatique n'est capable de retrouver que les méthodes ayant pour signature :

void [nomMethod](void);

ou encore

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

et cela tombe bien parce que nos deux premières méthodes "increment" et "decrement" ont cette signature là, mais ce n'est pas le cas des deux suivantes "setCount" et "getCount". Si l'on veut les faire fonctionner avec le même principe que celui déjà invoqué, il nous faudra retirer les paramètres correspondants. C'est ce que l'on a fait en OOoBasic lorsqu'on a enrobé les méthodes C++ : regardez le "Sub getCount" en OOoBasic n'a plus de paramètre dans le listing 3 !

Documentation caution.png Je me suis aperçu après coup que la signature de "increment" est en fait "long increment();" mais cela ne semble pas avoir perturbé le fonctionnement. A vérifier tout cela.
Tip.png En résumé : dès qu'un composant fournit l'interface XTypeProvider vous pouvez appeler une de ses méthodes si elle est de prototype "void nomMethode()" en changeant simplement l'événement associé avec l'éditeur de la boÎte de dialogue. Trouver plus simple est probablement difficile.


Déclenchement de toutes les méthodes du compteur

Dans cette section nous allons chercher à modifier le compteur pour qu'il fonctionne complètement par appel direct de toutes ses méthodes. Comme indiqué en fin de section précédente, cela veut dire que nos deux méthodes "setCount" et "getCount" n'auront plus de paramètres. Elles devront être capables de lire/écrire dans les contrôles de champs de textes de la boîte de dialogue. Ceci va avoir de profondes répercutions sur le code du compteur car il va nous falloir obtenir un Service Manager (com.sun.star.lang.XMultiComponentFactory) pour être capable de lancer la boîte de dialogue à partir du code C++ et surtout d'obtenir l'interface com.sun.star.awt.XControlContainer pour accéder aux contrôles qui nous intéressent. Le Developer's Guide considère que l'obtention d'un Service Manager se fait par l'intermédiaire du contexte. Nous allons donc d'abord résoudre ce problème.

Obtention du contexte

L'obtention du contexte peut se faire à l'aide de quatre modifications de notre fichier source counter.cxx.

Ajout d'un champ

Pour récupérer le contexte on ajoute le champ

Reference< XComponentContext > m_xContext;

dans la classe du compteur. On rappelle qu'un contexte est une paire chaîne de caractères/valeur.

Remplacement du sous-programme d'initialisation

L'ancien sous-programme d'initialisation (qui appelle le constructeur de notre compteur) doit être remplacé. Il s'agissait de

// ancien sous programme a supprimer
Reference< XInterface > SAL_CALL MyCounterImpl_create(
    Reference< XComponentContext > const & xContext )
    SAL_THROW( () )
{
    return static_cast< XTypeProvider * >( new MyCounterImpl() );
}

que l'on change maintenant par

// nouveau sous-programme de remplacement
Reference< XInterface > SAL_CALL MyCounterImpl_createInstance(
    Reference< XComponentContext > const & xContext )
    SAL_THROW( () )
{
    return static_cast< ::cppu::OWeakObject * >( new MyCounterImpl( xContext ) );
}

Le nom de ce sous-programme n'a aucune importance, car il va être mis dans une structure très particulière (ou un tableau de ces structures). C'est ce que l'on se propose d'examiner maintenant.

Changement du tableau des entrées

On doit procéder au changement dans le tableau des entrées indiqué dans le commentaire ci-dessous

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 }
};

Changement du constructeur de la classe

Le constructeur de la classe doit être modifié car il possède un paramètre com.sun.star.uno.XComponentContext maintenant.

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

Ces quatres opérations fonctionnent, on peut mémoriser le contexte dans notre classe mais on n'a toujours pas le service manager !

Documentation caution.png J'ai essayé de réaliser un constructeur de classe du type
// doesn't work
Reference< XInterface > SAL_CALL MyCounterImpl_createInstance( const Reference< XMultiServiceFactory > & rSMgr)
	throw( Exception )
{
	return (cppu::OWeakObject*) new MyCounterImpl( rSMgr );
}

qui obtient directement un service Manager (comme dans les AddOn) mais sans succès. A explorer plus tard.


Obtention du Service Manager

Il existe en fait plusieurs Service Manager mais celui qu'il nous est possible de récupérer à partir du contexte d'un composant est une interface com.sun.star.lang.XMultiComponentFactory. L'interface XMultiComponentFactory peut être mémorisée en ajoutant un champ :

Reference< XMultiComponentFactory > m_xMCF;

à notre classe compteur. Il nous faut alors changer notre constructeur de la classe pour qu'il mémorise vraiment le Service Manager :

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

Vous devez ajouter dans le code source une directive d'inclusion

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

mais rien dans le Makefile car si vous prenez soin de regarder le fichier CppComponent.uno.xml vous verrez bien apparaître l'interface com.sun.star.lang.XMultiComponentFactory et donc le fichier com/sun/star/lang/XMultiComponentFactory.hpp a été généré pour vous.

Prêt pour le grand saut

Nous sommes prêt à réaliser notre compteur autonome avec sa boîte de dialogue. Pour éviter un agacement du lecteur, nous allons cesser de détailler les modifications une par une et donner dans cette section les codes complets associés.

Fichier Counter.idl

J'ai beaucoup hésité sur la façon de procéder. Il y a un exemple dans le Developer's Guide détaillé en Java et j'ai pensé le traduire en C++ mais je n'ai pas réussi à traduire la ligne

// gros gros problemes avec le this !!!
XDialog xDialog = xDialogProvider.createDialogWithHandler( DialogURL, this );

Ce qui caractérise l'exemple en question, c'est que c'est un composant qui affiche tout seul sa boîte de dialogue. Je vais garder notre affichage en OOoBasic mais puisque mes sous programmes C++ devront lire les contrôles de texte, il faut que je passe une interface com.sun.star.awt.XDialog à mon composant. Notre nouveau fichier IDL va prendre en compte cette contrainte et respecte vraiment la signature des méthodes maintenant. Il devient donc :

// Fichier IDL
// 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 ;
};

où nous avons ajouté une méthode "setDialog". 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.

Documentation note.png Avant d'aller plus en avant vous avez pu noter qu'il y a une autre façon de procéder. Plutôt que d'ajouter la méthode "setDialog" vous pouvez ajouter les deux paramètres mentionnés plus haut aux deux méthodes "setCount" et "getCount" à savoir
  • [in] com::sun::star::awt::XDialog xDialog
  • [in] any aEvent.

Et donc les deux méthodes pourront utiliser le premier paramètre en lieu et place du champ positionné par setDialog.

Fichier counter.cxx

Documentation caution.png Je suis resté plusieurs jours bloqué par un message d'erreur pas très clair qui clamait son impossibilité d'invoquer le constructeur de ma classe compteur parce que la méthode que je rajoutais était abstraite. Le problème venait tout simplement du prototype de mon sous programme "setDialog" en C++. Donc regardez cela de très près si vous ne voulez pas être confronté à des problèmes identiques : les "const" et "&" sont obligatoires dans le(s) paramètre(s).

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.

// listing 5
// fichier cxx
/***************************************************************************************************
 ***************************************************************************************************
 *
 * 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 );
}
}
Documentation note.png Dans ce code il vous faut lire les méthodes "MyCounterImpl::getCount()" et "MyCounterImpl::setCount()" qui vont chercher ou mettent à jour des informations dans des contrôles de texte. La méthode ajoutée "MyCounterImpl::setDialog()" n'a quant à elle rien d'extraordinaire.

La partie OOoBasic

Le programme OOoBasic doit changer. On retire toutes les anciens sous-programmes et surtout on apelle le setDialog au bon moment. Rappelons que le service com.sun.star.awt.DialogProvider2 nous fournit la méthode "createDialogWithHandler" convoitée. Voici donc le nouveau programme :

' 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

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 technique 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, je veux dire efficace en rapidité d'exécution. En ce qui concerne le code, on va en rajouter pour gérer l'interface XDialogEventHandler mais par contre on peut garder notre fichier IDL initial de notre compteur (Listing 6 du chapitre précédent). Cette gestion ressemble à celle des AddOns et j'ai l'impression que l'on pourrait pousser la ressemblance assez loin et supprimer le fichier IDL du compteur. On n'utilisera pas cette option cependant car il faudrait changer profondément le makefile.

Utilisation de l'interface XDialogEventHandler

L'intérêt de cette façon de procéder est que tout est prévu : en particulier on n'aura pas besoin de la méthode supplémentaire "setDialog" que l'on a ajouté dans la section précédente pour mémoriser l'interface XDialog. En effet, maintenant cette interface fait partie des paramètres et donc nos méthodes pourront s'en servir pour retrouver le contrôles de texte dans la boîte de dialogue. Bon c'est un peu compliqué, il est temps de passer aux exemples concrets de code.

L'utilisation de cette interface nécessite d'implanter deux méthodes. C'est ce que nous allons détailler maintenant.

L'interface XDialogEventHandler détaillée

Comme d'habitude dans ce document, nous allons nous pencher sur le fichier IDL de l'interface XDialogEventHandler pour savoir ce que l'on a à réaliser. Nous y voyons deux méthodes :

// Fichier IDL
// 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" et "getSupportedMethodNames()" qu'il nous faudra donc implanter.

Fichier IDL

Comme déjà mentionné auparavant on reprend maintenant de nouveau le fichier IDL du compteur. Cela signifie en particulier que nous ne sommes plus tenu par des histoires de prototypes de méthodes maintenant. Nous rappelons pour la nième fois le fichier IDL correspondant :

// Fichier IDL
// 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 ;
};

Voilà, nous sommes prêt à examiner le code.

Programme source complet

Les interfaces 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 et com.sun.star.registry.XRegistryKey sont a consulter pour une bonne compréhension du code. Le programme source complet en C++ est donné maintenant :

// Fichier cxx
// 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 );
}
}

Nous voulons maintenant discuter de l'interface com.sun.star.awt.XDialogEventHandler et particulièrement de son implémentation. Cela est naturellement fait dans le code ci-dessus, mais pour faciliter son examen nous en donnons les extraits correspondants maintenant :

// langage cpp
// 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.equalsAsciiL("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.equalsAsciiL("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;
}
Documentation note.png Vous avez deux types de méthodes dans le code précédent : les méthodes "foo1", ..., "foo4" que je qualifierai de non réelles et les méthodes "increment()", ... "getCount()" qui sont bien réelles et définies dans le fichier IDL. Les méthodes non réelles ne sont en fait ni des méthodes ni des procédures mais sont quand même appelées méthodes dans la documentation de l'interface com.sun.star.awt.XDialogEventHandler (et aussi dans le Developer's Guide) comme cela peut être vu avec le paramètre qui a pour nom "MethodName".

Les noms des méthodes non réelles doivent être données comme propriété de chacun des boutons de la boîte de dialogue : par exemple pour notre bouton "increment" nous choisisson "foo1" comme nom de méthode. Et comme on peut le voir dans le code, si callHandlerMethod est déclenchée avec "foo1" comme argument MethodName, la méthodes "increment()" est en fait appelée. Bon il est temps de donner le code OOoBasic utilisé pour l'exemple.

OOoBasic File

Rappelons que le service com.sun.star.awt.DialogProvider2 nous fournit l'interface com.sun.star.awt.XDialogProvider2 qui nous fournit la méthode "createDialogWithHandler" qui nous intéresse. Comme d'habitude vous mettez le code OOoBasic ci-dessous dans le fichier "SimpleComponent.odt" :

' 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

Notre sous-programme "setDialog" utilisé précédemment est en commentaire maintenant : il n'appartient plus à l'interface du compteur.

Il va être temps de s'intéresser au fichier TestCppComponent.cxx pour voir s'il ne nous serait pas possible de réaliser des composants capables d'afficher eux-même leur boîte de dialogue.

Remplacer le OOoBasic par du C++

Dans cette section nous allons nous intéresser à faire disparaître soit complètement soit partiellement les programmes OOoBasic pour faire fonctionner notre exemple du compteur avec une boîte de dialogue. Nous commençons par remplacer complètement le code OOoBasic par un binaire exécutable (écrit en c++).

Un binaire exécutable qui lance notre compteur

Puisque nous avons fait une copie du répertoire de l'exemple CppComonent du SDK, nous avons un fichier appelé TestCppComponent.cxx que nous avons laissé de côté. En fait je vous ai demandé précédemment de vider tout le contenu de son main() pour réussir à compiler avec le Makefile. C'est à ce fichier que nous allons nous intéresser dans cette section, nous voulons lui faire jouer le rôle de notre programme OOoBasic, autrement dit c'est lui qui va lancer la boîte de dialogue. Voila le programme C++ que je vous propose, mais auparavant vous pouvez consulter les interfaces com.sun.star.bridge.XUnoUrlResolver, com.sun.star.uno.XInterface, com.sun.star.frame.XDesktop et com.sun.star.awt.XDialogProvider2 :

// 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();
// obtenir le Service fooCounter et l'interface foo.XCountable
        Reference< XInterface > CounterService(xMCF->createInstanceWithContext( 
            						OUString( RTL_CONSTASCII_USTRINGPARAM( "foo.Counter" ) ),xContext));
	Reference<foo::XCountable> xCountable ( CounterService, UNO_QUERY_THROW );
// Chercher le document courant et le préparer comme argument
	//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;
// lancer la boîte de dialogue proprement dite
	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;
}

Vous pouvez distinguer dans ce code trois parties

  1. une première partie qui a comme objectif d'obtenir le service et l'interface de notre compteur,
  2. une partie qui doit retrouver le document courant dans OpenOffice,
  3. une dernière partie qui prépare et lance la boîte de dialogue.
Documentation caution.png Cet exemple ne fonctionne que si on a lancé auparavant OpenOffice avec un document valide qui contient notre boîte de dialogue (SimpleComponent.oxt fait naturellement l'affaire si vous y avez inséré la boîte de dialogue).

Si vous voulez ne pas être dépendant d'un document mettez votre boîte de dialogue dans une bibliothèque OpenOffice (Library1 de nom Dialog1 par exemple) et remplacer "vnd.sun.star.script:Standard.Dialog1?location=document" par "vnd.sun.star.script:Library1.Dialog1?location=application".

Il nous reste à traiter un dernier cas qui me tient à coeur : réaliser un composant qui sait afficher sa boîte de dialogue par lui même.

Et si notre composant lançait sa boîte de dialogue par lui-même

Jusqu'à maintenant, l'URL de notre boîte de dialogue était connue par des programmes extérieurs au composant. En premier c'est le programme OOoBasic qui connaissait cet URL, puis à la section précédente nous avons laissé ce rôle au programme binaire C++. Maintenant nous voulons réaliser un composant capable par lui-même d'afficher sa boîte de dialogue et de réaliser les actions déclenchées par les boutons. Cela signifie que notre programme OOobasic va devenir tout simple maintenant. Nous commençons par montrer le programme en question :

'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

où vous avez certainement remarqué que presque tout est en commentaire. Conséquence immédiate, il nous faut changer notre constructeur (de notre composant). Tout est montré dans le listing ci-dessous :

// Listing 14
// Mon nouveau constructeur du 6/06/09 
    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();
	}
Documentation note.png Remarquez que le programme ci-dessus a besoin encore d'un document courant ouvert dans OpenOffice mais uniquement pour le récupérer et le passer à "createInstanceWithArgumentsAndContext()". Notez aussi que la boîte de dialogue est maintenant dans la librairie "Library1" avec comme nom "Dialog1" dans la librairie d'OpenOffice comme montré par l'URL de cette boîte.

Après un peu plus d'un mois, je suis capable de retirer le document. Dans le listing 14 précédent la boîte de dialogue avait comme fenêtre parent celle d'un document OOoWriter, bien que le dialogue appartienne à l'application. Pour ne pas laisser de confusion dans la tête du lecteur, je vais détailler un peu plus. Quand je dis que le dialogue appartient à l'application, je parle de l'endroit où est son "code" source et donc de son URL. Cela ne veut pas dire que son lancement ne nécessitera pas un document ouvert : c'est d'ailleurs le cas du listing 14 précédent. On peut retirer le document en utilisant un createInstanceWithContext au lieu d'un createInstanceWithArgumentsAndContext comme expliqué dans le Developer's Guide. Le nouveau constructeur est ainsi beaucoup plus simple :

// Listing 15
// Changé de constructeur le 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();
	}

L'étape suivante consiste à retirer complètement le programme OOoBasic. Mais il faut se poser la question de savoir qui va invoquer le composant ? Cela peut-être une entrée supplémentaire d'un menu et la terminologie d'OpenOffice parle alors d'Add-on, ce qui sera abordé dans un autre chapitre.

Retour à la page d'accueil

Page d'accueil du développement C++ à l'aide du SDK

Voir aussi

Personal tools