Il y a deux façons de créer des menus, la facile et la compliquée. Les
deux ont leur utilité, mais on peut généralement utiliser l'usine
à menus (c'est la méthode facile...). La méthode « compliquée »
consiste à créer tous les menus en utilisant directement les
appels. La méthode facile consiste à utiliser les appels
gtk_menu_factory. C'est beaucoup plus simple, mais chaque
approche a ses avantages et inconvénients.
L'usine à menus est beaucoup plus facile à utiliser, elle facilite
aussi l'ajout d'autres menus. Par contre, écrire quelques fonctions
permettant de créer des menus en utilisant la méthode manuelle peut
être le début d'un long chemin avant une quelconque utilisation. Avec
l'usine à menus, il n'est pas possible d'ajouter des images ou des « /
» aux menus.
Selon la tradition pédagogique, nous commencerons par le plus compliqué :)
Regardons les fonctions utilisées pour créer les menus. La première sert à créer un nouveau menu.
GtkWidget *gtk_menu_bar_new()
Cette fonction crée une nouvelle barre de menu. On utilise la fonction
gtk_container_add pour la placer dans une fenêtre, ou les
fonctions box_pack pour la placer dans une boîte - la même que
pour les boutons.
GtkWidget *gtk_menu_new();
Cette fonction retourne un pointeur vers un nouveau menu, il n'est
jamais montré (avec gtk_widget_show), il ne fait que contenir les
items du menu. Ceci deviendra plus clair lorsque nous étudierons
l'exemple ci-dessous.
Les deux appels suivants servent à créer des items de menu qui seront
placés dans le menu.
Ces appels servent à créer les menus qui doivent être affichés. On
doit bien faire la différence entre un « menu » qui est créé avec
gtk_menu_new() et un « item de menu » créé avec les fonctions
gtk_menu_item_new(). L'item de menu sera un véritable bouton avec
une action associée alors qu'un menu sera un container contenant les
items.
Les fonctions gtk_menu_new_with_label() et gtk_menu_new()
sont telles que vous les attendiez après avoir étudié les
boutons. L'une crée un nouvel item de menu contenant déjà un label, et
l'autre ne fait que créer un item de menu vide.
Voici les étapes pour créer une barre de menu avec des menus attachés :
Créer un nouveau menu avec gtk_menu_new()
Créer un
item de menu avec gtk_menu_item_new(). Ce sera la racine du
menu, le texte apparaissant ici sera aussi sur la barre de menu.
Utiliser plusieurs appels à gtk_menu_item_new() pour
chaque item que l'on désire dans le menu. Utiliser
gtk_menu_item_append() pour placer chacun de ces items les uns
après les autres. Cela crée une liste d'items de menu.
Utiliser gtk_menu_item_set_submenu() pour attacher les
items de menus nouvellement créés à l'item de menu racine (celui créé
à la seconde étape).
Créer une nouvelle barre de menu avec
gtk_menu_bar_new(). Cette étape ne doit être faite qu'une fois
lorsque l'on crée une série de menu sur une seule barre de menus.
Utiliser gtk_menu_bar_append() pour placer le menu racine
dans la barre de menu.
La création d'un menu surgissant est presque identique. La différence
est que le menu n'est pas posté « automatiquement » par une barre de
menu, mais explicitement en appelant la fonction gtk_menu_popup()
par un événement « bouton pressé ».
Suivez ces étapes
Créer une fonction de gestion d'événement. Elle doit avoir le prototype
et elle utilisera l'événement event pour savoir où faire surgir
le menu.
Ce gestionnaire, si l'événement est un appui sur un
bouton souris, traite event comme un événement bouton (ce qu'il
est) et l'utilise, de la façon indiquée dans le code d'exemple, pour
passer l'information à gtk_menu_popup().
où widget est le widget auquel vous le liez, handler est
le gestionnaire, et menu est un menu créé avec
gtk_menu_new(). Cela peut être un menu qui est aussi posté par
une barre de menu, comme le montre l'exemple.
#include <gtk/gtk.h>
static gint button_press (GtkWidget *, GdkEvent *);
static void menuitem_response (GtkWidget *, gchar *);
int main (int argc, char *argv[])
{
GtkWidget *window;
GtkWidget *menu;
GtkWidget *menu_bar;
GtkWidget *root_menu;
GtkWidget *menu_items;
GtkWidget *vbox;
GtkWidget *button;
char buf[128];
int i;
gtk_init (&argc, &argv);
/* Création d'un fenêtre */
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW (window), "Test de Menu GTK");
gtk_signal_connect(GTK_OBJECT (window), "delete_event",
(GtkSignalFunc) gtk_exit, NULL);
/* Initialise le widget menu -- Attention : n'appelez jamais
* gtk_show_widget() pour le widget menu !!!
* C'est le menu qui contient les items de menu, celui qui surgira
* lorsque vous cliquez sur le « menu racine » de l'application. */
menu = gtk_menu_new();
/* Voici le menu racine dont le label sera le nom du menu affiché sur la barre
* de menu. Il n'a pas de gestionnaire de signal attaché car il ne fait
* qu'afficher le reste du menu lorsqu'il est pressé. */
root_menu = gtk_menu_item_new_with_label("Menu racine");
gtk_widget_show(root_menu);
/* Puis, on crée une petite boucle créant trois entrées pour « menu test »
* Notez l'appel à gtk_menu_append(). Ici, on ajoute une liste d'items à
* notre menu. Normalement, on devrait aussi capturer le signal "clicked"
* pour chacun des items et configurer une fonction de rappel pour lui,
* mais on l'a omis ici pour gagner de la place. */
for(i = 0; i < 3; i++)
{
/* Copie des noms dans buf. */
sprintf(buf, "Sous-menu Test - %d", i);
/* Création d'un nouveau item de menu avec un nom... */
menu_items = gtk_menu_item_new_with_label(buf);
/* ...et ajout de celui-ci dans le menu. */
gtk_menu_append(GTK_MENU (menu), menu_items);
/* On fait quelque chose d'intéressant lorsque l'item est
* sélectionné. */
gtk_signal_connect (GTK_OBJECT(menu_items), "activate",
GTK_SIGNAL_FUNC(menuitem_response), (gpointer)
g_strdup(buf));
/* Affichage du widget. */
gtk_widget_show(menu_items);
}
/* Maintenant, on spécifié que nous voulons que notre nouveau « menu »
* soit le menu du « menu racine ». */
gtk_menu_item_set_submenu(GTK_MENU_ITEM (root_menu), menu);
/* Création d'une vbox pour y mettre un menu et un bouton. */
vbox = gtk_vbox_new(FALSE, 0);
gtk_container_add(GTK_CONTAINER(window), vbox);
gtk_widget_show(vbox);
/* Création d'une barre de menus pour contenir les menus. Puis, on
* l'ajoute à notre fenêtre principale. */
menu_bar = gtk_menu_bar_new();
gtk_box_pack_start(GTK_BOX(vbox), menu_bar, FALSE, FALSE, 2);
gtk_widget_show(menu_bar);
/* Création d'un bouton pour y attacher le menu. */
button = gtk_button_new_with_label("Pressez moi");
gtk_signal_connect_object(GTK_OBJECT(button), "event",
GTK_SIGNAL_FUNC (button_press), GTK_OBJECT(menu));
gtk_box_pack_end(GTK_BOX(vbox), button, TRUE, TRUE, 2);
gtk_widget_show(button);
/* Finalement, on ajoute l'item de menu à la barre de menu --
* c'est l'item de menu racine sur lequel je me suis déchaîné ;-) */
gtk_menu_bar_append(GTK_MENU_BAR (menu_bar), root_menu);
/* Affichage de la fenêtre. */
gtk_widget_show(window);
gtk_main ();
return 0;
}
/* On répond à un appui sur le bouton en postant un nouveau menu passé comme
* un widget.
*
* On remarque que le paramètre "widget" est le menu à poster, PAS le bouton
* qui a été pressé. */
static gint button_press (GtkWidget *widget, GdkEvent *event)
{
if (event->type == GDK_BUTTON_PRESS) {
GdkEventButton *bevent = (GdkEventButton *) event;
gtk_menu_popup (GTK_MENU(widget), NULL, NULL, NULL, NULL,
bevent->button, bevent->time);
/* On indique à l'appelant que l'on a géré cet événement. */
return TRUE;
}
/* On indique à l'appelant que l'on n'a pas géré cet événement. */
return FALSE;
}
/* Affiche une chaîne lorsqu'un item de menu est choisi. */
static void menuitem_response (GtkWidget *widget, gchar *string)
{
printf("%s\n", string);
}
Vous pouvez aussi configurer un item de menu pour qu'il ne soit pas
sélectionnable et, en utilisant une table de raccourcis clavier, lier
des touches aux fonctions du menu.
Voici un exemple utilisant l'usine à menu de GTK. Le premier
fichier est menus.h. Nous séparerons menus.c et main.c à
cause des variables globales utilisées dans le fichier menus.c.