Envoyé par : Epeios
Date : 24/08/2006 16:53
J'ai une page XUL dans lequel j'ai un élément listbox
. Je voudrais appeler la propriété selectItem()
de cet élément en C++. Un coup d'oeuil à http://www.xulplanet.com/references/elem(..) m'indique qu'un listbox
hérite de l'interface nsIDOMXULMultiSelectControlElement
. J'ai donc, dans le fichier .idl de mon composant, crée une méthode qui prend comme paramètre un nsIDOMXULMultiSelectControlElement
. Coté javascript, je donne comme valeur à ce paramètre ce qui m'est retourné par un getElementById()
sur l'id de mon élément listbox
. A noter qu'un getElementById()
retourne toujours un objet de type Element
que j'ai, jusqu'à présent, toujours pu récupèrer dans mon composant XPCOM sous forme de nsIDOMElement *
.
En consultant le fichier d'entête correspondant à mon nsIDOMXULMultiSelectControlElement
(nsIDOMXULMultSelectCntrlEl.h), je constate que la méthode SelectItem()
nécessite un nsIDOMXULSelectControlItemElement *
. Qu'à cela ne tienne, la page http://www.xulplanet.com/references/elem(..) m'indique qu'un élement listitem
hérite justement (c'était prévisible) de l'interface nsIDOMXULSelectControlItemElement
. Je crée donc, (toujours en C++), toutes les entrées de ma listbox
à coup de CreateElement("listitem")
(qui, soit dit en passant, retourne un nsIDOMElement *
) et d'AppendChild()
. Puis je passe l'entrée, qui doit apparaître comme sélectionnée dans ma listbox
, à la méthode SelectItem()
du nsIDOMXULMultiSelectControlElement *
correspondant à ladite listbox
. Comme j'ai obtenu l'entrée en question sous forme de nsIDOMElement *
à l'aide de la méthode CreateElement("listitem")
, je la caste d'autorité en nsIDOMXULSelectControlItemElement *
, le type requis par la méthode SelectItem()
.
Je compile le tout, installe mon composant, essaye mon extension et ... ça fonctionne !
Bon, me dis-je, pour simplifier l'appel en javascript à la méthode de mon composant XPCOM, je vais, non pas passer l'élément correspondant à la listbox
, mais son id, ce qui me permet de déporter la fonction getElementbyId()
du javascript vers le C++. Et je caste le nsIDOMElement *
obtenu par le GetElementById()
en C++ en nsIDOMXULMultiSelectControlElement *
, sur le même principe que précédemment où j'ai casté le nsIDOMElement *
obtenu par le CreateElement("listitem")
en nsIDOMXULSelectControlItemElement *
.
Pareil. Je compile le tout, installe mon composant, essaye mon extension et ... ça plante !
Bon, me direz-vous, ce n'est pas grave, je n'ai qu'à utiliser la méthode qui fonctionne ! Sauf que, évidemment, ce plantage est probablement dû au castage de mon nsIDOMElement *
en nsIDOMXULMultiSelectControlElement *
, ce qui me fait penser que c'est à éviter. Or, comme dit plus haut, je caste sur le même principe de nsIDOMElement *
en nsIDOMXULSelectControlItemElement *
. Et si ce castage ne plante pas, c'est parce que, probablement, nsIDOMXULSelectControlItemElement
hérite directement de nsIDOMXULElement
(qui lui-même hérite directement nsIDOMElement
), alors que nsIDOMXULMultiSelectControlElement
n'hérite pas directement de nsIDOMXULElement
, mais par l'intermédiaire de nsIDOMXULSelectControlElement
.
Voilà donc mes questions :
nsIDOMElement *
d'une listbox
, ou de l'id de cette même listbox
, le nsIDOMXULMultiSelectControlElement *
correspondant ?
Merci d'avance pour vos réponses ...
Envoyé par : laurentj
Date : 25/08/2006 16:54
euh... tu "castes" comment ? Tu nous fais un caste c++ classique, ou tu passes bien par un queryInterface/nsCOMPtr ? On peut voir un bout de code sur tes casts ?
Envoyé par : Epeios
Date : 25/08/2006 21:28
Je fais un bête cast C++. Mais, comme dit, bien que ça marche dans certaines situations, je me doute bien que ce n'est pas la bonne méthode ...
Pour fixer les idées, voilà la le contenu de mes différents fichiers, expurgé de tout ce qui ne concerne pas directement mon problème :
Fichier test.xul :
<window onload="MyComp.SetUI( document, document.getElementById('myListBox'), 'myListBox' );"> <listbox id="myListBox"/> </window>
Fichier test.idl :
interface mycomp : nsISupports { void SetUI( in nsIDOMDocument Document, in nsIDOMXULMultiSelectControlElement ListBoxAsElement, in string ListBoxId ); }
Fichier test.cpp
NS_IMETHODIMP mycomp::SetUI( nsIDOMDocument *Document, nsIDOMXULMultiSelectControlElement *ListBoxAsElement, const char *ListBoxId ) { nsIDOMXULMultiSelectControlElement *ListBox; nsIDOMXULSelectControlItemElement *ListItem, *SelectedListItem; nsIDOMElement *Element; #if 0 // Ne fonctionne pas (et j'aimerais savoir comment faire pour que cela fonctionne). Document->GetElementById( ListBoxId, &Element ); ListBox = (nsIDOMXULMultiSelectControlElement *)Element; #else // Fonctionne ! ListBox = ListBoxAsElement; #endif Document->CreateElement( "listitem", &Element ); // Ca fonctionne, mais ça ne doit pas ListItem = (nsIDOMXULSelectControlItemElement *)Element; // être la bonne manière de procèder ! ListBox->AppendChild( ListItem ); Document->CreateElement( "listitem", &Element ); SelectedListItem = ListItem = (nsIDOMXULSelectControlItemElement *)Element; ListBox->AppendChild( ListItem ); Document->CreateElement( "listitem", &Element ); ListItem = (nsIDOMXULSelectControlItemElement *)Element; ListBox->AppendChild( ListItem ); ListBox->SelectItem( SelectedListItem ); }
Par souci de simplification, j'ai omis certaines portions de codes, comme par exemple celui qui transforme un const char *
en un type accepté par les méthodes CreateElement()
et GetDocumentById()
.
Envoyé par : Paul Rouget
Date : 26/08/2006 11:11
Je pense qu'il faut que tu passes par un QueryInterface pour faire ça proprement. Un truc dans le genre:
nsresult rv = Element->QueryInterface(IID_DOMXULMultiSelectControlElement, &ListBox);
Envoyé par : Epeios
Date : 26/08/2006 17:49
Merci ! Le GetElementById
fonctionne bien mieux maintenant, bien que j'ai dû remplacer IID_DOMXULMultiSelectControlElement
par nsIDOMXULMultiSelectControlElement::GetIID()
. Par contre, j'ai des difficultés avec CreateElement()
. Quand j'essaye de récupèrer l'interface nsIDOMXULSelectControlItemElement
sur un élement crée avec CreateElement("listitem")
, j'obtiens une erreur comme quoi une telle interface n'existe pas ! Pourtant, un listitem
implémente bien cette interface !
Je remets le code C++ de mon précédent message, mais modifié pour utliser QueryInterface
, et dans lequel le QueryInterface
qui accompagne le CreateElement()
échoue.
Fichier test.cpp
NS_IMETHODIMP mycomp::SetUI( nsIDOMDocument *Document, nsIDOMXULMultiSelectControlElement *ListBoxAsElement, const char *ListBoxId ) { nsIDOMXULMultiSelectControlElement *ListBox; nsIDOMXULSelectControlItemElement *SelectedListItem; nsIDOMElement *Element; #if 1 // Version fonctionnelle utilisant l'id de la 'listbox'. Document->GetElementById( ListBoxId, &Element ); Element->QueryInterface( nsIDOMXULMultiSelectControlElement::GetIID(), (void **)&Listbox ); #else // Fonctionne aussi, mais moins intéressant ! ListBox = ListBoxAsElement; #endif Document->CreateElement( "listitem", &Element ); ListBox->AppendChild( Element ); Document->CreateElement( "listitem", &Element ); ListBox->AppendChild( ListItem ); #if 0 // Ne fonctionne pas, à ma grande surprise ! int ns_error = Element->QueryInterface( nsIDOMXULSelectControlItemElement::GetIID(), (void **)&SelectedListItem ); // On se retrouve avec 'NS_NOINTERFACE' dans 'ns_error' !!! #else // Fonctionne, mais vraiment pas propre est probablement pas généralisable ! SelectedListItem = (nsIDOMXULSelectControlItemElement *)Element; #endif Document->CreateElement( "listitem", &Element ); ListBox->AppendChild( Element ); ListBox->SelectItem( SelectedListItem ); }
Envoyé par : laurentj
Date : 29/08/2006 11:15
Beurk ! il faut absolument utiliser les nsCOMPtr, pour éviter les leak, et pour faciliter l'ecriture. Et pas de cast c++ traditionnel avec les xpcom !
Et puis pas de const char *. Utilise les nsString. Consulte les interfaces IDL des objets que tu utilises :-p
Et donc c'est normal tout tes trucs qui fonctionnent pas.
Voici la bonne façon de faire :
/* void setUI(in nsIDOMDocument Document, in nsIDOMXULMultiSelectControlElement ListBoxAsElement, in AString ListBoxId) */ NS_IMETHODIMP mycomp::SetUI( nsIDOMDocument *Document, nsIDOMXULMultiSelectControlElement *ListBoxAsElement, const nsAString & ListBoxId ) { nsCOMPtr<nsIDOMXULMultiSelectControlElement> ListBox; nsCOMPtr<nsIDOMXULSelectControlItemElement> SelectedListItem; nsCOMPtr<nsIDOMElement> Element; nsresult rv; Document->GetElementById( ListBoxId, getter_AddRefs(Element) ); ListBox= do_QueryInterface(Element); Document->CreateElement( NS_LITERAL_STRING("listitem"), getter_AddRefs(Element)); ListBox->AppendChild( Element ); Document->CreateElement( NS_LITERAL_STRING("listitem"), getter_AddRefs(Element)); ListBox->AppendChild( Element ); SelectedListItem = do_QueryInterface(Element, &rv); NS_ENSURE_SUCCESS(rv,rv); Document->CreateElement( NS_LITERAL_STRING("listitem"), getter_AddRefs(Element) ); ListBox->AppendChild( Element ); ListBox->SelectItem( SelectedListItem ); return NS_OK; // pas l'oublier celui là !! }
Ahh tout de suite, c'est plus propre ;-)
La convention veut aussi que les paramètres commencent tous par "a", (et les propriétés de ta classe, par "m"). On devrait donc avoir :
NS_IMETHODIMP mycomp::SetUI( nsIDOMDocument *aDocument, nsIDOMXULMultiSelectControlElement *aListBoxAsElement, const nsAString & aListBoxId )
Je te conseille de lire cette page et tous les liens qui y sont répertorier, pour apprendre à utiliser l'api XPCOM/gecko : http://www.mozilla.org/projects/xpcom/
Envoyé par : Epeios
Date : 29/08/2006 17:18
laurentj a écrit:
Beurk ! il faut absolument utiliser les
[nsCOMPtr|http://www.mozilla.org/projects/xpcom/ns
COMPtr.html], pour éviter les leak, et pour
faciliter l'ecriture.
Je voulais éviter de surcharger le code d'exemple, pour me concentrer sur l'essentiel (comme je l'avais précisé dans mon message).
Et pas de cast c++
traditionnel avec les xpcom !
Euh .. traditionnel, c'est vite dit. Jamais je n'utilise ce genre de cast, puisque ça contourne le contrôle de typage du C++ (on peut caster n'importe quoi en n'importe quoi avec ce genre de cast). Mais ça me permettait d'avancer dans mon travail, tout en ayant conscience que ce n'était pas la solution, d'où l'initiation de cette discussion.
Et puis pas de const char *. Utilise les nsString.
Consulte les interfaces IDL des objets que tu
utilises :-p
J'ai volontairement supprimé toute référence aux nsString
pour simplifier le code d'exemple.
Et donc c'est normal tout tes trucs qui
fonctionnent pas.
Pas le truc qui fait l'objet de mon précédent message.
Voici la bonne façon de faire :
...
Ahh tout de suite, c'est plus propre ;-)
C'est peut-être plus propre, mais ça ne fonctionne pas mieux ...
Voilà le code incriminé tel que je l'ai essayé, donc sans simplifications cette fois-ci :
NS_IMETHODIMP mycomp::SetUI( nsIDOMDocument *Document, nsIDOMXULMultiSelectControlElement *ListBoxAsElement, const char *ListBoxId ) { nsCOMPtr<nsIDOMXULSelectControlItemElement> Item; nsCOMPtr<nsIDOMElement> Element; nsresult rv; rv = Document->CreateElement( NS_LITERAL_STRING("listitem"), getter_AddRefs(Element)); NS_ENSURE_SUCCESS(rv,rv); // Ca passe : 'Element' est != NULL. Item = do_QueryInterface(Element, &rv); NS_ENSURE_SUCCESS(rv,rv); // Passe pas : 'rv' vaut 'NS_NOINTERFACE' et 'Item' est resté à NULL ! return NS_OK; }
Comme je l'ai indiqué dans les commentaires, le CreateElement()
se passe bien, ce que l'on peut constater en remarquant que Element
et différent de NULL
, mais pas le do_QueryInterface
, rv
prenant la valeur NS_NOINTERFACE
et Item
restant à NULL
.
Envoyé par : laurentj
Date : 04/09/2006 16:25
peut être faut il que tu fasse plutôt un createElementNS. createElement, selon la spec DOM, créé un élement sans namespace...
En ce qui conçerne tes remarques sur la "simplification" : en montrant des lignes de code c++ sans utiliser tous ce qui est xpcom, ce n'est vraiment pas simplifier, car du coup, on ne sait pas trop d'où peut venir telle ou telle erreur.
XPCom est un tout.
Envoyé par : Epeios
Date : 05/09/2006 10:07
laurentj a écrit:
peut être faut il que tu fasse plutôt un
createElementNS. createElement, selon la spec DOM,
créé un élement sans namespace...
Hélas, ça ne fonctionne toujours pas mieux. Voilà le code testé :
NS_IMETHODIMP mycomp::SetUI( nsIDOMDocument *Document, nsIDOMXULMultiSelectControlElement *ListBoxAsElement, const char *ListBoxId ) { nsCOMPtr<nsIDOMXULSelectControlItemElement> Item; nsCOMPtr<nsIDOMElement> Element; nsresult rv; rv = Document->CreateElementNS( NS_LITERAL_STRING("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" ), NS_LITERAL_STRING("listitem"), getter_AddRefs(Element)); NS_ENSURE_SUCCESS(rv,rv); // Ca passe : 'Element' est != NULL. Item = do_QueryInterface(Element, &rv); NS_ENSURE_SUCCESS(rv,rv); // Ne passe toujours pas : 'rv' vaut 'NS_NOINTERFACE' et 'Item' reste à NULL ! return NS_OK; }
En ce qui conçerne tes remarques sur la
"simplification" : en montrant des lignes de code
c++ sans utiliser tous ce qui est xpcom, ce n'est
vraiment pas simplifier, car du coup, on ne sait
pas trop d'où peut venir telle ou telle erreur.
Sauf que je ne connaissais pas NS_LITERAL_STRING
, alors si j'avais mis le code qui transformait un const char *
en un nsAString
, je doute que le code aurait été trés lisible. Maintenant que je connais NS_LITERAL_STRING
, je n'en ferais plus l'économie dorénavant.
J'ai pensé un moment qu'il y avait incompabilité entre Mozilla 1.8b, que j'utilise pour faire le test, et Xulrunner 1.8.0.4, des sources duquel j'ai génèré le SDK que j'utilise, mais le résultat est le même en utilisant le XULRunner obtenu en compilant ces mêmes sources ...
Envoyé par : Epeios
Date : 05/09/2006 17:11
Aprés de laborieuses recherches sur le net, je suis parvenu à la conclusion que le problème viendrait du fait qu'un composant XBL n'est pas correctement initialisé tant qu'il n'a pas été mis en oeuvre. Dans le cas qui nous occupe, en rattachant, juste aprés sa création, le listitem
à un listbox
existant à l'aide d'un AppendChild
, et bien le do_QueryInterface
fonctionne. Le problème, c'est que l'AppendChild
ne suffit pas en lui-même ; il faut qu'il s'applique à un listbox
. Ce qui laisse supposer que, si cela fonctionne pour un listitem
, cela ne fonctionnera probablement pas pour un textbox
, par exemple, car un textbox
n'a rien à voir avec un listbox
. Or, j'aimerais disposer d'une procédure permettant de forcer l'initialisation de n'importe quel composant XBL fraîchement crée.
Quelqu'un aurait-il une piste ?
Il n'est plus possible de poster des messages dans ce forum.
Copyright © 2003-2013 association xulfr, 2013-2016 Laurent Jouanneau - Informations légales.
Mozilla® est une marque déposée de la fondation Mozilla.
Mozilla.org™, Firefox™, Thunderbird™, Mozilla Suite™ et XUL™
sont des marques de la fondation Mozilla.