FR/Documentation/Plus loin avec Composants et boites de dialogue

From Apache OpenOffice Wiki
Jump to: navigation, search

Dans le chapitre précédent nous avons utilisé et programmé seulement deux types de contrôles dans nos boîtes de dialogue (bouton et zone de texte). Nous avons l'intention dans ce chapitre d'aller plus loin en utilisant des autres contrôles et voir comment ils fonctionnent. Vous ne pouvez pas aborder ce chapitre sans lire le chapitre précédent, particulièrement parce que seulement des extraits de code sont présentés.

Notre compteur sera encore le fil de ce chapitre même si nous finirons par l'abandonner à la fin.

Compteur avec des champs numériques

Quand nous avons construit la boîte de dialogue fonctionnant avec notre compteur, nous avons choisi deux zones de texte pour nos données numériques : une pour la saisie, associée au bouton "setCount", et l'autre pour l'affichage du contenu du compteur associé au bouton "getCount". Parce que les zones de textes sont très souvent utilisées en pratique, notre exemple précédent reste fondamental, mais il est temps de l'améliorer en utilisant des champs numériques. Puisque l'aspect visuel de la boîte de dialogue est pratiquement inchangée nous ne fournissons pas de copie d'écran de cette boîte.

Vous pouvez aller voir l'interface com.sun.star.awt.XNumericField et découvrir ainsi ce qui peut être fait avec celle-ci : seules les méthodes getvalue et setValue seront utilisées dans cette section, mais vous pouvez vous amuser avec les autres méthodes. Nous ne fournirons que le code de la fonction callHandlerMethod dans le listing ci-après (voir le chapitre précédent et aussi l'interface correspondante com.sun.star.awt.XDialogEventHandler). La solution à notre problème d'utilisation des deux contrôles numériques est donnée par l'extrait de code suivant :

// Listing 1
// c++
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 > xControl=xControlContainer->getControl(OUString::createFromAscii("NumericField1"));
	Reference< XNumericField > xNumericField(xControl,UNO_QUERY);
	setCount((sal_Int32)xNumericField->getValue());
	return sal_True;  
  } 
  if (MethodName.equalsAscii("foo4")){ //getCount
	Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY);
	Reference< XControl > xControl=xControlContainer->getControl(OUString::createFromAscii("NumericField2"));
	Reference< XNumericField > xNumericField(xControl,UNO_QUERY);
	xNumericField->setValue(getCount());
	return sal_True;
   }
  return sal_False;
}

Nous n'avons pas besoin d'expliquer ce listing en détail, mais peut être faire une remarque : il serait bon de changer le nom des événements "foo1" ... "foo4". Ce sera fait un peu plus loin.

Nous allons maintenant nous intéresser à un autre exemple de contrôle : les boutons radio (ou cases de choix 1 parmi N).

Utiliser les boutons radio

Un bouton d'option com.sun.star.awt.UnoControlRadioButton est un simple commutateur avec deux états, qui sont sélectionnés par l'utilisateur. Les boutons d'option sont habituellement utilisés en groupe pour choisir une parmi plusieurs options. Bien que les boutons d'option et les cases à cocher semblent similaires, sélectionner un bouton radio se fait au détriment des autres du même groupe (mais on peut choisir plusieurs cases à cocher).

Dans notre exemple, on veut utiliser des boutons radio pour choisir la valeur de l'incrément/décrément du compteur (entre 1, 5 et 10). Voici une copie d'écran de notre boîte de dialogue :

Notre deuxime boîte de dialogue pour le compteur

où vous pouvez voir qu'une valeur de 5 pour l'incrément/décrément est choisie.

Pour cet exemple l'interface com.sun.star.awt.XRadioButton doit être consultée. Nous allons fournir deux solutions différentes à cet exemple. Nous commençons par examiner une méthode qui n'utilise que les quatre événements déjà utilisés correspondant aux quatre méthodes du compteur.

Une solution toute simple

Cette solution garde donc nos quatre méthodes gérées par callHandlerMethod() encore nommées "foo1", "foo2", .. "foo4" de l'interface com.sun.star.awt.XDialogEventHandler. Pour dire les choses autrement aucun événement nouveau n'est généré par notre boîte de dialogue. Nous présentons maintenant le code correspondant, et encore une fois avec seulement le code de callHandlerMethod. Regardez le chapitre précédent si vous voulez vous rappeler comment cela fonctionne.

// Listing 2
// C++
sal_Bool SAL_CALL MyCounterImpl::callHandlerMethod(const Reference< XDialog >& xDialog,const Any& EventObject,const OUString & MethodName ) throw(WrappedTargetException, RuntimeException ){
  if (MethodName.equalsAscii("foo1")){//increment
        Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY);
	Reference< XControl > xControl=xControlContainer->getControl(OUString::createFromAscii("OptionButton1"));
	Reference< XRadioButton > xRadioButton(xControl,UNO_QUERY);
	if (xRadioButton->getState())  m_nDelta=1;
	xControl=xControlContainer->getControl(OUString::createFromAscii("OptionButton2"));
	Reference< XRadioButton > xRadioButton2(xControl,UNO_QUERY);
	if (xRadioButton2->getState())  m_nDelta=5;
	xControl=xControlContainer->getControl(OUString::createFromAscii("OptionButton3"));
	Reference< XRadioButton > xRadioButton3(xControl,UNO_QUERY);
	if (xRadioButton3->getState())  m_nDelta=10;
	increment();
	return sal_True;
  }
  if (MethodName.equalsAscii("foo2")){//decrement
	Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY);
	Reference< XControl > xControl=xControlContainer->getControl(OUString::createFromAscii("OptionButton1"));
	Reference< XRadioButton > xRadioButton(xControl,UNO_QUERY);
	if (xRadioButton->getState())  m_nDelta=1;
	xControl=xControlContainer->getControl(OUString::createFromAscii("OptionButton2"));
	Reference< XRadioButton > xRadioButton2(xControl,UNO_QUERY);
	if (xRadioButton2->getState())  m_nDelta=5;
	xControl=xControlContainer->getControl(OUString::createFromAscii("OptionButton3"));
	Reference< XRadioButton > xRadioButton3(xControl,UNO_QUERY);
	if (xRadioButton3->getState())  m_nDelta=10;
	decrement();
	return sal_True;
  }
  if (MethodName.equalsAscii("foo3")){ //setCount
	Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY);
	Reference< XControl > xControl=xControlContainer->getControl(OUString::createFromAscii("NumericField1"));
	Reference< XNumericField > xNumericField(xControl,UNO_QUERY);
	setCount((sal_Int32)xNumericField->getValue());
	return sal_True;  
  } 
  if (MethodName.equalsAscii("foo4")){ //getCount
	Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY);
	Reference< XControl > xControl=xControlContainer->getControl(OUString::createFromAscii("NumericField2"));
	Reference< XNumericField > xNumericField(xControl,UNO_QUERY);
	xNumericField->setValue(getCount());
	return sal_True;
   }
  return sal_False;
}

Comme vous pouvez le constater, seules les méthodes increment() et decrement() cherchent à savoir quel est le bouton radio positionné. Le code de test correspondant est similaire dans les deux méthodes et gagnerait certainement en clareté s'il était mis dans une fonction. Aucun événement n'est déclenché quand on change l'état des boutons radio dans cet exemple. Mais il est possible d'appeler une méthode chaque fois que l'état d'un bouton radio est changé : notez que dans ce cas, il vous faut fournir une méthode par bouton. Examinons cette deuxième possibilité.

Gérer des événements des boutons Radio

Chaque fois qu'un utilisateur coche ou décoche un bouton radio un événement est émis, événement géré par une méthode : vous avez ainsi à éditer chacune des propriétés correspondantes des boutons et les associer à une méthode de composant (voir encore le Developer's Guide). Parceque vous avez trois boutons radio, il vous faudra ajouter trois méthodes. On voit que ce genre de solution peut devenir vite très compliquée quand le nombre de bouton radio croit.

Le code est ainsi le suivant :

// C++
// Listing 3
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 > xControl=xControlContainer->getControl(OUString::createFromAscii("NumericField1"));
	Reference< XNumericField > xNumericField(xControl,UNO_QUERY);
	setCount((sal_Int32)xNumericField->getValue());
	return sal_True;  
  } 
  if (MethodName.equalsAscii("foo4")){ //getCount
	Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY);
	Reference< XControl > xControl=xControlContainer->getControl(OUString::createFromAscii("NumericField2"));
	Reference< XNumericField > xNumericField(xControl,UNO_QUERY);
	xNumericField->setValue(getCount());
	return sal_True;
   }
   if (MethodName.equalsAscii("foo5")){ //incr/decr +/-1
	if (m_nDelta!=1) m_nDelta=1;
	return sal_True;
   }
   if (MethodName.equalsAscii("foo6")){ //incr/decr +/-5
	if (m_nDelta!=5) m_nDelta=5;
	return sal_True;
   }
   if (MethodName.equalsAscii("foo7")){ //incr/decr +/-10
	if (m_nDelta!=10) m_nDelta=10;
	return sal_True;
   }
  return sal_False;
}
 
Sequence< OUString > SAL_CALL MyCounterImpl::getSupportedMethodNames() throw (RuntimeException){
  Sequence< OUString > SeqOUStr(7);
  SeqOUStr[0]=OUString::createFromAscii("foo1");
  SeqOUStr[1]=OUString::createFromAscii("foo2");
  SeqOUStr[2]=OUString::createFromAscii("foo3");
  SeqOUStr[3]=OUString::createFromAscii("foo4");
  SeqOUStr[4]=OUString::createFromAscii("foo5");
  SeqOUStr[5]=OUString::createFromAscii("foo6");
  SeqOUStr[6]=OUString::createFromAscii("foo7");
  return SeqOUStr;
}

Ce code fonctionne sans lire l'état des boutons radio (en ce qui concerne les méthodes associées "foo5" à "foo7"). Je ne peux pas vous certifier cependant que pour toutes les situations que vous rencontrerez dans votre vie de programmeur ce sera ainsi. Notez que l'on vous a déjà fourni le code pour accéder à l'état des boutons radio dans la section précédente. Notez aussi que m_nDelta est une nouvelle donnée membre positionnée à 1 par le constructeur (ce qui correspond au bouton radio sélectionné par défaut).

Utiliser une zone de liste dans une boîte de dialogue

Dans cette section, nous remplaçons les boutons radio par une zone de liste. Jetez un oeil à l'interface com.sun.star.awt.XListBox. On utilisera seulement la méthode getSelectedItemPos() dans le code ci-dessous.

// XDialogEventHandler implementation
// C++
// Listing 4
sal_Bool SAL_CALL MyCounterImpl::callHandlerMethod(const Reference< XDialog >& xDialog,const Any& EventObject,const OUString & MethodName ) throw(WrappedTargetException, RuntimeException ){
  if (MethodName.equalsAscii("foo1")){//increment
        Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY);
	Reference< XControl > xControl=xControlContainer->getControl(OUString::createFromAscii("ListBox1"));
	Reference< XListBox > xListBox(xControl,UNO_QUERY);
	switch (xListBox->getSelectedItemPos()){
	case 0:  m_nDelta=1;break;
	case 1:  m_nDelta=5;break;
	case 2:  m_nDelta=10;break;
	}
	increment();
	return sal_True;
  }
  if (MethodName.equalsAscii("foo2")){//decrement
	Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY);
	Reference< XControl > xControl=xControlContainer->getControl(OUString::createFromAscii("ListBox1"));
	Reference< XListBox > xListBox(xControl,UNO_QUERY);
	switch (xListBox->getSelectedItemPos()){
	case 0:  m_nDelta=1;break;
	case 1:  m_nDelta=5;break;
	case 2:  m_nDelta=10;break;
	}
	decrement();
	return sal_True;
  }
  if (MethodName.equalsAscii("foo3")){ //setCount
	Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY);
	Reference< XControl > xControl=xControlContainer->getControl(OUString::createFromAscii("NumericField1"));
	Reference< XNumericField > xNumericField(xControl,UNO_QUERY);
	setCount((sal_Int32)xNumericField->getValue());
	return sal_True;  
  } 
  if (MethodName.equalsAscii("foo4")){ //getCount
	Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY);
	Reference< XControl > xControl=xControlContainer->getControl(OUString::createFromAscii("NumericField2"));
	Reference< XNumericField > xNumericField(xControl,UNO_QUERY);
	xNumericField->setValue(getCount());
	return sal_True;
   }
  return sal_False;
}

qui nous produit la boîte de dialogue ci-dessous :

Our Third Counter Dialog

Les boîtes de dialogue Multi-Page

Les boîtes de dialogues multi-pages avec OOoBasic sont abordées sous le nom de "boîtes de dialogue à plusieurs onglets". Notre but est d'utiliser une boîte de dialogue multi-page encore et toujours avec notre compteur et ainsi de voir comment les choses fonctionnent. Notre seconde page sera très similaire à la boîte de dialogue toute simple du compteur tandis que la première page gérera les incréments. On passera d'une page à l'autre à l'aide des célèbres boutons "next>>" et "<<previous" (que je n'ai pas traduit). Et puis quant à faire on ajoutera pour la première fois les boutons "OK" et "Cancel" qui n'ont pas à être géré explicitement en fait. Puisque je n'ai jamais géré de dialogue multi-page avant d'écrire cette section, je commence par faire mes premières armes en OOoBasic.

Documentation note.png Comme déjà expliqué, je pense que c'est une bonne pratique de commencer par écrire du OOoBasic avant de résoudre le problème en C++. Avant de commencer ce document en 2004, OOoBasic était déjà documenté dans un guide OOoBASIC (et aussi dans des livres) mais vous n'aviez que quelques petits exemples en C++ dans le SDK. A ma connaissance il n'y a aucun livre C++/UNO. L'autre grande raison est que pour fabriquer les dialogues présentés dans ce chapitre (et le précédent) vous devez lancer l'environnement de développement du OOoBasic. Alors pour écrire un programme OOobasic, il n'y a plus qu'un pas.

Notre programme OOoBasic de départ

Nous commençons par donner notre programme OOoBasic. La première partie du main correspond à la gestion du compteur tandis que la deuxième partie correspond à la gestion du "next>>" et "<<previous" à l'aide de la propriété Step. Cette gestion est réalisée avec deux sous-programmes :

  • un sous-programme "cmdPrev_Initiated",
  • un sous-programme "cmdNext_Initiated".

Quand nous sommes en Step=1 le bouton "<<previous" est inactif tandis qu'en Step=2 c'est au tour du bouton "next>>" d'être desactivé.

REM  *****  BASIC  *****
'Listing 5
Dim Dlg As Object
Dim Count As integer 
Dim IncrDecr As integer
 
Sub Main
  DialogLibraries.LoadLibrary("Standard")
  Dlg = CreateUnoDialog(DialogLibraries.Standard.Dialog2)
'Because of Multi-page don't forget to manage Step :
  Dlg.Model.Step=1
  Dlg.Execute()
  Dlg.dispose()
End Sub
 
'Notre compteur commence ici <-----------
Sub Increment
  Count = Count + IncrDecr
End Sub
 
Sub Decrement
  Count = Count - IncrDecr
End Sub    
 
Sub getCount
   Dim oNumericField
   oNumericField = Dlg.getControl("NumericField2")
   oNumericField.Value = Count
End Sub
 
Sub setCount
   Dim oNumericField
   oNumericField = Dlg.getControl("NumericField1")
   Count = oNumericField.Value
End Sub
' Notre compteur finit ici <------------
 
Sub cmdNext_Initiated
   Dim cmdNext As Object
   Dim cmdPrev As Object
   cmdPrev = Dlg.getControl("cmdPrev")
   'inspect(cmdPrev)
   cmdNext = Dlg.getControl("cmdNext")
   cmdPrev.Model.Enabled = Not cmdPrev.Model.Enabled
   cmdNext.Model.Enabled = False
   'if Next update IncrDecr
   If Dlg.Model.Step = 1 Then
     If Dlg.getControl("OptionButton1").State Then
       IncrDecr = 1
     End If
     If Dlg.getControl("OptionButton2").State Then
       IncrDecr = 5
     End If
     If Dlg.getControl("OptionButton3").State Then
       IncrDecr = 10
     End If    
   End If
   Dlg.Model.Step = Dlg.Model.Step + 1 
End Sub
 
Sub cmdPrev_Initiated
   Dim cmdNext As Object
   Dim cmdPrev As Object
   cmdPrev = Dlg.getControl("cmdPrev")
   cmdNext = Dlg.getControl("cmdNext")
   cmdPrev.Model.Enabled = False
   cmdNext.Model.Enabled = True
   Dlg.Model.Step = Dlg.Model.Step - 1 
End Sub

Notez aussi que les états des boutons radio sont seulement testés quand on clique sur le bouton "Next>>" en Step 1. Les deux pages se présentent ainsi :

Page 1 (Step 1 in OOoBasic code)

où vous pouvez voir que le bouton "<<Previous" est inactif

Page 2 (Step 2 in OOoBasic code)

où c'est maintenant le bouton "Next>>" qui est inactif.

Programme C++

Notre but dans cette section est d'utiliser la même boîte de dialogue multi-page qu'en OOoBasic mais dans un composant C++. Si le travail a été simple en OOoBasic pour la gestion du "Next>>" et "<<Previous", j'ai passé plusieurs heures pour trouver la traduction de la toute simple ligne OOoBasic :

REM  *****  BASIC  *****
Dlg.Model.Step = Dlg.Model.Step + 1

Finalement, la génération automatique de code C++ par le Java Inspector a été mon salut. Le code est ainsi :

// C++
// Listing 6
// XDialogEventHandler implementation
sal_Bool SAL_CALL MyCounterImpl::callHandlerMethod(const Reference< XDialog >& xDialog,const Any& EventObject,const OUString & MethodName ) throw(WrappedTargetException, RuntimeException ){
  if (MethodName.equalsAscii("increment")){//increment
	increment();
	return sal_True;
  }
  if (MethodName.equalsAscii("decrement")){//decrement
	decrement();
	return sal_True;
  }
  if (MethodName.equalsAscii("setCount")){ //setCount
	Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY);
	Reference< XControl > xControl=xControlContainer->getControl(OUString::createFromAscii("NumericField1"));
	Reference< XNumericField > xNumericField(xControl,UNO_QUERY);
	setCount((sal_Int32)xNumericField->getValue());
	return sal_True;  
  } 
  if (MethodName.equalsAscii("getCount")){ //getCount
	Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY);
	Reference< XControl > xControl=xControlContainer->getControl(OUString::createFromAscii("NumericField2"));
	Reference< XNumericField > xNumericField(xControl,UNO_QUERY);
	xNumericField->setValue(getCount());
	return sal_True;
   }
   if (MethodName.equalsAscii("cmdPrev_Initiated")){ //<<Previous
// xDialog is a parameter
	Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY);
	Reference< XControl > xControl=xControlContainer->getControl(OUString::createFromAscii("cmdPrev"));
//com.sun.star.awt.XConrolModel
	Reference< XControlModel> xControlModel= xControl->getModel();
	Any any;
	sal_Bool Boolval;
	Reference< XPropertySet > xPropertySet( xControlModel,UNO_QUERY);
	any=xPropertySet->getPropertyValue(OUString::createFromAscii("Enabled"));
	any >>= Boolval;
	Boolval = !Boolval;
	any <<= Boolval;
	xPropertySet->setPropertyValue(OUString::createFromAscii("Enabled"),any);
 
	xControl=xControlContainer->getControl(OUString::createFromAscii("cmdNext"));
	xControlModel= xControl->getModel();
	Reference< XPropertySet > xPropertySet2( xControlModel,UNO_QUERY);
	any=xPropertySet2->getPropertyValue(OUString::createFromAscii("Enabled"));
	any >>= Boolval;
	Boolval = !Boolval;
	any <<= Boolval;
	xPropertySet2->setPropertyValue(OUString::createFromAscii("Enabled"),any);
 
// found with JavaInspector C++ code generation	:Step property management
	sal_Int32 step;
	Reference< XControl > xControl2 (xDialog,UNO_QUERY_THROW);
	xControlModel= xControl2->getModel(); 
	Reference< XPropertySet > xPropertySet3( xControlModel,UNO_QUERY);
	step = 1;
	any <<= step;
	xPropertySet3->setPropertyValue(OUString::createFromAscii("Step"),any);
	return sal_True;
   }
  if (MethodName.equalsAscii("cmdNext_Initiated")){ //Next>>
// xDialog is a parameter
	Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY);
	Reference< XControl > xControl=xControlContainer->getControl(OUString::createFromAscii("cmdPrev"));
//com.sun.star.awt.XConrolModel
	Reference< XControlModel> xControlModel= xControl->getModel();
	Any any;
	sal_Bool Boolval;
	Reference< XPropertySet > xPropertySet( xControlModel,UNO_QUERY);
	any <<= sal_True;
	xPropertySet->setPropertyValue(OUString::createFromAscii("Enabled"),any);
 
	xControl=xControlContainer->getControl(OUString::createFromAscii("cmdNext"));
	xControlModel= xControl->getModel();
	Reference< XPropertySet > xPropertySet2( xControlModel,UNO_QUERY);
	any <<= sal_False;
	xPropertySet2->setPropertyValue(OUString::createFromAscii("Enabled"),any);
// RadioButton management : not great with such if !
	xControl=xControlContainer->getControl(OUString::createFromAscii("OptionButton1"));
	Reference< XRadioButton > xRadioButton(xControl,UNO_QUERY);
	if (xRadioButton->getState())  m_nDelta=1;
	xControl=xControlContainer->getControl(OUString::createFromAscii("OptionButton2"));
	Reference< XRadioButton > xRadioButton2(xControl,UNO_QUERY);
	if (xRadioButton2->getState())  m_nDelta=5;
	xControl=xControlContainer->getControl(OUString::createFromAscii("OptionButton3"));
	Reference< XRadioButton > xRadioButton3(xControl,UNO_QUERY);
	if (xRadioButton3->getState())  m_nDelta=10;
// Trouvé avec la génération automatique de C++ du JavaInspector 
	sal_Int32 step;
	// L'astuce est ici : ne pas utiliser la ligne ci-après mais la suivante 
	// Reference< XControl >xControl2=xControlContainer->getControl(OUString::createFromAscii("Dialog1"))
	Reference< XControl > xControl2 (xDialog,UNO_QUERY_THROW);
	xControlModel= xControl2->getModel(); 
	Reference< XPropertySet > xPropertySet3( xControlModel,UNO_QUERY);
	step = 2;
	any <<= step;
	xPropertySet3->setPropertyValue(OUString::createFromAscii("Step"),any);
	return sal_True;
   } 
  return sal_False;
}
Sequence< OUString > SAL_CALL MyCounterImpl::getSupportedMethodNames() throw (RuntimeException){
  Sequence< OUString > SeqOUStr(6);
  SeqOUStr[0]=OUString::createFromAscii("increment");
  SeqOUStr[1]=OUString::createFromAscii("decrement");
  SeqOUStr[2]=OUString::createFromAscii("setCount");
  SeqOUStr[3]=OUString::createFromAscii("getCount");
  SeqOUStr[4]=OUString::createFromAscii("cmdNext_Initiated");
  SeqOUStr[5]=OUString::createFromAscii("cmdPrev_Initiated");
  return SeqOUStr;
}

Voir aussi les interfaces com.sun.star.awt.XControl, com.sun.star.awt.XControlContainer, com.sun.star.awt.XControlModel et com.sun.star.beans.XPropertySet.

On va s'intéresser maintenant à un autre exemple qui va nous amener à laisser tomber notre compteur (enfin pas tout à fait) à cause de sa simplicité.

Le nouveau contrôle pour les arborescences

Le contrôle "Tree Contol" est abordé aussi ici mais l'article en question est en construction (depuis 2006). Comme je ne sais pas vraiment comment traduire "Tree Control", je le traduirai par contrôle pour arborescences. Pour parler franchement, je ne suis pas complètement satisfait avec ce contrôle pour le moment pour différentes raisons :

  • le listener detectant l'expension de l'arbre est de mon point de vue incomplet. Si un noeud n'a pas d'enfant et que vous le déroulez, le listener ne sera pas appelé. Cela peut certainement poser des problèmes quand on veut gérer des arbres dynamiques (par exemple dans un outil d'introspection)
  • La gestion de la propriété événement dans le dialogue OpenOffice.org est bien trop simple : pas assez d'événements gérés. Elle permettra donc parfois difficilement le remplacement d'un listener. Par exemple aucun événement de changement de sélection est géré.

Alors vous vous demandez certainement pourquoi je passe du temps à documenter ce contrôle. C'est seulement parce que je pense que s'il n'est pas documenté, il sera peu utilisé, et si les gens l'utilisent peu personne n'aura l'idée d'améliorer son code. Et puis, après un peu de temps passé avec ce contrôle, je me suis aperçu que l'on pouvait gérer les arbres dynamiques tel qu'il est en ajoutant simplement un bouton pour cela.

Le concept entourant tous ces contrôles est une complète séparation des données et du rendu à l'écran. C'est ce qui est connu sous le nom de Model/View/Controller-design (MVC)

Notre programme OOoBasic de départ

rvc44 a posté dans le OOOForum (Thu Dec 20, 2007) un exemple en OOoBasic qui me servira de point de départ. J'ai ajouté un "event listener" et obtenu le morceau de code suivant :

REM  *****  BASIC  *****
'Listing 7
Option Explicit
'Most of this code belongs to rvc44 http://www.oooforum.org/forum/viewtopic.phtml?t=67005&highlight=
Private oDlg as Object, oLbDescription as Object
Private oTreeCtrl as Object, oTreeModel as Object
Private oMutableTreeDataModel  as Object
Private oRootNode as Object, oChildNode as Object, ogNode as Object
 
 
Sub mainTree '[Simple example: collapsing and expanding folders]
   DialogLibraries.loadLibrary("Standard")
 
   'create the dialog
   oDlg = CreateUnoDialog(DialogLibraries.Standard.SimpleTreeDialog)
   If IsNull(oDlg) Then Exit Sub
 
   'get a refrence to the control/s and its model/s
   oLbDescription = oDlg.getControl("lbDescription")
   oLbDescription.setText( "A very simple tree control with listeners." & _
                     chr(13) & chr(13) &_
                     "Expanding and collapsing happends automatically" )
 
   oTreeCtrl = oDlg.getControl("TreeControl1")
   oTreeModel = oTreeCtrl.Model   
 
   'XRay oTreeModel
   'instantiate the MutableTreeDataModel service
   oMutableTreeDataModel = createUnoService(_
         "com.sun.star.awt.tree.MutableTreeDataModel")
   'XRay oMutableTreeDataModel
   'MutableTreeDataModel implemets XMutableTreeDataModel
   'use XMutableTreeNode::createNode(
   '                  [in] any DisplayValue,
   '                  [in] boolean ChildsOnDemand )
   'to create the root node
   'If you pass to the boolean parameter TRUE as a argument,
   'the created node will be treated as a non-leaf (branch) node
   'by the XTreeControl , even when it has no child nodes.
   'If false, the node will be a leaf.
   'The root should obviously be a non-leaf node.
   oRootNode = oMutableTreeDataModel.createNode( "Root", true )  
   'use XMutableTreeNode::setRoot( [in] XMutableTreeNode RootNode )
   'to set this node as the root of the model
   oMutableTreeDataModel.setRoot(oRootNode)
 
   'now create the children of the root
 
   Dim oChildNode1
   oChildNode1 = oMutableTreeDataModel.createNode( "Parent 1", true )
   oRootNode.appendChild(oChildNode1)   
      'Create this child's own children      
            'In two steps:
            Dim oSubChildNode
            oSubChildNode = oMutableTreeDataModel.createNode(_
               "Child 1", true )
            oChildNode1.appendChild(oSubChildNode)
                  'In only one:
                  'Go deeper in the hierarchical structure
                  oSubChildNode.appendChild( _
                     oMutableTreeDataModel.createNode( _
                        "Grandson 1", false ) )
                  oSubChildNode.appendChild( _
                     oMutableTreeDataModel.createNode( _
                        "Grandson 2", false ) )
 
            'In only one:
            oChildNode1.appendChild( oMutableTreeDataModel.createNode( _
                                 "Child 2", true ) )
 
   '=======================================================================
   'As you see, the Data Model always creates the nodes
   '(XMutableDataModel::createNode)
   'and then you add it to the node you want, taking care that
   'the node created and the parent node belong to the same data model
 
   Dim oChildNode2
   oChildNode2 = oMutableTreeDataModel.createNode( "Parent 2", true )
   oRootNode.appendChild(oChildNode2)
 
   Dim oChildNode3
   oChildNode3 = oMutableTreeDataModel.createNode( "Parent 3", FALSE )
   'oChildNode3.setCollapsedGraphicURL( ANY_DOC )
   'oChildNode3.setExpandedGraphicURL( ANY_DOC )
   'oChildNode3.setNodeGraphicURL( BASIC_BRAKE )
   oRootNode.appendChild(oChildNode3)
 
   Dim oChildNode4
   oChildNode4 = oMutableTreeDataModel.createNode( "Parent 4", true )
   oRootNode.appendChild(oChildNode4)
 
   Dim oChildNode5
   oChildNode5 = oMutableTreeDataModel.createNode( "Parent 5", true )
   oRootNode.appendChild(oChildNode5)
 
   Dim oChildNode6
   oChildNode6 = oMutableTreeDataModel.createNode( "Parent 6", FALSE )
   oRootNode.appendChild(oChildNode6)
      'Although the API refrence states the following:
      '   "If you want to add child nodes to your tree on demand
      '   you can .[..] Make sure the parent node returns true
      '   for XTreeNode::hasChildsOnDemand() either by implementing
      '   XTreeNode yourself or, if you use the MutableTreeDataModel ,
      '   use XMutableTreeNode::setHasChildsOnDemand() ."
      'http://api.openoffice.org/docs/common/ref/com/sun/star/awt/tree/TreeControl.html
      Dim bHasChildernOnDemand as Boolean
      'in this case hasChildsOnDemand returns FALSE
      'bHasChildernOnDemand = oChildNode6.hasChildsOnDemand()
      Dim aChild : aChild = oMutableTreeDataModel.createNode( "Grandson", FALSE )
      'BUT the node is appended to this node that has no children on demand
      oChildNode6.appendChild(aChild)
      'Should it throw an exception?
 
   'set the data model at the TreeControlModel::DataModel property.
   oTreeModel.DataModel = oMutableTreeDataModel
 
   ' oTreeCtrl.DefaultExpandedGraphicURL = FOLDER_OPEN
   'oTreeCtrl.DefaultCollapsedGraphicURL = FOLDER_CLOSED
 
   Dim oListener,oListener2
   oListener = CreateUnoListener("Tree_","com.sun.star.awt.tree.XTreeExpansionListener")
   oListener2 = createUnoListener("Select_","com.sun.star.view.XSelectionChangeListener")
   'Xray oListener2
   oTreeCtrl.addTreeExpansionListener(oListener)
   oTreeCtrl.addSelectionChangeListener(oListener2)
   oDlg.execute()
   oTreeCtrl.removeSelectionChangeListener(oListener2)
   oTreeCtrl.removeTreeExpansionListener(oListener)
   oDlg.dispose()
End Sub
 
Sub Tree_treeExpanding(oEvt)
'oEvt.Node has methods :
  'getChildAt 	Returns the child tree node at Index .  
  'getChildCount 	Returns the number of child nodes.  
  'getParent 	Returns the parent node of this node.  
  'getIndex 	Returns the index of Node in this instances children.  
  'hasChildsOnDemand 	Returns true if the childs of this node are created on demand.  
  'getDisplayValue 	If not empty, the textual representation of this any is used as the text part of this node.  
  'getNodeGraphicURL 	The URL for a graphic that is rendered before the text part of this node.  
  'getExpandedGraphicURL 	The URL for a graphic that is rendered to visualize expanded non leaf nodes.  
  'getCollapsedGraphicURL 	The URL for a graphic that is rendered to visualize collapsed non leaf nodes.
   MsgBox("Node : " & oEvt.Node.getDisplayValue &" expanding")
   ogNode = oEvt.Node
end sub
Sub Tree_treeExpanded(oEvt)
'   MsgBox("Tree expanded")
end sub
Sub Tree_treeCollapsing(oEvt)
   'MsgBox("Tree collapsing")
end sub
Sub Tree_treeCollapsed(oEvt)
'   MsgBox("Tree collapsed")
end sub
Sub Tree_requestChildNodes(oEvt)
  'print oEvt.Node.getIndex(oEvt.Node)
    'XRay oEvt.Node
'   Msgbox ("Tree_requestChildNodes")
End Sub
Sub Tree_disposing(oEvt)
'   Msgbox ("Tree_disposing")
End Sub   
Sub Select_selectionChanged(oEvt)
  MsgBox("Selection Changed")
end sub

Voici une copie d'écran

Notre contrôle d'arborescance avec treeExpanding listener en fonctionnement

qui montre le fonctionnement d'un listener déclenché par une expansion d'un noeud, et une deuxième avec un autre listener déclenché (changement de sélection).

Our tree control with selectionChanged listener messageBox

Si vous voulez construire la même chose avec un autre langage de programmation, il vous faut vous investir dans les interfaces correspondantes :

Il est grand temps d'examiner la traduction en C++ de cet exemple.

Code C++ sans Listener

Comme d'habitude nous voulons utiliser la boîte de dialogue OOoBasic précédente mais avec du C++. Parce que nous voulons seulement un exemple démonstratif, nous allons simplifier l'arbre (couper quelques branches). Notre but est de retrouver le noeud sélectionné, mais comme mentionné dans le titre, sans listener. Cela signifie donc que nous voulons gérer les événements du contrôle d'arborescence seulement avec des fonctions membres. Nous allons essayer d'abord en OOoBasic.

Gérer les événements du contrôle d'arborescence avec OOoBasic

Notre but est simple : retrouver le noeud sélectionné de l'arborescence. On commence par retirer tous les listener du code OOoBasic :

REM  *****  BASIC  *****
'Listing 8
' code retiré pour simplification
...
 
Sub mainTree '[Simple example: collapsing and expanding folders]
   ' code retiré pour simplification
   .....
   'Dim oListener,oListener2
   'oListener = CreateUnoListener("Tree_","com.sun.star.awt.tree.XTreeExpansionListener")
   'oListener2 = createUnoListener("Select_","com.sun.star.view.XSelectionChangeListener")
   'Xray oListener2
   'oTreeCtrl.addTreeExpansionListener(oListener)
   'oTreeCtrl.addSelectionChangeListener(oListener2)
   oDlg.execute()
   'oTreeCtrl.removeSelectionChangeListener(oListener2)
   'oTreeCtrl.removeTreeExpansionListener(oListener)
   oDlg.dispose()
End Sub

Tout ce qui gère les listeners est mis en commentaire. Ensuite on choisit parmi les événements montrés ci-dessous :

The managable Events of a Tree Control

où vous pouvez voir que j'ai choisi l'événement "perte de focus" avec le sous-programme :

REM  *****  BASIC  *****
'Listing 9
Sub LosingFocus(Event As Object)
  'inspect(oTreeCtrl)
  'getSelection is member fuction of com.sun.star.awt.tree.XTreeControl interface
  'getDisplayValue is member function of com.sun.star.awt.tree.XTreeNode interface
  MsgBox(oTreeCtrl.getSelection.getDisplayValue())
End Sub

Si vous déroulez l'arbre et sélectionnez un noeud, cliquez dans le contrôle d'édition de texte pour voir automatiquement le résultat

Retrouver le nom du noeud sélectionné

où vous voyez que le sous-programme "LosingFocus" ci-dessus a été déclenché.

Gérer les événements avec des fonctions membres C++

Nous décidons de changer le programme OOoBasic précédent en quatre points :

  1. On simplifie l'arbre affiché,
  2. nous affichons ce qui est sélectionné dans le contrôle texte déjà existant plutôt que dans une boîte de message,
  3. parceque nous traitons encore et toujours de composants (et non pas de addon) il nous faut encore partir d'un fichier IDL. J'ai gardé le fichier IDL du compteur même si ma nouvelle boîte de dialogue ne me permet plus de le faire fonctionner. J'ai fait cela par paresse parce que dans ce genre de situation je ne vois pas très bien quelle interface sortir.
  4. l'événement "losing focus" est lié à une méthode appelée "LosingFocus" (voir le Developper's Guide pour savoir comment faire).

Notre nouveau constructeur

Le constructeurr doit afficher un arbre dans le contrôle correspondant. Pour dire les choses différemment nous allons retrouver la plus grande partie du sous-programme OOoBasic "mainTree" dans notre constructeur. Le code est ainsi :

// cpp
// Listing 10
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 );
// Uncomment below and comment above if you want the OOoDesktop as parent of your dialog
//	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:Standard.SimpleTreeDialog?location=application" ),
		(XCountable *)this);
// Constructing the Tree (17th Jul 2009)
	Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY);
	Reference< XControl > xControl = xControlContainer->getControl(OUString::createFromAscii("TreeControl"));
	Reference< XMutableTreeDataModel > xTreeMutDatMod (m_xMCF->createInstanceWithContext(
			OUString::createFromAscii("com.sun.star.awt.tree.MutableTreeDataModel"),xContext),UNO_QUERY_THROW);
//	Reference< XMutableTreeDataModel > xTreeMutDatMod(xTreeControl,UNO_QUERY); //NO NO NO:hangs the office
	Any any;
	any <<= OUString::createFromAscii("root");
	Reference< XMutableTreeNode > xMTN=xTreeMutDatMod->createNode(any,sal_True);
	xTreeMutDatMod->setRoot(xMTN);
	any <<= OUString::createFromAscii("Parent 1");
	Reference< XMutableTreeNode > xChildNode = xTreeMutDatMod->createNode(any,sal_True);
	xMTN->appendChild(xChildNode);
		any <<= OUString::createFromAscii("Child 1");
		Reference< XMutableTreeNode > xChildNode2 = xTreeMutDatMod->createNode(any,sal_True);
		xChildNode->appendChild(xChildNode2);
			any <<= OUString::createFromAscii("Grandson 1");
			Reference< XMutableTreeNode > xChildNode3 = xTreeMutDatMod->createNode(any,sal_False);
			xChildNode2->appendChild(xChildNode3);
			any <<= OUString::createFromAscii("Grandson 2");
			xChildNode3 = xTreeMutDatMod->createNode(any,sal_False);
			xChildNode2->appendChild(xChildNode3);
		any <<= OUString::createFromAscii("Child 2");
		xChildNode2 = xTreeMutDatMod->createNode(any,sal_True);
		xChildNode->appendChild(xChildNode2);
	any <<=  OUString::createFromAscii("Parent 2");
	xChildNode=xTreeMutDatMod->createNode(any,sal_True);
	xMTN->appendChild(xChildNode);
// positionner le model pour voir l'arbre control (deux heures pour trouver les 4 lignes qui suivent)
// traduction de oTreeModel = oTreeCtrl.Model et oTreeModel.DataModel = oMutableTreeDataModel
        Reference< XControlModel > xControlModel=xControl->getModel();
	Reference< XPropertySet > xPropertySet( xControlModel,UNO_QUERY);
	any <<= xTreeMutDatMod;
	xPropertySet->setPropertyValue(OUString::createFromAscii("DataModel"),any);
// execute dialog
	xDialog->execute();
	}

La méthode "LosingFocus"

Le sous-programme "LosingFocus" en OOoBasic était très simple. Je l'ai traduit à l'aide d'un outil d'Introspection et la documentation IDL comme com.sun.star.awt.tree.XTreeControl, com.sun.star.awt.tree.XTreeNode et com.sun.star.awt.XTextComponent.

// C++
// Listing 11
sal_Bool SAL_CALL MyCounterImpl::callHandlerMethod(const Reference< XDialog >& xDialog,const Any& EventObject,const OUString & MethodName ) throw(WrappedTargetException, RuntimeException ){
  if (MethodName.equalsAscii("LosingFocus")){//When Losing Focus in Tree control
        Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY);
	Reference< XControl > xControl = xControlContainer->getControl(OUString::createFromAscii("TreeControl"));
// com.sun.star.awt.tree.XTreeControl
        Reference< XTreeControl > xTreeControl(xControl,UNO_QUERY);
	Any any=xTreeControl->getSelection();
	Reference< XTreeNode > xTreeNode;
        any >>= xTreeNode;
	// met le resultat de selection dans le control de texte
	xControl = xControlContainer->getControl(OUString::createFromAscii("lbDescription"));
        Reference< XTextComponent > xTextComponent(xControl,UNO_QUERY);
	any = xTreeNode->getDisplayValue();
	OUString OUStr;
 	any >>= OUStr;
	xTextComponent->setText(OUStr + OUString::createFromAscii(" -- DONE --"));
	return sal_True;
  }
  return sal_False;
}

Cette méthode est appelée lors de la perte du focus : en cliquant dans le contrôle de texte par exemple.

Voici une copie d'écran de la boîte de dialogue :

Le nouveau contrôle d'arborescence en fonctionnement

Parceque vous ne gérez plus qu'un seul événement, il vous faut aussi changer la fonction getSupportedMethodNames() comme suit :

// C++
// Listing 12
Sequence< OUString > SAL_CALL MyCounterImpl::getSupportedMethodNames() throw (RuntimeException){
  Sequence< OUString > SeqOUStr(1);
  SeqOUStr[0]=OUString::createFromAscii("LosingFocus");
//  SeqOUStr[1]=OUString::createFromAscii("foo2");
//  SeqOUStr[2]=OUString::createFromAscii("foo3");
//  SeqOUStr[3]=OUString::createFromAscii("foo4");
  return SeqOUStr;
}
Documentation caution.png Je ne suis pas sûr du fonctionnement correct de xml2cmp sensé travailler avec le fichier CppComponent.uno.xml pour un type comme :
<type>com.sun.star.awt.tree.XTreeControl</type>

La profondeur des modules est-elle trop grande pour lui dans ce cas ? J'ai déplacé la création des fichier hpp dans le MakeFile pour que cela fonctionne. SergeMoutou 16:30, 17 July 2009 (UTC)

C++ avec listener

Si j'adore utiliser les listeners avec OOoBasic je ne les porte pas dans mon coeur en C++ : ils sont faciles à utiliser en OOoBasic mais difficiles en C++. Je les ai déjà utilisés à plusieurs reprises mais bon ceci ne sera pas documenté avant un certain temps. Si quelqu'un veut s'y coller, c'est sans refus. SergeMoutou 12:54, 25 July 2009 (UTC)

A FAIRE

Les arbres dynamiques

Dans cette section, nous voulons examiner la possibilité de gérer des arbres dynamiques. Un arbre dynamique est un arbre qui est construit à la demande. C'est un sujet qui me tenait à coeur parce que je me suis toujours dit que l'on ne pouvait pas faire de choses sérieuses sans cela. Avant d'écrire les deux sections suvantes j'ignorai si cela pouvait être fait ou pas !

Commençons encore à l'aide du langage OOoBasic.

Arbres dynamiques en OOoBasic

En fait historiquement, j'ai commencé par d'abord écrire la section suivante. Mais suite aux nombreux problèmes rencontrés je suis revenu sur cette section. Après plusieurs heures passées je n'ai pas été capable de le faire fonctionner en C++ alors que cela m'a pris seulement 10 minutes en OOoBasic ! Pour votre information, j'ai fini par le faire fonctionner aussi en C++.

Je garde encore la boîte de dialogue initiale et ajoute deux boutons : un bouton "OK" et un bouton "add Child". Vous sélectionnez un noeud et quand vous cliquez sur "add Child", un enfant est ajouté. Son nom est le nom du noeud sélectionné suivi de ".n" où n est le nombre d'enfants (déjà créés). C'est un arbre dynamique parce que sa taille n'est pas fixée. Son code est le suivant :

REM  *****  BASIC  *****
'Listing 14
Option Explicit
 
Private oDlg as Object, oLbDescription as Object
Private oTreeCtrl as Object, oTreeModel as Object
Private oMutableTreeDataModel  as Object
Private oRootNode as Object, oChildNode as Object, ogNode as Object
 
 
Sub mainTree '[Simple example: collapsing and expanding folders]
   DialogLibraries.loadLibrary("Standard")
 
   oDlg = CreateUnoDialog(DialogLibraries.Standard.SimpleTreeDialog)
   If IsNull(oDlg) Then Exit Sub
 
   oTreeCtrl = oDlg.getControl("TreeControl1")
   oTreeModel = oTreeCtrl.Model   
 
   oMutableTreeDataModel = createUnoService(_
         "com.sun.star.awt.tree.MutableTreeDataModel")
 
   oRootNode = oMutableTreeDataModel.createNode( "Root", true )  
 
   oMutableTreeDataModel.setRoot(oRootNode)
 
   Dim oChildNode1
   oChildNode1 = oMutableTreeDataModel.createNode( "Parent 1", true )
   oRootNode.appendChild(oChildNode1)   
      'Create this child's own children      
            'In two steps:
            Dim oSubChildNode
            oSubChildNode = oMutableTreeDataModel.createNode(_
               "Child 1", true )
            oChildNode1.appendChild(oSubChildNode)
                  'In only one:
                  'Go deeper in the hierarchical structure
                  oSubChildNode.appendChild( _
                     oMutableTreeDataModel.createNode( _
                        "Grandson 1", false ) )
                  oSubChildNode.appendChild( _
                     oMutableTreeDataModel.createNode( _
                        "Grandson 2", false ) )
 
            'In only one:
            oChildNode1.appendChild( oMutableTreeDataModel.createNode( _
                                 "Child 2", true ) )
 
   Dim oChildNode2
   oChildNode2 = oMutableTreeDataModel.createNode( "Parent 2", true )
   oRootNode.appendChild(oChildNode2)
 
   Dim oChildNode3
   oChildNode3 = oMutableTreeDataModel.createNode( "Parent 3", FALSE )
   oRootNode.appendChild(oChildNode3)
 
   Dim oChildNode4
   oChildNode4 = oMutableTreeDataModel.createNode( "Parent 4", true )
   oRootNode.appendChild(oChildNode4)
 
   Dim oChildNode5
   oChildNode5 = oMutableTreeDataModel.createNode( "Parent 5", true )
   oRootNode.appendChild(oChildNode5)
 
   Dim oChildNode6
   oChildNode6 = oMutableTreeDataModel.createNode( "Parent 6", FALSE )
   oRootNode.appendChild(oChildNode6)
      Dim bHasChildernOnDemand as Boolean
      Dim aChild : aChild = oMutableTreeDataModel.createNode( "Grandson", FALSE )
      oChildNode6.appendChild(aChild)
 
   oTreeModel.DataModel = oMutableTreeDataModel
 
   oDlg.execute()
   oDlg.dispose()
End Sub
 
Sub addChild(Event As Object)
  Dim oParentNode, oChildNode
  Dim nb as long
  Dim oChildName as String
  oParentNode = oTreeCtrl.getSelection()
  nb = oParentNode.getChildCount()
  nb=nb+1 'I want numerotation begins with 1
  oChildName = oParentNode.getDisplayValue() + "." + nb
  oChildNode = oMutableTreeDataModel.createNode(oChildName , TRUE )
  oParentNode.appendChild(oChildNode)
  oLbDescription = oDlg.getControl("lbDescription")
  oLbDescription.setText(oChildName)
End Sub

Le sous-programme "addChild" est lié au bouton correspondant (pas difficile, ils ont même nom). Voici donc une copie d'écran du résultat :

Arbre dynamique dans un contrôle d'arborescence (OOoBasic)

Question : quel est le dernier noeud créé ? Réponse dans le contrôle de texte : "Root.7.1.2".

Voyons maintenant ce que cela donne en C++.

Arbres dynamiques en C++

Je parts de l'exemple déjà traité(C++) avec le même fonctionnement. Quand un noeud est sélectionné, le bouton "add Child" créé un noeud enfant avec nodename.i comme nom, où i est le nombre d'enfants et nodename le nom du noeud. Pour la mise au point, j'ai aussi mis le nom du noeud ajouté dans le contrôle de texte.

Notre nouveau constructeur

Le constructeur de la section précédente est légèrement modifié. Voici comment :

// c++
// Listing 15
// added this constructor 06/06/09 
    inline MyCounterImpl( Reference< XComponentContext > const & xContext) throw ()
        : m_xContext( xContext ) {
	m_xMCF=m_xContext->getServiceManager();
//	Reference< XMutableTreeDataModel > xTreeMutDatMod(xTreeControl,UNO_QUERY); //NO NO NO:hangs the office : see below
	Reference< XMutableTreeDataModel > xMutTreeDatMod(m_xMCF->createInstanceWithContext(
			OUString::createFromAscii("com.sun.star.awt.tree.MutableTreeDataModel"),m_xContext),UNO_QUERY_THROW);
	m_xMutTreeDatMod = xMutTreeDatMod;
// If you want to use java Inspector
	Reference< XInstanceInspector > xInspector(m_xMCF->createInstanceWithContext( 
            					OUString( RTL_CONSTASCII_USTRINGPARAM( "org.openoffice.InstanceInspector" ) ),
						m_xContext),
						UNO_QUERY_THROW );
	m_xInspector = xInspector;	
	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< 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:Standard.SimpleTreeDialog?location=application" ),
		(XCountable *)this);
// Constructing the Tree 21th Jul 2009 (dynamic Tree)
	Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY);
	Reference< XControl > xControl = xControlContainer->getControl(OUString::createFromAscii("TreeControl"));
 
	Any any;
	any <<= OUString::createFromAscii("root");
	Reference< XMutableTreeNode > xMTN=m_xMutTreeDatMod->createNode(any,sal_True);
	m_xMutTreeDatMod->setRoot(xMTN);
	any <<= OUString::createFromAscii("Parent 1");
	Reference< XMutableTreeNode > xChildNode = m_xMutTreeDatMod->createNode(any,sal_True);
	xMTN->appendChild(xChildNode);
		any <<= OUString::createFromAscii("Child 1");
		Reference< XMutableTreeNode > xChildNode2 = m_xMutTreeDatMod->createNode(any,sal_True);
		xChildNode->appendChild(xChildNode2);
			any <<= OUString::createFromAscii("Grandson 1");
			Reference< XMutableTreeNode > xChildNode3 = m_xMutTreeDatMod->createNode(any,sal_False);
			xChildNode2->appendChild(xChildNode3);
			any <<= OUString::createFromAscii("Grandson 2");
			xChildNode3 = m_xMutTreeDatMod->createNode(any,sal_False);
			xChildNode2->appendChild(xChildNode3);
		any <<= OUString::createFromAscii("Child 2");
		xChildNode2 = m_xMutTreeDatMod->createNode(any,sal_True);
		xChildNode->appendChild(xChildNode2);
	any <<=  OUString::createFromAscii("Parent 2");
	xChildNode=m_xMutTreeDatMod->createNode(any,sal_True);
	xMTN->appendChild(xChildNode);
// set the model to see the tree in control (two hours to find four lines below)
// translation of oTreeModel = oTreeCtrl.Model and oTreeModel.DataModel = oMutableTreeDataModel
        Reference< XControlModel > xControlModel=xControl->getModel();
	Reference< XPropertySet > xPropertySet( xControlModel,UNO_QUERY);
	any <<= m_xMutTreeDatMod;
	xPropertySet->setPropertyValue(OUString::createFromAscii("DataModel"),any);
// execute dialog
	xDialog->execute();
	}
// end of constructor

Le nouvau sous-programme "callHandlerMethod"

Comme cela peut être facilement vu, la seule méthode utilisée est "addChild" :

// C++
// Listing 16
// XDialogEventHandler implementation
sal_Bool SAL_CALL MyCounterImpl::callHandlerMethod(const Reference< XDialog >& xDialog,const Any& EventObject,const OUString & MethodName ) throw(WrappedTargetException, RuntimeException ){
  if (MethodName.equalsAscii("LosingFocus")){//When Losing Focus in Tree control
	// not used in this example
	return sal_True;
  }
  if (MethodName.equalsAscii("addChild")){//add a child to the selected node
// First retrieve the selection
        Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY);
	Reference< XControl > xControl = xControlContainer->getControl(OUString::createFromAscii("TreeControl"));	
	// com.sun.star.awt.tree.XTreeControl
        Reference< XTreeControl > xTreeControl(xControl,UNO_QUERY);
	Any any=xTreeControl->getSelection();
	// Parent
	Reference< XMutableTreeNode > xParentNode;
	any >>= xParentNode;	
// adding
	sal_Int32 nb=xParentNode->getChildCount();
	// Child
	OUString OUStr;
 
	any=xParentNode->getDisplayValue();
	any >>= OUStr;
	OUStr = OUStr + OUString::createFromAscii(".") + OUString::valueOf((sal_Int32)++nb);
// put the new node name in text control
	xControl = xControlContainer->getControl(OUString::createFromAscii("lbDescription"));
        Reference< XTextComponent > xTextComponent(xControl,UNO_QUERY);
	xTextComponent->setText(OUStr);
	any <<= OUStr;
	Reference< XMutableTreeNode > xChildMutTreeNode = m_xMutTreeDatMod->createNode(any ,sal_True);
// Introspection
//	any <<= xChildMutTreeNode;
//	m_xInspector->inspect(any,OUString::createFromAscii("Inspector"));        
	xParentNode->appendChild(xChildMutTreeNode);
	return sal_True;
  }
  return sal_False;
}
Sequence< OUString > SAL_CALL MyCounterImpl::getSupportedMethodNames() throw (RuntimeException){
  Sequence< OUString > SeqOUStr(2);
  SeqOUStr[0]=OUString::createFromAscii("LosingFocus");
  SeqOUStr[1]=OUString::createFromAscii("addChild");
  return SeqOUStr;
}

Voici une capture d'écran

Arbre dynamique dans un contrôle d'arborescence en C++

où vous pouvez distinguer tous les nouveaux noeuds ajoutés.

Page d'accueil

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

See also

Personal tools