Didacticiel Ian Main, slow@intergate.bc.ca January 24, 1998. 11.. IInnttrroodduuccttiioonn GTK (GIMP Toolkit) a d'abord delopppour re une boe outils pour GIMP (General Image Manipulation Program). GTK est construit sur GDK (GIMP Drawing Kit) qui est, avant tout, une encapsulation des fonctions Xlib. On l'appelle GIMP toolkit car il fut cr pour delopper GIMP, mais il est dormais utilisdans plusieurs projets de logiciels libres. Les auteurs sont : +o Peter Mattis petm@xcf.berkeley.edu +o Spencer Kimball spencer@xcf.berkeley.edu +o Josh MacDonald jmacd@xcf.berkeley.edu GTK est essentiellement une interface de programmation (API) orient objet. Bien qu'il soit entiement rit en C, il est implanten utilisant la notion de classes et de fonctions de rappel (pointeurs de fonctions). Un troisie composant, appelglib, remplace certains appels standard et comporte quelques fonctions supplentaires pour ger les listes chas, etc. Les fonctions de remplacement sont utiliss pour accrore la portabilitde GTK car certaines de ces fonctions, comme g_strerror(), ne sont pas disponibles ou ne sont pas standard sur d'autres Unix. D'autres comportent des amiorations par rapport aux versions de la libc : g_malloc(), par exemple, facilite le duggage. Ce didacticiel tente de drire du mieux possible GTK, mais il n'est pas exhaustif. Il suppose une bonne connaissance du langage C, et de la fan de crr des programmes C. Il serait tr prieux au lecteur d'avoir d une expience de la programmation X, mais cela n'est pas nessaire. Si l'apprentissage de GTK marque vos duts dans l'approche des widgets, n'hitez pas faire des commentaires sur ce didacticiel et sur les probles qu'il vous a pos Il y a aussi une API C++ pour GTK (GTK--), si vous prez utiliser ce langage, consultez plut la documentation qui la concerne. Une encapsulation en Objective C et des liaisons Guile sont alement disponibles, mais ne seront pas abords ici. J'apprierais beaucoup avoir un ho des probles que vous avez rencontrpour apprendre GTK partir de ce document. De plus, toute suggestion sur son amioration est la bienvenue. 22.. BBiieenn dduutteerr La premie chose faire est, bien s, de ruper les sources de GTK et de les installer. Vous pouvez en obtenir la dernie version sur ftp.gimp.org dans le rertoire /pub/gtk. D'autres sources d'informations se trouvent sur http://www.gimp.org/gtk. GTK utilise _a_u_t_o_c_o_n_f de GNU pour se configurer. Lorsque vous l'aurez darr tapez _._/_c_o_n_f_i_g_u_r_e _-_-_h_e_l_p pour consulter la liste des options. Pour commencer notre introduction GTK, nous duterons avec le programme le plus simple qui soit. Celui-ci crra une fenre de 200x200 pixels et ne pourra se terminer qu'en le tuant partir du shell. #include int main (int argc, char *argv[]) { GtkWidget *window; gtk_init (&argc, &argv); window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_widget_show (window); gtk_main (); return 0; } Tous les programmes inclueront idemment le fichier gtk/gtk.h qui dlare les variables, fonctions, structures, etc. qui seront utiliss par votre application GTK. La ligne : gtk_init (&argc, &argv); appelle la fonction _g_t_k___i_n_i_t_(_g_i_n_t _*_a_r_g_c_, _g_c_h_a_r _*_*_*_a_r_g_v_) qui sera appel dans toutes les applications GTK. Cette fonction configure certaines choses pour nous, comme l'aspect visuel et les couleurs par daut, puis appelle _g_d_k___i_n_i_t_(_g_i_n_t _*_a_r_g_c_, _g_c_h_a_r _*_*_*_a_r_g_v_). Cette dernie initialise la bibliothue pour qu'elle puisse re utilis, configure les gestionnaires de signaux par daut et vifie les paramres pass notre application via la ligne de commande en recherchant l'un des suivants : +o --display +o --debug-level +o --no-xshm +o --sync +o --show-events +o --no-show-events Elle les supprime alors de la liste des paramres, en laissant tout ce qu'elle ne reconna pas pour que notre application l'analyse ou l'ignore. Ceci cr un ensemble de paramres standards accept par toutes les applications GTK. Les deux lignes de code suivantes crnt et affichent une fenre. window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_widget_show (window); Le paramre GTK_WINDOW_TOPLEVEL prise que l'on veut que la fenre cre suive l'aspect et le placement dinis par le gestionnaire de fenres. Plut que de crr une fenre de 0x0, une fenre sans fenre fille est de 200x200 par daut : on peut ainsi la manipuler facilement. La fonction _g_t_k___w_i_d_g_e_t___s_h_o_w_(_) informe GTK que l'on a configurle widget et qu'il peut l'afficher. La ligne suivante lance la boucle principale de traitement de GTK. gtk_main (); _g_t_k___m_a_i_n_(_) est un autre appel que vous verrez dans toute application GTK. Lorsque le contre atteind ce point, GTK se met en attente d' ements X (click sur un bouton, ou appui d'une touche, par exemple), de timeouts ou d'entrs-sorties fichier. Dans notre exemple simple, cependant, les ements sont ignor. 22..11.. BBoonnjjoouurr ttoouutt llee mmoonnddee eenn GGTTKK OK, rivons un programme avec un widget (bouton). C'est le classique Bonjour tout le monde la sauce GTK. #include /* fonction de rappel. Dans cet exemple, les paramres sont ignor... * Les fonctions de rappel sont daills plus loin. */ void hello (GtkWidget *widget, gpointer data) { g_print ("Bonjour tout le monde.\n"); } gint delete_event(GtkWidget *widget, GdkEvent *event, gpointer data) { g_print ("le signal delete_event est survenu.\n"); /* Si l'on renvoit TRUE dans le gestionnaire du signal "delete_event", * GTK ettra le signal "destroy". Retourner FALSE signifie que l'on * ne veut pas que la fenre soit druite. * Utilispour faire apparare des boes de dialogue du type * es-vous s de vouloir quitter ? */ /* Remplacez FALSE par TRUE et la fenre principale sera druite par * un signal delete_event */ return (FALSE); } /* Autre fonction de rappel */ void destroy (GtkWidget *widget, gpointer data) { gtk_main_quit (); } int main (int argc, char *argv[]) { /* GtkWidget est le type pour dlarer les widgets. */ GtkWidget *window; GtkWidget *button; /* Cette fonction est appel dans toutes les applications GTK. * Les paramres pass en ligne de commande sont analys et * retourn l'application. */ gtk_init (&argc, &argv); /* Crtion d'une nouvelle fenre. */ window = gtk_window_new (GTK_WINDOW_TOPLEVEL); /* Lorsque la fenre reit le signal "delete_event" * (envoypar le gestionnaire de fenres en utilisant l'option * close ou la barre de titre), on lui demande d'appeler la * fonction delete_event() dinie plus haut. La donn pass en * paramre la fonction de rappel est NULL et est ignordans le * rappel. */ gtk_signal_connect (GTK_OBJECT (window), "delete_event", GTK_SIGNAL_FUNC (delete_event), NULL); /* Ici, on connecte l'enement "destroy" un gestionnaire de signal. * Cet ement arrive lorsqu'on appelle gtk_widget_destroy() sur la * fenre, ou si l'on retourne TRUE dans le rappel "delete_event". */ gtk_signal_connect (GTK_OBJECT (window), "destroy", GTK_SIGNAL_FUNC (destroy), NULL); /* Configuration de la largeur du contour de la fenre. */ gtk_container_border_width (GTK_CONTAINER (window), 10); /* Crtion d'un nouveau bouton portant le label * "Bonjour tout le monde". */ button = gtk_button_new_with_label ("Bonjour tout le monde"); /* Quand le bouton recevra le signal "clicked", il appellera la * fonction hello() dinie plus haut en lui passant NULL en paramre. */ gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (hello), NULL); /* Ceci provoquera la destruction de la fenre par appel de la * fonction gtk_widget_destroy(window) lors du signal "clicked". * Le signal de destruction pourrait venir de l ou du * gestionnaire de fenres. */ gtk_signal_connect_object (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (gtk_widget_destroy), GTK_OBJECT (window)); /* Insertion du bouton dans la fenre (container gtk). */ gtk_container_add (GTK_CONTAINER (window), button); /* L'ape finale consiste afficher ce nouveau widget... */ gtk_widget_show (button); /* ... et la fenre. */ gtk_widget_show (window); /* Toutes les applications GTK doivent avoir un gtk_main(). * Le doulement du programme se termine let attend qu'un * ement survienne (touche press ou ement souris). */ gtk_main (); return 0; } 22..22.. CCoommppiillaattiioonn ddee BBoonnjjoouurr ttoouutt llee mmoonnddee ..XXSS 55 22..22.. CCoommppiillaattiioonn ddee BBoonnjjoouurr ttoouutt llee mmoonnddee ..XXEE Supposons que vous avez sauvegardle code prent dans un fichier nomm_b_o_n_j_o_u_r_._c, pour le compiler tapez la commande suivante : gcc -Wall -g bonjour.c -o bonjour_monde -L/usr/X11R6/lib \ -lgtk -lgdk -lglib -lXext -lX11 -lm Les bibliothues invoqus ci-dessus doivent toutes re dans vos chemins de recherche par daut, sinon, ajoutez -L pour que _g_c_c recherche dans ces rertoires les bibliothues nessaires. Sur mon syste Debian GNU/Linux, par exemple, je dois ajouter -L/usr/X11R6/lib pour qu'il trouve les bibliothues X11 (NdT : et c'est pareil sur mon syste Red Hat Linux...). L'ordre des bibliothues est important. L'iteur de liens doit connare les fonctions d'une bibliothue dont il a besoin avant de les traiter. Si vous compilez en utilisant des bibliothues statiques, l'ordre dans lequel vous listez les bibliothues devient tr important. L'exemple donnci-dessus devrait fonctionner dans tous les cas. Les bibliothues que l'on utilise sont : +o La bibliothue glib (-lglib), qui contient diverses fonctions. Seule _g___p_r_i_n_t_(_) est utilis dans cet exemple. GTK est construit au dessus de _g_l_i_b et vous aurez donc toujours besoin de celle-ci. Voir la section concernant ``glib'' pour plus de dails. +o La bibliothue GDK (-lgdk), l'enveloppe de Xlib. +o La bibliothue GTK (-lgtk), la bibliothue des widgets, construite au dessus de GDK. +o La bibliothue Xlib (-lX11 utilis par GDK. +o La bibliothue Xext (-lXext). Cette dernie contient le code pour les pixmaps en moire partag et les autres extensions X. +o La bibliothue mathatique (-lm). Elle est utilis pour diffentes raisons par GTK. 22..33.. TThhrriiee ddeess ssiiggnnaauuxx eett ddeess rraappppeellss Avant de voir en dail le programme Bonjour tout le monde nous parlerons d'abord des ements et des fonctions de rappel. GTK est dirigpar les ements, ce qui signifie qu'il restera inactif dans _g_t_k___m_a_i_n jusqu'ce qu'un ement survienne et que le contre soit passla fonction appropri. Ce passage du contre est rlisen utilisant le concept de signal Lorsqu'un ement survient, comme l'appui sur un bouton, le signal approprisera is par le widget qui a press C'est de cette fan que GTK rlise la plupart de son travail. Pour qu'un bouton rlise une action, on configure un gestionnaire de signal pour capturer ces signaux et appeler la fonction aduate. Ceci est fait en utilisant une fonction comme : gint gtk_signal_connect (GtkObject *object, gchar *name, GtkSignalFunc func, gpointer func_data); Ole premier paramre est le widget qui ettra le signal, et le deuxie est le nom du signal que l'on souhaite intercepter. Le troisie paramre est la fonction que l'on veut appeler quand le signal est captur et le quatrie sont les donns que l'on souhaite passer cette fonction. La fonction spifi par le troisie paramre s'appelle une fonction de rappel et doit re de la forme : void callback_func(GtkWidget *widget, gpointer *callback_data); Ole premier paramre sera un pointeur vers le widget qui a is le signal, et le second un pointeur vers les donns passs par le dernier paramre de la fonction _g_t_k___s_i_g_n_a_l___c_o_n_n_e_c_t_(_) drite plus haut. Un autre appel utilisdans l'exemple Bonjour tout le monde est : gint gtk_signal_connect_object (GtkObject *object, gchar *name, GtkSignalFunc func, GtkObject *slot_object); _g_t_k___s_i_g_n_a_l___c_o_n_n_e_c_t___o_b_j_e_c_t_(_) est la me chose que _g_t_k___s_i_g_n_a_l___c_o_n_n_e_c_t_(_) sauf que la fonction de rappel utilise un seul paramre : un pointeur vers un objet GTK. Lorsqu'on utilise cette fonction pour connecter des signaux, le rappel doit re de cette forme : void callback_func (GtkObject *object); Ol'objet est d'ordinaire un widget. En gal, on ne configure pas de rappels pour _g_t_k___s_i_g_n_a_l___c_o_n_n_e_c_t___o_b_j_e_c_t. D'habitude, ceux-ci sont utilis pour appeler une fonction GTK acceptant un simple widget ou objet comme paramre, comme dans notre exemple. La raison pour laquelle il y a deux fonctions pour connecter les signaux est simplement de permettre aux fonctions de rappel d'avoir un nombre diffent de paramres. De nombreuses fonctions de la bibliothue GTK n'acceptent qu'un simple pointeur vers un _G_t_k_W_i_d_g_e_t comme paramre et vous pouvez donc utiliser _g_t_k___s_i_g_n_a_l___c_o_n_n_e_c_t___o_b_j_e_c_t_(_) pour celles- ci, tandis que pour vos fonctions vous pouvez avoir besoin d'avoir de fournir plus de donns aux fonctions de rappel. 22..44.. BBoonnjjoouurr ttoouutt llee mmoonnddee ppaass ppaass Maintenant que nous connaissons la thrie, clarifions un peu en progressant travers le programme Bonjour tout le monde Voici la fonction de rappel appel lorsque le bouton est clicked Dans notre exemple, on ignore le widget et les donns mais il n'est pas difficile de faire quelque chose avec. Le prochain exemple utilisera le paramre des donns pour nous dire quel bouton a press void hello (GtkWidget *widget, gpointer *data) { g_print ("Bonjour tout le monde\n"); } Cette fonction de rappel est un peu spiale. L'ement "delete_event" survient lorsque le gestionnaire de fenres l'envoie l'application. On doit choisir ce qu'il faut faire de cesements. On peut les ignorer, leur donner une ronse, ou simplement quitter l'application. La valeur que l'on retourne dans cette fonction de rappel permet GTK de savoir ce qu'il a faire. En retournant FALSE, on l'informe que l'on ne veut pas que le signal "destroy" soit is, afin de laisser notre application tourner. En retournant TRUE, on lui demande d'ettre "destroy" qui appellera son tour notre gestionnaire du signal "destroy". gint delete_event(GtkWidget *widget, GdkEvent *event, gpointer data) { g_print ("le signal delete_event est survenu.\n"); return (FALSE); } Voici une autre fonction de rappel qui ne fait que quitter l'application en appelant _g_t_k___m_a_i_n___q_u_i_t_(_). Il n'y a pas grand chose de plus dire car elle est plut triviale : void destroy (GtkWidget *widget, gpointer *data) { gtk_main_quit (); } Je suppose que vous connaissez la fonction _m_a_i_n_(_)... oui, comme les autres programmes C, toutes les applications GTK en ont une. int main (int argc, char *argv[]) { La partie qui suit dlare deux pointeurs sur des structures de type _G_t_k_W_i_d_g_e_t. Ceux-ci sont utilis plus loin pour crr une fenre et un bouton. GtkWidget *window; GtkWidget *button; Et revoici notre _g_t_k___i_n_i_t. Comme premment, il initialise le toolkit et analyse les paramres de la ligne de commande. Il supprime chaque paramre reconnu de la liste et modifie _a_r_g_c et _a_r_g_v pour faire comme si ces paramres n'avaient jamais exist laissant notre application analyser les paramres restants. gtk_init (&argc, &argv); Crtion d'une nouvelle fenre. C'est plut classique. La moire est allou pour une structure _G_t_k_W_i_d_g_e_t et _w_i_n_d_o_w pointe donc sur celle-ci. Cela configure une nouvelle fenre, mais celle-ci ne sera pas affich tant que l'on n'a pas appel_g_t_k___w_i_d_g_e_t___s_h_o_w_(_w_i_n_d_o_w_) vers la fin de notre programme. window = gtk_window_new (GTK_WINDOW_TOPLEVEL); Voici maintenant un exemple de connexion d'un gestionnaire de signal un objet : la fenre. Le signal "destroy" est captur Il est is lorsqu'on utilise le gestionnaire de fenres pour tuer la fenre (et que l'on retourne TRUE dans le gestionnaire "delete_event"), ou lorsqu'on utilise l'appel _g_t_k___w_i_d_g_e_t___d_e_s_t_r_o_y_(_) en lui passant le widget _w_i_n_d_o_w comme objet druire. Ici, on appelle juste la fonction _d_e_s_t_r_o_y_(_) dinie ci-dessus avec le paramre NULL, ce qui quitte GTK pour nous. GTK_OBJECT et GTK_SIGNAL_FUNC sont des macros qui rlisent les conversions et les vifications de types pour nous. Elles rendent aussi le code plus lisible. gtk_signal_connect (GTK_OBJECT (window), "destroy", GTK_SIGNAL_FUNC (destroy), NULL); La fonction suivante sert configurer un attribut d'un objet container. Elle configure simplement la fenre pour qu'elle ait une zone vide autour d'elle de 10 pixels de large oaucun widget ne pourra se trouver. Il existe d'autres fonctions similaires que nous verrons dans la section sur la ``Configuration des attributs des widgets'' nouveau, GTK_CONTAINER est une macro rlisant la conversion de type. gtk_container_border_width (GTK_CONTAINER (window), 10); Cet appel cr un nouveau bouton. Il alloue l'espace moire pour une nouvelle structure GtkWidget, l'initialise et fait pointer _b_u_t_t_o_n vers elle. Ce bouton portera le label Bonjour tout le monde lorsqu'il sera affich button = gtk_button_new_with_label ("Bonjour tout le monde"); Maintenant, prenons ce bouton et faisons lui faire quelque chose d'utile. On lui attache un gestionnaire de signal pour que, lorsqu'il ettra le signal "clicked", notre fonction _h_e_l_l_o_(_) soit appel. On ignore les paramres et on ne passe donc que la valeur NULL la fonction de rappel _h_e_l_l_o_(_). idemment, le signal "clicked" est is lorsqu'on clique sur le bouton avec la souris. gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (hello), NULL); On utilisera aussi ce bouton pour quitter notre programme, ce qui permettra d'illustrer la fan dont le signal "destroy" peut venir soit du gestionnaire de fenres, soit de notre programme. Quand le bouton est "clicked" comme cela est drit plus haut, il appelle d'abord la fonction de rappel _h_e_l_l_o_(_) puis celle-ci dans l'ordre dans lequel elles sont configurs. On peut avoir autant de fonctions de rappel que l'on dire, elles seront exuts selon leur ordre de connexion. Puisque la fonction _g_t_k___w_i_d_g_e_t___d_e_s_t_r_o_y_(_) n'accepte que _G_t_k_W_i_d_g_e_t _*_w_i_d_g_e_t comme paramre, on utilise ici la fonction _g_t_k___s_i_g_n_a_l___c_o_n_n_e_c_t___o_b_j_e_c_t_(_) la place de _g_t_k___s_i_g_n_a_l___c_o_n_n_e_c_t_(_). gtk_signal_connect_object (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (gtk_widget_destroy), GTK_OBJECT (window)); Voici un appel de placement, qui sera expliquen dail plus tard, mais qui est plut facile comprendre. Il indique simplement GTK que le bouton doit re placdans la fenre oil s'affichera. gtk_container_add (GTK_CONTAINER (window), button); Maintenant, nous avons tout configurcomme on le souhaitait : les gestionnaires de signaux sont en place et le bouton est mis dans la fenre oil doit se trouver. On demande alors GTK de montrer les widgets l'ran. Le widget _w_i_n_d_o_w est affichen dernier afin que la fenre entie surgisse d'un coup plut que voir d'abord la fenre s'afficher puis ensuite le bouton apparare l'intieur. Il faut dire qu'avec des exemples simples comme celui-ci, vous ne ferez pas la diffence. gtk_widget_show(button); gtk_widget_show (window); Bien s, on appelle _g_t_k___m_a_i_n_(_) qui attendra les ements venant du serveur X et demandera aux widgets d'ettre les signaux lorsque ces ements surviendront. gtk_main (); Enfin, le _r_e_t_u_r_n final. Il est exutlorsque _g_t_k___q_u_i_t_(_) est appel return 0; Lorsque l'on clique sur un bouton GTK, le widget et un signal "clicked". Afin de pouvoir utiliser cette information, notre programme configure un gestionnaire pour capturer ce signal. Ce gestionnaire appelle la fonction de notre choix. Dans notre exemple, lorsque le bouton que l'on a cr est "clicked", la fonction _h_e_l_l_o_(_) est appel avec le paramre NULL, puis le gestionnaire suivant de ce signal est son tour appel Il appelle la fonction _g_t_k___w_i_d_g_e_t___d_e_s_t_r_o_y_(_) en lui passant le widget _w_i_n_d_o_w comme paramre, ce qui provoque la destruction de celui-ci. Ceci force la fenre envoyer un signal "destroy", qui est capturson tour et appelle notre fonction de rappel _d_e_s_t_r_o_y_(_) qui ferme simplement GTK. Une autre fan de procer consiste utiliser le gestionnaire de fenres pour druire la fenre. Cela provoquera l'ission du signal "delete_event" qui sera pris en charge par notre gestionnaire _d_e_l_e_t_e___e_v_e_n_t_(_). S'il retourne FALSE, la fenre restera telle quelle et rien ne se passera. Retourner TRUE forcera GTK ettre le signal "destroy" qui, bien s, appelera la fonction de rappel _d_e_s_t_r_o_y_(_) provoquant la sortie du GTK. On remarquera que ces signaux ne sont pas les mes que les signaux systes Unix et ne sont pas implant en utilisant ceux-ci, bien que la terminologie employ soit presque identique. 33.. CCoonnttiinnuuoonnss 33..11.. TTyyppeess ddee ddoonnnnss Vous avez probablement notcertaines choses qui nessitent des explications dans les exemples prents. les _g_i_n_t, _g_c_h_a_r, etc. que vous avez pu voir sont des redinitions de _i_n_t et _c_h_a_r, respectivement. Leur raison d're est de s'affranchir des dendances ennuyeuses concernant la taille des types de donns simples lorsqu'on rlise des calculs. Un bon exemple est _g_i_n_t_3_2 qui dignera un entier codsur 32 bits pour toutes les plateformes, que ce soit une station Alpha 64 bits ou un PC i386 32 bits. Les redinitions de type sont tr simples et intuitives. Elles sont toutes drites dans le fichier _g_l_i_b_/_g_l_i_b_._h (qui est inclus par _g_t_k_._h). On notera aussi la possibilitd'utiliser un _G_t_k_W_i_d_g_e_t lorsque la fonction attend un _G_t_k_O_b_j_e_c_t. GTK posse une architecture orient objet, et un widget est un objet. 33..22.. CCoommpplleennttss ssuurr lleess ggeessttiioonnnnaaiirreess ddee ssiiggnnaauuxx Regardons nouveau la dlaration de _g_t_k___s_i_g_n_a_l___c_o_n_n_e_c_t. gint gtk_signal_connect (GtkObject *object, gchar *name, GtkSignalFunc func, gpointer func_data); Vous avez remarquque le valeur de retour est de type _g_i_n_t ? Il s'agit d'un marqueur qui identifie votre fonction de rappel. Comme on le disait plus haut, on peut avoir autant de fonctions de rappel que l'on a besoin, par signal et par objet, et chacune sera exut son tour, dans l'ordre dans lequel elle a attach. Ce marqueur vous permet d'er ce rappel de la liste en faisant &;: void gtk_signal_disconnect (GtkObject *object, gint id); Ainsi, en passant le widget dont on veut supprimer le gestionnaire et le marqueur ou identificateur retournpar l'une des fonctions _s_i_g_n_a_l___c_o_n_n_e_c_t, on peut donnecter un gestionnaire de signal. Une autre fonction permettant de supprimer tous les gestionnaires de signaux pour un objet est : gtk_signal_handlers_destroy (GtkObject *object); Cet appel n'a pas trop besoin d'explications. Il e simplement tous les gestionnaires de signaux de l'objet passen paramre. 33..33.. UUnn BBoonnjjoouurr ttoouutt llee mmoonnddee aammiioorr..XXSS 1122 33..33.. UUnn BBoonnjjoouurr ttoouutt llee mmoonnddee aammiioorr..XXEE udions une version lement amior avec de meilleurs exemples de fonctions de rappel. Ceci permettra aussi d'introduire le sujet suivant : le placement des wigdets. #include /* Notre nouveau rappel amior La donn pass cette fonction est * imprim sur stdout. */ void rappel (GtkWidget *widget, gpointer *data) { g_print ("Re-Bonjour - %s a pressen", (char *) data); } /* Un autre rappel */ void delete_event (GtkWidget *widget, GdkEvent *event, gpointer *data) { gtk_main_quit (); } int main (int argc, char *argv[]) { /* GtkWidget est le type pour dlarer les widgets */ GtkWidget *window; GtkWidget *button; GtkWidget *box1; /* Cette fonction est appel dans toutes les applications GTK. * Les paramre pass en ligne de commande sont analys et * retourn l'application. */ gtk_init (&argc, &argv); /* Crtion d'une nouvelle fenre. */ window = gtk_window_new (GTK_WINDOW_TOPLEVEL); /* Nouvel appel qui intitule notre nouvelle fenre * "Salut les boutons !" */ gtk_window_set_title (GTK_WINDOW (window), "Salut les boutons !"); /* Configuration d'un gestionnaire pour "delete_event" afin de * quitter immiatement GTK. */ gtk_signal_connect (GTK_OBJECT (window), "delete_event", GTK_SIGNAL_FUNC (delete_event), NULL); /* Configuration de la largeur du contour de la fenre. */ gtk_container_border_width (GTK_CONTAINER (window), 10); /* Crtion d'une boe pour y placer les widgets. * Ceci est drit en dails plus loin dans la section * placement La boe n'est pas matialis, elle est juste * utilis comme moyen d'arranger les widgets. */ box1 = gtk_hbox_new(FALSE, 0); /* On met la boe dans la fenre principale. */ gtk_container_add (GTK_CONTAINER (window), box1); /* On cr un nouveau bouton portant le label Bouton 1 */ button = gtk_button_new_with_label ("Bouton 1"); /* Lorsque le bouton est cliqu on appelle la fonction rappel * avec un pointeur sur la chae Bouton 1 comme paramre. */ gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (rappel), (gpointer) "Bouton 1"); /* Au lieu d'utiliser gtk_container_add, on place ce bouton dans * la boe invisible qui a plac dans la fenre. */ gtk_box_pack_start(GTK_BOX(box1), button, TRUE, TRUE, 0); /* N'oubliez jamais cette ape qui indique GTK que la configuration * de ce bouton est termin et qu'il peut re affich */ gtk_widget_show(button); /* On fait la me chose pour crr un deuxie bouton. */ button = gtk_button_new_with_label ("Bouton 2"); /* On appelle la me fonction de rappel avec un paramre diffent, * un pointeur sur la chae Bouton 2 */ gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (rappel), (gpointer) "Bouton 2"); gtk_box_pack_start(GTK_BOX(box1), button, TRUE, TRUE, 0); /* L'ordre dans lequel on affiche les boutons n'est pas vraiment * important, mais il est prable d'afficher la fenre en dernier * pour qu'elle surgisse d'un coup. */ gtk_widget_show(button); gtk_widget_show(box1); gtk_widget_show (window); /* Le reste est dans gtk_main et on attend que la fe commence ! */ gtk_main (); return 0; } Compilez ce programme en utilisant les mes paramres que pour l'exemple prent. Vous remarquerez que, maintenant, il est plus difficile de quitter le programme : vous devez utiliser le gestionnaire de fenres ou une commande shell pour le druire. Un bon exercice pour le lecteur serait d'inser un troisie bouton Quitter qui permettrait de sortir du programme. Vous pouvez aussi jouer avec les options de _g_t_k___b_o_x___p_a_c_k___s_t_a_r_t_(_) en lisant la section suivante. Essayez de redimensionner la fenre, et observez son comportement. Juste une remarque : il existe une autre constante utilisable avec _g_t_k___w_i_n_d_o_w___n_e_w_(_) - GTK_WINDOW_DIALOG. Ceci permet d'interagir de fan un peu diffente avec le gestionnaire de fenres et doit re utilispour les fenres temporaires comme les boes de dialogue, par exemple. 44.. PPllaacceemmeenntt ddeess wwiiddggeettss Lorsqu'on cr une application, on veut mettre plus qu'un simple bouton dans une fenre. Notre premier exemple Bonjour le monde n'utilisait qu'un seul widget et on pouvait donc simplement faire un appel _g_t_k___c_o_n_t_a_i_n_e_r___a_d_d pour placer le widget dans la fenre. Mais si l'on dire en mettre plus, comment peut-on contrer l'endroit ole widget sera positionn? C'est ici que le placement entre en jeu. 44..11.. TThhrriiee ddeess bbooeess ddee ppllaacceemmeenntt La majeure partie du placement est faites en crnt des boes comme dans l'exemple ci-dessus. Ce sont des widgets containers invisibles ol'on peut placer nos widgets. Elles existent sous deux formes : boes horizontales et boes verticales. Lorsque l'on place des widgets dans une boe horizontale, les objets sont ins horizontalement de gauche droite ou de droite gauche selon l'appel utilis Dans une boe verticale, les widgets sont plac de haut en bas ou vice versa. On peut utiliser n'importe quelle combinaison de boes l'intieur ou cd'autres boes pour crr l'effet dir Pour crr une nouvelle boe horizontale, on appelle _g_t_k___h_b_o_x___n_e_w_(_), et pour les boes verticales, _g_t_k___v_b_o_x___n_e_w_(_). Les fonctions _g_t_k___b_o_x___p_a_c_k___s_t_a_r_t_(_) et _g_t_k___b_o_x___p_a_c_k___e_n_d_(_) servent placer les objets l'intieur de ces containers. La fonction _g_t_k___b_o_x___p_a_c_k___s_t_a_r_t_(_) placera de haut en bas dans une boe verticale et de gauche droite dans une boe horizontale. _g_t_k___b_o_x___p_a_c_k___e_n_d_(_) fera le contraire en plant de bas en haut et de droite gauche. En utilisant ces fonctions, on peut aligner droite ou gauche nos widgets et me les manger de n'importe quelle fan pour obtenir l'effet dir Dans la plupart de nos exemples, on utilisera _g_t_k___b_o_x___p_a_c_k___s_t_a_r_t_(_). Un objet peut re un autre container ou un widget. En fait, de nombreux widgets (dont les boutons) sont eux-mes des containers, mais on utilise galement seulement un label dans un bouton. En utilisant ces appels, GTK sait ovous voulez placer vos widgets et il peut donc les dimensionner automatiquement et faire d'autres choses bien pratiques. Il existe aussi plusieurs options permettant de priser comment les widgets doivent re plac. Comme vous pouvez l'imaginer, cette mhode nous donne pas mal de libertpour placer et crr les widgets. 44..22.. DDaaiillss ssuurr lleess bbooeess cause de cette libert le placement des boes avec GTK peut parare doutant au premier abord. Il existe beaucoup d'options et il n'est pas tout de suite ident de comprendre comment elles s'accordent toutes ensemble. En fait, il y a 5 styles de base diffents. Box Packing Example Image Chaque ligne contient une boe horizontale (_h_b_o_x) contenant plusieurs boutons. L'appel _g_t_k___b_o_x___p_a_c_k indique la fan dont sont plac tous les boutons dans la hbox. Chaque bouton est placdans la hbox de la me fan (mes paramres que la fonction _g_t_k___b_o_x___p_a_c_k___s_t_a_r_t_(_)). Voici la dlaration de la fonction _g_t_k___b_o_x___p_a_c_k___s_t_a_r_t. void gtk_box_pack_start (GtkBox *box, GtkWidget *child, gint expand, gint fill, gint padding); Le premier paramre est la boe dans laquelle on place l'objet, le second est cet objet. Tous les objets sont tous des boutons jusqu'maintenant, on place donc des boutons dans des boes. Le paramre _e_x_p_a_n_d de _g_t_k___b_o_x___p_a_c_k___s_t_a_r_t_(_) ou _g_t_k___b_o_x___p_a_c_k___e_n_d_(_) contre la fan dont le widget est placdans la boe. S'il vaut TRUE, les widgets sont dispos dans la boe de fan en occuper tout l'espace. S'il vaut FALSE, la boe est rrie pour correspondre la taille du widget. Mettre _e_x_p_a_n_d FALSE vous permettra d'aligner droite et gauche vos widgets. Sinon, ils s'argiront pour occuper toute la boe. Le me effet pourrait re obtenu en utilisant uniquement une des deux fonctions _g_t_k___b_o_x___p_a_c_k___s_t_a_r_t ou _p_a_c_k___e_n_d. Le paramre _f_i_l_l des fonctions _g_t_k___b_o_x___p_a_c_k contre si de l'espace supplentaire doit re allouaux objets eux-mes (TRUE), ou si on doit rajouter de l'espace (_p_a_d_d_i_n_g) dans la boe autour des objets (FALSE). Il n'a de sens que si le paramre _e_x_p_a_n_d vaut TRUE. Lorsque l'on cr une nouvelle boe, on utilise une fonction comme : GtkWidget * gtk_hbox_new (gint homogeneous, gint spacing); Le paramre _h_o_m_o_g_e_n_e_o_u_s de _g_t_k___h_b_o_x___n_e_w (et c'est la me chose pour _g_t_k___v_b_o_x___n_e_w) vifie que chaque objet de la boe ait la me taille (i.e. la me largeur dans une hbox, la me hauteur dans une vbox). S'il vaut TRUE, le paramre _e_x_p_a_n_d des fonctions _g_t_k___b_o_x___p_a_c_k sera toujours mis TRUE. Quelle est alors la diffence entre les paramres _s_p_a_c_i_n_g (configurlorsque la boe est cre) et _p_a_d_d_i_n_g (configurlorque les ents sont plac) ? _s_p_a_c_i_n_g ajoute de l'espace entre les objets, et _p_a_d_d_i_n_g en ajoute de chaque cd'un objet. La figure suivante devrait lairer tout cela : Box Packing Example Image Voici le code utilispour crr les images ci-dessus. J'y ai mis beaucoup de commentaires en espant que vous n'aurez pas de proble pour le relire. Compilez-le et jouez avec les diffents paramres. 44..33.. PPrrooggrraammmmee ddee ddoonnssttrraattiioonn ddeess ppllaacceemmeennttss #include "gtk/gtk.h" void delete_event (GtkWidget *widget, GdkEvent *event, gpointer *data) { gtk_main_quit (); } /* Construction d'une nouvelle hbox remplie de boutons. Les paramres qui * nous intessent sont pass cette fonction. * On n'affiche pas la boe, mais tout ce qu'elle contient. */ GtkWidget *make_box (gint homogeneous, gint spacing, gint expand, gint fill, gint padding) { GtkWidget *box; GtkWidget *button; char padstr[80]; /* Crtion d'une hbox avec les paramres homogeneous et spacing * voulus. */ box = gtk_hbox_new (homogeneous, spacing); /* Crtion d'une sie de boutons configur de fan appropri */ button = gtk_button_new_with_label ("gtk_box_pack"); gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding); gtk_widget_show (button); button = gtk_button_new_with_label ("(box,"); gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding); gtk_widget_show (button); button = gtk_button_new_with_label ("button,"); gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding); gtk_widget_show (button); /* Crtion d'un bouton portant un label dendant de la valeur * du paramre expand. */ if (expand == TRUE) button = gtk_button_new_with_label ("TRUE,"); else button = gtk_button_new_with_label ("FALSE,"); gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding); gtk_widget_show (button); /* Me chose que ci-dessus mais sous forme abr. */ button = gtk_button_new_with_label (fill ? "TRUE," : "FALSE,"); gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding); gtk_widget_show (button); /* Rupation du paramre padding sous forme de chae. */ sprintf (padstr, "%d);", padding); button = gtk_button_new_with_label (padstr); gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding); gtk_widget_show (button); return box; } int main (int argc, char *argv[]) { GtkWidget *window; GtkWidget *button; GtkWidget *box1; GtkWidget *box2; GtkWidget *separator; GtkWidget *label; GtkWidget *quitbox; int which; /* Initialisation, ne jamais oublier ! :) */ gtk_init (&argc, &argv); if (argc != 2) { fprintf (stderr, "usage : %s num, onum vaut 1, 2, ou 3.\n", *argv); /* Nettoyage dans GTK et sortie avec un code d'erreur de 1 */ gtk_exit (1); } which = atoi (argv[1]); /* Crtion de notre fenre. */ window = gtk_window_new (GTK_WINDOW_TOPLEVEL); /* Il ne faut jamais oublier de connecter le signal "destroy" la * fenre principale. C'est tr important pour disposer d'un * comportement intuitif aduat. */ gtk_signal_connect (GTK_OBJECT (window), "delete_event", GTK_SIGNAL_FUNC (delete_event), NULL); gtk_container_border_width (GTK_CONTAINER (window), 10); /* Crtion d'une boe verticale (vbox) pour y placer les boes * horizontales. * Ceci permet de placer les boes horizontales contenant les boutons * les unes au dessus des autres dans cette vbox. */ box1 = gtk_vbox_new (FALSE, 0); /* L'exemple afficher. Ils correspondent aux images ci-dessus. */ switch (which) { case 1: /* Crtion d'un label. */ label = gtk_label_new ("gtk_hbox_new (FALSE, 0);"); /* Alignement du label gauche. On prisera cette fonction ainsi * que les autres dans la section sur les attributs des widgets. */ gtk_misc_set_alignment (GTK_MISC (label), 0, 0); /* Placement du label dans la boe verticale (vbox box1). Il ne * faut pas oublier que les widgets qui s'ajoutent une vbox sont * plac les uns au dessus des autres. */ gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0); /* Affichage du label */ gtk_widget_show (label); /* On appelle notre fonction de construction de boe : * homogeneous = FALSE, spacing = 0, * expand = FALSE, fill = FALSE, padding = 0 */ box2 = make_box (FALSE, 0, FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0); gtk_widget_show (box2); /* On appelle notre fonction de construction de boe : * homogeneous = FALSE, spacing = 0, * expand = FALSE, fill = FALSE, padding = 0 */ box2 = make_box (FALSE, 0, TRUE, FALSE, 0); gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0); gtk_widget_show (box2); /* Paramres : homogeneous = FALSE, spacing = 0, * expand = TRUE, fill = TRUE, padding = 0 */ box2 = make_box (FALSE, 0, TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0); gtk_widget_show (box2); /* Crtion d'un sarateur, on verra cela plus tard, mais ils sont * simples utiliser. */ separator = gtk_hseparator_new (); /* Placement du sarateur dans la vbox. Ne pas oublier que tous les * widgets sont plac dans une vbox et qu'il seront plac * verticalement. */ gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5); gtk_widget_show (separator); /* Crtion d'un nouveau label et affichage de celui-ci. */ label = gtk_label_new ("gtk_hbox_new (TRUE, 0);"); gtk_misc_set_alignment (GTK_MISC (label), 0, 0); gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0); gtk_widget_show (label); /* Paramres : homogeneous = TRUE, spacing = 0, * expand = TRUE, fill = FALSE, padding = 0 */ box2 = make_box (TRUE, 0, TRUE, FALSE, 0); gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0); gtk_widget_show (box2); /* Paramres : homogeneous = TRUE, spacing = 0, * expand = TRUE, fill = TRUE, padding = 0 */ box2 = make_box (TRUE, 0, TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0); gtk_widget_show (box2); /* Un autre sarateur */ separator = gtk_hseparator_new (); /* Les 3 derniers paramres de gtk_box_pack_start sont : * expand = FALSE, fill = TRUE, padding = 5. */ gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5); gtk_widget_show (separator); break; case 2: /* Crtion d'un label, box1 est une vbox identique * celle cre au dut de main() */ label = gtk_label_new ("gtk_hbox_new (FALSE, 10);"); gtk_misc_set_alignment (GTK_MISC (label), 0, 0); gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0); gtk_widget_show (label); /* Paramres : homogeneous = FALSE, spacing = 10, * expand = TRUE, fill = FALSE, padding = 0 */ box2 = make_box (FALSE, 10, TRUE, FALSE, 0); gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0); gtk_widget_show (box2); /* Paramres : homogeneous = FALSE, spacing = 10, * expand = TRUE, fill = TRUE, padding = 0 */ box2 = make_box (FALSE, 10, TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0); gtk_widget_show (box2); separator = gtk_hseparator_new (); /* Les 3 derniers paramres de gtk_box_pack_start sont : * expand = FALSE, fill = TRUE, padding = 5. */ gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5); gtk_widget_show (separator); label = gtk_label_new ("gtk_hbox_new (FALSE, 0);"); gtk_misc_set_alignment (GTK_MISC (label), 0, 0); gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0); gtk_widget_show (label); /* Paramres : homogeneous = FALSE, spacing = 0, * expand = TRUE, fill = FALSE, padding = 10 */ box2 = make_box (FALSE, 0, TRUE, FALSE, 10); gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0); gtk_widget_show (box2); /* Paramres : homogeneous = FALSE, spacing = 0, * expand = TRUE, fill = TRUE, padding = 10 */ box2 = make_box (FALSE, 0, TRUE, TRUE, 10); gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0); gtk_widget_show (box2); separator = gtk_hseparator_new (); /* Les 3 derniers paramres de gtk_box_pack_start sont : * expand = FALSE, fill = TRUE, padding = 5. */ gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5); gtk_widget_show (separator); break; case 3: /* Ceci est une donstration de la possibilitd'utiliser * gtk_box_pack_end() pour aligner les widgets droite. * On cr d'abord une nouvelle boe comme d'habitude. */ box2 = make_box (FALSE, 0, FALSE, FALSE, 0); /* On cr le label qui sera mis la fin. */ label = gtk_label_new ("end"); /* On le place en utilisant gtk_box_pack_end(), il est ainsi * mis droite de la hbox cre par l'appel make_box(). */ gtk_box_pack_end (GTK_BOX (box2), label, FALSE, FALSE, 0); /* Affichage du label. */ gtk_widget_show (label); /* Placement de box2 dans box1 (la vbox, vous vous rappelez ? :) */ gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0); gtk_widget_show (box2); /* Sarateur pour le bas. */ separator = gtk_hseparator_new (); /* Configuration du sarateur en 400x5 pixels. * La hbox que l'on a cre aura donc 400 pixels de large, * et le label "end" sera sardes autres de la hbox. * Sinon, tous les widgets de la hbox seraient plac les plus * pr possible les uns des autres. */ gtk_widget_set_usize (separator, 400, 5); /* Placement du sarateur dans la vbox (box1) * cre au debut de main(). */ gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5); gtk_widget_show (separator); } /* Crtion d'une nouvelle hbox.. vous pouvez en utiliser autant que * que vous en avez besoin ! */ quitbox = gtk_hbox_new (FALSE, 0); /* Notre bouton pour quitter. */ button = gtk_button_new_with_label ("Quit"); /* Configuration du signal pour druire la fenre. Ceci enverra le * signal "destroy" la fenre. Ce signal sera son tour captur * par notre gestionnaire de signal dini plus haut. */ gtk_signal_connect_object (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (gtk_widget_destroy), GTK_OBJECT (window)); /* Placement du bouton dans la quitbox * Les 3 derniers paramres de gtk_box_pack_start sont : * expand = TRUE, fill = FALSE, padding = 0. */ gtk_box_pack_start (GTK_BOX (quitbox), button, TRUE, FALSE, 0); /* Placement de la quitbox dans la vbox (box1) */ gtk_box_pack_start (GTK_BOX (box1), quitbox, FALSE, FALSE, 0); /* Placement de la vbox (box1), qui contient maintenant tous nos * widgets, dans la fenre principale. */ gtk_container_add (GTK_CONTAINER (window), box1); /* Affichage */ gtk_widget_show (button); gtk_widget_show (quitbox); gtk_widget_show (box1); /* Affichage de la fenre en dernier */ gtk_widget_show (window); /* Ne pas oublier notre fonction principale. */ gtk_main (); /* Le contre revient ici lorsque gtk_main_quit() est appel, * jusqu'ce que gtk_exit() soitutilis. */ return 0; } 44..44.. PPllaacceemmeenntt aavveecc lleess ttaabblleess udions une autre mhode de placement : les tables. Elles peuvent s'aver tr utiles dans certaines situations. En utilisant des tables, on cr une grille dans laquelle on peut placer les widgets. Ceux-ci peuvent occuper tous les endroits que l'on dire. La premie chose faire est, bien s, d'udier la fonction _g_t_k___t_a_b_l_e___n_e_w : GtkWidget* gtk_table_new (gint rows, gint columns, gint homogeneous); Le premier paramre est le nombre de lignes de la table et le deuxie, le nombre de colonnes. Le paramre _h_o_m_o_g_e_n_e_o_u_s s'occupe de la fan dont les cases de la table seront dimensionns. Si homogeneous vaut TRUE, les cases prennent la taille du plus grand widget de la table. S'il vaut FALSE, la taille des cases dend du widget le plus haut de la ligne et du plus large de cette colonne. Le nombre de lignes et colonnes va de 0 n, on est le nombre spifidans l'appel _g_t_k___t_a_b_l_e___n_e_w. Ainsi, avec _r_o_w_s = 2 et _c_o_l_u_m_n_s = 2, la table ressemblera ceci : 0 1 2 0+----------+----------+ | | | 1+----------+----------+ | | | 2+----------+----------+ On notera que le syste de coordonns part du coin en haut gauche. Pour placer un widget dans une case, ou utilise la fonction suivante : void gtk_table_attach (GtkTable *table, GtkWidget *child, gint left_attach, gint right_attach, gint top_attach, gint bottom_attach, gint xoptions, gint yoptions, gint xpadding, gint ypadding); Ole premier paramre (_t_a_b_l_e) est la table que l'on a cre et le second (_c_h_i_l_d) est le widget que l'on veut placer dans la table. Les paramres _l_e_f_t___a_t_t_a_c_h et _r_i_g_h_t___a_t_t_a_c_h spifient l'emplacement du widget et le nombre de cases utiliser. Par exemple, si on veut placer un bouton dans le coin infieur droit de la table drite plus haut et que l'on dire ne remplir QUE cette case, _l_e_f_t___a_t_t_a_c_h vaudra 1, _r_i_g_h_t___a_t_t_a_c_h vaudra 2; _t_o_p___a_t_t_a_c_h vaudra 1 et _b_o_t_t_o_m___a_t_t_a_c_h vaudra 2. Si on veut un widget occupant toute la ligne supieure de notre table, on utilisera les valeurs 0, 2, 0, 1. Les paramres _x_o_p_t_i_o_n_s et _y_o_p_t_i_o_n_s servent priser les options de placement et peuvent re combins par un OU logique pour permettre des options multiples. Ces options sont : +o GTK_FILL - Si la case de la table est plus large que le widget, et que GTK_FILL est spifi le widget s'argira pour occuper toute la place disponible. +o GTK_SHRINK - Si la table a moins de place qu'il ne lui en faut (g alement, cause d'un redimensionnement de la fenre par l'utilisateur), les widgets sont alors simplement pouss vers le bas de la fenre et disparaissent. Si GTK_SHRINK est spifi les widgets se ruiront en me temps que la table. +o GTK_EXPAND - Cette option provoque l'extension de la table pour qu'elle utilise tout l'espace restant dans la fenre. Le paramres de _p_a_d_d_i_n_g jouent le me re que pour les boes, il crnt une zone libre, spifi en pixels, autour du widget. gtk_table_attach() a BEAUCOUP d'options. Voici donc une fonction-raccourci : void gtk_table_attach_defaults (GtkTable *table, GtkWidget *widget, gint left_attach, gint right_attach, gint top_attach, gint bottom_attach); _x_o_p_t_i_o_n_s et _o_p_t_i_o_n_s valent par daut GTK_FILL | GTK_EXPAND, et _x_p_a_d_d_i_n_g et _y_p_a_d_d_i_n_g valent 0. Les autres paramres sont les mes que ceux de la fonction prente. Il existe aussi les fonctions _g_t_k___t_a_b_l_e___s_e_t___r_o_w___s_p_a_c_i_n_g_(_) et _g_t_k___t_a_b_l_e___s_e_t___c_o_l___s_p_a_c_i_n_g_(_). Elles permettent de placer des espaces apr une ligne ou une colonne. void gtk_table_set_row_spacing (GtkTable *table, gint row, gint spacing); et void gtk_table_set_col_spacing (GtkTable *table, gint column, gint spacing); Pour les colonnes, l'espace est ajoutdroite de la colonne et pour les lignes, il est ajouten dessous. On peut aussi configurer un espacement pour toutes les lignes et/ou colonnes avec : void gtk_table_set_row_spacings (GtkTable *table, gint spacing); Et, void gtk_table_set_col_spacings (GtkTable *table, gint spacing); Avec ces appels, la dernie ligne et la dernie colonne n'ont pas d'espace supplentaire. 44..55.. EExxeemmppllee ddee ppllaacceemmeenntt aavveecc ttaabbllee Pour le moment, udiez l'exemple sur les tables (testgtk.c) distribuavec les sources de GTK. 55.. VVuuee dd''eennsseemmbbllee ddeess wwiiddggeettss Les apes pour crr un widget en GTK sont : 1. _g_t_k___*___n_e_w_(_) - une des fonctions disponibles pour crr un nouveau widget. Ces fonctions sont drites dans cette section. 2. Connexion de tous les signaux que l'on souhaite utiliser avec les gestionnaires aduats. 3. Configuration des attributs du widget. 4. Placement du widget dans un container en utilisant un appel appropricomme _g_t_k___c_o_n_t_a_i_n_e_r___a_d_d_(_) ou _g_t_k___b_o_x___p_a_c_k___s_t_a_r_t_(_). 5. Affichage du widget gre _g_t_k___w_i_d_g_e_t___s_h_o_w_(_). _g_t_k___w_i_d_g_e_t___s_h_o_w_(_) permet GTK de savoir que l'on a fini de configurer les attributs du widget et qu'il est pr re affich On peut aussi utiliser _g_t_k___w_i_d_g_e_t___h_i_d_e_(_) pour le faire disparare. L'ordre dans lequel on affiche les widgets n'est pas important, mais il est prable d'afficher la fenre en dernier pour qu'elle surgisse d'un seul coup plut que de voir les diffents widgets apparare l'ran au fur et mesure. Les fils d'un widget (une fenre est aussi un widget) ne seront pas affich tant que la fenre elle-me n'est pas affich par la fonction _g_t_k___w_i_d_g_e_t___s_h_o_w_(_). 55..11.. CCoonnvveerrssiioonnss ddee ttyyppeess Vous remarquerez, au fur et mesure que vous progressez, que GTK utilise un syste de coercition de type. Celle-ci est toujours rlis en utilisant des macros qui vifient si l'objet donnpeut re converti et qui rlisent cette coercition. Les macros que vous rencontrerez le plus sont : +o GTK_WIDGET(widget) +o GTK_OBJECT(object) +o GTK_SIGNAL_FUNC(function) +o GTK_CONTAINER(container) +o GTK_WINDOW(window) +o GTK_BOX(box) Elles sont toutes utiliss pour convertir les paramres des fonctions. Vous les verrez dans les exemples et, en rle gale, vous saurez les utiliser simplement en regardant la dlaration d'une fonction. Comme vous pouvez le voir dans la hiarchie de classes ci-dessous, tous les _G_t_k_W_i_d_g_e_t_s divent d'une classe de base _G_t_k_O_b_j_e_c_t. Ceci signifie que vous pouvez utiliser un widget chaque fois qu'une fonction requiert un objet - il suffit d'utiliser la macro GTK_OBJECT(). Par exemple : gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(fonction_rappel), donnee_de_rappel); Cet appel convertit le bouton en objet et fournit une conversion pour le pointeur de fonction vers la fonction de rappel. De nombreux widgets sont aussi des containers. Si vous regardez la hiarchie de classe ci-dessous, vous remarquerez que beaucoup de widgets viennent de la classe _G_t_k_C_o_n_t_a_i_n_e_r. N'importe lequel de ces widgets peut re utilisavec la macro GTK_CONTAINER pour re passen paramre une fonction qui attend un container. Malheureusement, ces macros ne peuvent re couvertes en dail dans ce didacticiel, Je vous recommande donc de jeter un coup d'oeil sur les fichier en-tes GTK : ils peuvent s'aver tr instructifs. En fait, il n'est pas difficile de comprendre comment fonctionne un widget, il suffit d'udier les dlarations des fonctions. 55..22.. LLaa hhiiaarrcchhiiee ddeess wwiiddggeettss Voici l'arbre de la hiarchie de classes utiliss pour implanter les widgets. GtkObject +-- GtkData | \-- GtkAdjustment | \-- GtkWidget +-- GtkContainer | +-- GtkBin | | +-- GtkAlignment | | +-- GtkFrame | | | *-- GtkAspectFrame | | | | | +-- GtkItem | | | +-- GtkListItem | | | +-- GtkMenuItem | | | | +-- GtkCheckMenuItem | | | | *-- GtkRadioMenuItem | | | | | | | *-- GtkTreeItem | | | | | +-- GtkViewport | | \-- GtkWindow | | +-- GtkDialog | | \-- GtkFileSelection | | | +-- GtkBox | | +-- GtkHBox | | \-- GtkVBox | | +-- GtkColorSelection | | \-- GtkCurve | | | +-- GtkButton | | +-- GtkOptionMenu | | \-- GtkToggleButton | | \-- GtkCheckButton | | \-- GtkRadioButton | | | +-- GtkList | +-- GtkMenuShell | | +-- GtkMenu | | \-- GtkMenuBar | | | +-- GtkNotebook | +-- GtkScrolledWindow | +-- GtkTable | \-- GtkTree | +-- GtkDrawingArea +-- GtkEntry +-- GtkMisc | +-- GtkArrow | +-- GtkImage | +-- GtkLabel | \-- GtkPixmap | +-- GtkPreview +-- GtkProgressBar +-- GtkRange | +-- GtkScale | | +-- GtkHScale | | \-- GtkVScale | | | \-- GtkScrollbar | +-- GtkHScrollbar | \-- GtkVScrollbar | +-- GtkRuler | +-- GtkHRuler | \-- GtkVRuler | \-- GtkSeparator +-- GtkHSeparator \-- GtkVSeparator 55..33.. WWiiddggeettss ssaannss ffeennrree Les widgets suivants n'ont pas de fenre associ. Si vous voulez capturer des ements, vous devez utiliser _G_t_k_E_v_e_n_t_B_o_x. Reportez-vous la section sur ``Le widget EventBox'' GtkAlignment GtkArrow GtkBin GtkBox GtkImage GtkItem GtkLabel GtkPaned GtkPixmap GtkScrolledWindow GtkSeparator GtkTable GtkViewport GtkAspectFrame GtkFrame GtkVPaned GtkHPaned GtkVBox GtkHBox GtkVSeparator GtkHSeparator Nous continuerons notre exploration de GTK en examinant chaque widget tour tour, crnt quelques fonctions simples pour les afficher. Une autre source intessante est le programme _t_e_s_t_g_t_k_._c livravec GTK. Il se trouve dans le rertoire _g_t_k_/ 66.. WWiiddggeettss bboouuttoonnss 66..11.. BBoouuttoonnss nnoorrmmaauuxx On a dpresque vu tout ce qu'il y avait voir sur le widget bouton. Il est tr simple. Cependant, il y a deux fans de crr un bouton. On peut utiliser _g_t_k___b_u_t_t_o_n___n_e_w___w_i_t_h___l_a_b_e_l_(_) pour crr un bouton avec un label, ou _g_t_k___b_u_t_t_o_n___n_e_w_(_) pour crr un bouton vide. Dans ce dernier cas, c'est vous de placer un label ou un pixmap sur celui-ci. Pour ce faire, crz une boe, puis placez vos objets dans celle-ci en utilisant la fonction habituelle _g_t_k___b_o_x___p_a_c_k___s_t_a_r_t, utilisez alors _g_t_k___c_o_n_t_a_i_n_e_r___a_d_d pour placer la boe dans le bouton. Voici un exemple d'utilisation de _g_t_k___b_u_t_t_o_n___n_e_w_(_) pour crr un bouton contenant une image et un label. J'ai sardu reste le code qui cr une boe pour que vous puissiez l'utiliser dans vos programmes. #include /* Crtion d'une hbox avec une image et un label. Cette fonction * retourne la boe... */ GtkWidget *xpm_label_box (GtkWidget *parent, gchar *xpm_filename, gchar *label_text) { GtkWidget *box1; GtkWidget *label; GtkWidget *pixmapwid; GdkPixmap *pixmap; GdkBitmap *mask; GtkStyle *style; /* Crtion de la boite pour un xpm et un label */ box1 = gtk_hbox_new (FALSE, 0); gtk_container_border_width (GTK_CONTAINER (box1), 2); /* Choix d'un style de bouton... Je suppose que c'est pour obtenir * la couleur du fond. Si quelqu'un conna la vraie raison, qu'il * m'laire sur ce point. */ style = gtk_widget_get_style(parent); /* Chargement de xpm pour crr une image */ pixmap = gdk_pixmap_create_from_xpm (parent->window, &mask, &style->bg[GTK_STATE_NORMAL], xpm_filename); pixmapwid = gtk_pixmap_new (pixmap, mask); /* Crtion d'un label */ label = gtk_label_new (label_text); /* placement de l'image et du label dans la boe */ gtk_box_pack_start (GTK_BOX (box1), pixmapwid, FALSE, FALSE, 3); gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 3); gtk_widget_show(pixmapwid); gtk_widget_show(label); return (box1); } /* Notre fonction de rappel habituelle */ void callback (GtkWidget *widget, gpointer *data) { g_print ("Bonjour - %s a pressen", (char *) data); } int main (int argc, char *argv[]) { /* GtkWidget est le type utilispour dlarer les widgets */ GtkWidget *window; GtkWidget *button; GtkWidget *box1; gtk_init (&argc, &argv); /* Crtion d'une fenre */ window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_title (GTK_WINDOW (window), "Pixmap'd Buttons!"); /* Il est prable de faire cela pour toutes les fenres */ gtk_signal_connect (GTK_OBJECT (window), "destroy", GTK_SIGNAL_FUNC (gtk_exit), NULL); /* Configuration du bord de la fenre */ gtk_container_border_width (GTK_CONTAINER (window), 10); /* Crtion d'un bouton */ button = gtk_button_new (); /* Vous devriez re habituvoir ces fonctions maintenant */ gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (callback), (gpointer) "cool button"); /* Appel de notre fonction de crtion de boe */ box1 = xpm_label_box(window, "info.xpm", "cool button"); /* Placement et affichage de tous nos widgets */ gtk_widget_show(box1); gtk_container_add (GTK_CONTAINER (button), box1); gtk_widget_show(button); gtk_container_add (GTK_CONTAINER (window), button); gtk_widget_show (window); /* Le reste est dans gtk_main */ gtk_main (); return 0; } La fonction _x_p_m___l_a_b_e_l___b_o_x_(_) peut re utilis pour placer des xpms et des labels sur tout widget qui peut re container. 66..22.. BBoouuttoonnss ccoommmmuuttaatteeuurrss Les boutons commutateurs ressemblent beaucoup aux boutons normaux, sauf qu'ils seront toujours alternativement dans un at ou dans un autre. Le changement d'at s'effectue par un click. Ils peuvent re enfonc et, lorsqu'on clique dessus, ils se relent. Re-cliquez, et ils se renfoncent. Les boutons commutateurs sont la base des cases cocher ou des boutons radio, donc la plupart des appels utilis pour les boutons commutateurs sont hit par les cases cocher et les boutons radio. J'insisterai ldessus quand nous les aborderons. Crtion d'un bouton commutateur : GtkWidget* gtk_toggle_button_new (void); GtkWidget* gtk_toggle_button_new_with_label (gchar *label); Comme vous pouvez l'imaginer, elles fonctionnent comme celles des boutons normaux. La premie cr un bouton commutateur vide et la deuxie un bouton commutateur contenant dun label. Pour ruper l'at d'un commutateur et cela comprend aussi les cases cocher et les boutons radio, on utilise une macro comme nous le montrons dans l'exemple qui suit et qui teste l'at du commutateur dans une fonction de rappel. Le signal qui nous intesse et qui est is par les boutons commutateurs (ce qui comprend aussi les cases cocher et les boutons radio), est le signal "toggled". Pour vifier l'at de ces boutons, on configure un gestionnaire de signal qui capture "toggled" et utilise la macro pour derminer l'at. La fonction de rappel ressemblera ceci : void rappel_bouton_commutateur (GtkWidget *widget, gpointer data) { if (GTK_TOGGLE_BUTTON(widget)->active) { /* Si l'on est ici, c'est que le bouton est relh */ } else { /* le bouton est enfonc*/ } } L'appel qui suit peut re utilispour configurer l'at d'un bouton commutateur et de ses descendants, les cases cocher et les boutons radio. On lui passe notre bouton en premier paramre et TRUE ou FALSE pour spifier s'il doit re relhou enfonc Par daut, il est relh(FALSE). void gtk_toggle_button_set_state (GtkToggleButton *toggle_button, gint state); On notera que lorsqu'on utilise cette fonction, et que l'at est modifi cela force le bouton ettre un signal "clicked". void gtk_toggle_button_toggled (GtkToggleButton *toggle_button); Cet appel ne fait que commuter le bouton et ettre le signal "toggled". 66..33.. CCaasseess ccoocchheerr Les cases cocher hitent de nombreuses propri et fonctions des boutons commutateurs, mais ont un aspect diffent. Au lieu d're des boutons contenant du texte, ce sont de petits carr avec un texte sur leur droite. Il sont souvent utilis pour valider ou non des options dans les applications. Les deux fonctions de crtion sont identiques celles des boutons normaux. GtkWidget* gtk_check_button_new (void); GtkWidget* gtk_check_button_new_with_label (gchar *label); La fonction _n_e_w___w_i_t_h___l_a_b_e_l cr une case cocher avec un texte cotd'elle. La vification de l'at d'une case cocher est identique celle des boutons commutateurs. 66..44.. BBoouuttoonnss rraaddiioo Les boutons radio ressemblent aux cases cocher sauf qu'ils sont group de fan ce qu'un seul d'entre-eux puisse re sectionnun moment donn Ils sont utilis par les applications lorsqu'il s'agit d'effectuer un choix dans une liste d'options. La crtion d'un bouton radio s'effectue gre l'un des appels suivants : GtkWidget* gtk_radio_button_new (GSList *group); GtkWidget* gtk_radio_button_new_with_label (GSList *group, gchar *label); On notera le paramre supplentaire de ces fonctions. Elles nessitent un groupe pour rliser correctement leur the. Le premier appel doit passer NULL au premier paramre puis on peut crr un groupe en utilisant : GSList* gtk_radio_button_group (GtkRadioButton *radio_button); On passe alors ce groupe en premier paramre des appels suivants aux fonctions de crtion. Il est prable, aussi, de priser quel bouton doit re choisi par daut avec la fonction : void gtk_toggle_button_set_state (GtkToggleButton *toggle_button, gint state); Celle-ci est drite dans la section sur les boutons commutateurs et fonctionne exactement de la me fan. [Mettre ici un exemple d'utilisation de tout cela car je crois que cela ferait beaucoup de bien...] 77.. WWiiddggeettss ddiivveerrss 77..11.. LLaabbeellss Les labels sont tr utilis dans GTK et sont relativement simples. Ils n'ettent pas de signaux car ils n'ont pas de fenre X qui leur est associ. Si vous avez besoin de capturer des signaux ou de faire des coupures (clippings , utilisez un widget EventBox. Pour crr un label, on utilise : GtkWidget* gtk_label_new (char *str); Ol'unique paramre est la chae de caractes que l'on veut que le label affiche. Pour changer le texte d'un label apr sa crtion, on utilise la fonction : void gtk_label_set (GtkLabel *label, char *str); ole premier paramre est le label que l'on veut modifier, que l'on convertit en utilisant la macro GTK_LABEL(), et le second est la nouvelle chae. L'espace nessaire la nouvelle chae sera automatiquement ajustsi nessaire. Pour ruper la chae courante, on utilise la fonction : void gtk_label_get (GtkLabel *label, char **str); ole premier paramre est le label dont on veut ruper la chae et le second sert retourner cette chae. 77..22.. LLee wwiiddggeett bbuullllee dd''aaiiddee Ce sont les petits textes qui surgissent lorsque vous laissez votre pointeur sur un bouton ou un autre widget pendant quelques secondes. Ils sont faciles utiliser, on ne donnera donc pas d'exemple. Si vous voulez voir du code, consultez le programme _t_e_s_t_g_t_k_._c distribuavec GTK. Certains widgets (comme les labels) ne fonctionnent pas avec les bulles d'aide. Le premier appel que vous utiliserez sera pour crr une nouvelle bulle d'aide. Vous n'avez besoin que de le faire une fois dans une fonction donn. Le _G_t_k_T_o_o_l_t_i_p que cette fonction retourne peut re utilispour crr plusieurs bulles d'aide. GtkTooltips *gtk_tooltips_new (void); Lorsque vous avez cr une nouvelle bulle d'aide et le widget sur lequel vous voulez l'utiliser, vous n'avez qu'faire cet appel pour la configurer : void gtk_tooltips_set_tips (GtkTooltips *tooltips, GtkWidget *widget, gchar *tips_text); Les paramres sont la bulle d'aide dcre, suivi du widget pour lequel vous voulez voir apparare cette bulle et le texte que vous voulez qu'elle contienne. Voici un petit exemple : GtkTooltips *tooltips; GtkWidget *button; ... tooltips = gtk_tooltips_new (); button = gtk_button_new_with_label ("bouton 1"); ... gtk_tooltips_set_tips (tooltips, button, "C'est le bouton 1"); D'autres fonctions peuvent re utiliss avec les bulles d'aide. Je ne ferais que les umer et les drire briement. void gtk_tooltips_destroy (GtkTooltips *tooltips); Destruction de bulles d'aide. void gtk_tooltips_enable (GtkTooltips *tooltips); Activation d'un ensemble de bulles d'aide dactivs. void gtk_tooltips_disable (GtkTooltips *tooltips); Dactivation d'un ensemble de bulles d'aide activs. void gtk_tooltips_set_delay (GtkTooltips *tooltips, gint delay); Configure le nombre de millisecondes pendant lequel le pointeur soit se trouver sur le widget avant que la bulle d'aide n'apparaisse. Par daut, ce dai est de 1000 millisecondes, soit 1 seconde. void gtk_tooltips_set_tips (GtkTooltips *tooltips, GtkWidget *widget, gchar *tips_text); Change le texte d'une bulle d'aide dcre. void gtk_tooltips_set_colors (GtkTooltips *tooltips, GdkColor *background, GdkColor *foreground); Configure les couleurs de fond et de premier plan des bulles d'aides. Je ne sais toujours pas comment spifier les couleurs... Et c'est tout concernant les fonctions associs aux bulles d'aide. C'est plus que vous ne vouliez sement en savoir :) 77..33.. BBaarrrreess ddee pprrooggrreessssiioonn Les barres de progression sont utiliss pour afficher la progression d'une opation. Elles sont tr simple utiliser comme vous pourrez le constater en udiant le code ci-dessous. Commenns d'abord par l'appel permettant de crr une nouvelle barre. GtkWidget *gtk_progress_bar_new (void); Maintenant que la barre est cre, nous pouvons l'utiliser. void gtk_progress_bar_update (GtkProgressBar *pbar, gfloat percentage); Le premier paramre est la barre de progression sur laquelle on veut agir, et le second est le pourcentage effectu signifiant le remplissage de la barres de 0 100 % (rl compris entre 0 et 1). Les barres de progression sont galement utiliss avec les dais d'expiration ou autres fonctions identiques (voir la section sur ``Expirations, fonctions d'E/S et d'attente'') pour donner l'illusion du multi-thes. Toutes emploient la fonction _g_t_k___p_r_o_g_r_e_s_s___b_a_r___u_p_d_a_t_e de la me fan. Voici un exemple de barre de progression mise jour par des expirations. Ce code montre aussi comment rnitialiser une barre. #include static int ptimer = 0; int pstat = TRUE; /* Cette fonction incrente et met jour la barre de progression, * elle la rnitialise si pstat vaut FALSE */ gint progress (gpointer data) { gfloat pvalue; /* rupation de la valeur courante de la barre */ pvalue = GTK_PROGRESS_BAR (data)->percentage; if ((pvalue >= 1.0) || (pstat == FALSE)) { pvalue = 0.0; pstat = TRUE; } pvalue += 0.01; gtk_progress_bar_update (GTK_PROGRESS_BAR (data), pvalue); return TRUE; } /* Cette fonction signale une rnitialisation de la barre */ void progress_r (void) { pstat = FALSE; } void destroy (GtkWidget *widget, gpointer *data) { gtk_main_quit (); } int main (int argc, char *argv[]) { GtkWidget *window; GtkWidget *button; GtkWidget *label; GtkWidget *table; GtkWidget *pbar; gtk_init (&argc, &argv); window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_signal_connect (GTK_OBJECT (window), "delete_event", GTK_SIGNAL_FUNC (destroy), NULL); gtk_container_border_width (GTK_CONTAINER (window), 10); table = gtk_table_new(3,2,TRUE); gtk_container_add (GTK_CONTAINER (window), table); label = gtk_label_new ("Exemple de barre de progression"); gtk_table_attach_defaults(GTK_TABLE(table), label, 0,2,0,1); gtk_widget_show(label); /* Cr une barre, la place dans la table et l'affiche */ pbar = gtk_progress_bar_new (); gtk_table_attach_defaults(GTK_TABLE(table), pbar, 0,2,1,2); gtk_widget_show (pbar); /* Configure le dai d'expiration pour ger automatiquement la * mise jour de la barre */ ptimer = gtk_timeout_add (100, progress, pbar); /* Ce bouton indique la barre qu'elle doit se rnitialiser */ button = gtk_button_new_with_label ("Reset"); gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (progress_r), NULL); gtk_table_attach_defaults(GTK_TABLE(table), button, 0,1,2,3); gtk_widget_show(button); button = gtk_button_new_with_label ("Annuler"); gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (destroy), NULL); gtk_table_attach_defaults(GTK_TABLE(table), button, 1,2,2,3); gtk_widget_show (button); gtk_widget_show(table); gtk_widget_show(window); gtk_main (); return 0; } Dans ce petit programme, il y a quatre parties concernant le fonctionnement gal des barres de progression, nous les udierons dans l'ordre de leurs appels. pbar = gtk_progress_bar_new (); Cet appel cr une nouvelle barre, nomm _p_b_a_r. ptimer = gtk_timeout_add (100, progress, pbar); Cet appel utilise des dais d'expiration pour permettre un intervalle de temps constant. ces dais ne sont pas nessaires l'utilisation des barres de progression. pvalue = GTK_PROGRESS_BAR (data)->percentage; Ce code assigne _p_v_a_l_u_e la valeur du pourcentage de la barre. gtk_progress_bar_update (GTK_PROGRESS_BAR (data), pvalue); Finalement, ce code met jour la barre avec la valeur de _p_v_a_l_u_e. Et c'est tout ce qu'il y a savoir sur les barres de progression. Amusez-vous bien. 77..44.. BBooeess ddee ddiiaalloogguuee Les widgets boes de dialogue sont tr simples : ce sont simplement des fenres avec plusieurs choses dplacs dedans. La structure d'une boe de dialogue est : struct GtkDialog { GtkWindow window; GtkWidget *vbox; GtkWidget *action_area; }; Comme vous le voyez, cela cr simplement une fenre et la place dans une vbox suivie d'un sarateur et d'une hbox pour la zone d'action Le widget boe de dialogue peut servir produire des messages pour l'utilisateur ainsi qu'd'autres thes. Il est vraiment rudimentaire et il n'y a qu'une seule fonction pour les boes de dialogue : GtkWidget* gtk_dialog_new (void); Ainsi, pour crr un nouveau dialogue, on utilise : GtkWidget window; window = gtk_dialog_new (); Ceci crra la boe de dialogue et c'est maintenant vous de l'utiliser. Vous pouvez, par exemple, placer un bouton dans la zone d'action en faisant quelque chose comme : button = ... gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->action_area), button, TRUE, TRUE, 0); gtk_widget_show (button); Et vous pouvez aussi ajouter un label la zone de la vboxb : label = gtk_label_new ("Les boes de dialogues sont pratiques"); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->vbox), label, TRUE, TRUE, 0); gtk_widget_show (label); Comme exemple d'utilisation d'une boe de dialogue, vous pourriez mettre deux boutons dans la zone d'action (un bouton Annuler et un bouton Ok et un label dans la zone de la vbox posant une question l'utilisateur ou signalant une erreur, etc. Vous pouvez alors attacher un signal diffent chacun des boutons et rliser l'opation que l'utilisateur a choisie. 77..55.. PPiixxmmaappss Les pixmaps sont des structures de donns contenant des images. Celles- ci peuvent re utiliss diffents endroits, mais le plus souvent comme ices dans le bureau X Window. Un bitmap est un pixmap de 2 couleurs. Pour utiliser des pixmaps avec GTK, on doit d'abord construire une structure _G_d_k_P_i_x_m_a_p en utilisant les fonctions de la couche GDK. Les pixmaps peuvent soit re crs partir de donns en memoire, ou partir de donns lues dans un fichier. Nous utiliserons chacun des appels pour crr un pixmap. GdkPixmap *gdk_bitmap_create_from_data( GdkWindow *window, gchar *data, gint width, gint height ); Cette fonction sert crr un pixmap mono-plan (2 couleurs) partir de donns en moire. Chaque bit de la donn _d_a_t_a. _w_i_d_t_h et _h_e_i_g_h_t sont exprim en pixels. Le pointeur vers un _G_d_k_W_i_n_d_o_w pointe sur la fenre courante car les ressources d'un pixmap n'ont de signification que dans le contexte de l'ran oil doit s'afficher. GdkPixmap* gdk_pixmap_create_from_data( GdkWindow *window, gchar *data, gint width, gint height, gint depth, GdkColor *fg, GdkColor *bg ); Cette fonction est utilis pour crr un pixmap d'une profondeur donn (nombre de couleurs) partir de la donn spifi pas _d_a_t_a. _f_g et _b_g sont les couleurs utiliser pour l'avant et l'arrie-plan. GdkPixmap* gdk_pixmap_create_from_xpm( GdkWindow *window, GdkBitmap **mask, GdkColor *transparent_color, const gchar *filename ); Le format XPM est une reprentation des pixmaps reconnue par le syste X Window. Il est largement utiliset de nombreux utilitaires pour crr des fichiers d'images ce format sont disponibles. Le fichier _f_i_l_e_n_a_m_e doit contenir une image dans ce format qui sera charg dans la structure pixmap. Le masque _m_a_s_k indique quels sont les bits opaques du pixmap. Tous les autres bits sont coloris en utilisant la couleur spifi par _t_r_a_n_s_p_a_r_e_n_t___c_o_l_o_r. Un exemple d'utilisation est prentci-dessous. GdkPixmap* gdk_pixmap_create_from_xpm_d (GdkWindow *window, GdkBitmap **mask, GdkColor *transparent_color, gchar **data); De petites images peuvent re intrs dans un programme sous la forme de donns _d_a_t_a au format XPM. Un pixmap est cr en utilisant ces donns au lieu de les lire dans un fichier. Un exemple de telles donns est : /* XPM */ static const char * xpm_data[] = { "16 16 3 1", " c None", ". c #000000000000", "X c #FFFFFFFFFFFF", " ", " ...... ", " .XXX.X. ", " .XXX.XX. ", " .XXX.XXX. ", " .XXX..... ", " .XXXXXXX. ", " .XXXXXXX. ", " .XXXXXXX. ", " .XXXXXXX. ", " .XXXXXXX. ", " .XXXXXXX. ", " .XXXXXXX. ", " ......... ", " ", " "}; void gdk_pixmap_destroy( GdkPixmap *pixmap ); Lorsqu'on a utilisun pixmap et que l'on en a plus besoin tout de suite, il est prable de liber la ressource en utilisant un appel _g_d_k___p_i_x_m_a_p___d_e_s_t_r_o_y. Les pixmaps doivent re consids comme une ressource prieuse. Quand le pixmap est cr, on peut l'afficher comme un widget GTK. On doit crr un widget pixmap qui contiendra le pixmap GDK. Ceci est rlisde la fan suivante : GtkWidget* gtk_pixmap_new( GdkPixmap *pixmap, GdkBitmap *mask ); Les autres fonctions pour les widgets pixmap sont : guint gtk_pixmap_get_type( void ); void gtk_pixmap_set( GtkPixmap *pixmap, GdkPixmap *val, GdkBitmap *mask); void gtk_pixmap_get( GtkPixmap *pixmap, GdkPixmap **val, GdkBitmap **mask); _g_t_k___p_i_x_m_a_p___s_e_t sert changer le pixmap pris en charge par le widget. _v_a_l est le pixmap cr par le GDK. Voici un exemple illustrant l'utilisation d'un pixmap dans un bouton : #include /* donns XPM d'une ice "Ouvrir fichier" */ static const char * xpm_data[] = { "16 16 3 1", " c None", ". c #000000000000", "X c #FFFFFFFFFFFF", " ", " ...... ", " .XXX.X. ", " .XXX.XX. ", " .XXX.XXX. ", " .XXX..... ", " .XXXXXXX. ", " .XXXXXXX. ", " .XXXXXXX. ", " .XXXXXXX. ", " .XXXXXXX. ", " .XXXXXXX. ", " .XXXXXXX. ", " ......... ", " ", " "}; /* Termine l'application lorsqu'elle est appel * via le signal "delete_event" */ void close_application( GtkWidget *widget, GdkEvent *event, gpointer *data ) { gtk_main_quit(); } /* Invoqu lorsque le bouton est cliqu Affiche simplement * un message. */ void button_clicked( GtkWidget *widget, gpointer *data ) { printf( "bouton cliquen" ); } int main( int argc, char *argv[] ) { /* GtkWidget est le type pour dlarer les widgets */ GtkWidget *window, *pixmapwid, *button; GdkPixmap *pixmap; GdkBitmap *mask; GtkStyle *style; /* Cr la fenre principale et attache le signal "delete_event" pour * terminer l'application */ gtk_init( &argc, &argv ); window = gtk_window_new( GTK_WINDOW_TOPLEVEL ); gtk_signal_connect( GTK_OBJECT (window), "delete_event", GTK_SIGNAL_FUNC (close_application), NULL ); gtk_container_border_width( GTK_CONTAINER (window), 10 ); gtk_widget_show( window ); /* Utilisation de GDK pour crr le pixmap */ style = gtk_widget_get_style( window ); pixmap = gdk_pixmap_create_from_xpm_d( window->window, &mask, &style->bg[GTK_STATE_NORMAL], (gchar **)xpm_data ); /* Crtion d'un widget pixmap GTK pour contenir le pixmap GDK */ pixmapwid = gtk_pixmap_new( pixmap, mask ); gtk_widget_show( pixmapwid ); /* Crtion d'un bouton pour contenir le widget pixmap */ button = gtk_button_new(); gtk_container_add( GTK_CONTAINER(button), pixmapwid ); gtk_container_add( GTK_CONTAINER(window), button ); gtk_widget_show( button ); gtk_signal_connect( GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(button_clicked), NULL ); /* Affichage de la fenre */ gtk_main (); return 0; } Pour charger un fichier partir d'un fichier XPM appel_i_c_o_n_0_._x_p_m se trouvant dans le rertoire courant, on aurait cr le pixmap ainsi : /* Charge un pixmap partir d'un fichier */ pixmap = gdk_pixmap_create_from_xpm( window->window, &mask, &style->bg[GTK_STATE_NORMAL], "./icon0.xpm" ); pixmapwid = gtk_pixmap_new( pixmap, mask ); gtk_widget_show( pixmapwid ); gtk_container_add( GTK_CONTAINER(window), pixmapwid ); Utilisation des formes Un davantage de l'utilisation des pixmaps est que l'objet affichest toujours rectangulaire, quelle que soit l'image. On voudrait pouvoir crr des bureaux et des applications avec des ices ayant des formes plus naturelles. Par exemple, pour une interface de jeu, on aimerait avoir des boutons ronds pousser. Pour faire cela, on doit utiliser des fenres avec des formes. Une fenre avec forme est simplement un pixmap dont les pixels du fond sont transparents. Ainsi, lorsque l'image d'arrie-plan est multicolore, on ne la cache pas avec le bord de notre ice. L'exemple suivant affiche une image de brouette sur le bureau. #include /* XPM */ static char * WheelbarrowFull_xpm[] = { "48 48 64 1", " c None", ". c #DF7DCF3CC71B", "X c #965875D669A6", "o c #71C671C671C6", "O c #A699A289A699", "+ c #965892489658", "@ c #8E38410330C2", "# c #D75C7DF769A6", "$ c #F7DECF3CC71B", "% c #96588A288E38", "& c #A69992489E79", "* c #8E3886178E38", "= c #104008200820", "- c #596510401040", "; c #C71B30C230C2", ": c #C71B9A699658", "> c #618561856185", ", c #20811C712081", "< c #104000000000", "1 c #861720812081", "2 c #DF7D4D344103", "3 c #79E769A671C6", "4 c #861782078617", "5 c #41033CF34103", "6 c #000000000000", "7 c #49241C711040", "8 c #492445144924", "9 c #082008200820", "0 c #69A618611861", "q c #B6DA71C65144", "w c #410330C238E3", "e c #CF3CBAEAB6DA", "r c #71C6451430C2", "t c #EFBEDB6CD75C", "y c #28A208200820", "u c #186110401040", "i c #596528A21861", "p c #71C661855965", "a c #A69996589658", "s c #30C228A230C2", "d c #BEFBA289AEBA", "f c #596545145144", "g c #30C230C230C2", "h c #8E3882078617", "j c #208118612081", "k c #38E30C300820", "l c #30C2208128A2", "z c #38E328A238E3", "x c #514438E34924", "c c #618555555965", "v c #30C2208130C2", "b c #38E328A230C2", "n c #28A228A228A2", "m c #41032CB228A2", "M c #104010401040", "N c #492438E34103", "B c #28A2208128A2", "V c #A699596538E3", "C c #30C21C711040", "Z c #30C218611040", "A c #965865955965", "S c #618534D32081", "D c #38E31C711040", "F c #082000000820", " ", " .XoO ", " +@#$%o& ", " *=-;#::o+ ", " >,<12#:34 ", " 45671#:X3 ", " +89<02qwo ", "e* >,67;ro ", "ty> 459@>+&& ", "$2u+ > ", "Oh$;ya *3d.a8j,Xe.d3g8+ ", " Oh$;ka *3d$a8lz,,xxc:.e3g54 ", " Oh$;kO *pd$%svbzz,sxxxxfX..&wn> ", " Oh$@mO *3dthwlsslszjzxxxxxxx3:td8M4 ", " Oh$@g& *3d$XNlvvvlllm,mNwxxxxxxxfa.:,B* ", " Oh$@,Od.czlllllzlmmqV@V#V@fxxxxxxxf:%j5& ", " Oh$1hd5lllslllCCZrV#r#:#2AxxxxxxxxxcdwM* ", " OXq6c.%8vvvllZZiqqApA:mq:Xxcpcxxxxxfdc9* ", " 2r<6gde3bllZZrVi7S@SV77A::qApxxxxxxfdcM ", " :,q-6MN.dfmZZrrSS:#riirDSAX@Af5xxxxxfevo", " +A26jguXtAZZZC7iDiCCrVVii7Cmmmxxxxxx%3g", " *#16jszN..3DZZZZrCVSA2rZrV7Dmmwxxxx&en", " p2yFvzssXe:fCZZCiiD7iiZDiDSSZwwxx8e*>", " OA1666 >=01-kuu666> ", " ,6ky& &46-10ul,66, ", " Ou0<> o66y66By7=xu664 ", " <> +66uv,zN666* ", " 566,xxj669 ", " 4666FF666> ", " >966666M ", " oM6668+ ", " *4 ", " ", " "}; /* Termine l'application lorsqu'elle est appel * (via le signal "delete_event"). */ void close_application( GtkWidget *widget, GdkEvent *event, gpointer *data ) { gtk_main_quit(); } int main (int argc, char *argv[]) { GtkWidget *window, *pixmap, *fixed; GdkPixmap *gdk_pixmap; GdkBitmap *mask; GtkStyle *style; GdkGC *gc; /* cr la fenre principale et attache le signal "delete_event" * pour terminer l'application. On notera que la fenre principale * n'a pas de barre de titre car nous la faisons surgir. */ gtk_init (&argc, &argv); window = gtk_window_new( GTK_WINDOW_POPUP ); gtk_signal_connect (GTK_OBJECT (window), "delete_event", GTK_SIGNAL_FUNC (close_application), NULL); gtk_widget_show (window); /* Crtion du pixmap et du widget pixmap */ style = gtk_widget_get_default_style(); gc = style->black_gc; gdk_pixmap = gdk_pixmap_create_from_xpm_d( window->window, &mask, &style->bg[GTK_STATE_NORMAL], WheelbarrowFull_xpm ); pixmap = gtk_pixmap_new( gdk_pixmap, mask ); gtk_widget_show( pixmap ); /* Pour afficher le pixmap, on utilise un widget fixe pour placer * le pixmap */ fixed = gtk_fixed_new(); gtk_widget_set_usize( fixed, 200, 200 ); gtk_fixed_put( GTK_FIXED(fixed), pixmap, 0, 0 ); gtk_container_add( GTK_CONTAINER(window), fixed ); gtk_widget_show( fixed ); /* On masque tout sauf l'image elle-me */ gtk_widget_shape_combine_mask( window, mask, 0, 0 ); /* Affichage de la fenre */ gtk_widget_set_uposition( window, 20, 400 ); gtk_widget_show( window ); gtk_main (); return 0; } Pour rendre l'image de la brouette sensible aux clics, on peut lui attacher le signal "button_press_event" pour lui faire faire quelque chose. Les quelques lignes suivantes font que l'image sera sensible un clic souris qui provoquera l'arr de l'application. gtk_widget_set_events( window, gtk_widget_get_events( window ) | GDK_BUTTON_PRESS_MASK ); gtk_signal_connect( GTK_OBJECT(window), "button_press_event", GTK_SIGNAL_FUNC(close_application), NULL ); 88.. WWiiddggeettss ccoonnttaaiinneerrss 88..11.. BBlloocc--nnootteess Le widget bloc-notes est un ensemble de pages qui se chevauchent. Chaque page contient des informations diffentes. Remment, ce widget est devenu plus commun dans la programmation des interfaces graphiques et c'est un bon moyen de montrer des blocs d'information similaires qui justifient une saration de leur affichage. Le premier appel de fonction que l'on doit connare est, vous l'aviez devin celui qui cr un widget bloc-notes. GtkWidget* gtk_notebook_new (void); Lorsque le bloc-notes a cr, il y a 12 fonctions permettant de travailler sur les blocs-notes. udions-les sarent. La premie permet de positionner les indicateurs de pages. Ceux-ci (dign par le mot tab (signet)), peuvent se trouver en haut, en bas, gauche ou droite des pages. void gtk_notebook_set_tab_pos (GtkNotebook *notebook, GtkPositionType pos); _G_t_k_P_o_s_i_t_i_o_n_T_y_p_e peut prendre les valeurs suivantes qu'il n'est pas nessaire d'expliquer : +o GTK_POS_LEFT +o GTK_POS_RIGHT +o GTK_POS_TOP +o GTK_POS_BOTTOM GTK_POS_TOP est la valeur par daut. La fonction suivante permet d'ajouter des pages un bloc-notes. Il y a trois fans d'ajouter des pages. Regardons les deux premies qui sont tr semblables. void gtk_notebook_append_page (GtkNotebook *notebook, GtkWidget *child, GtkWidget *tab_label); void gtk_notebook_prepend_page (GtkNotebook *notebook, GtkWidget *child, GtkWidget *tab_label); Ces fonctions ajoutent des pages au bloc-notes_*_n_o_t_e_b_o_o_k en les insant la fin (_a_p_p_e_n_d) ou au dut (_p_r_e_p_e_n_d). _*_c_h_i_l_d est le widget qui est placdans la page du bloc-notes, et _*_t_a_b___l_a_b_e_l est le label de la page qui est ajout. La troisie fonction ajoutant une page un bloc-notes conserve toutes les propri des deux prentes, mais elle nous permet en plus de spifier la position ol'on dire inser cette page. void gtk_notebook_insert_page (GtkNotebook *notebook, GtkWidget *child, GtkWidget *tab_label, gint position); Les paramres sont les mes que ___a_p_p_e_n_d__ et ___p_r_e_p_e_n_d__ sauf qu'il y en a un de plus : _p_o_s_i_t_i_o_n. Celui-ci sert spifier l'endroit ocette page sera ins. Maintenant que nous savons inser une page, voyons comment en supprimer une. void gtk_notebook_remove_page (GtkNotebook *notebook, gint page_num); Cette fonction e la page spifi par _p_a_g_e___n_u_m du widget _*_n_o_t_e_b_o_o_k. Pour connare la page courante d'un bloc-notes, on dispose de la fonction : gint gtk_notebook_current_page (GtkNotebook *notebook); Les deux fonctions suivantes permettent de passer la page suivante ou prente d'un bloc-notes. Il suffit de faire l'appel de la fonction aduate avec le widget sur lequel on veut oper. Remarque : lorsqu'on est sur la dernie page du bloc-notes et que l'on appelle _g_t_k___n_o_t_e_b_o_o_k___n_e_x_t___p_a_g_e, on revient la premie page. De me, si l'on est sur la premie page et que l'onappelle _g_t_k___n_o_t_e_b_o_o_k___p_r_e_v___p_a_g_e, on se retrouve sur sa dernie page. void gtk_notebook_next_page (GtkNoteBook *notebook); void gtk_notebook_prev_page (GtkNoteBook *notebook); La fonction qui suit permet de choisir la page active Si vous voulez ouvrir le bloc-notes la page 5, par exemple, vous utiliserez cette fonction. Sans elle, le bloc-notes s'ouvre sur sa premie page par daut. void gtk_notebook_set_page (GtkNotebook *notebook, gint page_num); Les deux fonctions suivantes ajoutent ou ent les indicateurs de page et le contour du bloc-notes, respectivement. void gtk_notebook_set_show_tabs (GtkNotebook *notebook, gint show_tabs); void gtk_notebook_set_show_border (GtkNotebook *notebook, gint show_border); _s_h_o_w___t_a_b_s et _s_h_o_w___b_o_r_d_e_r peuvent valoir TRUE ou FALSE (0 ou 1). Voyons maintenant un exemple, il est tirdu code de _t_e_s_t_g_t_k_._c de la distribution GTK et montre l'utilisation des 13 fonctions. Ce petit programme cr une fenre contenant un bloc-notes et six boutons. Le bloc-notes contient 11 pages, ajouts par trois moyens diffents : la fin, au milieu et au dut. Les boutons permettent de faire tourner les indicateurs de page, ajouter/er les indicateurs et le contour, er une page, passer la page suivante et prente, et sortir du programme. #include /* Rotation des indicateurs de page */ void rotate_book (GtkButton *button, GtkNotebook *notebook) { gtk_notebook_set_tab_pos (notebook, (notebook->tab_pos +1) %4); } /* Ajout/Suppression des indicateurs de pages et des contours */ void tabsborder_book (GtkButton *button, GtkNotebook *notebook) { gint tval = FALSE; gint bval = FALSE; if (notebook->show_tabs == 0) tval = TRUE; if (notebook->show_border == 0) bval = TRUE; gtk_notebook_set_show_tabs (notebook, tval); gtk_notebook_set_show_border (notebook, bval); } /* Suppression d'une page */ void remove_book (GtkButton *button, GtkNotebook *notebook) { gint page; page = gtk_notebook_current_page(notebook); gtk_notebook_remove_page (notebook, page); /* Il faut rafrahir le widget -- * ce qui force le widget se redessiner. */ gtk_widget_draw(GTK_WIDGET(notebook), NULL); } void delete (GtkWidget *widget, GdkEvent *event, gpointer *data) { gtk_main_quit (); } int main (int argc, char *argv[]) { GtkWidget *window; GtkWidget *button; GtkWidget *table; GtkWidget *notebook; GtkWidget *frame; GtkWidget *label; GtkWidget *checkbutton; int i; char bufferf[32]; char bufferl[32]; gtk_init (&argc, &argv); window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_signal_connect (GTK_OBJECT (window), "delete_event", GTK_SIGNAL_FUNC (delete), NULL); gtk_container_border_width (GTK_CONTAINER (window), 10); table = gtk_table_new(2,6,TRUE); gtk_container_add (GTK_CONTAINER (window), table); /* Crtion d'un bloc-notes, placement des indicateurs de page. */ notebook = gtk_notebook_new (); gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_TOP); gtk_table_attach_defaults(GTK_TABLE(table), notebook, 0,6,0,1); gtk_widget_show(notebook); /* Ajoute un groupe de pages la fin du bloc-notes. */ for (i=0; i < 5; i++) { sprintf(bufferf, "Append Frame %d", i+1); sprintf(bufferl, "Page %d", i+1); frame = gtk_frame_new (bufferf); gtk_container_border_width (GTK_CONTAINER (frame), 10); gtk_widget_set_usize (frame, 100, 75); gtk_widget_show (frame); label = gtk_label_new (bufferf); gtk_container_add (GTK_CONTAINER (frame), label); gtk_widget_show (label); label = gtk_label_new (bufferl); gtk_notebook_append_page (GTK_NOTEBOOK (notebook), frame, label); } /* Ajoute une page un endroit pris. */ checkbutton = gtk_check_button_new_with_label ("Cochez moi !"); gtk_widget_set_usize(checkbutton, 100, 75); gtk_widget_show (checkbutton); label = gtk_label_new ("Emplacement de la nouvelle page"); gtk_container_add (GTK_CONTAINER (checkbutton), label); gtk_widget_show (label); label = gtk_label_new ("Ajout de page"); gtk_notebook_insert_page (GTK_NOTEBOOK (notebook), checkbutton, label, 2); /* Ajout de pages au dut du bloc-notes */ for (i=0; i < 5; i++) { sprintf(bufferf, "Prepend Frame %d", i+1); sprintf(bufferl, "Page %d", i+1); frame = gtk_frame_new (bufferf); gtk_container_border_width (GTK_CONTAINER (frame), 10); gtk_widget_set_usize (frame, 100, 75); gtk_widget_show (frame); label = gtk_label_new (bufferf); gtk_container_add (GTK_CONTAINER (frame), label); gtk_widget_show (label); label = gtk_label_new (bufferl); gtk_notebook_prepend_page (GTK_NOTEBOOK(notebook), frame, label); } /* Configuration de la page de dart (page 4) */ gtk_notebook_set_page (GTK_NOTEBOOK(notebook), 3); /* Crtion des boutons */ button = gtk_button_new_with_label ("Fermer"); gtk_signal_connect_object (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (delete), NULL); gtk_table_attach_defaults(GTK_TABLE(table), button, 0,1,1,2); gtk_widget_show(button); button = gtk_button_new_with_label ("Page suivante"); gtk_signal_connect_object (GTK_OBJECT (button), "clicked", (GtkSignalFunc) gtk_notebook_next_page, GTK_OBJECT (notebook)); gtk_table_attach_defaults(GTK_TABLE(table), button, 1,2,1,2); gtk_widget_show(button); button = gtk_button_new_with_label ("Page prente"); gtk_signal_connect_object (GTK_OBJECT (button), "clicked", (GtkSignalFunc) gtk_notebook_prev_page, GTK_OBJECT (notebook)); gtk_table_attach_defaults(GTK_TABLE(table), button, 2,3,1,2); gtk_widget_show(button); button = gtk_button_new_with_label ("Position des indicateurs"); gtk_signal_connect_object (GTK_OBJECT (button), "clicked", (GtkSignalFunc) rotate_book, GTK_OBJECT(notebook)); gtk_table_attach_defaults(GTK_TABLE(table), button, 3,4,1,2); gtk_widget_show(button); button = gtk_button_new_with_label ("Indicateurs/Contours oui/non"); gtk_signal_connect_object (GTK_OBJECT (button), "clicked", (GtkSignalFunc) tabsborder_book, GTK_OBJECT (notebook)); gtk_table_attach_defaults(GTK_TABLE(table), button, 4,5,1,2); gtk_widget_show(button); button = gtk_button_new_with_label ("Oter page"); gtk_signal_connect_object (GTK_OBJECT (button), "clicked", (GtkSignalFunc) remove_book, GTK_OBJECT(notebook)); gtk_table_attach_defaults(GTK_TABLE(table), button, 5,6,1,2); gtk_widget_show(button); gtk_widget_show(table); gtk_widget_show(window); gtk_main (); return 0; } En espant que ceci vous aide crr des blocs-notes pour vos applications GTK. 88..22.. FFeennrreess aavveecc bbaarrrreess ddee ddiilleemmeenntt Les fenres avec barres de dilement servent crr des zones dilantes l'intieur d'une vraie fenre. On peut inser n'importe quel widget dans ces fenres, ils seront accessibles quelle que soit leur taille en utilisant les barres de dilement. La fonction suivante sert crr une fenre avec barre de dilement : GtkWidget* gtk_scrolled_window_new (GtkAdjustment *hadjustment, GtkAdjustment *vadjustment); Le premier paramre est l'ajustement horizontal, et le second l'ajustement vertical. Ils sont presque toujours positionn NULL. void gtk_scrolled_window_set_policy (GtkScrolledWindow *scrolled_window, GtkPolicyType hscrollbar_policy, GtkPolicyType vscrollbar_policy); Cela permet de configurer le fonctionnement des barres de dilement. Le premier paramre est la fenre dilement que l'on veut modifier, le second configure le fonctionnement de la barre horizontale et le troisie celui de la barre verticale. Ce fonctionnement peut re GTK_POLICY AUTOMATIC ou GTK_POLICY_ALWAYS. GTK_POLICY_AUTOMATIC didera automatiquement de votre besoin en barres de dilement, alors que GTK_POLICY_ALWAYS mettra toujours celles-ci. Voici un exemple simple qui place 100 boutons commutateurs dans une fenre dilement. Je n'ai commentque les parties qui sont nouvelles pour vous. #include void destroy(GtkWidget *widget, gpointer *data) { gtk_main_quit(); } int main (int argc, char *argv[]) { static GtkWidget *window; GtkWidget *scrolled_window; GtkWidget *table; GtkWidget *button; char buffer[32]; int i, j; gtk_init (&argc, &argv); /* Crtion d'une boe de dialogue pour y placer la fenre dilement. * Une boe de dialogue est une fenre comme les autres sauf qu'elle contient * une vbox et un sarateur horizontal. Ce n'est qu'un raccourci pour crr des * zones de dialogue. */ window = gtk_dialog_new (); gtk_signal_connect (GTK_OBJECT (window), "destroy", (GtkSignalFunc) destroy, NULL); gtk_window_set_title (GTK_WINDOW (window), "dialog"); gtk_container_border_width (GTK_CONTAINER (window), 0); /* Crtion d'une fenre dilement. */ scrolled_window = gtk_scrolled_window_new (NULL, NULL); gtk_container_border_width (GTK_CONTAINER (scrolled_window), 10); /* La gestion des barres est soit GTK_POLICY AUTOMATIC, soit GTK_POLICY_ALWAYS. * GTK_POLICY_AUTOMATIC dide automatiquement s'il faut ou non des barres, * GTK_POLICY_ALWAYS met toujours des barres * Le premier paramre correspond la barre horizontale, * le second la barre verticale. */ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); /* Crtion d'une boe de dialogue */ gtk_box_pack_start (GTK_BOX (GTK_DIALOG(window)->vbox), scrolled_window, TRUE, TRUE, 0); gtk_widget_show (scrolled_window); /* Crtion d'une table de 10x10 cases. */ table = gtk_table_new (10, 10, FALSE); /* Configure l'espace des lignes et des colonnes de 10 pixels */ gtk_table_set_row_spacings (GTK_TABLE (table), 10); gtk_table_set_col_spacings (GTK_TABLE (table), 10); /* Place la table fans la fenre dilement */ gtk_container_add (GTK_CONTAINER (scrolled_window), table); gtk_widget_show (table); /* Cr une grille de boutons commutateurs dans la table */ for (i = 0; i < 10; i++) for (j = 0; j < 10; j++) { sprintf (buffer, "bouton (%d,%d)\n", i, j); button = gtk_toggle_button_new_with_label (buffer); gtk_table_attach_defaults (GTK_TABLE (table), button, i, i+1, j, j+1); gtk_widget_show (button); } /* Ajoute un bouton Fermer en bas de la boe de dialogue */ button = gtk_button_new_with_label ("Fermer"); gtk_signal_connect_object (GTK_OBJECT (button), "clicked", (GtkSignalFunc) gtk_widget_destroy, GTK_OBJECT (window)); /* On met ce bouton en bouton par daut */ GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->action_area), button, TRUE, TRUE, 0); /* Rupe le bouton par daut. Le fait de presser la touche Entr * activera le bouton. */ gtk_widget_grab_default (button); gtk_widget_show (button); gtk_widget_show (window); gtk_main(); return(0); } Essayez de changer la taille de la fenre et faites attention aux rctions des barres de dilement. On peut aussi utiliser la fonction _g_t_k___w_i_d_g_e_t___s_e_t___u_s_i_z_e_(_) pour configurer la taille par daut de la fenre et des autres widgets. 99.. WWiiddggeettss lliisstteess Le widget _G_t_k_L_i_s_t sert de container vertical pour des widgets _G_t_k_L_i_s_t_I_t_e_m. Un widget _G_t_k_L_i_s_t posse sa propre fenre pour recevoir lesements et sa propre couleur de fond qui est habituellement blanche. Comme il est directement divde _G_t_k_C_o_n_t_a_i_n_e_r, il peut re traitcomme tel en utilisant la macro GTK_CONTAINER(List) : voir le widget _G_t_k_C_o_n_t_a_i_n_e_r pour en savoir plus. On doit d'abord connare l'utilisation des _G_L_i_s_t et des fonctions _g___l_i_s_t___*_(_) qui leur sont lis pour pouvoir utiliser pleinement le widget _G_t_k_L_i_s_t. Un champ de la structure d'un widget _G_t_k_L_i_s_t nous intesse particuliement : struct _GtkList { ... GList *selection; guint selection_mode; ... }; Le champ _s_e_l_e_c_t_i_o_n d'un _G_t_k_L_i_s_t pointe sur une liste cha de tous les items qui sont sectionn, ou vaut NULL si aucune section n'est faite. Ainsi, pour connare la section courante, on consulte le champ _G_T_K___L_I_S_T_(_)_-_>_s_e_l_e_c_t_i_o_n mais on ne doit pas le modifier car ses champs internes sont g par les fonctions _g_t_k___l_i_s_t___*_(_). Le champ _s_e_l_e_c_t_i_o_n___m_o_d_e dermine les options de section d'un _G_t_k_L_i_s_t et donc le contenu du champ du _G_T_K___L_I_S_T_(_)_-_>_s_e_l_e_c_t_i_o_n : _s_e_l_e_c_t_i_o_n___m_o_d_e peut avoir l'une des valeurs suivantes : +o GTK_SELECTION_SINGLE - _s_e_l_e_c_t_i_o_n vaut NULL ou contient un pointeur vers un seul item sectionn +o GTK_SELECTION_BROWSE - _s_e_l_e_c_t_i_o_n vaut NULL si la liste ne contient aucun widget ou seulement des widgets non sensitifs. Sinon, ce champ contient un pointeur vers une seule structure Glist, et donc vers exactement un item. +o GTK_SELECTION_MULTIPLE - _s_e_l_e_c_t_i_o_n vaut NULL si aucun item n'est sectionnou pointe vers le premier item sectionn Ce dernier point son tour vers le second item, etc. +o GTK_SELECTION_EXTENDED - _s_e_l_e_c_t_i_o_n vaut toujours NULL. La valeur par daut est GTK_SELECTION_MULTIPLE. 99..11.. SSiiggnnaauuxx void GtkList::selection_changed (GtkList *LIST) Ce signal sera invoquchaque fois que le champ section d'un GtkList a chang Cela arrive lorsqu'un fils d'un GtkList a sectionnou dectionn void GtkList::select_child (GtkList *LIST, GtkWidget *CHILD) Ce signal est invoqulorsqu'un fils du GtkList va re sectionn Ceci arrive principalement lors d'appels _g_t_k___l_i_s_t___s_e_l_e_c_t___i_t_e_m_(_)_, _g_t_k___l_i_s_t___s_e_l_e_c_t___c_h_i_l_d_(_) et lors d'appuis de boutons. Quelques fois, il est indirectement dlenchlorsque des fils sont ajout ou supprim du GtkList. void GtkList::unselect_child (GtkList *LIST, GtkWidget *CHILD) Ce signal est invoqulorsqu'un fils du GtkList va re dectionn Cela arrive principalement lors d'appels _g_t_k___l_i_s_t___u_n_s_e_l_e_c_t___i_t_e_m_(_)_, _g_t_k___l_i_s_t___u_n_s_e_l_e_c_t___c_h_i_l_d_(_), et lors d'appuis de boutons. Quelques fois, il est indirectement dlenchlorsque des fils sont ajout ou supprim du GtkList. 99..22.. FFoonnccttiioonnss guint gtk_list_get_type (void) Retourne l'identificateur de type GtkList GtkWidget* gtk_list_new (void) Cr un nouvel objet GtkList Le nouveau widget est retournsous la forme d'un pointeur vers un objet GtkWidget NULL est retournen cas d'erreur. void gtk_list_insert_items (GtkList *LIST, GList *ITEMS, gint POSITION) Inse des items dans _L_I_S_T, partir de _P_O_S_I_T_I_O_N. _I_T_E_M_S est une liste doublement cha ochaque noeud doit pointer vers un nouveau _G_t_k_L_i_s_t_I_t_e_m. Les noeuds _G_L_i_s_t de _I_T_E_M_S sont pris en charge par _L_I_S_T. void gtk_list_append_items (GtkList *LIST, GList *ITEMS) Inse des items la fin de _L_I_S_T selon le me principe que _g_t_k___l_i_s_t___i_n_s_e_r_t___i_t_e_m_s. Les noeuds _G_L_i_s_t de _I_T_E_M_S sont pris en charge par _L_I_S_T. void gtk_list_prepend_items (GtkList *LIST, GList *ITEMS) Inse des items au dut de _L_I_S_T selon le me principe que _g_t_k___l_i_s_t___i_n_s_e_r_t___i_t_e_m_s. Les noeuds _G_L_i_s_t de _I_T_E_M_S sont pris en charge par _L_I_S_T. void gtk_list_remove_items (GtkList *LIST, GList *ITEMS) e des items de _L_I_S_T. _I_T_E_M_S est une liste doublement cha dont chaque noeud pointe vers un fils direct de _L_I_S_T. Il est de la responsabilitde l'appelant de faire un appel _g___l_i_s_t___f_r_e_e_(_I_T_E_M_S_) apr cela. L'appelant doit aussi druire lui-me les items. void gtk_list_clear_items (GtkList *LIST, gint START, gint END) e et druit des items de _L_I_S_T. Un widget est concernsi sa position courante dans _L_I_S_T est dans l'intervalle spifipar _S_T_A_R_T et _E_N_D. void gtk_list_select_item (GtkList *LIST, gint ITEM) Invoque le signal _G_t_k_L_i_s_t_:_:_s_e_l_e_c_t___c_h_i_l_d pour un item spifipar sa position courante dans _L_I_S_T. void gtk_list_unselect_item (GtkList *LIST, gint ITEM) Invoque le signal _G_t_k_L_i_s_t_:_:_u_n_s_e_l_e_c_t___c_h_i_l_d pour un item spifipar sa position courante dans _L_I_S_T. void gtk_list_select_child (GtkList *LIST, GtkWidget *CHILD) Invoque le signal _G_t_k_L_i_s_t_:_:_s_e_l_e_c_t___c_h_i_l_d pour le fils _C_H_I_L_D spifi void gtk_list_unselect_child (GtkList *LIST, GtkWidget *CHILD) Invoque le signal _G_t_k_L_i_s_t_:_:_u_n_s_e_l_e_c_t___c_h_i_l_d pour le fils _C_H_I_L_D spifi gint gtk_list_child_position (GtkList *LIST, GtkWidget *CHILD) Retourne la position de _C_H_I_L_D dans _L_I_S_T. Retourne -1 en cas d'erreur. void gtk_list_set_selection_mode (GtkList *LIST, GtkSelectionMode MODE) Configure _L_I_S_T dans le mode de section _M_O_D_E qui peut re GTK_SELECTION_SINGLE, GTK_SELECTION_BROWSE, GTK_SELECTION_MULTIPLE ou GTK_SELECTION_EXTENDED. GtkList* GTK_LIST (gpointer OBJ) Convertit un pointeur gique en <\em GtkList*\ Voir _S_t_a_n_d_a_r_d _M_a_c_r_o_s_:_:, pour plus d'informations. GtkListClass* GTK_LIST_CLASS (gpointer CLASS) Convertit un pointeur gique en GtkListClass* Voir _S_t_a_n_d_a_r_d _M_a_c_r_o_s_:_:, pour plus d'informations. gint GTK_IS_LIST (gpointer OBJ) Dermine si un pointeur gique rence un objet GtkList Voir _S_t_a_n_d_a_r_d _M_a_c_r_o_s_:_:, pour plus d'informations. 99..33.. EExxeemmppllee Voici un programme affichant les changements de section dans une _G_t_k_L_i_s_t et permettant d'emprisonner des items en les sectionnant avec le bouton droit de la souris. /* Compilez ce programme avec : * $ gcc -L/usr/X11R6/lib/ -I/usr/local/include/ -lgtk -lgdk -lglib -lX11 -lm -Wall main.c */ #include #include /* Chae pour stocker les donns dans les items de la liste. */ const gchar *list_item_data_key="list_item_data"; /* prototypes des gestionnaires de signaux que l'on connectera au widget GtkList. */ static void sigh_print_selection (GtkWidget *gtklist, gpointer func_data); static void sigh_button_event (GtkWidget *gtklist, GdkEventButton *event, GtkWidget *frame); /* fonction principale pour configurer l'interface utilisateur */ gint main (int argc, gchar *argv[]) { GtkWidget *separator; GtkWidget *window; GtkWidget *vbox; GtkWidget *scrolled_window; GtkWidget *frame; GtkWidget *gtklist; GtkWidget *button; GtkWidget *list_item; GList *dlist; guint i; gchar buffer[64]; /* initialise gtk (et donc gdk) */ gtk_init(&argc, &argv); /* Crtion d'une fenre pour placer tous les widgets. * Connexion de gtk_main_quit() l'ement "destroy" de * la fenre afin de prendre en charge les ements fermeture d'une * fenre du gestionnaire de fenre. */ window=gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(window), "Exemple de widget GtkList"); gtk_signal_connect(GTK_OBJECT(window), "destroy", GTK_SIGNAL_FUNC(gtk_main_quit), NULL); /* l'intieur de la fenre, on a besoin d'une boe pour placer * verticalement les widgets. */ vbox=gtk_vbox_new(FALSE, 5); gtk_container_border_width(GTK_CONTAINER(vbox), 5); gtk_container_add(GTK_CONTAINER(window), vbox); gtk_widget_show(vbox); /* Fenre dilement pour placer le widget GtkList l'intieur. */ scrolled_window=gtk_scrolled_window_new(NULL, NULL); gtk_widget_set_usize(scrolled_window, 250, 150); gtk_container_add(GTK_CONTAINER(vbox), scrolled_window); gtk_widget_show(scrolled_window); /* Crtion du widget GtkList * Connexion du gestionnaire de signal sigh_print_selection() au signal * "selection_changed" du GtkList pour afficher les items sectionn * chaque fois que la section change. */ gtklist=gtk_list_new(); gtk_container_add(GTK_CONTAINER(scrolled_window), gtklist); gtk_widget_show(gtklist); gtk_signal_connect(GTK_OBJECT(gtklist), "selection_changed", GTK_SIGNAL_FUNC(sigh_print_selection), NULL); /* Crtion d'une Prison pour y mettre un item. */ frame=gtk_frame_new("Prison"); gtk_widget_set_usize(frame, 200, 50); gtk_container_border_width(GTK_CONTAINER(frame), 5); gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT); gtk_container_add(GTK_CONTAINER(vbox), frame); gtk_widget_show(frame); /* Connexion du gestionnaire de signal sigh_button_event() au signal * mise au arrs des items du GtkList. */ gtk_signal_connect(GTK_OBJECT(gtklist), "button_release_event", GTK_SIGNAL_FUNC(sigh_button_event), frame); /* Crtion d'un sarateur. */ separator=gtk_hseparator_new(); gtk_container_add(GTK_CONTAINER(vbox), separator); gtk_widget_show(separator); /* Crtion d'un bouton et connexion de son signal "clicked" la * destruction de la fenre. */ button=gtk_button_new_with_label("Fermeture"); gtk_container_add(GTK_CONTAINER(vbox), button); gtk_widget_show(button); gtk_signal_connect_object(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(window)); /* Crtion de 5 items, chacun ayant son propre label. * Ajout de ceux-ci au GtkList en utilisant gtk_container_add(). * On interroge le texte du label et on l'associe avec * list_item_data_key chaque item. */ for (i=0; i<5; i++) { GtkWidget *label; gchar *string; sprintf(buffer, "ListItemContainer avec Label #%d", i); label=gtk_label_new(buffer); list_item=gtk_list_item_new(); gtk_container_add(GTK_CONTAINER(list_item), label); gtk_widget_show(label); gtk_container_add(GTK_CONTAINER(gtklist), list_item); gtk_widget_show(list_item); gtk_label_get(GTK_LABEL(label), &string); gtk_object_set_data(GTK_OBJECT(list_item), list_item_data_key, string); } /* Crtion de 5 autres labels. Cette fois-ci, on utilise * gtk_list_item_new_with_label(). On ne peut interroger la chae * des labels car on n'a pas les pointeurs de labels et on associe * donc simplement le list_item_data_key de chaque item ayant la me * chae de texte pour l'ajouter au items que l'on place dans une liste * doublement cha (GList). On les ajoute alors par un simple appel * gtk_list_append_items(). * Comme on utilise g_list_prepend() pour mettre les items dans la liste * doublement cha, leur ordre sera droissant (au lieu d're croissant si * on utilisait g_list_append()). */ dlist=NULL; for (; i<10; i++) { sprintf(buffer, "Item avec le label %d", i); list_item=gtk_list_item_new_with_label(buffer); dlist=g_list_prepend(dlist, list_item); gtk_widget_show(list_item); gtk_object_set_data(GTK_OBJECT(list_item), list_item_data_key, "Item avec label intr&"); } gtk_list_append_items(GTK_LIST(gtklist), dlist); /* Enfin, on veut voir la fenre... */ gtk_widget_show(window); /* Lancement de la boucle principale de gtk */ gtk_main(); /* On arrive ici apr que gtk_main_quit() ait appel lorsque * la fenre principale a druite. */ } /* Gestionnaire de signal connectaux ements boutons presser/relher * du GtkList. */ void sigh_button_event (GtkWidget *gtklist, GdkEventButton *event, GtkWidget *frame) { /* On ne fait quelque chose que si le troisie bouton (celui de droite) a * relh */ if (event->type==GDK_BUTTON_RELEASE && event->button==3) { GList *dlist, *free_list; GtkWidget *new_prisoner; /* On recherche l'item sectionnce moment pris. * Ce sera notre prisonnier ! */ dlist=GTK_LIST(gtklist)->selection; if (dlist) new_prisoner=GTK_WIDGET(dlist->data); else new_prisoner=NULL; /* On recherche les items dprisonniers et on les * remet dans la liste. * Il ne faut pas oublier de liber la liste doublement * cha que gtk_container_children() retourne. */ dlist=gtk_container_children(GTK_CONTAINER(frame)); free_list=dlist; while (dlist) { GtkWidget *list_item; list_item=dlist->data; gtk_widget_reparent(list_item, gtklist); dlist=dlist->next; } g_list_free(free_list); /* Si l'on a un nouveau prisonnier, on l'e du GtkList et on le place * dans le cadre Prison On doit dectionner l'item avant. if (new_prisoner) { GList static_dlist; static_dlist.data=new_prisoner; static_dlist.next=NULL; static_dlist.prev=NULL; gtk_list_unselect_child(GTK_LIST(gtklist), new_prisoner); gtk_widget_reparent(new_prisoner, frame); } } } /* Gestionnaire de signal appellorsque le GtkList * et le signal "selection_changed". */ void sigh_print_selection (GtkWidget *gtklist, gpointer func_data) { GList *dlist; /* Recherche dans la liste doublement cha des items sectionn * du GtkList, faire en lecture seulement ! */ dlist=GTK_LIST(gtklist)->selection; /* S'il n'y a pas d'items sectionn il n'y a rien d'autre faire que * de le dire l'utilisateur. */ if (!dlist) { g_print("Section nettoy\n"); return; } /* Ok, on a une section et on l'affiche. */ g_print("La section est "); /* On rupe l'item dans la liste doublement cha * puis on interroge la donn associ par list_item_data_key * et on l'affiche. */ while (dlist) { GtkObject *list_item; gchar *item_data_string; list_item=GTK_OBJECT(dlist->data); item_data_string=gtk_object_get_data(list_item, list_item_data_key); g_print("%s ", item_data_string); dlist=dlist->next; } g_print("\n"); } 99..44.. WWiiddggeett iitteemm ddee lliissttee Le widget _G_t_k_L_i_s_t_I_t_e_m sert de container pour contenir un fils, lui fournissant des fonctions de section/delection exactement comme le widget GtkList les demande pour ses fils. Un _G_t_k_L_i_s_t_I_t_e_m a sa propre fenre pour recevoir les ements et a sa propre couleur de fond, habituellement blanche. Comme il est directement divd'un _G_t_k_I_t_e_m, il peut re traitcomme tel en utilisant la macro GTK_ITEM(ListItem), reportez-vous la section sur le widget GtkItem pour plus de dail sur celui-ci. Habituellement, un _G_t_k_L_i_s_t_I_t_e_m contient juste un label pour identifier, par exemple, un nom de fichier dans un _G_t_k_L_i_s_t -- la fonction appropri _g_t_k___l_i_s_t___i_t_e_m___n_e_w___w_i_t_h___l_a_b_e_l_(_) est donc fournie. Le me effet peut re obtenu en crnt un _G_t_k_L_a_b_e_l part, en configurant son alignement avec _x_a_l_i_g_n=0 et _y_a_l_i_g_n=0.5 suivi d'un ajout ultieur au _G_t_k_L_i_s_t_I_t_e_m. Tout comme on n'est pas forcd'ajouter un _G_t_k_L_a_b_e_l un _G_t_k_L_i_s_t_I_t_e_m, on peut aussi ajouter un _G_t_k_V_B_o_x ou un _G_t_k_A_r_r_o_w etc. un _G_t_k_L_i_s_t_I_t_e_m. 99..55.. SSiiggnnaauuxx Un _G_t_k_L_i_s_t_I_t_e_m ne cr pas de nouveaux signaux par lui-me, mais hite de ceux d'un _G_t_k_I_t_e_m. Voir _G_t_k_I_t_e_m_:_:, pour plus d'informations. 99..66.. FFoonnccttiioonnss guint gtk_list_item_get_type (void) Retourne l'identificateur du type GtkListItem GtkWidget* gtk_list_item_new (void) Crtion d'un objet _G_t_k_L_i_s_t_I_t_e_m. Le nouveau widget est retournsous la forme d'un pointeur vers un objet _G_t_k_W_i_d_g_e_t. NULL est retournen cas d'erreur. GtkWidget* gtk_list_item_new_with_label (gchar *LABEL) Crtion d'un objet _G_t_k_L_i_s_t_I_t_e_m ayant un simple _G_t_k_L_a_b_e_l comme seul fils. Le nouveau widget est retournsous la forme d'un pointeur vers un objet _G_t_k_W_i_d_g_e_t. NULL est retournen cas d'erreur. void gtk_list_item_select (GtkListItem *LIST_ITEM) Cette fonction est surtout un emballage de _g_t_k___i_t_e_m___s_e_l_e_c_t _(_G_T_K___I_T_E_M _(_l_i_s_t___i_t_e_m_)_) qui ettra le signal _G_t_k_I_t_e_m_:_:_s_e_l_e_c_t. Voir _G_t_k_I_t_e_m_:_:, pour plus d'informations. void gtk_list_item_deselect (GtkListItem *LIST_ITEM) Cette fonction est surtout un emballage de _g_t_k___i_t_e_m___d_e_s_e_l_e_c_t _(_G_T_K___I_T_E_M _(_l_i_s_t___i_t_e_m_)_) qui ettra le signal _G_t_k_I_t_e_m_:_:_d_e_s_e_l_e_c_t. Voir _G_t_k_I_t_e_m_:_:, pour plus d'informations. GtkListItem* GTK_LIST_ITEM (gpointer OBJ) Convertit un pointeur gique en _G_t_k_L_i_s_t_I_t_e_m_*. Voir _S_t_a_n_d_a_r_d _M_a_c_r_o_s_:_: pour plus d'informations. GtkListItemClass* GTK_LIST_ITEM_CLASS (gpointer CLASS) Convertit un pointeur gique en _G_t_k_L_i_s_t_I_t_e_m_C_l_a_s_s_*. Voir _S_t_a_n_d_a_r_d _M_a_c_r_o_s_:_: pour plus d'informations. gint GTK_IS_LIST_ITEM (gpointer OBJ) Dermine si un pointeur gique se re un objet _G_t_k_L_i_s_t_I_t_e_m. Voir _S_t_a_n_d_a_r_d _M_a_c_r_o_s_:_: pour plus d'informations. 99..77.. EExxeemmppllee L'exemple des GtkList couvre aussi l'utilisation des GtkListItem. udiez-le attentivement. 1100.. WWiiddggeettss sseeccttiioonnss ddee ffiicchhiieerrss Le widget section de fichier est un moyen simple et rapide pour afficher un fichier dans une boe de dialogue. Il est complet, avec des boutons Ok, Annuler et Aide. C'est un bon moyen de raccourcir les temps de programmation. Pour crr une boe de section de fichier, on utilise : GtkWidget* gtk_file_selection_new (gchar *title); Pour configurer le nom de fichier, par exemple pour aller dans un rertoire pris ou donner un nom de fichier par daut, on utilise la fonction : void gtk_file_selection_set_filename (GtkFileSelection *filesel, gchar *filename); Pour ruper le texte que l'utilisateur a entr ou sur lequel il a cliqu on utilisera la fonction : gchar* gtk_file_selection_get_filename (GtkFileSelection *filesel); Des pointeurs permettent d'accer aux widgets contenus dans la widget de section de fichiers. Ce sont : +o dir_list +o file_list +o selection_entry +o selection_text +o main_vbox +o ok_button +o cancel_button +o help_button Le plus souvent, on utilise les pointeurs _o_k___b_u_t_t_o_n_, _c_a_n_c_e_l___b_u_t_t_o_n, et _h_e_l_p___b_u_t_t_o_n pour priser leurs utilisations. Voici un exemple emprunt_t_e_s_t_g_t_k_._c et modifipour fonctionner tout seul. Comme vous le verrez, il n'y a pas grand chose faire pour crr un wigdget de section de fichier. Cependant, dans cet exemple, si le bouton Aide appara l'ran, il ne fait rien car aucun signal ne lui est attach #include /* Rupe le nom de fichier sectionnet l'affiche sur la console. */ void file_ok_sel (GtkWidget *w, GtkFileSelection *fs) { g_print ("%s\n", gtk_file_selection_get_filename (GTK_FILE_SELECTION (fs))); } void destroy (GtkWidget *widget, gpointer *data) { gtk_main_quit (); } int main (int argc, char *argv[]) { GtkWidget *filew; gtk_init (&argc, &argv); /* Crtion d'un widget de section de fichier. */ filew = gtk_file_selection_new ("File selection"); gtk_signal_connect (GTK_OBJECT (filew), "destroy", (GtkSignalFunc) destroy, &filew); /* Connexion de ok_button la fonction file_ok_sel() */ gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (filew)->ok_button), "clicked", (GtkSignalFunc) file_ok_sel, filew ); /* Connexion de cancel_button pour druire le widget */ gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION (filew)->cancel_button), "clicked", (GtkSignalFunc) gtk_widget_destroy, GTK_OBJECT (filew)); /* Configuration du nom de fichier, comme s'il s'agissait d'un dialogue de * sauvegarde et que nous donnions un nom de fichier par daut. */ gtk_file_selection_set_filename (GTK_FILE_SELECTION(filew), "penguin.png"); gtk_widget_show(filew); gtk_main (); return 0; } 1111.. WWiiddggeettss MMeennuu Il y a deux fans de crr des menus, la facile et la compliqu. Les deux ont leur utilit mais on peut galement utiliser l'_u_s_i_n_e _m_e_n_u_s (c'est la mhode facile...). La mhode compliqu consiste crr tous les menus en utilisant directement les appels. La mhode facile consiste utiliser les appels _g_t_k___m_e_n_u___f_a_c_t_o_r_y. C'est beaucoup plus simple, mais chaque approche a ses avantages et inconvients. L'usine menus est beaucoup plus facile utiliser, elle facilite aussi l'ajout d'autres menus. Par contre, rire quelques fonctions permettant de crr des menus en utilisant la mhode manuelle peut re le dut 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. 1111..11.. CCrrttiioonn mmaannuueellllee ddee mmeennuuss Selon la tradition pagogique, nous commencerons par le plus compliqu:) Regardons les fonctions utiliss pour crr les menus. La premie sert crr un nouveau menu. GtkWidget *gtk_menu_bar_new() Cette fonction cr une nouvelle barre de menu. On utilise la fonction _g_t_k___c_o_n_t_a_i_n_e_r___a_d_d pour la placer dans une fenre, ou les fonctions _b_o_x___p_a_c_k pour la placer dans une boe - la me que pour les boutons. GtkWidget *gtk_menu_new(); Cette fonction retourne un pointeur vers un nouveau menu, il n'est jamais montr(avec _g_t_k___w_i_d_g_e_t___s_h_o_w), il ne fait que contenir les items du menu. Ceci deviendra plus clair lorsque nous udierons l'exemple ci- dessous. Les deux appels suivants servent crr des items de menu qui seront plac dans le menu. GtkWidget *gtk_menu_item_new() et GtkWidget *gtk_menu_item_new_with_label(const char *label) Ces appels servent crr les menus qui doivent re affich. On doit bien faire la diffence entre un menu qui est cr avec _g_t_k___m_e_n_u___n_e_w_(_) et un item de menu cr avec les fonctions _g_t_k___m_e_n_u___i_t_e_m___n_e_w_(_). L'item de menu sera un vitable bouton avec une action associ alors qu'un menu sera un container contenant les items. gtk_menu_item_append() gtk_menu_item_set_submenu() Les fonctions _g_t_k___m_e_n_u___n_e_w___w_i_t_h___l_a_b_e_l_(_) et _g_t_k___m_e_n_u___n_e_w_(_) sont telles que vous les attendiez apr avoir udiles boutons. L'une cr un nouvel item de menu contenant dun label, et l'autre ne fait que crr un item de menu vide. Voici les apes pour crr une barre de menu avec des menus attach : +o Crr un nouveau menu avec _g_t_k___m_e_n_u___n_e_w_(_) +o Crr un item de menu avec _g_t_k___m_e_n_u___i_t_e_m___n_e_w_(_). Ce sera la racine du menu, le texte apparaissant ici sera aussi sur la barre de menu. +o Utiliser plusieurs appels _g_t_k___m_e_n_u___i_t_e_m___n_e_w_(_) pour chaque item que l'on dire dans le menu. Utiliser _g_t_k___m_e_n_u___i_t_e_m___a_p_p_e_n_d_(_) pour placer chacun de ces items les uns apr les autres. Cela cr une liste d'items de menu. +o Utiliser _g_t_k___m_e_n_u___i_t_e_m___s_e_t___s_u_b_m_e_n_u_(_) pour attacher les items de menus nouvellement crs l'item de menu racine (celui crla seconde ape). +o Crr une nouvelle barre de menu avec _g_t_k___m_e_n_u___b_a_r___n_e_w_(_). Cette ape ne doit re faite qu'une fois lorsque l'on cr une sie de menu sur une seule barre de menus. +o Utiliser _g_t_k___m_e_n_u___b_a_r___a_p_p_e_n_d_(_) pour placer le menu racine dans la barre de menu. La crtion d'un menu surgissant est presque identique. La diffence est que le menu n'est pas postautomatiquement par une barre de menu, mais explicitement en appelant la fonction _g_t_k___m_e_n_u___p_o_p_u_p_(_) par un ement bouton press Suivez ces apes +o Crr une fonction de gestion d'ement. Elle doit avoir le prototype static gint handler(GtkWidget *widget, GdkEvent *event); et elle utilisera l'ement _e_v_e_n_t pour savoir ofaire surgir le menu. +o Ce gestionnaire, si l'ement est un appui sur un bouton souris, traite _e_v_e_n_t comme un ement bouton (ce qu'il est) et l'utilise, de la fan indiqu dans le code d'exemple, pour passer l'information _g_t_k___m_e_n_u___p_o_p_u_p_(_). +o Lier ce gestionnaire un widget avec : gtk_signal_connect_object(GTK_OBJECT(widget), "event", GTK_SIGNAL_FUNC (handler), GTK_OBJECT(menu)); o_w_i_d_g_e_t est le widget auquel vous le liez, _h_a_n_d_l_e_r est le gestion- naire, et _m_e_n_u est un menu cr avec _g_t_k___m_e_n_u___n_e_w_(_). Cela peut re un menu qui est aussi postpar une barre de menu, comme le montre l'exem- ple. 1111..22.. EExxeemmppllee ddee mmeennuu mmaannuueell #include 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); /* Crtion d'un fenre */ 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 affichsur la barre * de menu. Il n'a pas de gestionnaire de signal attachcar 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 une petite boucle crnt trois entrs 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); /* Crtion 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'intessant lorsque l'item est * sectionn */ 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 spifique nous voulons que notre nouveau menu * soit le menu du menu racine */ gtk_menu_item_set_submenu(GTK_MENU_ITEM (root_menu), menu); /* Crtion 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); /* Crtion d'une barre de menus pour contenir les menus. Puis, on * l'ajoute notre fenre principale. */ menu_bar = gtk_menu_bar_new(); gtk_box_pack_start(GTK_BOX(vbox), menu_bar, FALSE, FALSE, 2); gtk_widget_show(menu_bar); /* Crtion 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 dha;-) */ gtk_menu_bar_append(GTK_MENU_BAR (menu_bar), root_menu); /* Affichage de la fenre. */ gtk_widget_show(window); gtk_main (); return 0; } /* On rond un appui sur le bouton en postant un nouveau menu passcomme * un widget. * * On remarque que le paramre "widget" est le menu poster, PAS le bouton * qui a 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 gcet ement. */ return TRUE; } /* On indique l'appelant que l'on n'a pas gcet ement. */ return FALSE; } /* Affiche une chae 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 sectionnable et, en utilisant une table de raccourcis clavier, lier des touches aux fonctions du menu. 1111..33.. UUttiilliissaattiioonn ddee GGttkkMMeennuuFFaaccttoorryy Maintenant que nous avons explorla voie difficile, nous allons voir l'utilisation des appels _g_t_k___m_e_n_u___f_a_c_t_o_r_y_. 1111..44.. EExxeemmppllee dd''uussiinnee mmeennuu Voici un exemple utilisant l'usine menu de GTK. Le premier fichier est _m_e_n_u_s_._h. Nous sarerons _m_e_n_u_s_._c et _m_a_i_n_._c cause des variables globales utiliss dans le fichier _m_e_n_u_s_._c. #ifndef __MENUS_H__ #define __MENUS_H__ #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ void get_main_menu (GtkWidget **menubar, GtkAcceleratorTable **table); void menus_create(GtkMenuEntry *entries, int nmenu_entries); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* __MENUS_H__ */ Voici le fichier _m_e_n_u_s_._c : #include #include #include "main.h" static void menus_remove_accel(GtkWidget * widget, gchar * signal_name, gchar * path); static gint menus_install_accel(GtkWidget * widget, gchar * signal_name, gchar key, gchar modifiers, gchar * path); void menus_init(void); void menus_create(GtkMenuEntry * entries, int nmenu_entries); /* Structure GtkMenuEntry utilis pour crr les menus. Le premier champ * est la chae de dinition du menu. Le second, la touche de raccourci * utilis pour accer cette fonction du menu avec le clavier. * Le troisie est la fonction de rappel utiliser lorsque l'item de menu * est choisi (par la touche de raccourci ou avec la souris). Le dernier * ent est la donn passer la fonction de rappel. */ static GtkMenuEntry menu_items[] = { {"
/Fichier/Nouveau", "N", NULL, NULL}, {"
/Fichier/Ouvrir", "O", NULL, NULL}, {"
/Fichier/Sauver", "S", NULL, NULL}, {"
/Fichier/Sauver sous", NULL, NULL, NULL}, {"
/Fichier/", NULL, NULL, NULL}, {"
/Fichier/Quitter", "Q", file_quit_cmd_callback, "OK, c'est fini"}, {"
/Options/Test", NULL, NULL, NULL} }; /* Calcul du nombre d'ents de menu_item */ static int nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]); static int initialize = TRUE; static GtkMenuFactory *factory = NULL; static GtkMenuFactory *subfactory[1]; static GHashTable *entry_ht = NULL; void get_main_menu(GtkWidget ** menubar, GtkAcceleratorTable ** table) { if (initialize) menus_init(); if (menubar) *menubar = subfactory[0]->widget; if (table) *table = subfactory[0]->table; } void menus_init(void) { if (initialize) { initialize = FALSE; factory = gtk_menu_factory_new(GTK_MENU_FACTORY_MENU_BAR); subfactory[0] = gtk_menu_factory_new(GTK_MENU_FACTORY_MENU_BAR); gtk_menu_factory_add_subfactory(factory, subfactory[0], "
"); menus_create(menu_items, nmenu_items); } } void menus_create(GtkMenuEntry * entries, int nmenu_entries) { char *accelerator; int i; if (initialize) menus_init(); if (entry_ht) for (i = 0; i < nmenu_entries; i++) { accelerator = g_hash_table_lookup(entry_ht, entries[i].path); if (accelerator) { if (accelerator[0] == '\0') entries[i].accelerator = NULL; else entries[i].accelerator = accelerator; } } gtk_menu_factory_add_entries(factory, entries, nmenu_entries); for (i = 0; i < nmenu_entries; i++) if (entries[i].widget) { gtk_signal_connect(GTK_OBJECT(entries[i].widget), "install_accelerator", (GtkSignalFunc) menus_install_accel, entries[i].path); gtk_signal_connect(GTK_OBJECT(entries[i].widget), "remove_accelerator", (GtkSignalFunc) menus_remove_accel, entries[i].path); } } static gint menus_install_accel(GtkWidget * widget, gchar * signal_name, gchar key, gchar modifiers, gchar * path) { char accel[64]; char *t1, t2[2]; accel[0] = '\0'; if (modifiers & GDK_CONTROL_MASK) strcat(accel, ""); if (modifiers & GDK_SHIFT_MASK) strcat(accel, ""); if (modifiers & GDK_MOD1_MASK) strcat(accel, ""); t2[0] = key; t2[1] = '\0'; strcat(accel, t2); if (entry_ht) { t1 = g_hash_table_lookup(entry_ht, path); g_free(t1); } else entry_ht = g_hash_table_new(g_string_hash, g_string_equal); g_hash_table_insert(entry_ht, path, g_strdup(accel)); return TRUE; } static void menus_remove_accel(GtkWidget * widget, gchar * signal_name, gchar * path) { char *t; if (entry_ht) { t = g_hash_table_lookup(entry_ht, path); g_free(t); g_hash_table_insert(entry_ht, path, g_strdup("")); } } void menus_set_sensitive(char *path, int sensitive) { GtkMenuPath *menu_path; if (initialize) menus_init(); menu_path = gtk_menu_factory_find(factory, path); if (menu_path) gtk_widget_set_sensitive(menu_path->widget, sensitive); else g_warning("Impossible de configurer la sensitivitd'un menu qui n'existe pas : %s", path); } Voici _m_a_i_n_._h : #ifndef __MAIN_H__ #define __MAIN_H__ #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ void file_quit_cmd_callback(GtkWidget *widget, gpointer data); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* __MAIN_H__ */ Et, enfin, _m_a_i_n_._c : #include #include "main.h" #include "menus.h" int main(int argc, char *argv[]) { GtkWidget *window; GtkWidget *main_vbox; GtkWidget *menubar; GtkAcceleratorTable *accel; gtk_init(&argc, &argv); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_signal_connect(GTK_OBJECT(window), "destroy", GTK_SIGNAL_FUNC(file_quit_cmd_callback), "WM destroy"); gtk_window_set_title(GTK_WINDOW(window), "Usine menu"); gtk_widget_set_usize(GTK_WIDGET(window), 300, 200); main_vbox = gtk_vbox_new(FALSE, 1); gtk_container_border_width(GTK_CONTAINER(main_vbox), 1); gtk_container_add(GTK_CONTAINER(window), main_vbox); gtk_widget_show(main_vbox); get_main_menu(&menubar, &accel); gtk_window_add_accelerator_table(GTK_WINDOW(window), accel); gtk_box_pack_start(GTK_BOX(main_vbox), menubar, FALSE, TRUE, 0); gtk_widget_show(menubar); gtk_widget_show(window); gtk_main(); return(0); } /* Juste une donstration du fonctionnement des fonctions de rappel * lorsqu'on utilise l'usine menus. Souvent, on met tous les rappels * des menus dans un fichier sar ce qui assure une meilleure * organisation. */ void file_quit_cmd_callback (GtkWidget *widget, gpointer data) { g_print ("%s\n", (char *) data); gtk_exit(0); } Un _m_a_k_e_f_i_l_e pour que cela soit plus facile compiler : CC = gcc PROF = -g C_FLAGS = -Wall $(PROF) -L/usr/local/include -DDEBUG L_FLAGS = $(PROF) -L/usr/X11R6/lib -L/usr/local/lib L_POSTFLAGS = -lgtk -lgdk -lglib -lXext -lX11 -lm PROGNAME = at O_FILES = menus.o main.o $(PROGNAME): $(O_FILES) rm -f $(PROGNAME) $(CC) $(L_FLAGS) -o $(PROGNAME) $(O_FILES) $(L_POSTFLAGS) .c.o: $(CC) -c $(C_FLAGS) $< clean: rm -f core *.o $(PROGNAME) nohup.out distclean: clean rm -f *~ Pour l'instant, il n'y a que cet exemple. Une explication et de nombreux commentaires seront intr plus tard. 1122.. WWiiddggeettss nnoonn ddooccuummeenntt On a besoin de leurs auteurs! :). Participez notre didacticiel. Si vous devez utiliser un de ces widgets non document, je vous recommande fortement de consulter leurs fichiers en-tes respectifs dans la distribution GTK. Les noms de fonctions du GTK sont tr parlantes. Lorsque vous avez compris comment les choses fonctionnent, il n'est pas difficile de savoir comment utiliser un widget partir des dlarations de ses fonctions. Cela, avec quelques exemples de codes pris ailleurs, devrait ne pas poser de proble. Lorsque vous avez compris toutes les fonctions d'un nouveau widget non document pensez rire un didacticiel pour que les autres puissent b icier du temps que vous y avez pass 1122..11.. EEnnttrrss ddee tteexxttee 1122..22.. SSeeccttiioonnss ddee ccoouulleeuurrss 1122..33.. CCoonnttrree dd''iinntteerrvvaallllee 1122..44.. RRlleess 1122..55.. BBooeess ddee tteexxttee 1122..66.. PPrriissuuaalliissaattiioonn (Ceci peut devoir re rcrit pour suivre le style du reste de ce didacticiel). Les prisualisateurs servent plusieurs choses dans GIMP/GTK. La plus importante est celle-ci : les images de haute qualitpeuvent occuper des dizaines de mega-octets en moire - facilement ! Toute opation sur une image aussi grosse implique un temps de traitement ev Si cela vous prend 5 10 essais (i.e. 10 20 apes puisque vous devez recommencer lorsque vous avez fait une erreur) pour choisir la bonne modification, cela prendra littalement des heures pour produire la bonne image - pour peu que vous ne manquiez pas de moire avant. Ceux qui on passdes heures dans les chambres noires de deloppement couleur connaissent cette sensation. Les prisualisations sont notre planche de salut ! L'aspect pible de l'attente n'est pas le seul proble. souvent, il est utile de comparer les versions Avant et Apr ce ce ou, au pire l'une apr l'autre. Si vous travaillez avec de grosses images et des attentes de 10 secondes, l'obtention des versions Avant et Apr est, pour le moins, difficile. Pour des images de 30Mo (4"x6", 600dpi, 24 bits), la comparaison ce ce est impossible pour la plupart des gens, et la comparaison suentielle n'est gue mieux. Les prisualisations sont notre planche de salut ! Mais il y a plus. Les prisualisations permettent les prprisualisations ce ce. En d'autres termes, vous rivez un plug-in (par exemple la simulation filterpack) qui aurait plusieurs prisualisations de ce-que-ce-serait-si-vous-faisiez-ceci. Une approche comme celle ci agit comme une sorte de palette de prisualisation et est tr pratique pour les petites modifications. Utilisons les prisualisations ! Encore plus : pour certains plug-ins une intervention humaine en temps rl, spifique aux images, peut s'aver nessaire. Dans le plug-in SuperNova, par exemple, on demande l'utilisateur d'entrer les coordonns du centre de la future supernova. La fan la plus simple de faire cela, vraiment, est de prenter une prisualisation l'utilisateur et de lui demander de choisir interactivement le point. Utilisons les prisualisations ! Enfin, quelques utilisations diverses : on peut utiliser les prisualisations, me lorsqu'on ne travaille pas avec de grosses images. Elles sont utiles, par exemple, lorsqu'on veut avoir un rendu de motifs complexes. (Testez le vable plug-in Diffraction et d'autres !). Comme autre exemple, regardez le plug-in de rotation de couleur (travail en cours). Vous pouvez aussi utiliser les prisualisations pour des petits logos dans vos plug-ins et me pour une photo de vous, l'Auteur. Utilisons les prisualisations ! Quand ne pas utiliser les prisualisations N'utilisez pas les prisualisations pour les graphes, les trac, etc. GDK est bien plus rapide pour . N'utilisez les que pour les images ! Utilisons les prisualisations ! Vous pouvez mettre une prisualisation dans peu pr n'importe quoi. Dans une vbox, une hbox, un bouton, etc. Mais elles donnent leur meilleur d'elles-mes dans des cadres resserr autour d'elles. Les prisualisations n'ont, par elles-mes, aucun contour et semblent plates sans eux. (Bien s, si c'est cet aspect que vous voulez...). Les cadres serr fournissent les bordures nessaires. [Image][Image] Les prisualisations sont, bien des ards, comme tous les autres widgets de GTK (avec tout ce que cela implique) sauf qu'il disposent d'une fonctionnalitsupplentaire : ils doivent re remplis avec une image ! Nous traiterons d'abord exclusivement de l'aspect GTK des prisualisations, puis nous verrons comment les remplir. /* Crtion d'un widget prisualisation, * configuration de sa taille et affichage */ GtkWidget *preview; preview=gtk_preview_new(GTK_PREVIEW_COLOR) /* Autre option : GTK_PREVIEW_GRAYSCALE);*/ gtk_preview_size (GTK_PREVIEW (preview), WIDTH, HEIGHT); gtk_widget_show(preview); my_preview_rendering_function(preview); Ah oui, comme je le disais, les prisualisations rendent mieux dans des cadres : GtkWidget *create_a_preview(int Width, int Height, int Colorfulness) { GtkWidget *preview; GtkWidget *frame; frame = gtk_frame_new(NULL); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN); gtk_container_border_width (GTK_CONTAINER(frame),0); gtk_widget_show(frame); preview=gtk_preview_new (Colorfulness?GTK_PREVIEW_COLOR :GTK_PREVIEW_GRAYSCALE); gtk_preview_size (GTK_PREVIEW (preview), Width, Height); gtk_container_add(GTK_CONTAINER(frame),preview); gtk_widget_show(preview); my_preview_rendering_function(preview); return frame; } Ceci est ma prisualisation de base. Cette fonction retourne le cadre pe on peut ainsi le placer ailleurs dans notre interface. Bien s, on peut passer le cadre pe en paramre cette fonction. Dans de nombreuses situations, toutefois, le contenu de la prisualisation est chang continuellement par notre application. En ce cas, on peut passer un pointeur vers une prisualisation la fonction bpp; ReducedImage *temp=(ReducedImage *)malloc(sizeof(ReducedImage)); guchar *tempRGB, *src_row, *tempmask, *src_mask_row,R,G,B; gint i, j, whichcol, whichrow, x1, x2, y1, y2; GPixelRgn srcPR, srcMask; gint NoSelectionMade=TRUE; /* Suppose que l'on traite l'image entie */ gimp_drawable_mask_bounds (drawable->id, &x1, &y1, &x2, &y2); width = x2-x1; height = y2-y1; /* S'il y a une SELECTION, on rupe ses fronties ! */ if (width != drawable->width && height != drawable->height) NoSelectionMade=FALSE; /* On vifie si l'utilisateur a rendu une section active */ /* Ceci sera important plus tard, lorsqu'on crra un masque ruit */ /* Si on veut prisualiser l'image entie, supprimer ce qui suit ! */ /* Bien s, s'il n'y a pas de section, cela n'a aucun effet ! */ if (Selection==ENTIRE_IMAGE) { x1=0; x2=drawable->width; y1=0; y2=drawable->height; } /* Si on veut prisualiser une section avec une surface qui l'entoure, */ /* on doit l'agrandir un petit peu. Considez comme une devinette. */ if (Selection==SELECTION_IN_CONTEXT) { x1=MAX(0, x1-width/2.0); x2=MIN(drawable->width, x2+width/2.0); y1=MAX(0, y1-height/2.0); y2=MIN(drawable->height, y2+height/2.0); } /* Calcul de la largeur et de la hauteur de la surface ruire. */ width = x2-x1; height = y2-y1; /* Les lignes ci-dessous derminent la dimension qui sera le cot*/ /* le plus long. Cette id est emprunt au plug-in Supernova. */ /* Je soupnne que j'aurais pu y penser moi-me, mais la vit */ /* doit re dite. Le plagiat pue ! */ if (width>height) { RW=LongerSize; RH=(float) height * (float) LongerSize/ (float) width; } else { RH=LongerSize; RW=(float)width * (float) LongerSize/ (float) height; } /* L'image entie est ruite dans une chae ! */ tempRGB = (guchar *) malloc(RW*RH*bytes); tempmask = (guchar *) malloc(RW*RH); gimp_pixel_rgn_init (&srcPR, drawable, x1, y1, width, height, FALSE, FALSE); gimp_pixel_rgn_init (&srcMask, mask, x1, y1, width, height, FALSE, FALSE); /* Rervation pour sauver une ligne d'image et une ligne du masque */ src_row = (guchar *) malloc (width*bytes); src_mask_row = (guchar *) malloc (width); for (i=0; i < RH; i++) { whichrow=(float)i*(float)height/(float)RH; gimp_pixel_rgn_get_row (&srcPR, src_row, x1, y1+whichrow, width); gimp_pixel_rgn_get_row (&srcMask, src_mask_row, x1, y1+whichrow, width); for (j=0; j < RW; j++) { whichcol=(float)j*(float)width/(float)RW; /* Pas de section = chaque point est complement sectionn! */ if (NoSelectionMade) tempmask[i*RW+j]=255; else tempmask[i*RW+j]=src_mask_row[whichcol]; /* Ajout de la ligne la longue chae qui contient maintenant */ /* l'image ! */ tempRGB[i*RW*bytes+j*bytes+0]=src_row[whichcol*bytes+0]; tempRGB[i*RW*bytes+j*bytes+1]=src_row[whichcol*bytes+1]; tempRGB[i*RW*bytes+j*bytes+2]=src_row[whichcol*bytes+2]; /* On s'accroche aussi l'alpha */ if (bytes==4) tempRGB[i*RW*bytes+j*bytes+3]=src_row[whichcol*bytes+3]; } } temp->bpp=bytes; temp->width=RW; temp->height=RH; temp->rgb=tempRGB; temp->mask=tempmask; return temp; } La suite est une fonction de prisualisation qui utilise le me type 0 ) return 64; else return 196; } Voici maintenant la fonction de prisualisationbsp;: void my_preview_render_function(GtkWidget *preview, gint changewhat, gint changewhich) { gint Inten, bytes=drawable->bpp; gint i, j, k; float partial; gint RW=reduced->width; gint RH=reduced->height; guchar *row=malloc(bytes*RW);; for (i=0; i < RH; i++) { for (j=0; j < RW; j++) { row[j*3+0] = reduced->rgb[i*RW*bytes + j*bytes + 0]; row[j*3+1] = reduced->rgb[i*RW*bytes + j*bytes + 1]; row[j*3+2] = reduced->rgb[i*RW*bytes + j*bytes + 2]; if (bytes==4) for (k=0; k<3; k++) { float transp=reduced->rgb[i*RW*bytes+j*bytes+3]/255.0; row[3*j+k]=transp*a[3*j+k]+(1-transp)*fake_transparency(i,j); } } gtk_preview_draw_row( GTK_PREVIEW(preview),row,0,i,RW); } free(a); gtk_widget_draw(preview,NULL); gdk_flush(); } Fonctions applicables guint gtk_preview_get_type (void); /* Aucune id */ void gtk_preview_uninit (void); /* Aucune id */ GtkWidget* gtk_preview_new (GtkPreviewType type); /* Drite ci-dessous */ void gtk_preview_size (GtkPreview *preview, gint width, gint height); /* Permet de changer la taille d'une prisualisation existante */ /* Apparamment, il y a un bug dans GTK qui rend ce traitement */ /* hasardeux. Une mhode pour corriger ce proble consiste */ /* changer manuellement la taille de la fenre contenant la */ /* prisualisation apr avoir changla taille de la */ /* prisualisation. */ void gtk_preview_put (GtkPreview *preview, GdkWindow *window, GdkGC *gc, gint srcx, gint srcy, gint destx, gint desty, gint width, gint height); /* Aucune id */ void gtk_preview_put_row (GtkPreview *preview, guchar *src, guchar *dest, gint x, gint y, gint w); /* Aucune id */ void gtk_preview_draw_row (GtkPreview *preview, guchar *data, gint x, gint y, gint w); /* Drite dans le texte */ void gtk_preview_set_expand (GtkPreview *preview, gint expand); /* Aucune id */ /* Aucune piste pour celles qui suivent mais devrait re */ /* un standard pour la plupart des widgets. */ void gtk_preview_set_gamma (double gamma); void gtk_preview_set_color_cube (guint nred_shades, guint ngreen_shades, guint nblue_shades, guint ngray_shades); void gtk_preview_set_install_cmap (gint install_cmap); void gtk_preview_set_reserved (gint nreserved); GdkVisual* gtk_preview_get_visual (void); GdkColormap* gtk_preview_get_cmap (void); GtkPreviewInfo* gtk_preview_get_info (void); That's all, folks! 1122..77.. CCoouurrbbeess 1133.. WWiiddggeett EEvveennttBBooxx Il n'est disponible que dans _g_t_k_+_9_7_0_9_1_6_._t_a_r_._g_z et les distributions ultieures. Certains widgets GTK n'ont pas de fenre X associ, il se dessinent donc sur leurs parents. cause de cela, ils ne peuvent recevoir d'ements et, s'ils ont une taille incorrecte, ils ne peuvent pas se mettre en place correctement : on peut alors avoir des surimpressions douteuses, etc. Si vous avez besoin de ces widgets, _E_v_e_n_t_B_o_x est fait pour vous. Au premier abord, le widget _E_v_e_n_t_B_o_x peut apparare comme totalement dud'int. Il ne dessine rien l'ran et ne rond aucun enement. Cependant, il joue un re - il fournit une fenre X pour son widget fils. Ceci est important car de nombreux widgets GTK n'ont pas de fenre X associ. Ne pas avoir de fenre permet d'onomiser de la moire mais a aussi quelques inconvients. Un widget sans fenre ne peut recevoir d'ement, et ne rlise aucune mise en place de ce qu'il contient. Bien que le nom EventBox insiste sur la fonction de gestion d'ement, le widget peut aussi re utilispour la mise en place (et plus... voir l'exemple ci- dessous). Pour crr un widget EventBox, on utilise : GtkWidget* gtk_event_box_new (void); Un widget fils peut alors re ajoutcet _E_v_e_n_t_B_o_x : gtk_container_add (GTK_CONTAINER(event_box), widget); L'exemple suivant montre l'utilisation d'un _E_v_e_n_t_B_o_x - un label est cr et mis en place sur une petite boe, et configurpour qu'un clic souris sur le label provoque la fin du programme. #include int main (int argc, char *argv[]) { GtkWidget *window; GtkWidget *event_box; GtkWidget *label; gtk_init (&argc, &argv); window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_title (GTK_WINDOW (window), "Event Box"); gtk_signal_connect (GTK_OBJECT (window), "destroy", GTK_SIGNAL_FUNC (gtk_exit), NULL); gtk_container_border_width (GTK_CONTAINER (window), 10); /* Crtion d'un EventBox et ajout de celui-ci dans la fenre. */ event_box = gtk_event_box_new (); gtk_container_add (GTK_CONTAINER(window), event_box); gtk_widget_show (event_box); /* Crtion d'un long label */ label = gtk_label_new ("Cliquez ici pour quitter, quitter, quitter, quitter, quitter"); gtk_container_add (GTK_CONTAINER (event_box), label); gtk_widget_show (label); /* Placement serr */ gtk_widget_set_usize (label, 110, 20); /* Attachement d'une action celui-ci. */ gtk_widget_set_events (event_box, GDK_BUTTON_PRESS_MASK); gtk_signal_connect (GTK_OBJECT(event_box), "button_press_event", GTK_SIGNAL_FUNC (gtk_exit), NULL); /* Encore une fois, vous avez besoin d'une fenre X pour... */ gtk_widget_realize (event_box); gdk_window_set_cursor (event_box->window, gdk_cursor_new (GDK_HAND1)); gtk_widget_show (window); gtk_main (); return 0; } 1144.. CCoonnffiigguurraattiioonn ddeess aattttrriibbuuttss ddee wwiiddggeett Cette section drit les fonctions opant sur les widgets. Elles peuvent re utiliss pour configurer le style, l'espacement, la taille, etc. (Je devrais peut re faire une section uniquement consacr aux raccourcis clavier.) void gtk_widget_install_accelerator (GtkWidget *widget, GtkAcceleratorTable *table, gchar *signal_name, gchar key, guint8 modifiers); void gtk_widget_remove_accelerator (GtkWidget *widget, GtkAcceleratorTable *table, gchar *signal_name); void gtk_widget_activate (GtkWidget *widget); void gtk_widget_set_name (GtkWidget *widget, gchar *name); gchar* gtk_widget_get_name (GtkWidget *widget); void gtk_widget_set_sensitive (GtkWidget *widget, gint sensitive); void gtk_widget_set_style (GtkWidget *widget, GtkStyle *style); GtkStyle* gtk_widget_get_style (GtkWidget *widget); GtkStyle* gtk_widget_get_default_style (void); void gtk_widget_set_uposition (GtkWidget *widget, gint x, gint y); void gtk_widget_set_usize (GtkWidget *widget, gint width, gint height); void gtk_widget_grab_focus (GtkWidget *widget); void gtk_widget_show (GtkWidget *widget); void gtk_widget_hide (GtkWidget *widget); 1155.. TTeemmppoorriissaattiioonnss,, ffoonnccttiioonnss dd''EE//SS eett dd''aatttteennttee 1155..11.. TTeemmppoorriissaattiioonnss Vous pouvez vous demander comment faire pour que GTK fasse quelque chose d'utile lorsqu'il est dans _g_t_k___m_a_i_n. En fait, on a plusieurs options. L'utilisation des fonctions suivantes permet de crr une temporisation qui sera appel tous les _i_n_t_e_r_v_a_l millisecondes. gint gtk_timeout_add (guint32 interval, GtkFunction function, gpointer data); Le premier paramre est le nombre de millisecondes entre les appels notre fonction. Le deuxie est la fonction appeler et le troisie est la donn pass cette fonction de rappel. La valeur retourn est un marqueur de type entier qui pourra re utilispour arrer la temporisation en appelant : void gtk_timeout_remove (gint tag); On peut aussi stopper la fonction de temporisation en faisant retourner zo ou FALSE notre fonction de rappel. idemment, cela veut dire que si vous voulez que votre fonction continue re appel, elle doit retourner une valeur non nulle, ou TRUE. La dlaration de votre fonction de rappel doit ressembler : gint timeout_callback (gpointer data); 1155..22.. SSuurrvveeiillllaannccee ddeess EE//SS Une autre caractistique intessante du GTK est la possibilitde vifier les donns d'un descripteur de fichier (celles retourns par _o_p_e_n(2) ou _s_o_c_k_e_t(2)). C'est particuliement pratique pour les applications reau. La fonction suivante permet cette vification : gint gdk_input_add (gint source, GdkInputCondition condition, GdkInputFunction function, gpointer data); Le premier paramre est le descripteur de fichier que l'on veut udier, le second spifie ce qu'on veut que le GDK recherche. Cela peut re : GDK_INPUT_READ - Appel _f_u_n_c_t_i_o_n lorsqu'il y a une donn pre re lue dans le descripteur de fichier. GDK_INPUT_WRITE - Appel de _f_u_n_c_t_i_o_n lorsque le descripteur de fichier est pr pour une riture. Je suis s que vous vous doutez, maintenant, que le troisie paramre est la fonction que l'on veut appeler lorsque les conditions ci-dessus sont satisfaites. Le dernier paramre est la donn passer cette fonction. La valeur retourn est un marqueur qui pourra re utilispour dire au GDK de cesser de surveiller ce descripteur l'aide de la fonction : void gdk_input_remove (gint tag); La fonction de rappel doit re dlar de la fan suivante : void input_callback (gpointer data, gint source, GdkInputCondition condition); 1155..33.. FFoonnccttiioonnss dd''aatttteennttee Que se passe-t'il si vous avez une fonction qui doit re appel lorsque rien d'autre ne se passe ? On utilise la fonction suivante qui force GTK appeler _f_u_n_c_t_i_o_n lorsqu'on est en phase d'inaction ; gint gtk_idle_add (GtkFunction function, gpointer data); void gtk_idle_remove (gint tag); Je n'expliquerai pas la signification des paramres car ils ressemblent beaucoup ceux dvus ci-dessus. La fonction point par le premier paramre de _g_t_k___i_d_l_e___a_d_d_(_) sera appel chaque occasion. Comme pour les autres, retourner FALSE emphera la fonction d'attente d're appel. 1166.. GGeessttiioonn ddeess sseeccttiioonnss 1166..11.. IInnttrroodduuccttiioonn Un type de communication inter-processus g par GTK est les _s_e_c_t_i_o_n_s. Une section identifie un morceau de donns, par exemple une portion de texte sectionn par l'utilisateur avec la souris. Seule une application sur un ran (le _p_r_o_p_r_i_a_i_r_e) peut posser une section particulie un moment donn ainsi lorsqu'une section est rlam par une application, le propriaire prent doit indiquer l'utilisateur que la section a abandonn. Les autres applications peuvent demander le contenu d'une section sous diffentes formes appels _c_i_b_l_e_s. Il peut y avoir un nombre quelconque de sections, mais la plupart des applications X n'en gent qu'une, la _s_e_c_t_i_o_n _p_r_i_m_a_i_r_e. Dans la plupart des cas, une application GTK n'a pas besoin de ger elle-me les sections. Les widgets standards, comme le widget Entr de texte, possent dla capacitde rlamer la section lorsqu'il le faut (par exemple, lorsque l'utilisateur glisse au dessus d'un texte) et de ruper le contenu de la section denue par un autre widget ou une autre application (par exemple, lorsque l'utilisateur clique avec le deuxie bouton de la souris). Cependant, il peut il y avoir des cas dans lesquels vous voulez donner aux autres widgets la possibilitde fournir la section, ou vous direz ruper des cibles non supports par daut. Un concept fondamental dans la comprension du fonctionnement des sections est celui d'_a_t_o_m_e. Un atome est un entier qui dinit de fan unique une chae (sur un affichage particulier). Certains atomes sont prinis par le serveur X et, dans certains cas, des constantes dinies dans _g_t_k_._h correspondent ces atomes. Par exemple, la constante GDK_PRIMARY_SELECTION correspond la chae "PRIMARY". Dans d'autres cas, on doit utiliser les fonctions _g_d_k___a_t_o_m___i_n_t_e_r_n_(_), pour obtenir l'atome correspondant une chae, et _g_d_k___a_t_o_m___n_a_m_e_(_), pour obtenir le nom d'un atome. Les sections et les cibles sont identifi par des atomes. 1166..22.. RRuuppaattiioonn ddee llaa sseeccttiioonn La rupation de la section est un processus asynchrone. Pour darrer le processus, on appelle : gint gtk_selection_convert (GtkWidget *widget, GdkAtom selection, GdkAtom target, guint32 time) Cela _c_o_n_v_e_r_t_i_t la section dans la forme spifi par _t_a_r_g_e_t. Si tout est possible, le paramre _t_i_m_e sera le moment de l'ement qui a dlenchla section. Ceci aide s'assurer que les ements arrivent dans l'ordre ol'utilisateur les a demand Cependant, si cela n'est pas possible (par exemple, lorsque la conversion a dlench par un signal "clicked"), alors on peut utiliser la macro GDK_CURRENT_TIME. Quand le propriaire de la section rond la reque, un signal "selection_received" est envoynotre application. Le gestionnaire de ce signal reit un pointeur vers une structure GtkSelectionData dinie ainsi : struct _GtkSelectionData { GdkAtom selection; GdkAtom target; GdkAtom type; gint format; guchar *data; gint length; }; _s_e_l_e_c_t_i_o_n et _t_a_r_g_e_t sont les valeurs que l'on a donndans notre appel _g_t_k___s_e_l_e_c_t_i_o_n___c_o_n_v_e_r_t_(_). _t_y_p_e est un atome qui identifie le type de donns retournpar le propriaire de la section. Quelques valeurs possibles sont : "STRING", une chae de caractes latin-1, "ATOM", une sie d'atomes, "INTEGER", un entier, etc. La plupart des cibles ne peuvent retourner qu'un type. _f_o_r_m_a_t donne la longueur des unit (les caractes, par exemple) en bits. Habituellement, on ne se prccupe pas de cela lorsqu'on reit des donns. _d_a_t_a est un pointeur vers la donn retourn et _l_e_n_g_t_h donne la longueur en octets de la donn retourn. Si _l_e_n_g_t_h est native, cela indique qu'une erreur est survenue et que la section ne peut re rup. Ceci peut arriver si aucune application n'est propriaire de la section, ou si vous avez demandune cible que l'application ne sait pas ger. Le tampon est garanti d're un octet plus long que _l_e_n_g_t_h ; l'octet supplentaire sera toujours zo, et il n'est donc pas nessaire de faire une copie de chae simplement pour qu'elle soit termin par zo (comme doivent l're toutes les chaes C). Dans l'exemple qui suit, on rupe la cible spiale "TARGETS", qui est une liste de toutes les cibles en lesquelles la section peut re convertie. #include void selection_received (GtkWidget *widget, GtkSelectionData *selection_data, gpointer data); /* Gestionnaire de signal invoqulorsque l'utilisateur clique sur * le bouton Obtenir les cibles */ void get_targets (GtkWidget *widget, gpointer data) { static GdkAtom targets_atom = GDK_NONE; /* Obtention de l'atome correspondant la chae "TARGETS" */ if (targets_atom == GDK_NONE) targets_atom = gdk_atom_intern ("TARGETS", FALSE); /* Demande de la cible "TARGETS" pour la section primaire */ gtk_selection_convert (widget, GDK_SELECTION_PRIMARY, targets_atom, GDK_CURRENT_TIME); } /* Gestionnaire de signal appelquand le propriaire des sections * retourne la donn. */ void selection_received (GtkWidget *widget, GtkSelectionData *selection_data, gpointer data) { GdkAtom *atoms; GList *item_list; int i; /* **** IMPORTANT **** On vifie si la rupation s'est bien pass. */ if (selection_data->length < 0) { g_print ("Selection retrieval failed\n"); return; } /* On s'assure que l'on a obtenu la donn sous la forme attendue. */ if (selection_data->type != GDK_SELECTION_TYPE_ATOM) { g_print ("La section \"TARGETS\" n'a pas retourn sous la forme d'atomes !\n"); return; } /* Affichage des atomes res. */ atoms = (GdkAtom *)selection_data->data; item_list = NULL; for (i=0; ilength/sizeof(GdkAtom); i++) { char *name; name = gdk_atom_name (atoms[i]); if (name != NULL) g_print ("%s\n",name); else g_print ("(atome incorrect)\n"); } return; } int main (int argc, char *argv[]) { GtkWidget *window; GtkWidget *button; gtk_init (&argc, &argv); /* Crtion de la fenre de l'application. */ window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_title (GTK_WINDOW (window), "Sections"); gtk_container_border_width (GTK_CONTAINER (window), 10); gtk_signal_connect (GTK_OBJECT (window), "destroy", GTK_SIGNAL_FUNC (gtk_exit), NULL); /* Crtion d'un bouton pour obtenir les cibles */ button = gtk_button_new_with_label ("Obtenir les cibles"); gtk_container_add (GTK_CONTAINER (window), button); gtk_signal_connect (GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC (get_targets), NULL); gtk_signal_connect (GTK_OBJECT(button), "selection_received", GTK_SIGNAL_FUNC (selection_received), NULL); gtk_widget_show (button); gtk_widget_show (window); gtk_main (); return 0; } 1166..33.. FFoouurrnniirr llaa sseeccttiioonn Fournir la section est un peu plus compliqu On doit enregistrer les gestionnaires qui seront appel lorsque notre section est demand. Pour chaque paire section/cible que l'on gera, on fera un appel ~: void gtk_selection_add_handler (GtkWidget *widget, GdkAtom selection, GdkAtom target, GtkSelectionFunction function, GtkRemoveFunction remove_func, gpointer data); _w_i_d_g_e_t, _s_e_l_e_c_t_i_o_n et _t_a_r_g_e_t identifient les reques que ce gestionnaire gera. S'il ne vaut pas NULL, _r_e_m_o_v_e___f_u_n_c sera appellorsque le gestionnaire de signal est supprim Ceci est utile, par exemple, pour des langages interpr qui doivent garder une trace du nombre de rences _d_a_t_a. La fonction de rappel _f_u_n_c_t_i_o_n doit avoir la signature suivante : typedef void (*GtkSelectionFunction) (GtkWidget *widget, GtkSelectionData *selection_data, gpointer data); Le _G_t_k_S_e_l_e_c_t_i_o_n_D_a_t_a est le me qu'au dessus, mais, cette fois, nous sommes responsables de l'initialisation de ses champs _t_y_p_e, _f_o_r_m_a_t, _d_a_t_a, et _l_e_n_g_t_h. (Le champ _f_o_r_m_a_t est important ici - le serveur X l'utilise pour savoir si la donn doit re hang par octet ou non. Habituellement, ce sera 8 (un caracte), ou 32 (un entier)). Cette initialisation est faite en utilisant l'appel : void gtk_selection_data_set (GtkSelectionData *selection_data, GdkAtom type, gint format, guchar *data, gint length); Cette fonction s'occupe de faire une copie correcte des donns afin que l'on n'ait pas se soucier du reste. (On ne doit pas remplir ces champs la main). Lorsque cela est demandpar l'utilisateur, on rlame la possession de la section en appelant : gint gtk_selection_owner_set (GtkWidget *widget, GdkAtom selection, guint32 time); Si une autre application rlame la possession de la section, on recevra un "selection_clear_event". Comme exemple de fourniture de section, l'exemple suivant ajoute une fonctionnalitde section un bouton commutateur. Lorsque ce bouton est appuy le programme rlame la section primaire. La seule cible support (part certaines cibles fournies par GTK lui-me, comme TARGETS est STRING Lorsque celle-ci est demand, on retourne une reprentation de l'heure sous forme de chae. #include #include /* Fonction de rappel appel lorsque l'utilisateur commute la section. */ void selection_toggled (GtkWidget *widget, gint *have_selection) { if (GTK_TOGGLE_BUTTON(widget)->active) { *have_selection = gtk_selection_owner_set (widget, GDK_SELECTION_PRIMARY, GDK_CURRENT_TIME); /* Si la demande de section houe, on remet le bouton en position sortie. */ if (!*have_selection) gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON(widget), FALSE); } else { if (*have_selection) { /* Avant de nettoyer la selection en mettant son propriaire NULL, * on vifie que nous sommes bien son propriaire actuel. */ if (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == widget->window) gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY, GDK_CURRENT_TIME); *have_selection = FALSE; } } } /* Appel lorsqu'une autre application demande la section. */ gint selection_clear (GtkWidget *widget, GdkEventSelection *event, gint *have_selection) { *have_selection = FALSE; gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON(widget), FALSE); return TRUE; } /* Fournit l'heure comme section. */ void selection_handle (GtkWidget *widget, GtkSelectionData *selection_data, gpointer data) { gchar *timestr; time_t current_time; current_time = time (NULL); timestr = asctime (localtime(¤t_time)); /* Lorsqu'on retourne une chae, elle ne doit pas se terminer par * 0, ce sera fait pour nous. */ gtk_selection_data_set (selection_data, GDK_SELECTION_TYPE_STRING, 8, timestr, strlen(timestr)); } int main (int argc, char *argv[]) { GtkWidget *window; GtkWidget *selection_button; static int have_selection = FALSE; gtk_init (&argc, &argv); /* Crtion de la fenre principale. */ window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_title (GTK_WINDOW (window), "Event Box"); gtk_container_border_width (GTK_CONTAINER (window), 10); gtk_signal_connect (GTK_OBJECT (window), "destroy", GTK_SIGNAL_FUNC (gtk_exit), NULL); /* Crtion d'un bouton commutateur pour qu'il agisse comme une section. */ selection_button = gtk_toggle_button_new_with_label ("Demande de section"); gtk_container_add (GTK_CONTAINER (window), selection_button); gtk_widget_show (selection_button); gtk_signal_connect (GTK_OBJECT(selection_button), "toggled", GTK_SIGNAL_FUNC (selection_toggled), &have_selection); gtk_signal_connect (GTK_OBJECT(selection_button), "selection_clear_event", GTK_SIGNAL_FUNC (selection_clear), &have_selection); gtk_selection_add_handler (selection_button, GDK_SELECTION_PRIMARY, GDK_SELECTION_TYPE_STRING, selection_handle, NULL, NULL); gtk_widget_show (selection_button); gtk_widget_show (window); gtk_main (); return 0; } 1177.. gglliibb La _g_l_i_b fournit de nombreuses fonctions et dinitions utiles, pres re utiliss lorsqu'on cr des applications GDK et GTK. Je les umerais toutes avec une bre explication. Beaucoup sont des rliques des fonctions standards de la _l_i_b_c, et je ne les daillerais donc pas trop. Ceci doit surtout servir de rence afin de savoir ce qui est disponible pour re utilis 1177..11.. DDiinniittiioonnss Les dinitions pour les bornes de la plupart des types standards sont : G_MINFLOAT G_MAXFLOAT G_MINDOUBLE G_MAXDOUBLE G_MINSHORT G_MAXSHORT G_MININT G_MAXINT G_MINLONG G_MAXLONG Voici aussi les redinitions de types. Celles qui ne sont pas spifis sont configurs dynamiquement selon l'architecture. itez surtout de compter sur la taille d'un pointeur si vous voulez un programme portable ! Un pointeur sur un Alpha fait 8 octets, mais il en fait 4 sur un Intel. char gchar; short gshort; long glong; int gint; char gboolean; unsigned char guchar; unsigned short gushort; unsigned long gulong; unsigned int guint; float gfloat; double gdouble; long double gldouble; void* gpointer; gint8 guint8 gint16 guint16 gint32 guint32 1177..22.. LLiisstteess ddoouubblleemmeenntt cchhaass Les fonctions suivantes servent crr, ger et druire des listes doublement chas. Je suppose que vous savez ce qu'est une liste cha car leur explication n'entre pas dans le cadre de ce document. Bien s, il n'y a pas besoin de les connare pour une utilisation gale de GTK, mais c'est bien de savoir comment elles fonctionnent. GList* g_list_alloc (void); void g_list_free (GList *list); void g_list_free_1 (GList *list); GList* g_list_append (GList *list, gpointer data); GList* g_list_prepend (GList *list, gpointer data); GList* g_list_insert (GList *list, gpointer data, gint position); GList* g_list_remove (GList *list, gpointer data); GList* g_list_remove_link (GList *list, GList *link); GList* g_list_reverse (GList *list); GList* g_list_nth (GList *list, gint n); GList* g_list_find (GList *list, gpointer data); GList* g_list_last (GList *list); GList* g_list_first (GList *list); gint g_list_length (GList *list); void g_list_foreach (GList *list, GFunc func, gpointer user_data); 1177..33.. LLiisstteess ssiimmpplleemmeenntt cchhaass La plupart des fonctions pour les listes simplement chas ci-dessous sont identiques celles vues plus haut. Voici une liste comple : GSList* g_slist_alloc (void); void g_slist_free (GSList *list); void g_slist_free_1 (GSList *list); GSList* g_slist_append (GSList *list, gpointer data); GSList* g_slist_prepend (GSList *list, gpointer data); GSList* g_slist_insert (GSList *list, gpointer data, gint position); GSList* g_slist_remove (GSList *list, gpointer data); GSList* g_slist_remove_link (GSList *list, GSList *link); GSList* g_slist_reverse (GSList *list); GSList* g_slist_nth (GSList *list, gint n); GSList* g_slist_find (GSList *list, gpointer data); GSList* g_slist_last (GSList *list); gint g_slist_length (GSList *list); void g_slist_foreach (GSList *list, GFunc func, gpointer user_data); 1177..44.. GGeessttiioonn ddee llaa mmooiirree gpointer g_malloc (gulong size); Remplace _m_a_l_l_o_c_(_). On n'a pas besoin de vifier la valeur de retour car cela est fait pour nous dans cette fonction. gpointer g_malloc0 (gulong size); Identique la prente, mais initialise la moire zo avant de retourner un pointeur vers la zone rerv. gpointer g_realloc (gpointer mem, gulong size); Rlloue _s_i_z_e octets de moire partir de _m_e_m. idemment, la moire doit avoir allou auparavant. void g_free (gpointer mem); Libe la moire. Facile. void g_mem_profile (void); Produit un profil de la moire utilis, mais requiert l'ajout de _#_d_e_f_i_n_e _M_E_M___P_R_O_F_I_L_E au dut de _g_l_i_b_/_g_m_e_m_._c, de refaire un _m_a_k_e et un _m_a_k_e _i_n_s_t_a_l_l. void g_mem_check (gpointer mem); Vifie qu'un emplacement moire est valide. Nessite que l'on ajoute _#_d_e_f_i_n_e _M_E_M___C_H_E_C_K au dut de _g_m_e_m_._c que l'on refasse un _m_a_k_e et un _m_a_k_e _i_n_s_t_a_l_l. 1177..55.. TTiimmeerrss Fonctions des timers... GTimer* g_timer_new (void); void g_timer_destroy (GTimer *timer); void g_timer_start (GTimer *timer); void g_timer_stop (GTimer *timer); void g_timer_reset (GTimer *timer); gdouble g_timer_elapsed (GTimer *timer, gulong *microseconds); 1177..66.. GGeessttiioonn ddeess cchhaaeess Un ensemble complet de fonction de gestion des chaes. Elles semblent toutes tr intessantes et sont sement meilleures, bien des ards, que les fonctions C standards, mais elle nessitent de la documentation. GString* g_string_new (gchar *init); void g_string_free (GString *string, gint free_segment); GString* g_string_assign (GString *lval, gchar *rval); GString* g_string_truncate (GString *string, gint len); GString* g_string_append (GString *string, gchar *val); GString* g_string_append_c (GString *string, gchar c); GString* g_string_prepend (GString *string, gchar *val); GString* g_string_prepend_c (GString *string, gchar c); void g_string_sprintf (GString *string, gchar *fmt, ...); void g_string_sprintfa (GString *string, gchar *fmt, ...); 1177..77.. UUttiilliittaaiirreess eett ffoonnccttiioonnss dd''eerrrreeuurrss gchar* g_strdup (const gchar *str); Remplace la fonction _s_t_r_d_u_p. Elle copie le contenu de la chae d'origine dans la moire venant d're allou et retourne un pointeur sur cette zone. gchar* g_strerror (gint errnum); Je recommande de l'utiliser pour tous les messages d'erreur. Elle est beaucoup plus propre et plus portable que _p_e_r_r_o_r_(_) ou les autres. La sortie est habituellement de la forme : nom du programme:fonction qui a houfichier ou autre descripteur:strerror Voici un exemple d'appel utilisdans le programme Bonjour tout le monde ! ~: g_print("bonjour_monde:open:%s:%s\n", filename, g_strerror(errno)); void g_error (gchar *format, ...); Affiche un message d'erreur. Le format est comme _p_r_i_n_t_f, mais il ajoute ** ERROR **: au dut du message et sort du programme. n'utiliser que pour les erreurs fatales. void g_warning (gchar *format, ...); Comme au dessus, mais ajoute ** WARNING **: et ne termine pas le programme. void g_message (gchar *format, ...); Affiche message: avant la chae pass en paramre. void g_print (gchar *format, ...); Remplace _p_r_i_n_t_f_(_). Enfin la dernie fonction : gchar* g_strsignal (gint signum); Affiche le nom du signal syste Unix correspondant au numo de signal. Utile pour les fonctions giques de gestion de signaux. Tout ce qui est ci-dessus est plus ou moins vol_g_l_i_b_._h. Si quelqu'un s'occupe de documenter une fonction, qu'il m'envoit un courrier ! 1188.. FFiicchhiieerrss rrcc ddee GGTTKK GTK a sa propre mhode pour ger les configurations par daut des applications, en utilisant des fichiers rc. Ceux-ci peuvent re utilis pour configurer les couleurs de presque tous les widgets, et pour mettre des pixmaps sur le fond de certains widgets. 1188..11.. FFoonnccttiioonnss ppoouurr lleess ffiicchhiieerrss rrcc Au darrage de votre application, ajoutez un appel ~: void gtk_rc_parse (char *filename); en lui passant le nom de votre fichier rc. Ceci forcera GTK analyser ce fichier et utiliser les configurations de styles pour les types de widgets qui y sont dinis. Si vous voulez avoir un ensemble particulier de widgets qui prenne le pas sur le style des autres, ou une autre division logique de widgets, utilisez un appel ~: void gtk_widget_set_name (GtkWidget *widget, gchar *name); En lui passant comme premier paramre le widget que vous avez cr, et le nom que vous voulez lui donner comme second paramre. Ceci vous permettra de changer les attributs de ce widget par son nom dans le fichier rc. Si vous utilisez un appel comme celui-ci : button = gtk_button_new_with_label ("Bouton Spial"); gtk_widget_set_name (button, "bouton special"); Ce bouton s'appelle bouton special et peut re accpar son nom dans le fichier rc en tant que bouton special.GtkButton [<--- Vifiez !] Le fichier rc ci-dessous configure les propri de la fenre principale et fait hiter tous les fils de celle-ci du style drit par bouton_principal Le code utilisdans l'application est : window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_widget_set_name (window, "fenetre principale"); Et le style est dini dans le fichier rc avec : widget "fenetre principale.*GtkButton*" style "bouton_principal" Ce qui configure tous les widgets _G_t_k_B_u_t_t_o_n de fenre principale avec le style bouton_principal dini dans le fichier rc. Ainsi que vous pouvez le voir, il s'agit d'un syste puissant et flexible. Utilisez votre imagination pour en tirer le meilleur. 1188..22.. FFoorrmmaatt ddeess ffiicchhiieerrss rrcc ddee GGTTKK Le format du fichier GTK est illustrdans l'exemple suivant. Il s'agit du fichier _t_e_s_t_g_t_k_r_c de la distribution GTK mais j'ai ajoutquelques commentaires et autres choses. Vous pouvez inclure cette explication votre application pour permettre l'utilisateur de rler finement son application. Il y a plusieurs directives pour changer les attributs d'un widget. +o fg - configure la couleur de premier plan d'un widget. +o bg - configure la couleur d'arrie plan d'un widget. +o bg_pixmap - configure l'arrie plan d'un widget avec un pixmap. +o font - configure la fonte utiliser pour un widget. De plus, un widget peut se trouver dans diffents ats et l'on peut configurer des couleurs, pixmaps et fontes diffentes pour chacun d'eux. Ces ats sont : +o NORMAL - L'at normal d'un widget, sans la souris au dessus de lui, non press etc. +o PRELIGHT - Lorsque la souris se trouve au dessus du widget, les couleurs dinies pour cet at sont actives. +o ACTIVE - Lorsque le widget est pressou cliqu il devient actif et les attributs associ cet at sont appliqu. +o INSENSITIVE - Quand un widget est configurpour re insensible et qu'il ne peut re activ il prend ces attributs. +o SELECTED - Lorsqu'un objet est choisi, il prend ces attributs. Lorsqu'on utilise les mots-cl _f_g et _b_g pour configurer les couleurs des widgets, le format est : fg[] = { Red, Green, Blue } OSTATE est l'un des ats vus plus haut (PRELIGHT, ACTIVE etc), et o_R_e_d, _G_r_e_e_n et _B_l_u_e sont des valeurs comprises entre 0 et 1.0. { 1.0, 1.0, 1.0 } reprente la couleur blanche. Ces valeurs doivent re de type rl ou elles seront consids comme valant 0, ainsi un simple 1 ne marchera pas, il faut mettre 1.0 Un 0 simple convient car ce n'est pas un proble s'il n'est pas reconnu puisque toutes les valeurs non reconnues sont mises 0. _b_g___p_i_x_m_a_p est tr similaire, sauf que les couleurs sont remplacs par un nom de fichier. _p_i_x_m_a_p___p_a_t_h est une liste de chemins sar par des : Ces chemins seront parcourus pour chaque pixmap que l'on spifie. La directive _f_o_n_t est simplement : font = "" Ola seule partie difficile est d'arriver comprendre la chae contenant le nom de la fonte. L'utilisation de _x_f_o_n_t_s_e_l ou d'un autre utilitaire semblable peut aider. _w_i_d_g_e_t___c_l_a_s_s configure le style d'une classe de widgets. Ces classes sont lists dans la section sur la hiarchie des widgets. La directive _w_i_d_g_e_t configure un ensemble spifique de widgets selon un style donn annulant tout style de configuration pour la classe de widget donn. Ces widgets sont enregistr dans l'application en utilisant l'appel _g_t_k___w_i_d_g_e_t___s_e_t___n_a_m_e_(_). Ceci vous permet de spifier les attributs d'un widget, widget par widget, au lieu de configurer les attributs d'une classe entie de widgets. Je vous demande instamment de documenter tous ces widgets spiaux pour que les utilisateurs puisse les adapter leurs besoins. Lorsque le mot-cl_p_a_r_e_n_t est utiliscomme attribut, le widget prendra les attributs de son parent dans l'application. Lorsqu'on dinit un style, on peut assigner les attributs d'un style d dini ce nouveau style. style "bouton_principal" = "button" { font = "-adobe-helvetica-medium-r-normal--*-100-*-*-*-*-*-*" bg[PRELIGHT] = { 0.75, 0, 0 } } Cet exemple prend le style "button" et cr un nouveau style "bouton_principal"en changeant simplement la fonte et la couleur de fond pour l'at PRELIGHT. Bien s, un bon nombre de ces attributs ne s'applique pas tous les widgets. C'est une question de bon sens. Tout ce qui peut s'appliquer s'applique. 1188..33.. EExxeemmppllee ddee ffiicchhiieerr rrcc # pixmap_path ":::..." # pixmap_path "/usr/include/X11R6/pixmaps:/home/imain/pixmaps" # # style [= ] # { #