Accéder à un widget en tout point d'un programme sans déclaration en global
1. Problème :
Nous rencontrons tous un jour ou l'autre le problème. Mince, il faudrait déclarer cette variable en global, ca serait quand même plus simple pour ma programmation. Seulement, une fois commencé, on ne s'arrête plus et on finit par tout déclarer en global et la lisibilité du code s'en trouve perdue. Alors comment faire?
2. Solution :
L'idée qui suit est d'utiliser
LesListesChainees. Je vous propose quelques fonctions utilisant les possibilités de la Glib pour mémoriser, retrouver, et supprimer des éléments.
Avec ce système une seule déclaration en global est nécessaire : le pointeur de la liste chaînée!
3. Prototypes et explications :
Tout d'abord l'idée était de mémoriser les widgets, puis en essayant ce principe dans mon propre code je me suis vite rendu compte des limites de ce système. J'ai donc rendu ces fonctions plus générales en permettant finalement la mémorisation de n'importe type de pointeur. Vous pouvez donc l'utiliser à d'autres fins sans aucun soucis d'adaptation.
3.1 Les déclarations des prototypes
Avant tout vous devez inclure au moins ces librairies :
#include <gtk/gtk.h>
#include <string.h>
GSList *gtk_data_list_add_by_name(GSList *list, gpointer data, gchar *name);
GSList *gtk_data_list_remove_by_name(GSList *list, gchar *name);
gpointer *gtk_data_list_get_by_name(GSList *list, gchar *name);
void gtk_data_list_remove_all(GSList *list);
3.2 Explication de chaque fonction
GSList *gtk_data_list_add_by_name(GSList *list, gpointer data, gchar *name);
Elle permet d'ajouter un nouvel élément à la liste transmise avec l'attribution d'un nom. En retour on récupére le pointeur sur le premier élément de la liste transmise.
GSList *list : liste contenant les différentes données mémorisés avec leur nom.
gpointer data : donnée à mémorisée (comme c'est un gpointer, vous pouvez mémoriser n'importe quoi)
gchar *name : nom attribué à la donnée.
GSList *gtk_data_list_remove_by_name(GSList *list, gchar *name);
Supprime une donnée de la liste en fonction de son nom. Il est important de noter que le fait d'utiliser cette fonction ne supprime pas l'objet pointé par la liste. Prenons un exemple. Si vous avez mémorisé un
GtkWidget ce widget existera toujours aprés suppression dans la liste. Il faudra le détruire explicitement avec un gtk_widget_destroy();. L'inverse est aussi vrai. Si vous supprimez un
GtkWidget et que vous l'aviez préalablement mémorisé dans la liste il est important aussi de le supprimer dans la liste. Dans le cas contraire vous auriez une donnée qui pointe "dans le vide". DANGER de "segdaults" en perspective!
En retour on récupére le pointeur sur le premier élément de la liste transmise
ou NULL si la donnée n'a pas été trouvée.
GSList *list : liste contenant les différentes données mémorisés avec leur nom.
gchar *name : nom de la donnée à supprimée.
gpointer *gtk_data_list_get_by_name(GSList *list, gchar *name);
La plus importante ! Vous récupérez la donnée en fonction de son nom.
Renvoie NULL si le nom n'a pas été trouvé.
GSList *list : liste contenant les différentes données mémorisés avec leur nom.
gchar *name : nom de la donnée à rechercher.
gpointer data : donnée recherchée (ou NULL si non trouvée)
void gtk_data_list_remove_all(GSList *list);
Supprime tous les éléments de la liste et libère la mémoire utilisée.
GSList *list : liste contenant les différentes données mémorisés avec leur nom.
4. Code :
GSList *gtk_data_list_add_by_name(GSList *list, gpointer data, gchar *name);
GSList *gtk_data_list_add_by_name(GSList *list, gpointer data, gchar *name)
{
/* Déclaration d'une structure interne pour mémoriser le pointeur du widget
ainsi que son nom.*/
struct Struct_list_widget
{
gpointer Sdata;
gchar *Sname;
};
struct Struct_list_widget *Slist_widget=NULL;
// Allocation mémoire d'une nouvelle structure. Le transtypage n'est
// nécessaire que si vous compilez en C++.
Slist_widget=(struct Struct_list_widget*)
g_malloc(sizeof(struct Struct_list_widget));
// Affection des valeurs
Slist_widget->Sdata=data;
Slist_widget->Sname=g_strdup(name);
// Ajout des nouvelles données à la liste.
list=g_slist_append(list, Slist_widget);
return list;
}
GSList *gtk_data_list_remove_by_name(GSList *list, gchar *name);
GSList *gtk_data_list_remove_by_name(GSList *list, gchar *name)
{
GSList *tmp_list=NULL;
/* Déclaration d'une structure interne pour mémoriser le pointeur du widget
ainsi que son nom.*/
struct Struct_list_widget
{
gpointer Sdata;
gchar *Sname;
};
// Recherche de l'objet dans la liste
tmp_list=list;
while (tmp_list)
{
if (strcmp(((struct Struct_list_widget*)(tmp_list->data))->Sname,name)==0)
{
// Supression de l'objet
g_free(((struct Struct_list_widget*)(tmp_list->data))->Sname);
g_free((struct Struct_list_widget*)(tmp_list->data));
list=g_slist_remove_link(list, tmp_list);
g_slist_free_1(tmp_list);
return list;
}
tmp_list=g_slist_next(tmp_list);
}
return NULL;
}
gpointer *gtk_data_list_get_by_name(GSList *list, gchar *name);
gpointer *gtk_data_list_get_by_name(GSList *list, gchar *name)
{
GSList *tmp_list=NULL;
/* Déclaration d'une structure interne pour mémoriser le pointeur du widget
ainsi que son nom.*/
struct Struct_list_widget
{
gpointer Sdata;
gchar *Sname;
};
// Recherche de l'objet dans la liste
tmp_list=list;
while (tmp_list)
{
if (strcmp(((struct Struct_list_widget*)(tmp_list->data))->Sname,name)==0)
return ((struct Struct_list_widget*)(tmp_list->data))->Sdata;
tmp_list=g_slist_next(tmp_list);
}
return NULL;
}
void gtk_data_list_remove_all(GSList *list);
void gtk_data_list_remove_all(GSList *list)
{
GSList *tmp_list=list;
/* Déclaration d'une structure interne pour mémoriser le pointeur du widget
ainsi que son nom.*/
struct Struct_list_widget
{
gpointer Sdata;
gchar *Sname;
};
while (tmp_list)
{
// Supression de l'objet
g_free(((struct Struct_list_widget*)(tmp_list->data))->Sname);
g_free((struct Struct_list_widget*)(tmp_list->data));
tmp_list=g_slist_next(tmp_list);
}
g_slist_free(list);
}
5. Exemple :
Aprés tout ce code un petit exemple va éclaircir une bonne fois pour toute tout ce bric à brac. Nous allons créez une fenêtre avec un
GtkWidget déclaré en local. Ce
GtkWidget sera mémorisé dans une liste. Une nouvelle fenêtre sera aussi créée avec comme parent la première. Pour se faire nous aurons besoin du pointeur parent. Nous utiliserons alors la liste pour le récupérer.
#include <gtk/gtk.h>
#include <string.h>
// Déclaration en global. Comme on peut le voir pas de déclaration autre!
GSList *liste=NULL;
/******************************************************************************/
/* Les fonctions qui permettent la gestion des widget en fct d'un nom */
/******************************************************************************/
GSList *gtk_data_list_add_by_name(GSList *list, gpointer data, gchar *name)
{
/* Déclaration d'une structure interne pour mémoriser le pointeur du widget
ainsi que son nom.*/
struct Struct_list_widget
{
gpointer Sdata;
gchar *Sname;
};
struct Struct_list_widget *Slist_widget=NULL;
// Allocation mémoire d'une nouvelle structure. Le transtypage n'est
// nécessaire que si vous compilez en C++.
Slist_widget=(struct Struct_list_widget*)
g_malloc(sizeof(struct Struct_list_widget));
// Affection des valeurs
Slist_widget->Sdata=data;
Slist_widget->Sname=g_strdup(name);
// Ajout des nouvelles données à la liste.
list=g_slist_append(list, Slist_widget);
return list;
}
GSList *gtk_data_list_remove_by_name(GSList *list, gchar *name)
{
GSList *tmp_list=NULL;
/* Déclaration d'une structure interne pour mémoriser le pointeur du widget
ainsi que son nom.*/
struct Struct_list_widget
{
gpointer Sdata;
gchar *Sname;
};
// Recherche de l'objet dans la liste
tmp_list=list;
while (tmp_list)
{
if (strcmp(((struct Struct_list_widget*)(tmp_list->data))->Sname,name)==0)
{
// Supression de l'objet
g_free(((struct Struct_list_widget*)(tmp_list->data))->Sname);
g_free((struct Struct_list_widget*)(tmp_list->data));
list=g_slist_remove_link(list, tmp_list);
g_slist_free_1(tmp_list);
return list;
}
tmp_list=g_slist_next(tmp_list);
}
return NULL;
}
gpointer *gtk_data_list_get_by_name(GSList *list, gchar *name)
{
GSList *tmp_list=NULL;
/* Déclaration d'une structure interne pour mémoriser le pointeur du widget
ainsi que son nom.*/
struct Struct_list_widget
{
gpointer Sdata;
gchar *Sname;
};
// Recherche de l'objet dans la liste
tmp_list=list;
while (tmp_list)
{
if (strcmp(((struct Struct_list_widget*)(tmp_list->data))->Sname,name)==0)
return ((struct Struct_list_widget*)(tmp_list->data))->Sdata;
tmp_list=g_slist_next(tmp_list);
}
return NULL;
}
void gtk_data_list_remove_all(GSList *list)
{
GSList *tmp_list=list;
/* Déclaration d'une structure interne pour mémoriser le pointeur du widget
ainsi que son nom.*/
struct Struct_list_widget
{
gpointer Sdata;
gchar *Sname;
};
while (tmp_list)
{
// Supression de l'objet
g_free(((struct Struct_list_widget*)(tmp_list->data))->Sname);
g_free((struct Struct_list_widget*)(tmp_list->data));
tmp_list=g_slist_next(tmp_list);
}
g_slist_free(list);
}
/******************************************************************************/
void fenetre_fille()
{
GtkWidget *Window_parent;
GtkWidget *Window_fille;
/* Nous allons tout d'abord récupérer le GtkWidget de la fenêtre principale
en fonction du nom donné dans la fonction main();.
Remarque importante : du fait que la donnée transmise est du type gpointer
il faut transtyper cette donnée lors de sa récupération.*/
Window_parent=(GtkWidget*)gtk_data_list_get_by_name(liste, "fenetre principale");
// Création de la fenêtre fille
Window_fille = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(Window_fille), "Fenêtre fille");
gtk_window_set_default_size(GTK_WINDOW(Window_fille), 400, 300);
// Ici j'utilise le GtkWidget de la fenêtre parente récupérer dans la liste.
gtk_window_set_transient_for(GTK_WINDOW(Window_fille), GTK_WINDOW(Window_parent));
// Signaux associées à la fenêtre fille pour la fermer.
g_signal_connect(G_OBJECT(Window_fille), "destroy", G_CALLBACK(gtk_widget_destroy), NULL);
g_signal_connect(G_OBJECT(Window_fille), "delete_event", G_CALLBACK(gtk_widget_destroy), NULL);
// Affichage de la fenêtre fille
gtk_widget_show_all(Window_fille);
}
/******************************************************************************/
/* Nous déclarons ici un GtkWidget *pWindow. Nous l'ajout à la liste avec le */
/* nom "fenetre principale". */
/******************************************************************************/
gint main(int argc, char **argv)
{
GtkWidget *pWindow;
// Initialisation des librairies GTK+
gtk_init(&argc, &argv);
// Création d'une fenêtre principale.
pWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(pWindow), "Fenêtre principale");
gtk_window_set_default_size(GTK_WINDOW(pWindow), 800, 600);
// Mémorisation du pointeur pWindow dans la liste.
liste=gtk_data_list_add_by_name(liste, pWindow, "fenetre principale");
// Signaux associées à la fenêtre principale pour quitter.
g_signal_connect(G_OBJECT(pWindow), "destroy", G_CALLBACK(gtk_main_quit), NULL);
g_signal_connect(G_OBJECT(pWindow), "delete_event", G_CALLBACK(gtk_main_quit), NULL);
/* Appel d'une fonction pour créer une deuxième fenêtre fille de pWindow.
Il serait possible de transmettre directement pWindow mais ce n'est le but
de cet exemple!*/
fenetre_fille();
// Boucle principale GTK+
gtk_widget_show_all(pWindow);
gtk_main();
// On supprime la liste avant de quitter
gtk_data_list_remove_all(liste);
return 0;
}