Introduction

Ce tutoriel présente un moyen rapide de construire une applet. Une applet est composé de deux parties: un serveur personnel (fabrique ou factory) qui permet de créé plusieurs applets et l'applet.

Environnement de développement

L'environnement de développement ainsi que les chemins d'accès des programmes et fichiers proviennent de la distribution GNU/Linux Debian Sid. L'environnement de bureau Gnome 2.22 est utilisé pour les tests.

  • Les paquets suivant sont nécessaires: build-essential pkg-config libpanel-applet2-dev libgnome-menu-dev libwnck-1.0.
  • Les applets (exécutables) sont stockés dans le répertoire: /usr/lib/gnome-panel/
  • Les icônes et fichiers images sont stockés dans le répertoire: /usr/share/pixmaps/
  • L'entête de panel-applet.h est dans le répertoire: /usr/include/panel-2.0
  • Les fichiers bonobo .server sont stockés dans le répertoire: /usr/lib/bonobo/servers/
  • On retrouve un générateur de fichier bonobo .server à l'adresse suivante: http://yansanmo.progysm.com/outil/gen/genbonoboserver/
  • Un fichier "Makefile" simple sera utilisé pour construire notre application.

Makefile

APP_NAME=hello_applet
libdir=/usr/lib
APPLETDIR=$(libdir)/gnome-panel
BONOBOSERVERDIR=$(libdir)/bonobo/servers/

.PHONY: install

$(APP_NAME): $(APP_NAME).c
  gcc `pkg-config --cflags --libs libpanelapplet-2.0` -o $@ $^

install: $(APP_NAME)
  cp $(APP_NAME) $(APPLETDIR)/
  cp HelloApplet_Factory.server $(BONOBOSERVERDIR)

Dans ce Makefile, la commande "make" ou "make hello_applet" permettront de générer l'applet et la commande "make install" permettra de copier l'applet ainsi que le fichier bonobo .server au bon endroit. Il faut être connecté en utilisateur root pour installer l'applet.

Applet hello_world #1 : une étiquette

Le but de cette applet est de créer une étiquette dans le tableau de bord écrit "Hello World". L'applet sera ajoutée via le menu d'ajout d'applet de Gnome. Avec la traduction Gnome en français, on doit cliquer-droit sur un endroit vide du tableau de bord, cliquer sur le menu "Ajouter au tableau de bord..." et double-cliquer sur notre applet dans la liste. L'applet apparaîtra sur le tableau de bord.

On peut surveiller la liste des processus à l'aide de la commande "ps xwww". Deux processus devront être surveillés:

  • /usr/lib/bonobo-activation/bonobo-activation-server --ac-activate --ior-output-fd=19 : bonobo-activation-server maintient la liste des serveurs bonobo. Il est relié au fichier .bonobo et est utilisé par la liste des applets dans gnome.
  • /usr/lib/gnome-panel/hello_applet : hello_applet est le constructeur de l'applet dans le tableau de bord.

HelloApplet_Factory.server

Comme déjà indiqué, notre première applet affichera une étiquette dans le tableau de bord. Pour ce faire, nous devons créé un fichier bonobo .server qui décrit l'applet. Un fichier bonobo .server est un fichier de type XML qui décrit l'applet et le constructeur de l'applet (factory). Il faut spécifier plusieurs informations dont: le nom et la description de l'applet, le chemin de l'applet, l'icône utilisé. On peut utilisé mon générateur de fichier .server pour ce faire. Je vais entrer les informations suivantes:

  • Factory, Name: Fabrique d'applet HelloWorld
  • Factory, Description: Ceci est la fabrique d'applet HelloWorld
  • Applet, Name: Applet HelloWorld
  • Applet, Metaname: HelloWorld
  • Applet, Description: Applet HelloWorld est ma première applet Gnome
  • Applet, Final file location: /usr/lib/gnome-panel/hello_applet
  • Applet, panel icon filename: gnome-applets.png
  • Applet, panel category:

On clique sur le bouton "Generate" et on devrait obtenir le code suivant. Copier ce code dans le fichier HelloApplet_Factory.server. Le nom du fichier est composé du iid de la fabrique "HelloWorld_Factory" et de l'extension .server.

<oaf_info>
  <oaf_server iid="OAFIID:HelloWorld_Factory" type="exe" location="/usr/lib/gnome-panel/hello_applet">
    <oaf_attribute name="repos_ids" type="stringv">
      <item value="IDL:Bonobo/GenericFactory:1.0"/>
      <item value="IDL:Bonobo/Unknown:1.0"/>
    </oaf_attribute>
    <oaf_attribute name="name" type="string" value="Fabrique d'applet HelloWorld"/>
    <oaf_attribute name="description" type="string" value="Ceci est la fabrique d'applet HelloWorld"/>
  </oaf_server>
  <oaf_server iid="OAFIID:HelloWorld" type="factory" location="OAFIID:HelloWorld_Factory">
    <oaf_attribute name="repos_ids" type="stringv">
      <item value="IDL:GNOME/Vertigo/PanelAppletShell:1.0"/>
      <item value="IDL:Bonobo/Control:1.0"/>
      <item value="IDL:Bonobo/Unknown:1.0"/>
    </oaf_attribute>
    <oaf_attribute name="name" type="string" value="Applet HelloWorld"/>
    <oaf_attribute name="description" type="string" value="Applet HelloWorld est ma première applet Gnome"/>
    <oaf_attribute name="panel:icon" type="string" value="gnome-applets.png"/>
  </oaf_server>
</oaf_info>

Dans ce fichier, il y a deux oaf_server. La description de la fabrique (factory) et la description de l'applet. Il est important de noter 3 renseignements.

  • L'attribut "location" (/usr/lib/gnome-panel/hello_applet) de la fabrique (factory) représente le chemin de l'exécutable à créer.
  • L'attribut "iid" (OAFIID:HelloWorld_Factory) de la fabrique (factory) sera utilisé dans le code C pour identifier d'une façon unique notre fabrique.
  • L'attribut "iid" (OAFIID:HelloWorld) de l'applet sera aussi utilisé dans le code C pour identifier d'une façon unique notre applet.

hello_applet.c

Les entêtes des applets gnome et l'entête du composant GtkLabel sera utilisé.

#include <panel-applet.h>
#include <gtk/gtklabel.h>

Ensuite, il est préférable de créé trois constantes (macros) pour les iid et le nom de notre applet.

#define HELLO_APPLET_IID_FACTORY "OAFIID:HelloApplet_Factory"
#define HELLO_APPLET_IID "OAFIID:HelloApplet"
#define HELLO_APPLET_NAME "Applet HelloWorld"

Le code par la suite est assez simple, on doit utilisé une macro qui enregistre notre applet et qui appelle une fonction de construction de l'interface graphique de l'applet. Dans la fonction de construction, l'étiquette "label" sera ajoutée à l'applet.

Le prototype de la fonction de construction est: static gboolean nom_applet_fill(PanelApplet *applet, const gchar *iid, gpointer data). Le pointer *applet est un Gtk Widget de type PanelApplet (un héritier de GtkEventBox). Il est créé par la fabrique d'applet et non pas dans notre code.

La macro d'enregistrement est la suivante: PANEL_APPLET_BONOBO_FACTORY(iid_factory,PANEL_TYPE_APPLET,applet_name, "0", gui_interface_constructor, NULL); Cette macro créé une fonction main() et appelle les fonctions gnome_program_init() et panel_applet_factory_main().

Le code sera donc:

static gboolean
hello_applet_fill (PanelApplet *applet,
                   const gchar *iid,
                   gpointer data)
{
  GtkWidget *label;

  if (strcmp (iid, HELLO_APPLET_IID) != 0)
    return FALSE;

  label = gtk_label_new ("Hello World");
  gtk_container_add (GTK_CONTAINER (applet), label);

  gtk_widget_show_all (GTK_WIDGET(applet));
  return TRUE;
}

PANEL_APPLET_BONOBO_FACTORY(HELLO_APPLET_IID_FACTORY,~
                            PANEL_TYPE_APPLET,
                            HELLO_APPLET_NAME,
                            "0",
                            hello_applet_fill,
                            NULL);

Il ne reste qu'à compiler avec "make" et à installer avec "su && make install".

Si vous effectuer plusieurs tests, vous pouvez rafraîchir la liste des applets en tuant le processus bonobo-activation-server. (killall bonobo-activation-server). Il sera automatiquement redémarrer par la suite.

Applet hello_world #2 : Ajouter des interactions

Jusqu'à maintenant, notre applet affichait du texte. Voyons comment ajouter une icône et gérer lorsqu'on clique dessus l'icône.

Tout d'abord, nous avons à remplacer le composant GtkLabel (label) par une image. Ensuite, puisque les objets GtkImage ne reçoive pas d'évènements, nous devons ajouter l'image dans un composant GtkEventBox. Finalement, l'évènements "click" sur le contrôle sera associé à une nouvelle fonction.

#include <gtk/gtkeventbox.h>
#include <gtk/gtkimage.h>
#define HELLO_APPLET_ICON "/usr/share/pixmaps/gnome-applets.png"

static gboolean
hello_applet_on_button_press (GtkWidget *event_box,
                              GdkEventButton *event,
                              gpointer *data)
{
  if (event->button != 1) // 1 = bouton gauche habituellement
    return FALSE;

  // code lorsque l'utilisateur a cliqué-gauche
  return TRUE;
}

static gboolean
hello_applet_fill (PanelApplet *applet,
                   const gchar *iid,
                   gpointer data)
{
  GtkWidget *image, *event_box;

  if (strcmp (iid, HELLO_APPLET_IID) != 0)
    return FALSE;

  // creation d'un composant event box et d'une image
  event_box = gtk_event_box_new (); 
  image = gtk_image_new_from_file (HELLO_APPLET_ICON);

  // association de l'évènement clic avec une fonction.
  g_signal_connect (G_OBJECT (event_box), "button_press_event", G_CALLBACK (hello_applet_on_button_press), image);

  // affichage ( image dans event_box dans applet )
  gtk_container_add (GTK_CONTAINER (event_box), image);
  gtk_container_add (GTK_CONTAINER (applet), event_box);
  gtk_widget_show_all (GTK_WIDGET(applet));
  return TRUE;
}

Compiler et tester... Un redémarrage de bonobo-activation-server pourrait être nécessaire.

Applet hello_world #3 : Ajouter des actions au menu contextuel

On peut ajouter des actions au menu contextuel de l'application. Ces actions vont s'ajouter après les actions standards "Enlever du tableau de bord, Déplacer et Verrouiller au tableau de bord". Les parties de code à ajouter sont:

  • La description XML de chaque nouveaux items à ajouter dans le menu contextuel. On peut le mettre dans un fichier .xml ou dans une chaîne de caractères. Chaque item est associé à un "verb", un identifiant pour identifier l'action.
  • Les fonctions appelées (callback) lorsqu'on clique sur un item du menu.
  • Une structure BonoboUIVerb qui définit l'association entre un "verb" (identifiant d'action) et les fonctions callback.
  • L'appel de la fonction panel_applet_setup_menu pour configurer le menu.

Voici le code pour deux nouveaux items dans le menu. L'attribut "verb" est utilisé dans la structure BonoboUIVerb.

static const char hello_applet_context_menu_xml [] =
   "<popup name='button3'>\n"
   "   <menuitem name='Properties Item' "
   "             verb='HelloAppletProperties' "
   "           _label='_Preferences...'\n"
   "          pixtype='stock' "
   "          pixname='gtk-properties'/>\n"
   "   <menuitem name='About Item' "
   "             verb='HelloAppletAbout' "
   "           _label='_About...'\n"
   "          pixtype='stock' "
   "          pixname='gnome-stock-about'/>\n"
   "</popup>\n";

static void
hello_applet_properties_dialog (BonoboUIComponent *uic,
                                gpointer user_data,
                                const char *verbname) 
{
  // PanelApplet *applet = (PanelApplet *) user_data;
}

static void
hello_applet_about_dialog (BonoboUIComponent *uic,
                           gpointer user_data,
                           const char *verbname) 
{
  // PanelApplet *applet = (PanelApplet *) user_data;
}

static const BonoboUIVerb hello_applet_menu_verbs [] = {~
        BONOBO_UI_VERB ("HelloAppletProperties", hello_applet_properties_dialog),
        BONOBO_UI_VERB ("HelloAppletAbout", hello_applet_about_dialog),
        BONOBO_UI_VERB_END
};

Dans la fonction hello_applet_fill(), on rajoute le code suivant pour configurer la chaîne XML, l'association verb-callback et envoyer comme objet (*user_data) un pointeur vers applet.

 panel_applet_setup_menu (PANEL_APPLET (applet),
                           hello_applet_context_menu_xml,
                           hello_applet_menu_verbs,
                           applet);