XIV. Les racourcis clavier▲
XIV-A. Aperçu▲
XIV-B. Mise en place des raccourcis clavier▲
Il ne faut pas confondre les raccourcis clavier et les mnémoniques, ces derniers permettent d'accéder à un élément séquentiellement alors que les raccourcis appellent une fonction si l'utilisateur appuie simultanément sur une ou plusieurs touches (généralement de la forme <Modificateur>Touche, où Modificateur représente les touches Shift, Alt et Control). Ce raccourci peut être attaché à un élément du menu mais ceci n'est pas obligatoire.
Les raccourcis sont regroupés en groupe dans un GtkAccelGroup qu'il faut commencer par créer :
#include <gtk/gtk.h>
#include "callback.h"
#include "raccourcis.h"
GtkAccelGroup *
accel_group_new (
gpointer user_data)
{
GtkAccelGroup *
p_accel_group =
NULL
;
p_accel_group =
gtk_accel_group_new (
);
/* ... */
return
p_accel_group;
}
Vous commencez à avoir l'habitude de cette organisation, nous allons donc créer une fonction qui va se charger d'ajouter un raccourci au groupe :
static
void
accelerator_new (
GtkAccelGroup *
p_accel_group, const
gchar *
accelerator, const
gchar *
accel_path,
GCallback callback, gpointer user_data)
{
guint key;
GdkModifierType mods;
GClosure *
closure =
NULL
;
gtk_accelerator_parse (
accelerator, &
key, &
mods);
closure =
g_cclosure_new (
callback, user_data, NULL
);
gtk_accel_group_connect (
p_accel_group, key, mods, GTK_ACCEL_VISIBLE, closure);
gtk_accel_map_add_entry (
accel_path, key, mods);
}
La fonction gtk_accelerator_parse permet de décomposer une chaîne de caractères de la forme "<Control>F1" ou encore "<Alt>A" en numéro de touche et modificateur.
En plus des touches, pour créer un raccourci clavier, nous avons besoin d'une fonction à appeler lorsque l'utilisateur appuie sur les touches spécifiées. gtk_accel_group_connect attend une fonction du type GClosure, regardons la documentation de gobject pour savoir à quoi cela correspond :
typedef
struct
{
}
GClosure;
Bon pas très instructif :( Heureusement en regardant le constructeur de la classe GClosure on retombe sur des choses connues :
GClosure *
g_cclosure_new (
GCallback callback_func, gpointer user_data, GClosureNotify destroy_data);
Le dernier paramètre est une fonction qui sera appelée pour détruire l'objet user_data lorsqu'il ne sera plus utilisé.
Voilà nous pouvons déjà connecter le raccourci clavier à notre fonction callback.
Pour finir, pour associer un élément du menu à un raccourci clavier, nous avons besoin de l'ajouter à la carte des raccourcis, cette carte est unique et spécifique à chaque application :
void
gtk_accel_map_add_entry (
const
gchar *
accel_path, guint accel_key, GdkModifierType accel_mods);
Il nous manque juste le paramètre accel_path. Il s'agit, comme son nom le laisse penser, d'un chemin pour notre raccourci. Ce chemin est semblable à un chemin de fichier : "<WINDOWTYPE>/Category1/Category2/.../Action" où WINDOWTYPE est un identifiant spécifique à chaque application, pour notre application, nous utiliserons EditeurGTK, ensuite la documentation de GTK+ conseille, pour les éléments du menu, d'utiliser son chemin, par exemple pour l'élément Nouveau : "Fichier/Nouveau" ce qui nous donne : "<EditeurGTK>/Fichier/Nouveau". Comme il va être nécessaire de reprendre ces chemins lors de la création des éléments du menu, il est préférable d'en faire des constantes :
#define ACCEL_PATH_NEW "<EditeurGTK>/Fichier/Nouveau"
#define ACCEL_PATH_OPEN "<EditeurGTK>/Fichier/Ouvrir"
#define ACCEL_PATH_SAVE "<EditeurGTK>/Fichier/Enregistrer"
#define ACCEL_PATH_SAVEAS "<EditeurGTK>/Fichier/Enregistrer sous"
#define ACCEL_PATH_CLOSE "<EditeurGTK>/Fichier/Fermer"
#define ACCEL_PATH_QUIT "<EditeurGTK>/Fichier/Quitter"
Avant de créer nos raccourcis, il faut régler un problème (sur ce point je trouve que GTK+ est mal fait), en effet il est précisé que la fonction callback pour les raccourcis doit avoir la signature suivante :
gboolean (*
GtkAccelGroupActivate) (
GtkAccelGroup *
accel_group, GObject *
acceleratable, guint keyval, GdkModifierType modifier);
Alors que nous avons des fonctions de la forme :
void
callback (
GtkWidget *
p_widget, gpointer user_data);
On est donc obligé de créer des fonctions de type GtkAccelGroupActivate qui vont se charger d'appeler nos fonctions callback :
static
gboolean accel_new (
GtkAccelGroup *
accel_group, GObject *
acceleratable, guint keyval, GdkModifierType modifier, gpointer user_data)
{
cb_new (
NULL
, user_data);
return
TRUE;
}
Notre fonction n'a pas la même signature que les GtkAccelGroupActivate puisque g_cclosure_new précise qu'il appelle la fonction callback ainsi créée avec user_data comme dernier paramètre.
Maintenant que le problème est résolu, créons nos raccourcis :
accelerator_new (
p_accel_group, "
<Control>N
"
, ACCEL_PATH_NEW, G_CALLBACK (
accel_new), user_data);
accelerator_new (
p_accel_group, "
<Control>O
"
, ACCEL_PATH_OPEN, G_CALLBACK (
accel_open), user_data);
accelerator_new (
p_accel_group, "
<Control>S
"
, ACCEL_PATH_SAVE, G_CALLBACK (
accel_save), user_data);
accelerator_new (
p_accel_group, "
<Control><Shift>S
"
, ACCEL_PATH_SAVEAS, G_CALLBACK (
accel_saveas), user_data);
accelerator_new (
p_accel_group, "
<Control>W
"
, ACCEL_PATH_CLOSE, G_CALLBACK (
accel_close), user_data);
accelerator_new (
p_accel_group, "
<Control>Q
"
, ACCEL_PATH_QUIT, G_CALLBACK (
accel_quit), user_data);
Pour finir, il faut ajouter le GtkAccelGroup à notre fenêtre principale :
gtk_window_add_accel_group (
docs.p_main_window, p_accel_group);
Voilà, nous en avons fini avec ce fichier, maintenant il faut revenir à menu.c pour associer les raccouris aux éléments du menu. Il nous suffit de modifier notre constructeur de GtkMenuItem pour qu'il prenne en argument le accel_path :
static
void
menu_item_new (
GtkMenu *
p_menu, const
gchar *
title, const
gchar *
accel_path, GCallback callback, gpointer user_data)
{
/* ... */
gtk_menu_item_set_accel_path (
GTK_MENU_ITEM (
p_menu_item), accel_path);
}
Et d'utiliser nos constantes lors de l'appel à la fonction meni_item_new :
menu_item_new (
GTK_MENU (
p_menu), "
_Nouveau
"
, ACCEL_PATH_NEW, G_CALLBACK (
cb_new), user_data);