Voici le deuxième volet de notre dossier consacré à la programmation sous GNOME. Ce mois ci, nous allons continuer notre tour d'horizon des éléments principaux d'une interface graphique. Nous verrons comment créer des élements détachables, des dialogues et comment ajouter facilement des aides à une application.
Le mois dernier, nous avons rapidement passé en revue l'ensemble des possibilités offertes par l'environnement GNOME à l'utilisateur comme au programmeur. Nous avons écrit un petit programme, nous avons vu comment le compiler, puis comment ajouter une barre de menus et une barre d'outils. Nous allons, ce mois ci, créer des contrôles détachables, des dialogues et des aides. Il existe de nombreux autres éléments graphiques sous Gnome. Ces deux premiers articles vous donneront les bases nécessaires à la compréhension de la documentation en ligne de GNOME. Notez que l'ensemble des exemples de code donnés dans cette suite d'articles est disponible sur internet. Inspirez vous en, améliorez les ! Les adresses des sites utiles vous sont rappelées à la fin de cet article.
Ajouter des éléments détachables
Dans l'article précédent, nous avons vu que la barre de menus ainsi que la barre d'outils de la fenêtre principale de l'application pouvaient être détachables, selon les options choisies par l'utilisateur dans le centre de contrôle de GNOME. Il s'avère parfois très utile de pouvoir créer des éléments détachables un peu plus souples. Dans les applications récentes, il est par exemple très fréquent que les barres d'outils ne soient plus des contrôles monolithiques, mais plutôt un ensemble de petites barres détachables que l'utilisateur peut agencer à sa guise au sein de son application ou encore sur son bureau. Nous allons voir maintenant qu'il est très facile, sous GNOME, de créer des contrôles détachables. En fait, n'importe quel objet descendant du type GtkWidget peut devenir un élément détachable. Une application de conception graphique, par exemple, gagne beaucoup en souplesse d'utilisation lorsque les palettes de couleurs ainsi que les différentes boîtes d'outils sont détachables.
Dans l'exemple qui suit, nous allons ajouter une toute petite barre d'outils détachable à notre programme. Ceci nous premettra de revoir ensemble le processus de création d'une barre d'outils étudié le mois dernier. Voici le code correspondant:
Il ne vous reste plus qu'à ajouter la ligne suivante à la fonction main de votre programme:
/* creer une barre flottante */
create_toplevel_dock (GNOME_APP (application));
et le tour est joué.
Ce morceau de code appelle quelques commentaires. Tout d'abord, nous voyons ici apparaitre la fonction gnome_app_fill_toolbar() qui, à partir d'une structure GnomeUIInfo, permet de fabriquer une barre d'outils. Lorsque vous ne créez qu'une barre d'outils principale, la fonction gnome_app_create_toolbar se charge magiquement de cette tâche. gnome_app_fill_toolbar() ainsi que de nombreuses routines similaires sont décrites en détails dans la documentation de référence de Gnome à la rubrique "gnome-app-helper".
Afin que la barre d'outils devienne un élément détachable, nous avons crée un objet dock_item. C'est en fait cet objet qui est détachable. C'est un descendant du type GtkContainer, si bien que nous aurions pu utiliser pratiquement n'importe quel contrôle Gtk à la place de la barre d'outils. Les objets GtkContainer peuvent contenir tous les descendants du type GtkWidget. Dans une application graphique, nous aurions peut être crée une palette de couleurs plutôt qu'une barre d'outils, par exemple.
La routine gnome_app_add_dock_item prend six paramètres. La fonction des deux premiers est relativement facile à comprendre. Les quatre derniers sont des paramètres de placement de l'objet détachable. Ceux ci, dans notre cas, ne sont pas très importants. En effet, comme nous le mentionnions précédemment, l'interêt des éléments détachables est essentiellement de permettre à l'utilisateur de personnaliser l'agencement des contrôles graphiques de son application. Or, direz vous, dans le cas d'interfaces un tant soit peu complexes, il peut devenir relativement fastidieux de réorganiser les contrôles à chaque démarrage de l'application. Il faudrait donc trouver un système pour sauvegarder la disposition des élément graphiques mobiles. C'est là qu'opère la magie de GNOME. Essayez le morceau de code précedent: déplacez la barre d'outils détachable, quittez l'application, redémarrez la. La barre d'outils se replace à la position exacte où vous l'aviez laissée.
GNOME met à votre disposition de puissants outils de sauvegarde de la configuration des applications. Nous verrons comment les utiliser dans un prochain article. Dans le cas des éléments détachables, vous n'avez rien à faire, la configuration de l'utilisateur est sauvegardée automatiquement. La clef de ce miracle réside évidemment dans l'utilisation de la routine gnome_app_add_dock_item. Si pour une raison quelconque vous désiriez que la position des éléments détachables ne soit pas sauvegardée, vous n'auriez qu'à insérer la ligne suivante dans le code de création de la fenêtre principale
gnome_app_enable_layout_config (application, FALSE);
Dialoguer avec l'utilisateur
Très souvent, la communication entre l'application et l'utilisateur se fait via l'apparition de dialogues. Ce sont des fenêtres qui apparaissent au cours de l'exécution d'une application et qui ont, en général, une durée de vie assez courte. Les applications destinées à un public ouvert en font souvent un usage relativement intensif, guidant ainsi l'utilisateur au cours de ses opérations. Les dialogues servent aussi très souvent à délivrer un message à l'utilisateur. Dans ce cas, on les appelle plutôt des boîtes de message (traduction littérale de "message box"). La programmation des dialogues prend fréquemment une part non négligeable du temps de développement d'une interface. Aussi y a-t-il, sous Gnome, de nombreuses fonctions accélérant leur création.
Dans l'exemple que nous avons écrit, lorsque l'utilisateur veut quitter l'application, aucune confirmation ne lui est demandée. Nous allons ajouter un dialogue à notre exemple afin d'obtenir l'accord de l'utilisateur avant de quitter le programme. create_main_widget est la routine qui crée le bouton central. Nous allons modifier un peu cette fonction ainsi que le callback associé au bouton.
/************************************************/
/* callback du bouton principal */
static void
button_cb (GtkWidget *widget, void *data)
{
GnomeDialog *dialog = GNOME_DIALOG(data);
gint num_bouton;
/* afficher le dialogue de confirmation,
bloquer tant que l'utilisateur n'a pas
clique sur un bouton, et recuperer le
numero du bouton sur lequel il a clique */
num_bouton = gnome_dialog_run(dialog);
/* si l'utilisateur n'a pas clique sur OK,
ne pas quitter l'application */
if (num_bouton != 0) return;
gtk_main_quit ();
return;
}
/************************************************/
/* creation du bouton principal */
static GtkWidget *
create_main_widget(GnomeApp *application)
{
GtkButton *button;
GtkWidget *dialogue;
GtkWidget *texte;
GtkBox *dialogue_vbox;
/* creer le dialogue de confirmation
de sortie */
dialogue = gnome_dialog_new
("Confirmation", GNOME_STOCK_BUTTON_OK,
"Un bouton inutile", GNOME_STOCK_BUTTON_CANCEL,
NULL);
gnome_dialog_set_parent(GNOME_DIALOG (dialogue),
GTK_WINDOW(application));
/* quand l'utilisateur clique sur un bouton,
fermer le dialogue */
gnome_dialog_set_close(GNOME_DIALOG (dialogue),
TRUE);
/* quand le dialogue est ferme, ne pas le
detruire, le cacher uniquement */
gnome_dialog_close_hides (
GNOME_DIALOG (dialogue), TRUE);
/* Ajouter un message dans la
fenetre de dialogue */
texte = gtk_label_new(
"Voulez vous vraiment quitter?");
dialogue_vbox =
GTK_BOX (GNOME_DIALOG (dialogue)->vbox);
gtk_box_pack_end(dialogue_vbox, texte,
TRUE, TRUE, GNOME_PAD);
gtk_widget_show(texte);
/* creer le bouton principal de
la fenetre d'exemple ... */
button = GTK_BUTTON(gtk_button_new_with_label (
"Quitter le programme d'exemple"));
/* et quand l'utilsateur clique dessus,appeler
la routine "button_cb". Notez que l'on passe
l'objet dialogue au callback */
gtk_signal_connect (GTK_OBJECT (button),
"clicked", GTK_SIGNAL_FUNC (button_cb),
dialogue);
gtk_container_set_border_width (
GTK_CONTAINER (button), 60);
return GTK_WIDGET(button);
}
La routine gnome_dialog_new permet de créer une fenêtre de dialogue munie de boutons. C'est une routine à nombre de paramètres variable. Chaque paramètre représente un bouton. Ici, nous avons utilisé deux boutons standards de GNOME et un bouton contenant une chaîne de caractères. Le dernier paramètre doit toujours être NULL.
La routine gnome_dialog_close_hides est très importante. Elle permet de spécifier que, lorsque le dialogue est fermé, il ne doit pas être détruit mais plutôt caché. Ainsi, l'application peut elle le faire apparaitre et disparaitre plusieurs fois sans devoir recréer l'objet dialogue. Ceci permet de gagner en temps de réaction, puisque l'objet dialogue est crée au démarrage de l'application et non pas juste avant son affichage. C'est finalement la routine gnome_dialog_run qui provoque l'apparition du dialogue en temps voulu. Cette routine suspend l'exécution du programme tant que l'utilisateur n'a pas cliqué sur l'un des boutons du dialogue. Elle renvoie comme valeur le numéro du bouton qui a été séléctionné. Les numéros des boutons sont attribués en fonction de leur ordre d'apparition dans la liste des paramètres de gnome_dialog_new. Le premier bouton (GNOME_STOCK_BUTTON_OK dans notre cas) a pour numéro 0. Lorsque le dialogue est détruit sans qu'aucun bouton n'ait été séléctionné (si la fenêtre est détruite par le Window Manager, par exemple), la routine renvoie -1.
Nous avons donc réussi à éviter que l'utilisateur ne quitte malencontreusement l'application en cliquant sur le bouton principal. Cependant, il peut toujours la quitter directement en cliquant sur le bouton de fermeture de la fenêtre. Dans ce cas, c'est le callback quit_cb de notre application qui est appelé. Nous pourrions tout à fait réutiliser le même dialogue, mais nous allons plutôt profiter de cette occasion pour voir une autre manière de créer les dialogues simples. Modifions donc la routine quit_cb:
/************************************************/
/* cette routine sera appelee lorsque
l'utilisateur cliquera sur un des boutons du
dialogue cree dans le callback quit_cb */
static void
gerer_reponse_confirmation (gint reponse,
gpointer data)
{
if (reponse == 0) gtk_main_quit ();
else g_print("Fin de l'application annulee par
l'utilisateur\n");
}
/* Callback appele lorsque la fenêtre de
l'application est fermée */
static void
quit_cb (GtkWidget *widget, void *data)
{
GnomeApp *application = GNOME_APP (widget);
/* créer une boite de dialogue simple en passant
gerer_reponse_confirmation comme callback */
gnome_app_question (application,
"Etes vous sur de vouloir quitter?",
gerer_reponse_confirmation,
NULL);
}
La routine gnome_app_question permet de créer des boîtes de dialogues simples avec un bouton de confirmation et un bouton d'annulation. Lorsque l'utilisateur a fait son choix, la routine gerer_reponse_confirmation est appelée avec comme premier paramètre le numéro du bouton qui a été sélectionné. C'est un moyen extrèmement simple de communiquer avec l'utilisateur. Il existe de nombreuses autres routines similaires. Elles sont décrites en détails dans la documentation de Gnome à la rubrique "gnome-app-util".
Ajouter de l'aide
Pour terminer l'article de ce mois-ci, nous allons apprendre à ajouter des aides aux programmes. Tout d'abord, puisque nous sommes très fier de notre réalisation, nous allons la signer en rajoutant une boîte de dialogue "A propos". C'est très simple à faire, profitons en:
static GnomeUIInfo help_menu [] = {
GNOMEUIINFO_ITEM_STOCK ("A propos", NULL,
a_propos_cb, GNOME_STOCK_MENU_ABOUT),
GNOMEUIINFO_END
};
/************************************************/
/* Afficher la boite de dialogue "A propos" */
static void
a_propos_cb (GtkWidget *widget, void *data)
{
GtkWidget *a_propos;
/* indiquer la liste des auteurs */
const gchar *auteurs[] = {
"Bertrand Guiheneuf",
NULL };
a_propos = gnome_about_new ("Deuxieme programme
tutoriel", "1.0","(C) 1999 Bertrand Guiheneuf",
auteur,
/* Commentaire */
"Ce petit programme demontre a quel point "
"il est facile de programmer sous Gnome",
NULL);
gtk_widget_show (a_propos);
return;
}
Il suffit ensuite d'ajouter le menu help_menu à la barre de menu principale.
Passons à des choses un peu plus sérieuses, les aides en ligne. Au sein du projet GNOME, il a été décidé que les documents seraient écrits en SGML, en suivant le style (le dtd en fait, pour les connaisseurs) DocBook. Depuis, ce format a aussi été choisi par l'équipe de développement de sgml-tools (ex linux-doc), l'outil de génération de documents du Linux Documentation Project. C'est en fait un méta format qui permet de générer du HTML aussi bien que du postscript ou du nroff (format des man unix). Apprendre à écrire correctement des documents en SGML DocBook nécessiterait probablement d'y consacrer un article entier. Heureusement, seule la sortie HTML est utilisée par le système d'aide en ligne de GNOME. Pour l'instant, vous pouvez donc écrire vos aides en HTML.
Par manque de place nous n'allons pas écrire un fichier d'aide complet, mais plutôt un squelette, ce sera suffisant pour comprendre le principe des aides en ligne:
<!-- appeler ce fichier index.html -->
<HTML><BODY>
<H1> Linux France Mag example documentation </H1>
<A name="INTRO">Introduction</A><BR>
<A name="QUIT">How to quit the application</A>
</BODY></HTML>
Créons maintenant le fichier topic.dat, qui associe à chaque sujet un "ANCHOR" html :
index.html Manual
index.html#INTRO Introduction
index.html#QUIT How to quit
Lors du développement d'applications GNOME, il est commun d'utiliser les outils aclocal, autoconf et automake pour l'écriture des Makefiles (les fichiers de gestion de projets sous Linux). Dans ce cas, l'installation des fichiers d'aide est un jeu d'enfant. Nous aborderons l'utilisation de ces outils plus tard, et pour le moment, nous devons installer les fichiers index.html et topic.dat "à la main". Ceci permettra uniquement de tester le système d'aide, vous n'aurez évidemment jamais à le faire dans le cadre d'applications réelles.
Placez vous dans le répertoire où se situent les sources du programme d'exemple. Créez la hierarchie de répertoires "share/gnome/help/linuxmag/C". Placez les fichiers index.html et topic.dat dans le dernier répertoire ("C"). Vous devez ensuite changer la valeur de la variable d'environnement GNOMEDIR dans votre shell. Dans le cas où vous travaillez avec bash, l'instruction est export GNOMEDIR=`pwd`
(en csh, l'instruction équivalente est
setenv GNOMEDIR `pwd`)
Reprenons maintenant la définition du menu d'aide et de la barre de menus:
/* definition du menu d'aide */
static GnomeUIInfo help_menu [] = {
GNOMEUIINFO_HELP("linuxmag"),
GNOMEUIINFO_ITEM_STOCK ("A propos", NULL,
a_propos_cb, GNOME_STOCK_MENU_ABOUT),
GNOMEUIINFO_END
};
/* Definition de la barre de menu principale */
static GnomeUIInfo main_menu [] = {
GNOMEUIINFO_SUBTREE ("Fichier", &file_menu),
GNOMEUIINFO_SUBTREE ("Aide", &help_menu),
GNOMEUIINFO_END
};
Nous n'avons fait que rajouter la ligne
GNOMEUIINFO_HELP("exemple")
et pourtant, si vous recompilez le programme, vous verrez apparaitre dans le menu d'aide les sujets indiqués dans "topic.dat". Selectionnez une des rubriques, vous la verrez apparaitre dans netscape ou dans le navigateur d'aide de GNOME (selon votre configuration).
Vous remarquerez que les fichiers d'aide ci-desssus sont en anglais. C'est uniquement pour introduire la première notion d'internationalisation disponible sous GNOME. Traduisez les en français, puis au niveau du répertoire "C", créez un répertoire "fr". Placez y les fichiers traduits "index.html" et "topic.dat". Enfin changez les variables d'environnement de langue:
export LANG=fr
export LC_ALL=fr
Relancez le programme, l'aide doit maintenant apparaitre en français.
Conclusion
Ce mois ci, nous avons vu comment créer des éléments détachables, divers dialogues ainsi que des aides, et nous avons eu un premier aperçu de l'internationalisation sous GNOME. Vous avez maintenant suffisamment d'élements entre les mains pour pouvoir commencer à créer des interfaces graphiques puissantes. Le mois prochain, nous terminerons ce tour d'horizon des éléments de l'interface, nous verrons comment créer des interfaces multidocuments (MDI) puis nous aborderons le sujet passionnant du canevas de GNOME.
références
Les exemples de programmes écrits au cours de ces articles sont disponibles à l'adresse:
http://perso.cybercable.fr/guiheneu
La documentation de référence Gnome:
http://www.gnome.org/devel/docs/index.shtml
La documentation de référence Gtk+ 1.2
Le tutoriel Gtk+ 1.2:
http://www.gtk.org/tutorial1.2/