1 <!doctype linuxdoc system>
4 <author>Ian Main, <tt><htmlurl url="mailto:slow@intergate.bc.ca"
5 name="slow@intergate.bc.ca"></tt>
6 <date>January 24, 1998.
11 GTK (GIMP Toolkit) a été d'abord développé pour être une boîte à
12 outils pour GIMP (General Image Manipulation Program). GTK est
13 construit sur GDK (GIMP Drawing Kit) qui est, avant tout, une
14 encapsulation des fonctions Xlib. On l'appelle « GIMP toolkit » car il
15 fut créé pour développer GIMP, mais il est désormais utilisé dans
16 plusieurs projets de logiciels libres. Les auteurs sont :
18 <item> Peter Mattis <tt><htmlurl url="mailto:petm@xcf.berkeley.edu"
19 name="petm@xcf.berkeley.edu"></tt>
20 <item> Spencer Kimball <tt><htmlurl url="mailto:spencer@xcf.berkeley.edu"
21 name="spencer@xcf.berkeley.edu"></tt>
22 <item> Josh MacDonald <tt><htmlurl url="mailto:jmacd@xcf.berkeley.edu"
23 name="jmacd@xcf.berkeley.edu"></tt>
27 GTK est essentiellement une interface de programmation (API) orientée
28 objet. Bien qu'il soit entièrement écrit en C, il est implanté en
29 utilisant la notion de classes et de fonctions de rappel (pointeurs
32 Un troisième composant, appelé glib, remplace certains appels
33 standard et comporte quelques fonctions supplémentaires pour gérer les
34 listes chaînées, etc. Les fonctions de remplacement sont utilisées
35 pour accroître la portabilité de GTK car certaines de ces fonctions,
36 comme g_strerror(), ne sont pas disponibles ou ne sont pas standard
37 sur d'autres Unix. D'autres comportent des améliorations par rapport
38 aux versions de la libc : g_malloc(), par exemple, facilite le
41 Ce didacticiel tente de décrire du mieux possible GTK, mais il n'est pas
42 exhaustif. Il suppose une bonne connaissance du langage C, et de la façon de
43 créer des programmes C. Il serait très précieux au lecteur d'avoir déjà une
44 expérience de la programmation X, mais cela n'est pas nécessaire. Si
45 l'apprentissage de GTK marque vos débuts dans l'approche des widgets, n'hésitez
46 pas à faire des commentaires sur ce didacticiel et sur les problèmes qu'il vous
47 a posé. Il y a aussi une API C++ pour GTK (GTK--), si vous préférez utiliser
48 ce langage, consultez plutôt la documentation qui la concerne. Une
49 encapsulation en Objective C et des liaisons Guile sont également disponibles,
50 mais ne seront pas abordées ici.
52 J'apprécierais beaucoup avoir un écho des problèmes que vous avez
53 rencontré pour apprendre GTK à partir de ce document. De plus, toute
54 suggestion sur son amélioration est la bienvenue.
58 La première chose à faire est, bien sûr, de récupérer les sources de
59 GTK et de les installer. Vous pouvez en obtenir la dernière version
60 sur <tt/ftp.gimp.org/ dans le répertoire <tt>/pub/gtk</tt>. D'autres
61 sources d'informations se trouvent sur
62 <tt>http://www.gimp.org/gtk</tt>. GTK utilise <em/autoconf/ de GNU
63 pour se configurer. Lorsque vous l'aurez détarré, tapez
64 <em>./configure --help</em> pour consulter la liste des options.
66 Pour commencer notre introduction à GTK, nous débuterons avec le
67 programme le plus simple qui soit. Celui-ci créera une fenêtre de
68 200x200 pixels et ne pourra se terminer qu'en le tuant à partir du
74 int main (int argc, char *argv[])
78 gtk_init (&argc, &argv);
80 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
81 gtk_widget_show (window);
89 Tous les programmes inclueront évidemment le fichier
90 <tt>gtk/gtk.h</tt> qui déclare les variables, fonctions, structures,
91 etc. qui seront utilisées par votre application GTK.
96 gtk_init (&argc, &argv);
99 appelle la fonction <em/gtk_init(gint *argc, gchar ***argv)/ qui sera
100 appelée dans toutes les applications GTK. Cette fonction configure
101 certaines choses pour nous, comme l'aspect visuel et les couleurs par
102 défaut, puis appelle <em/gdk_init(gint *argc, gchar ***argv)/. Cette
103 dernière initialise la bibliothèque pour qu'elle puisse être utilisée,
104 configure les gestionnaires de signaux par défaut et vérifie les
105 paramètres passés à notre application via la ligne de commande en
106 recherchant l'un des suivants :
109 <item> <tt/--display/
110 <item> <tt/--debug-level/
111 <item> <tt/--no-xshm/
113 <item> <tt/--show-events/
114 <item> <tt/--no-show-events/
117 Elle les supprime alors de la liste des paramètres, en laissant tout
118 ce qu'elle ne reconnaît pas pour que notre application l'analyse ou
119 l'ignore. Ceci crée un ensemble de paramètres standards acceptés par
120 toutes les applications GTK.
122 Les deux lignes de code suivantes créent et affichent une fenêtre.
125 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
126 gtk_widget_show (window);
129 Le paramètre <tt/GTK_WINDOW_TOPLEVEL/ précise que l'on veut que la
130 fenêtre créée suive l'aspect et le placement définis par le
131 gestionnaire de fenêtres. Plutôt que de créer une fenêtre de 0x0, une
132 fenêtre sans fenêtre fille est de 200x200 par défaut : on peut
133 ainsi la manipuler facilement.
135 La fonction <em/gtk_widget_show()/ informe GTK que l'on a configuré
136 le widget et qu'il peut l'afficher.
138 La ligne suivante lance la boucle principale de traitement de GTK.
144 <em/gtk_main()/ est un autre appel que vous verrez dans toute
145 application GTK. Lorsque le contrôle atteind ce point, GTK se met en
146 attente d'événements X (click sur un bouton, ou appui d'une touche, par
147 exemple), de timeouts ou d'entrées-sorties fichier. Dans notre exemple
148 simple, cependant, les événements sont ignorés.
151 <sect1>« Bonjour tout le monde » en GTK
153 OK, écrivons un programme avec un widget (bouton). C'est le classique « Bonjour tout le monde » à la sauce GTK.
159 /* fonction de rappel. Dans cet exemple, les paramètres sont ignorés...
160 * Les fonctions de rappel sont détaillées plus loin. */
162 void hello (GtkWidget *widget, gpointer data)
164 g_print ("Bonjour tout le monde.\n");
167 gint delete_event(GtkWidget *widget, GdkEvent *event, gpointer data)
169 g_print ("le signal delete_event est survenu.\n");
171 /* Si l'on renvoit TRUE dans le gestionnaire du signal "delete_event",
172 * GTK émettra le signal "destroy". Retourner FALSE signifie que l'on
173 * ne veut pas que la fenêtre soit détruite.
174 * Utilisé pour faire apparaître des boîtes de dialogue du type
175 * « Êtes-vous sûr de vouloir quitter ? » */
177 /* Remplacez FALSE par TRUE et la fenêtre principale sera détruite par
178 * un signal « delete_event ». */
183 /* Autre fonction de rappel */
185 void destroy (GtkWidget *widget, gpointer data)
190 int main (int argc, char *argv[])
192 /* GtkWidget est le type pour déclarer les widgets. */
197 /* Cette fonction est appelée dans toutes les applications GTK.
198 * Les paramètres passés en ligne de commande sont analysés et
199 * retournés à l'application. */
201 gtk_init (&argc, &argv);
203 /* Création d'une nouvelle fenêtre. */
205 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
207 /* Lorsque la fenêtre reçoit le signal "delete_event"
208 * (envoyé par le gestionnaire de fenêtres en utilisant l'option
209 * « close » ou la barre de titre), on lui demande d'appeler la
210 * fonction delete_event() définie plus haut. La donnée passée en
211 * paramètre à la fonction de rappel est NULL et est ignoré dans le
214 gtk_signal_connect (GTK_OBJECT (window), "delete_event",
215 GTK_SIGNAL_FUNC (delete_event), NULL);
217 /* Ici, on connecte l'évenement "destroy" à un gestionnaire de signal.
218 * Cet événement arrive lorsqu'on appelle gtk_widget_destroy() sur la
219 * fenêtre, ou si l'on retourne TRUE dans le rappel "delete_event". */
221 gtk_signal_connect (GTK_OBJECT (window), "destroy",
222 GTK_SIGNAL_FUNC (destroy), NULL);
224 /* Configuration de la largeur du contour de la fenêtre. */
226 gtk_container_border_width (GTK_CONTAINER (window), 10);
228 /* Création d'un nouveau bouton portant le label
229 * "Bonjour tout le monde". */
231 button = gtk_button_new_with_label ("Bonjour tout le monde");
233 /* Quand le bouton recevra le signal "clicked", il appellera la
234 * fonction hello() définie plus haut en lui passant NULL en paramètre. */
236 gtk_signal_connect (GTK_OBJECT (button), "clicked",
237 GTK_SIGNAL_FUNC (hello), NULL);
239 /* Ceci provoquera la destruction de la fenêtre par appel de la
240 * fonction gtk_widget_destroy(window) lors du signal "clicked".
241 * Le signal de destruction pourrait venir de là, ou du
242 * gestionnaire de fenêtres. */
244 gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
245 GTK_SIGNAL_FUNC (gtk_widget_destroy),
246 GTK_OBJECT (window));
248 /* Insertion du bouton dans la fenêtre (container gtk). */
250 gtk_container_add (GTK_CONTAINER (window), button);
252 /* L'étape finale consiste à afficher ce nouveau widget... */
254 gtk_widget_show (button);
256 /* ... et la fenêtre. */
258 gtk_widget_show (window);
260 /* Toutes les applications GTK doivent avoir un gtk_main().
261 * Le déroulement du programme se termine là et attend qu'un
262 * événement survienne (touche pressée ou événement souris). */
270 <sect1>Compilation de « Bonjour tout le monde »
272 Supposons que vous avez sauvegardé le code précédent dans un fichier
273 nommé <em/bonjour.c/, pour le compiler tapez la commande
277 gcc -Wall -g bonjour.c -o bonjour_monde -L/usr/X11R6/lib \
278 -lgtk -lgdk -lglib -lXext -lX11 -lm
281 Les bibliothèques invoquées ci-dessus doivent toutes être dans vos
282 chemins de recherche par défaut, sinon, ajoutez <tt/-L<library
283 directory>/ pour que <em/gcc/ recherche dans ces répertoires les
284 bibliothèques nécessaires. Sur mon système Debian GNU/Linux, par exemple,
285 je dois ajouter <tt>-L/usr/X11R6/lib</> pour qu'il trouve les
286 bibliothèques X11 (NdT : et c'est pareil sur mon système Red Hat
289 L'ordre des bibliothèques est important. L'éditeur de liens doit
290 connaître les fonctions d'une bibliothèque dont il a besoin avant de
293 Si vous compilez en utilisant des bibliothèques statiques, l'ordre
294 dans lequel vous listez les bibliothèques devient très
295 important. L'exemple donné ci-dessus devrait fonctionner dans tous les
298 Les bibliothèques que l'on utilise sont :
300 <item>La bibliothèque glib (<tt/-lglib/), qui contient diverses
301 fonctions. Seule <em/g_print()/ est utilisée dans cet exemple. GTK est
302 construit au dessus de <em/glib/ et vous aurez donc toujours besoin de
303 celle-ci. Voir la section concernant <ref id="sec_glib" name="glib">
304 pour plus de détails.
306 <item>La bibliothèque GDK (<tt/-lgdk/), l'enveloppe de Xlib.
308 <item>La bibliothèque GTK (<tt/-lgtk/), la bibliothèque des widgets,
309 construite au dessus de GDK.
311 <item>La bibliothèque Xlib (<tt/-lX11/ utilisée par GDK.
313 <item>La bibliothèque Xext (<tt/-lXext/). Cette dernière contient le
314 code pour les pixmaps en mémoire partagée et les autres extensions X.
316 <item>La bibliothèque mathématique (<tt/-lm/). Elle est utilisée pour
317 différentes raisons par GTK.
320 <sect1>Théorie des signaux et des rappels
322 Avant de voir en détail le programme « Bonjour tout le monde », nous
323 parlerons d'abord des événements et des fonctions de rappel. GTK est
324 dirigé par les événements, ce qui signifie qu'il restera inactif dans
325 <em/gtk_main/ jusqu'à ce qu'un événement survienne et que le contrôle
326 soit passé à la fonction appropriée.
328 Ce passage du contrôle est réalisé en utilisant le concept de « signal
329 ». Lorsqu'un événement survient, comme l'appui sur un bouton, le
330 signal approprié sera « émis » par le widget qui a été pressé. C'est
331 de cette façon que GTK réalise la plupart de son travail. Pour qu'un
332 bouton réalise une action, on configure un gestionnaire de signal pour
333 capturer ces signaux et appeler la fonction adéquate. Ceci est fait
334 en utilisant une fonction comme :
337 gint gtk_signal_connect (GtkObject *object,
343 Où le premier paramètre est le widget qui émettra le signal, et le
344 deuxième est le nom du signal que l'on souhaite intercepter. Le
345 troisième paramètre est la fonction que l'on veut appeler quand le
346 signal est capturé, et le quatrième sont les données que l'on souhaite
347 passer à cette fonction.
349 La fonction spécifiée par le troisième paramètre s'appelle une «
350 fonction de rappel » et doit être de la forme :
353 void callback_func(GtkWidget *widget, gpointer *callback_data);
356 Où le premier paramètre sera un pointeur vers le widget qui a émis le
357 signal, et le second un pointeur vers les données passées par le
358 dernier paramètre de la fonction <em/gtk_signal_connect()/ décrite
361 Un autre appel utilisé dans l'exemple « Bonjour tout le monde » est :
364 gint gtk_signal_connect_object (GtkObject *object,
367 GtkObject *slot_object);
370 <em/gtk_signal_connect_object()/ est la même chose que
371 <em/gtk_signal_connect()/ sauf que la fonction de rappel utilise un
372 seul paramètre : un pointeur vers un objet GTK. Lorsqu'on utilise
373 cette fonction pour connecter des signaux, le rappel doit être de
377 void callback_func (GtkObject *object);
380 Où l'objet est d'ordinaire un widget. En général, on ne configure pas
381 de rappels pour <em/gtk_signal_connect_object/. D'habitude, ceux-ci sont
382 utilisés pour appeler une fonction GTK acceptant un simple widget ou
383 objet comme paramètre, comme dans notre exemple.
385 La raison pour laquelle il y a deux fonctions pour connecter les
386 signaux est simplement de permettre aux fonctions de rappel d'avoir un
387 nombre différent de paramètres. De nombreuses fonctions de la
388 bibliothèque GTK n'acceptent qu'un simple pointeur vers un
389 <em/GtkWidget/ comme paramètre et vous pouvez donc utiliser
390 <em/gtk_signal_connect_object()/ pour celles-ci, tandis que pour vos
391 fonctions vous pouvez avoir besoin d'avoir de fournir plus de données
392 aux fonctions de rappel.
394 <sect1>« Bonjour tout le monde » pas à pas
396 Maintenant que nous connaissons la théorie, clarifions un peu en progressant à travers le programme « Bonjour tout le monde ».
398 Voici la fonction de rappel appelée lorsque le bouton est « clicked
399 ». Dans notre exemple, on ignore le widget et les données mais il
400 n'est pas difficile de faire quelque chose avec. Le prochain exemple
401 utilisera le paramètre des données pour nous dire quel bouton a été
405 void hello (GtkWidget *widget, gpointer *data)
407 g_print ("Bonjour tout le monde\n");
412 Cette fonction de rappel est un peu spéciale. L'événement
413 "delete_event" survient lorsque le gestionnaire de fenêtres l'envoie à
414 l'application. On doit choisir ce qu'il faut faire de ces
415 événements. On peut les ignorer, leur donner une réponse, ou
416 simplement quitter l'application.
418 La valeur que l'on retourne dans cette fonction de rappel permet à GTK
419 de savoir ce qu'il a à faire. En retournant FALSE, on l'informe que
420 l'on ne veut pas que le signal "destroy" soit émis, afin de laisser
421 notre application tourner. En retournant TRUE, on lui demande
422 d'émettre "destroy" qui appellera à son tour notre gestionnaire du
426 gint delete_event(GtkWidget *widget, GdkEvent *event, gpointer data)
428 g_print ("le signal delete_event est survenu.\n");
435 Voici une autre fonction de rappel qui ne fait que quitter
436 l'application en appelant <em/gtk_main_quit()/. Il n'y a pas grand
437 chose de plus à dire car elle est plutôt triviale :
440 void destroy (GtkWidget *widget, gpointer *data)
446 Je suppose que vous connaissez la fonction <em/main()/... oui, comme
447 les autres programmes C, toutes les applications GTK en ont une.
450 int main (int argc, char *argv[])
454 La partie qui suit déclare deux pointeurs sur des structures de type
455 <em/GtkWidget/. Ceux-ci sont utilisés plus loin pour créer une
456 fenêtre et un bouton.
463 Et revoici notre <em/gtk_init/. Comme précédemment, il initialise le toolkit
464 et analyse les paramètres de la ligne de commande. Il supprime chaque
465 paramètre reconnu de la liste et modifie <em/argc/ et <em/argv/ pour
466 faire comme si ces paramètres n'avaient jamais existé, laissant notre
467 application analyser les paramètres restants.
470 gtk_init (&argc, &argv);
473 Création d'une nouvelle fenêtre. C'est plutôt classique. La mémoire
474 est allouée pour une structure <em/GtkWidget/ et <em/window/ pointe
475 donc sur celle-ci. Cela configure une nouvelle fenêtre, mais celle-ci
476 ne sera pas affichée tant que l'on n'a pas appelé
477 <em/gtk_widget_show(window)/ vers la fin de notre programme.
480 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
483 Voici maintenant un exemple de connexion d'un gestionnaire de signal à
484 un objet : la fenêtre. Le signal "destroy" est capturé. Il est
485 émis lorsqu'on utilise le gestionnaire de fenêtres pour tuer la
486 fenêtre (et que l'on retourne TRUE dans le gestionnaire
487 "delete_event"), ou lorsqu'on utilise l'appel
488 <em/gtk_widget_destroy()/ en lui passant le widget <em/window/ comme
489 objet à détruire. Ici, on appelle juste la fonction <em/destroy()/
490 définie ci-dessus avec le paramètre NULL, ce qui quitte GTK pour
493 <tt/GTK_OBJECT/ et <tt/GTK_SIGNAL_FUNC/ sont des macros qui réalisent
494 les conversions et les vérifications de types pour nous. Elles rendent
495 aussi le code plus lisible.
498 gtk_signal_connect (GTK_OBJECT (window), "destroy",
499 GTK_SIGNAL_FUNC (destroy), NULL);
502 La fonction suivante sert à configurer un attribut d'un objet
503 container. Elle configure simplement la fenêtre pour qu'elle ait une
504 zone vide autour d'elle de 10 pixels de large où aucun widget ne
505 pourra se trouver. Il existe d'autres fonctions similaires que nous
506 verrons dans la section sur la
507 <ref id="sec_setting_widget_attributes"
508 name="Configuration des attributs des widgets">
510 À nouveau, <tt/GTK_CONTAINER/ est une macro réalisant la conversion de type.
512 gtk_container_border_width (GTK_CONTAINER (window), 10);
515 Cet appel crée un nouveau bouton. Il alloue l'espace mémoire pour une
516 nouvelle structure GtkWidget, l'initialise et fait pointer <em/button/
517 vers elle. Ce bouton portera le label « Bonjour tout le monde »
518 lorsqu'il sera affiché.
521 button = gtk_button_new_with_label ("Bonjour tout le monde");
524 Maintenant, prenons ce bouton et faisons lui faire quelque chose
525 d'utile. On lui attache un gestionnaire de signal pour que, lorsqu'il
526 émettra le signal "clicked", notre fonction <em/hello()/ soit
527 appelée. On ignore les paramètres et on ne passe donc que la valeur
528 NULL à la fonction de rappel <em/hello()/. Évidemment, le signal
529 "clicked" est émis lorsqu'on clique sur le bouton avec la souris.
532 gtk_signal_connect (GTK_OBJECT (button), "clicked",
533 GTK_SIGNAL_FUNC (hello), NULL);
536 On utilisera aussi ce bouton pour quitter notre programme, ce qui
537 permettra d'illustrer la façon dont le signal "destroy" peut venir
538 soit du gestionnaire de fenêtres, soit de notre programme. Quand le
539 bouton est "clicked" comme cela est décrit plus haut, il appelle
540 d'abord la fonction de rappel <em/hello()/ puis celle-ci dans l'ordre
541 dans lequel elles sont configurées. On peut avoir autant de fonctions
542 de rappel que l'on désire, elles seront exécutées selon leur ordre de
543 connexion. Puisque la fonction <em/gtk_widget_destroy()/ n'accepte que
544 <em/GtkWidget *widget/ comme paramètre, on utilise ici la fonction
545 <em/gtk_signal_connect_object()/ à la place de
546 <em/gtk_signal_connect()/.
549 gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
550 GTK_SIGNAL_FUNC (gtk_widget_destroy),
551 GTK_OBJECT (window));
554 Voici un appel de placement, qui sera expliqué en détail plus tard,
555 mais qui est plutôt facile à comprendre. Il indique simplement à GTK
556 que le bouton doit être placé dans la fenêtre où il s'affichera.
558 <tscreen><verb> gtk_container_add (GTK_CONTAINER (window), button);
561 Maintenant, nous avons tout configuré comme on le souhaitait :
562 les gestionnaires de signaux sont en place et le bouton est mis dans
563 la fenêtre où il doit se trouver. On demande alors à GTK de « montrer
564 » les widgets à l'écran. Le widget <em/window/ est affiché en dernier
565 afin que la fenêtre entière surgisse d'un coup plutôt que voir d'abord
566 la fenêtre s'afficher puis ensuite le bouton apparaître à
567 l'intérieur. Il faut dire qu'avec des exemples simples comme celui-ci,
568 vous ne ferez pas la différence.
570 gtk_widget_show(button);
572 gtk_widget_show (window);
575 Bien sûr, on appelle <em/gtk_main()/ qui attendra les événements
576 venant du serveur X et demandera aux widgets d'émettre les signaux
577 lorsque ces événements surviendront.
581 Enfin, le <em/return/ final. Il est exécuté lorsque <em/gtk_quit()/ est appelé.
586 Lorsque l'on clique sur un bouton GTK, le widget émet un
587 signal "clicked". Afin de pouvoir utiliser cette information, notre
588 programme configure un gestionnaire pour capturer ce signal. Ce
589 gestionnaire appelle la fonction de notre choix. Dans notre exemple,
590 lorsque le bouton que l'on a créé est "clicked", la fonction
591 <em/hello()/ est appelée avec le paramètre NULL, puis le gestionnaire
592 suivant de ce signal est à son tour appelé. Il appelle la fonction
593 <em/gtk_widget_destroy()/ en lui passant le widget <em/window/ comme
594 paramètre, ce qui provoque la destruction de celui-ci. Ceci
595 force la fenêtre à envoyer un signal "destroy", qui est capturé à son
596 tour et appelle notre fonction de rappel <em/destroy()/ qui ferme
599 Une autre façon de procéder consiste à utiliser le gestionnaire de
600 fenêtres pour détruire la fenêtre. Cela provoquera l'émission du
601 signal "delete_event" qui sera pris en charge par notre gestionnaire
602 <em/delete_event()/. S'il retourne FALSE, la fenêtre restera telle
603 quelle et rien ne se passera. Retourner TRUE forcera GTK à émettre
604 le signal "destroy" qui, bien sûr, appelera la fonction de rappel
605 <em/destroy()/ provoquant la sortie du GTK. <p>
606 On remarquera que ces signaux ne sont pas les mêmes que les signaux
607 systèmes Unix et ne sont pas implantés en utilisant ceux-ci, bien que
608 la terminologie employée soit presque identique.
613 <sect1>Types de données
615 Vous avez probablement noté certaines choses qui nécessitent des
616 explications dans les exemples précédents. les <em/gint/, <em/gchar/,
617 etc. que vous avez pu voir sont des redéfinitions de <em/int/ et
618 <em/char/, respectivement. Leur raison d'être est de s'affranchir des
619 dépendances ennuyeuses concernant la taille des types de données
620 simples lorsqu'on réalise des calculs. Un bon exemple est <em/gint32/
621 qui désignera un entier codé sur 32 bits pour toutes les plateformes,
622 que ce soit une station Alpha 64 bits ou un PC i386 32 bits. Les
623 redéfinitions de type sont très simples et intuitives. Elles sont
624 toutes décrites dans le fichier <em>glib/glib.h</em> (qui est inclus
627 On notera aussi la possibilité d'utiliser un <em/GtkWidget/ lorsque la
628 fonction attend un <em/GtkObject/. GTK possède une architecture
629 orientée objet, et un widget est un objet.
631 <sect1>Compléments sur les gestionnaires de signaux
633 Regardons à nouveau la déclaration de <em/gtk_signal_connect/.
636 gint gtk_signal_connect (GtkObject *object, gchar *name,
637 GtkSignalFunc func, gpointer func_data);
640 Vous avez remarqué que le valeur de retour est de type <em/gint/ ? Il
641 s'agit d'un marqueur qui identifie votre fonction de rappel. Comme on
642 le disait plus haut, on peut avoir autant de fonctions de rappel que
643 l'on a besoin, par signal et par objet, et chacune sera exécutée à son
644 tour, dans l'ordre dans lequel elle a été attachée. Ce marqueur vous
645 permet d'ôter ce rappel de la liste en faisant &;:
648 void gtk_signal_disconnect (GtkObject *object, gint id);
651 Ainsi, en passant le widget dont on veut supprimer le gestionnaire et
652 le marqueur ou identificateur retourné par l'une des fonctions
653 <em/signal_connect/, on peut déconnecter un gestionnaire de signal.
655 Une autre fonction permettant de supprimer tous les gestionnaires de
656 signaux pour un objet est :
659 gtk_signal_handlers_destroy (GtkObject *object);
662 Cet appel n'a pas trop besoin d'explications. Il ôte simplement tous
663 les gestionnaires de signaux de l'objet passé en paramètre.
666 <sect1>Un « Bonjour tout le monde » amélioré
668 Étudions une version légèrement améliorée avec de meilleurs exemples
669 de fonctions de rappel. Ceci permettra aussi d'introduire le sujet
670 suivant : le placement des wigdets.
675 /* Notre nouveau rappel amélioré. La donnée passée à cette fonction est
676 * imprimée sur stdout. */
678 void rappel (GtkWidget *widget, gpointer *data)
680 g_print ("Re-Bonjour - %s a été pressé\n", (char *) data);
683 /* Un autre rappel */
685 void delete_event (GtkWidget *widget, GdkEvent *event, gpointer *data)
690 int main (int argc, char *argv[])
692 /* GtkWidget est le type pour déclarer les widgets */
698 /* Cette fonction est appelée dans toutes les applications GTK.
699 * Les paramètre passés en ligne de commande sont analysés et
700 * retournés à l'application. */
702 gtk_init (&argc, &argv);
704 /* Création d'une nouvelle fenêtre. */
706 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
708 /* Nouvel appel qui intitule notre nouvelle fenêtre
709 * "Salut les boutons !" */
711 gtk_window_set_title (GTK_WINDOW (window), "Salut les boutons !");
713 /* Configuration d'un gestionnaire pour "delete_event" afin de
714 * quitter immédiatement GTK. */
716 gtk_signal_connect (GTK_OBJECT (window), "delete_event",
717 GTK_SIGNAL_FUNC (delete_event), NULL);
720 /* Configuration de la largeur du contour de la fenêtre. */
722 gtk_container_border_width (GTK_CONTAINER (window), 10);
724 /* Création d'une boîte pour y placer les widgets.
725 * Ceci est décrit en détails plus loin dans la section
726 * « placement ». La boîte n'est pas matérialisée, elle est juste
727 * utilisée comme moyen d'arranger les widgets. */
729 box1 = gtk_hbox_new(FALSE, 0);
731 /* On met la boîte dans la fenêtre principale. */
733 gtk_container_add (GTK_CONTAINER (window), box1);
735 /* On crée un nouveau bouton portant le label « Bouton 1 ». */
737 button = gtk_button_new_with_label ("Bouton 1");
739 /* Lorsque le bouton est cliqué, on appelle la fonction « rappel »
740 * avec un pointeur sur la chaîne « Bouton 1 » comme paramètre. */
742 gtk_signal_connect (GTK_OBJECT (button), "clicked",
743 GTK_SIGNAL_FUNC (rappel), (gpointer) "Bouton 1");
745 /* Au lieu d'utiliser gtk_container_add, on place ce bouton dans
746 * la boîte invisible qui a été placée dans la fenêtre. */
748 gtk_box_pack_start(GTK_BOX(box1), button, TRUE, TRUE, 0);
750 /* N'oubliez jamais cette étape qui indique à GTK que la configuration
751 * de ce bouton est terminée et qu'il peut être affiché. */
753 gtk_widget_show(button);
755 /* On fait la même chose pour créer un deuxième bouton. */
757 button = gtk_button_new_with_label ("Bouton 2");
759 /* On appelle la même fonction de rappel avec un paramètre différent,
760 * un pointeur sur la chaîne « Bouton 2 ». */
762 gtk_signal_connect (GTK_OBJECT (button), "clicked",
763 GTK_SIGNAL_FUNC (rappel), (gpointer) "Bouton 2");
765 gtk_box_pack_start(GTK_BOX(box1), button, TRUE, TRUE, 0);
767 /* L'ordre dans lequel on affiche les boutons n'est pas vraiment
768 * important, mais il est préférable d'afficher la fenêtre en dernier
769 * pour qu'elle surgisse d'un coup. */
771 gtk_widget_show(button);
773 gtk_widget_show(box1);
775 gtk_widget_show (window);
777 /* Le reste est dans gtk_main et on attend que la fête commence ! */
785 Compilez ce programme en utilisant les mêmes paramètres que pour
786 l'exemple précédent. Vous remarquerez que, maintenant, il est plus
787 difficile de quitter le programme : vous devez utiliser le
788 gestionnaire de fenêtres ou une commande shell pour le détruire. Un
789 bon exercice pour le lecteur serait d'insérer un troisième bouton «
790 Quitter » qui permettrait de sortir du programme. Vous pouvez aussi
791 jouer avec les options de <em/gtk_box_pack_start()/ en lisant la
792 section suivante. Essayez de redimensionner la fenêtre, et observez
795 Juste une remarque : il existe une autre constante utilisable
796 avec <em/gtk_window_new()/ - GTK_WINDOW_DIALOG. Ceci permet
797 d'interagir de façon un peu différente avec le gestionnaire de
798 fenêtres et doit être utilisé pour les fenêtres temporaires comme les
799 boîtes de dialogue, par exemple.
801 <sect>Placement des widgets
803 Lorsqu'on crée une application, on veut mettre plus qu'un simple
804 bouton dans une fenêtre. Notre premier exemple « Bonjour le monde »
805 n'utilisait qu'un seul widget et on pouvait donc simplement faire un
806 appel à <em/gtk_container_add/ pour « placer » le widget dans la
807 fenêtre. Mais si l'on désire en mettre plus, comment peut-on contrôler
808 l'endroit où le widget sera positionné ? C'est ici que le placement
811 <sect1>Théorie des boîtes de placement
813 La majeure partie du placement est faites en créant des boîtes comme
814 dans l'exemple ci-dessus. Ce sont des widgets containers invisibles où
815 l'on peut placer nos widgets. Elles existent sous deux formes :
816 boîtes horizontales et boîtes verticales. Lorsque l'on place des
817 widgets dans une boîte horizontale, les objets sont insérés
818 horizontalement de gauche à droite ou de droite à gauche selon l'appel
819 utilisé. Dans une boîte verticale, les widgets sont placés de haut en
820 bas ou vice versa. On peut utiliser n'importe quelle combinaison de
821 boîtes à l'intérieur ou à côté d'autres boîtes pour créer l'effet
824 Pour créer une nouvelle boîte horizontale, on appelle
825 <em/gtk_hbox_new()/, et pour les boîtes verticales,
826 <em/gtk_vbox_new()/. Les fonctions <em/gtk_box_pack_start()/ et
827 <em/gtk_box_pack_end()/ servent à placer les objets à l'intérieur de
828 ces containers. La fonction <em/gtk_box_pack_start()/ placera de haut
829 en bas dans une boîte verticale et de gauche à droite dans une boîte
830 horizontale. <em/gtk_box_pack_end()/ fera le contraire en plaçant de
831 bas en haut et de droite à gauche. En utilisant ces fonctions, on peut
832 aligner à droite ou à gauche nos widgets et même les mélanger de
833 n'importe quelle façon pour obtenir l'effet désiré. Dans la plupart de
834 nos exemples, on utilisera <em/gtk_box_pack_start()/. Un objet peut
835 être un autre container ou un widget. En fait, de nombreux widgets
836 (dont les boutons) sont eux-mêmes des containers, mais on utilise
837 généralement seulement un label dans un bouton.
839 En utilisant ces appels, GTK sait où vous voulez placer vos widgets et
840 il peut donc les dimensionner automatiquement et faire d'autres choses
841 bien pratiques. Il existe aussi plusieurs options permettant de
842 préciser comment les widgets doivent être placés. Comme vous pouvez
843 l'imaginer, cette méthode nous donne pas mal de liberté pour placer et
846 <sect1>Détails sur les boîtes
848 À cause de cette liberté, le placement des boîtes avec GTK peut
849 paraître déroutant au premier abord. Il existe beaucoup d'options et
850 il n'est pas tout de suite évident de comprendre comment elles
851 s'accordent toutes ensemble. En fait, il y a 5 styles de base
856 <IMG ALIGN="center" SRC="packbox1.gif"
857 VSPACE="15" HSPACE="10" ALT="Box Packing Example Image" WIDTH="528"
861 Chaque ligne contient une boîte horizontale (<em/hbox/) contenant
862 plusieurs boutons. L'appel à <em/gtk_box_pack/ indique la façon dont
863 sont placés tous les boutons dans la hbox. Chaque bouton est placé
864 dans la hbox de la même façon (mêmes paramètres que la fonction
865 <em/gtk_box_pack_start()/).
867 Voici la déclaration de la fonction <em/gtk_box_pack_start/.
870 void gtk_box_pack_start (GtkBox *box,
877 Le premier paramètre est la boîte dans laquelle on place l'objet, le
878 second est cet objet. Tous les objets sont tous des boutons jusqu'à
879 maintenant, on place donc des boutons dans des boîtes.
881 Le paramètre <em/expand/ de <em/gtk_box_pack_start()/ ou
882 <em/gtk_box_pack_end()/ contrôle la façon dont le widget est placé
883 dans la boîte. S'il vaut TRUE, les widgets sont disposés dans la boîte
884 de façon à en occuper tout l'espace. S'il vaut FALSE, la boîte est
885 rétrécie pour correspondre à la taille du widget. Mettre <em/expand/ à
886 FALSE vous permettra d'aligner à droite et à gauche vos
887 widgets. Sinon, ils s'élargiront pour occuper toute la boîte. Le même
888 effet pourrait être obtenu en utilisant uniquement une des deux
889 fonctions <em/gtk_box_pack_start/ ou <em/pack_end/.
891 Le paramètre <em/fill/ des fonctions <em/gtk_box_pack/ contrôle si de
892 l'espace supplémentaire doit être alloué aux objets eux-mêmes (TRUE),
893 ou si on doit rajouter de l'espace (<em/padding/) dans la boîte autour
894 des objets (FALSE). Il n'a de sens que si le paramètre <em/expand/
897 Lorsque l'on crée une nouvelle boîte, on utilise une fonction comme :
900 GtkWidget * gtk_hbox_new (gint homogeneous,
904 Le paramètre <em/homogeneous/ de <em/gtk_hbox_new/ (et c'est la même
905 chose pour <em/gtk_vbox_new/) vérifie que chaque objet de la boîte ait
906 la même taille (i.e. la même largeur dans une hbox, la même hauteur
907 dans une vbox). S'il vaut TRUE, le paramètre <em/expand/ des fonctions
908 <em/gtk_box_pack/ sera toujours mis à TRUE.
910 Quelle est alors la différence entre les paramètres <em/spacing/
911 (configuré lorsque la boîte est créée) et <em/padding/ (configuré
912 lorque les éléments sont placés) ? <em/spacing/ ajoute de l'espace
913 entre les objets, et <em/padding/ en ajoute de chaque côté d'un
914 objet. La figure suivante devrait éclairer tout cela :
917 <IMG ALIGN="center" SRC="packbox2.gif"
918 VSPACE="15" HSPACE="10" ALT="Box Packing Example Image" WIDTH="509"
922 Voici le code utilisé pour créer les images ci-dessus. J'y ai mis beaucoup de
923 commentaires en espérant que vous n'aurez pas de problème pour le
924 relire. Compilez-le et jouez avec les différents paramètres.
926 <sect1>Programme de démonstration des placements
933 delete_event (GtkWidget *widget, GdkEvent *event, gpointer *data)
938 /* Construction d'une nouvelle hbox remplie de boutons. Les paramètres qui
939 * nous intéressent sont passés à cette fonction.
940 * On n'affiche pas la boîte, mais tout ce qu'elle contient. */
942 GtkWidget *make_box (gint homogeneous, gint spacing,
943 gint expand, gint fill, gint padding)
949 /* Création d'une hbox avec les paramètres homogeneous et spacing
952 box = gtk_hbox_new (homogeneous, spacing);
954 /* Création d'une série de boutons configurés de façon appropriée */
956 button = gtk_button_new_with_label ("gtk_box_pack");
957 gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
958 gtk_widget_show (button);
960 button = gtk_button_new_with_label ("(box,");
961 gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
962 gtk_widget_show (button);
964 button = gtk_button_new_with_label ("button,");
965 gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
966 gtk_widget_show (button);
968 /* Création d'un bouton portant un label dépendant de la valeur
969 * du paramètre expand. */
972 button = gtk_button_new_with_label ("TRUE,");
974 button = gtk_button_new_with_label ("FALSE,");
976 gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
977 gtk_widget_show (button);
979 /* Même chose que ci-dessus mais sous forme abrégée. */
981 button = gtk_button_new_with_label (fill ? "TRUE," : "FALSE,");
982 gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
983 gtk_widget_show (button);
985 /* Récupération du paramètre padding sous forme de chaîne. */
987 sprintf (padstr, "%d);", padding);
989 button = gtk_button_new_with_label (padstr);
990 gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
991 gtk_widget_show (button);
996 int main (int argc, char *argv[])
1002 GtkWidget *separator;
1007 /* Initialisation, à ne jamais oublier ! :) */
1009 gtk_init (&argc, &argv);
1012 fprintf (stderr, "usage : %s num, où num vaut 1, 2, ou 3.\n", *argv);
1014 /* Nettoyage dans GTK et sortie avec un code d'erreur de 1 */
1018 which = atoi (argv[1]);
1020 /* Création de notre fenêtre. */
1022 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1024 /* Il ne faut jamais oublier de connecter le signal "destroy" à la
1025 * fenêtre principale. C'est très important pour disposer d'un
1026 * comportement intuitif adéquat. */
1028 gtk_signal_connect (GTK_OBJECT (window), "delete_event",
1029 GTK_SIGNAL_FUNC (delete_event), NULL);
1031 gtk_container_border_width (GTK_CONTAINER (window), 10);
1034 /* Création d'une boîte verticale (vbox) pour y placer les boîtes
1036 * Ceci permet de placer les boîtes horizontales contenant les boutons
1037 * les unes au dessus des autres dans cette vbox. */
1039 box1 = gtk_vbox_new (FALSE, 0);
1041 /* L'exemple à afficher. Ils correspondent aux images ci-dessus. */
1045 /* Création d'un label. */
1047 label = gtk_label_new ("gtk_hbox_new (FALSE, 0);");
1049 /* Alignement du label à gauche. On précisera cette fonction ainsi
1050 * que les autres dans la section sur les attributs des widgets. */
1052 gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
1054 /* Placement du label dans la boîte verticale (vbox box1). Il ne
1055 * faut pas oublier que les widgets qui s'ajoutent à une vbox sont
1056 * placés les uns au dessus des autres. */
1058 gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0);
1060 /* Affichage du label */
1062 gtk_widget_show (label);
1064 /* On appelle notre fonction de construction de boîte :
1065 * homogeneous = FALSE, spacing = 0,
1066 * expand = FALSE, fill = FALSE, padding = 0 */
1068 box2 = make_box (FALSE, 0, FALSE, FALSE, 0);
1069 gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
1070 gtk_widget_show (box2);
1072 /* On appelle notre fonction de construction de boîte :
1073 * homogeneous = FALSE, spacing = 0,
1074 * expand = FALSE, fill = FALSE, padding = 0 */
1076 box2 = make_box (FALSE, 0, TRUE, FALSE, 0);
1077 gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
1078 gtk_widget_show (box2);
1080 /* Paramètres : homogeneous = FALSE, spacing = 0,
1081 * expand = TRUE, fill = TRUE, padding = 0 */
1083 box2 = make_box (FALSE, 0, TRUE, TRUE, 0);
1084 gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
1085 gtk_widget_show (box2);
1087 /* Création d'un séparateur, on verra cela plus tard, mais ils sont
1088 * simples à utiliser. */
1090 separator = gtk_hseparator_new ();
1092 /* Placement du séparateur dans la vbox. Ne pas oublier que tous les
1093 * widgets sont placés dans une vbox et qu'il seront placés
1096 gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
1097 gtk_widget_show (separator);
1099 /* Création d'un nouveau label et affichage de celui-ci. */
1101 label = gtk_label_new ("gtk_hbox_new (TRUE, 0);");
1102 gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
1103 gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0);
1104 gtk_widget_show (label);
1106 /* Paramètres : homogeneous = TRUE, spacing = 0,
1107 * expand = TRUE, fill = FALSE, padding = 0 */
1109 box2 = make_box (TRUE, 0, TRUE, FALSE, 0);
1110 gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
1111 gtk_widget_show (box2);
1113 /* Paramètres : homogeneous = TRUE, spacing = 0,
1114 * expand = TRUE, fill = TRUE, padding = 0 */
1116 box2 = make_box (TRUE, 0, TRUE, TRUE, 0);
1117 gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
1118 gtk_widget_show (box2);
1120 /* Un autre séparateur */
1122 separator = gtk_hseparator_new ();
1124 /* Les 3 derniers paramètres de gtk_box_pack_start sont :
1125 * expand = FALSE, fill = TRUE, padding = 5. */
1127 gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
1128 gtk_widget_show (separator);
1134 /* Création d'un label, box1 est une vbox identique à
1135 * celle créée au début de main() */
1137 label = gtk_label_new ("gtk_hbox_new (FALSE, 10);");
1138 gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
1139 gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0);
1140 gtk_widget_show (label);
1142 /* Paramètres : homogeneous = FALSE, spacing = 10,
1143 * expand = TRUE, fill = FALSE, padding = 0 */
1145 box2 = make_box (FALSE, 10, TRUE, FALSE, 0);
1146 gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
1147 gtk_widget_show (box2);
1149 /* Paramètres : homogeneous = FALSE, spacing = 10,
1150 * expand = TRUE, fill = TRUE, padding = 0 */
1152 box2 = make_box (FALSE, 10, TRUE, TRUE, 0);
1153 gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
1154 gtk_widget_show (box2);
1156 separator = gtk_hseparator_new ();
1158 /* Les 3 derniers paramètres de gtk_box_pack_start sont :
1159 * expand = FALSE, fill = TRUE, padding = 5. */
1161 gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
1162 gtk_widget_show (separator);
1164 label = gtk_label_new ("gtk_hbox_new (FALSE, 0);");
1165 gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
1166 gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0);
1167 gtk_widget_show (label);
1169 /* Paramètres : homogeneous = FALSE, spacing = 0,
1170 * expand = TRUE, fill = FALSE, padding = 10 */
1172 box2 = make_box (FALSE, 0, TRUE, FALSE, 10);
1173 gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
1174 gtk_widget_show (box2);
1176 /* Paramètres : homogeneous = FALSE, spacing = 0,
1177 * expand = TRUE, fill = TRUE, padding = 10 */
1179 box2 = make_box (FALSE, 0, TRUE, TRUE, 10);
1180 gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
1181 gtk_widget_show (box2);
1183 separator = gtk_hseparator_new ();
1185 /* Les 3 derniers paramètres de gtk_box_pack_start sont :
1186 * expand = FALSE, fill = TRUE, padding = 5. */
1188 gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
1189 gtk_widget_show (separator);
1194 /* Ceci est une démonstration de la possibilité d'utiliser
1195 * gtk_box_pack_end() pour aligner les widgets à droite.
1196 * On crée d'abord une nouvelle boîte comme d'habitude. */
1198 box2 = make_box (FALSE, 0, FALSE, FALSE, 0);
1200 /* On crée le label qui sera mis à la fin. */
1202 label = gtk_label_new ("end");
1204 /* On le place en utilisant gtk_box_pack_end(), il est ainsi
1205 * mis à droite de la hbox créée par l'appel à make_box(). */
1207 gtk_box_pack_end (GTK_BOX (box2), label, FALSE, FALSE, 0);
1209 /* Affichage du label. */
1211 gtk_widget_show (label);
1213 /* Placement de box2 dans box1 (la vbox, vous vous rappelez ? :) */
1215 gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
1216 gtk_widget_show (box2);
1218 /* Séparateur pour le bas. */
1220 separator = gtk_hseparator_new ();
1222 /* Configuration du séparateur en 400x5 pixels.
1223 * La hbox que l'on a créée aura donc 400 pixels de large,
1224 * et le label "end" sera séparé des autres de la hbox.
1225 * Sinon, tous les widgets de la hbox seraient placés les plus
1226 * près possible les uns des autres. */
1228 gtk_widget_set_usize (separator, 400, 5);
1230 /* Placement du séparateur dans la vbox (box1)
1231 * créée au debut de main(). */
1233 gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
1234 gtk_widget_show (separator);
1237 /* Création d'une nouvelle hbox.. vous pouvez en utiliser autant que
1238 * que vous en avez besoin ! */
1240 quitbox = gtk_hbox_new (FALSE, 0);
1242 /* Notre bouton pour quitter. */
1244 button = gtk_button_new_with_label ("Quit");
1246 /* Configuration du signal pour détruire la fenêtre. Ceci enverra le
1247 * signal "destroy" à la fenêtre. Ce signal sera à son tour capturé
1248 * par notre gestionnaire de signal défini plus haut. */
1250 gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
1251 GTK_SIGNAL_FUNC (gtk_widget_destroy),
1252 GTK_OBJECT (window));
1254 /* Placement du bouton dans la « quitbox ».
1255 * Les 3 derniers paramètres de gtk_box_pack_start sont :
1256 * expand = TRUE, fill = FALSE, padding = 0. */
1258 gtk_box_pack_start (GTK_BOX (quitbox), button, TRUE, FALSE, 0);
1260 /* Placement de la quitbox dans la vbox (box1) */
1262 gtk_box_pack_start (GTK_BOX (box1), quitbox, FALSE, FALSE, 0);
1264 /* Placement de la vbox (box1), qui contient maintenant tous nos
1265 * widgets, dans la fenêtre principale. */
1267 gtk_container_add (GTK_CONTAINER (window), box1);
1271 gtk_widget_show (button);
1272 gtk_widget_show (quitbox);
1274 gtk_widget_show (box1);
1276 /* Affichage de la fenêtre en dernier */
1278 gtk_widget_show (window);
1280 /* Ne pas oublier notre fonction principale. */
1284 /* Le contrôle revient ici lorsque gtk_main_quit() est appelée,
1285 * jusqu'à ce que gtk_exit() soitutilisée. */
1292 <sect1>Placement avec les tables
1294 Étudions une autre méthode de placement : les tables. Elles
1295 peuvent s'avérer très utiles dans certaines situations.
1297 En utilisant des tables, on crée une grille dans laquelle on peut
1298 placer les widgets. Ceux-ci peuvent occuper tous les endroits que l'on
1301 La première chose à faire est, bien sûr, d'étudier la fonction
1302 <em/gtk_table_new/ :
1305 GtkWidget* gtk_table_new (gint rows,
1310 Le premier paramètre est le nombre de lignes de la table et le
1311 deuxième, le nombre de colonnes.
1313 Le paramètre <em/homogeneous/ s'occupe de la façon dont les cases de
1314 la table seront dimensionnées. Si homogeneous vaut TRUE, les cases
1315 prennent la taille du plus grand widget de la table. S'il vaut FALSE,
1316 la taille des cases dépend du widget le plus haut de la ligne et du
1317 plus large de cette colonne.
1319 Le nombre de lignes et colonnes va de 0 à n, où n est le nombre spécifié dans
1320 l'appel à <em/gtk_table_new/. Ainsi, avec <em/rows/ = 2 et
1321 <em/columns/ = 2, la table ressemblera à ceci :
1325 0+----------+----------+
1327 1+----------+----------+
1329 2+----------+----------+
1332 On notera que le système de coordonnées part du coin en haut à
1333 gauche. Pour placer un widget dans une case, ou utilise la fonction
1337 void gtk_table_attach (GtkTable *table,
1349 Où le premier paramètre (<em/table/) est la table que l'on a créée et
1350 le second (<em/child/) est le widget que l'on veut placer dans la
1353 Les paramètres <em/left_attach/ et <em/right_attach/ spécifient
1354 l'emplacement du widget et le nombre de cases à utiliser. Par exemple,
1355 si on veut placer un bouton dans le coin inférieur droit de la table
1356 décrite plus haut et que l'on désire ne remplir QUE cette case,
1357 <em/left_attach/ vaudra 1, <em/right_attach/ vaudra 2; <em/top_attach/
1358 vaudra 1 et <em/bottom_attach/ vaudra 2.
1360 Si on veut un widget occupant toute la ligne supérieure de notre table, on utilisera
1361 les valeurs 0, 2, 0, 1.
1363 Les paramètres <em/xoptions/ et <em/yoptions/ servent à préciser les
1364 options de placement et peuvent être combinées par un OU logique pour
1365 permettre des options multiples.
1367 Ces options sont :
1369 <item>GTK_FILL - Si la case de la table est plus large que le widget, et que
1370 GTK_FILL est spécifié, le widget s'élargira pour occuper toute la place
1373 <item>GTK_SHRINK - Si la table a moins de place qu'il ne lui en faut
1374 (généralement, à cause d'un redimensionnement de la fenêtre par
1375 l'utilisateur), les widgets sont alors simplement poussés vers le bas
1376 de la fenêtre et disparaissent. Si GTK_SHRINK est spécifié, les
1377 widgets se réduiront en même temps que la table.
1379 <item>GTK_EXPAND - Cette option provoque l'extension de la table pour
1380 qu'elle utilise tout l'espace restant dans la fenêtre.
1383 Le paramêtres de <em/padding/ jouent le même rôle que pour les boîtes,
1384 il créent une zone libre, spécifiée en pixels, autour du widget.
1386 gtk_table_attach() a BEAUCOUP d'options. Voici donc une fonction-raccourci :
1389 void gtk_table_attach_defaults (GtkTable *table,
1394 gint bottom_attach);
1397 <em/xoptions/ et <em/options/ valent par défaut GTK_FILL | GTK_EXPAND,
1398 et <em/xpadding/ et <em/ypadding/ valent 0. Les autres paramètres sont
1399 les mêmes que ceux de la fonction précédente.
1401 Il existe aussi les fonctions <em/gtk_table_set_row_spacing()/ et
1402 <em/gtk_table_set_col_spacing()/. Elles permettent de placer des
1403 espaces après une ligne ou une colonne.
1406 void gtk_table_set_row_spacing (GtkTable *table,
1412 void gtk_table_set_col_spacing (GtkTable *table,
1417 Pour les colonnes, l'espace est ajouté à droite de la colonne et pour
1418 les lignes, il est ajouté en dessous.
1420 On peut aussi configurer un espacement pour toutes les lignes et/ou
1421 colonnes avec :
1424 void gtk_table_set_row_spacings (GtkTable *table,
1430 void gtk_table_set_col_spacings (GtkTable *table,
1434 Avec ces appels, la dernière ligne et la dernière colonne n'ont pas
1435 d'espace supplémentaire.
1437 <sect1>Exemple de placement avec table
1439 Pour le moment, étudiez l'exemple sur les tables (testgtk.c) distribué
1440 avec les sources de GTK.
1442 <sect>Vue d'ensemble des widgets
1445 Les étapes pour créer un widget en GTK sont :
1447 <item> <em/gtk_*_new()/ - une des fonctions disponibles pour créer un
1448 nouveau widget. Ces fonctions sont décrites dans cette section.
1450 <item> Connexion de tous les signaux que l'on souhaite utiliser avec
1451 les gestionnaires adéquats.
1453 <item> Configuration des attributs du widget.
1455 <item> Placement du widget dans un container en utilisant un appel approprié comme
1456 <em/gtk_container_add()/ ou <em/gtk_box_pack_start()/.
1458 <item> Affichage du widget grâce à <em/gtk_widget_show()/.
1461 <em/gtk_widget_show()/ permet à GTK de savoir que l'on a fini de
1462 configurer les attributs du widget et qu'il est prêt à être
1463 affiché. On peut aussi utiliser <em/gtk_widget_hide()/ pour le faire
1464 disparaître. L'ordre dans lequel on affiche les widgets n'est pas
1465 important, mais il est préférable d'afficher la fenêtre en dernier
1466 pour qu'elle surgisse d'un seul coup plutôt que de voir les différents
1467 widgets apparaître à l'écran au fur et à mesure. Les fils d'un widget
1468 (une fenêtre est aussi un widget) ne seront pas affichés tant que la
1469 fenêtre elle-même n'est pas affichée par la fonction
1470 <em/gtk_widget_show()/.
1472 <sect1> Conversions de types
1474 Vous remarquerez, au fur et à mesure que vous progressez, que GTK
1475 utilise un système de coercition de type. Celle-ci est toujours
1476 réalisée en utilisant des macros qui vérifient si l'objet donné peut
1477 être converti et qui réalisent cette coercition. Les macros que vous
1478 rencontrerez le plus sont :
1481 <item> GTK_WIDGET(widget)
1482 <item> GTK_OBJECT(object)
1483 <item> GTK_SIGNAL_FUNC(function)
1484 <item> GTK_CONTAINER(container)
1485 <item> GTK_WINDOW(window)
1489 Elles sont toutes utilisées pour convertir les paramètres des
1490 fonctions. Vous les verrez dans les exemples et, en règle générale,
1491 vous saurez les utiliser simplement en regardant la
1492 déclaration d'une fonction.
1494 Comme vous pouvez le voir dans la hiérarchie de classes ci-dessous,
1495 tous les <em/GtkWidgets/ dérivent d'une classe de base
1496 <em/GtkObject/. Ceci signifie que vous pouvez utiliser un widget à
1497 chaque fois qu'une fonction requiert un objet - il suffit d'utiliser
1498 la macro GTK_OBJECT().
1503 gtk_signal_connect(GTK_OBJECT(button), "clicked",
1504 GTK_SIGNAL_FUNC(fonction_rappel), donnee_de_rappel);
1507 Cet appel convertit le bouton en objet et fournit une conversion pour
1508 le pointeur de fonction vers la fonction de rappel.
1510 De nombreux widgets sont aussi des containers. Si vous regardez la
1511 hiérarchie de classe ci-dessous, vous remarquerez que beaucoup de
1512 widgets viennent de la classe <em/GtkContainer/. N'importe lequel de
1513 ces widgets peut être utilisé avec la macro GTK_CONTAINER pour être
1514 passé en paramètre à une fonction qui attend un container.
1516 Malheureusement, ces macros ne peuvent être couvertes en détail dans
1517 ce didacticiel, Je vous recommande donc de jeter un coup d'oeil sur
1518 les fichier en-têtes GTK : ils peuvent s'avérer très
1519 instructifs. En fait, il n'est pas difficile de comprendre comment
1520 fonctionne un widget, il suffit d'étudier les déclarations des
1524 <sect1>La hiérarchie des widgets
1526 Voici l'arbre de la hiérarchie de classes utilisées pour implanter les widgets.
1536 | | +-- GtkAlignment
1538 | | | *-- GtkAspectFrame
1541 | | | +-- GtkListItem
1542 | | | +-- GtkMenuItem
1543 | | | | +-- GtkCheckMenuItem
1544 | | | | *-- GtkRadioMenuItem
1546 | | | *-- GtkTreeItem
1551 | | \-- GtkFileSelection
1556 | | +-- GtkColorSelection
1560 | | +-- GtkOptionMenu
1561 | | \-- GtkToggleButton
1562 | | \-- GtkCheckButton
1563 | | \-- GtkRadioButton
1571 | +-- GtkScrolledWindow
1605 <sect1>Widgets sans fenêtre
1607 Les widgets suivants n'ont pas de fenêtre associée. Si vous voulez
1608 capturer des événements, vous devez utiliser <em/GtkEventBox/.
1609 Reportez-vous à la section sur
1610 <ref id="sec_The_EventBox_Widget" name="Le widget EventBox">
1636 Nous continuerons notre exploration de GTK en examinant chaque widget
1637 tour à tour, créant quelques fonctions simples pour les afficher. Une
1638 autre source intéressante est le programme <em/testgtk.c/ livré avec
1639 GTK. Il se trouve dans le répertoire <em>gtk/</em>
1641 <sect>Widgets boutons
1643 <sect1>Boutons normaux
1645 On a déjà presque vu tout ce qu'il y avait à voir sur le widget
1646 bouton. Il est très simple. Cependant, il y a deux façons de créer un
1647 bouton. On peut utiliser <em/gtk_button_new_with_label()/ pour créer
1648 un bouton avec un label, ou <em/gtk_button_new()/ pour créer un bouton
1649 vide. Dans ce dernier cas, c'est à vous de placer un label ou un
1650 pixmap sur celui-ci. Pour ce faire, créez une boîte, puis placez vos
1651 objets dans celle-ci en utilisant la fonction habituelle
1652 <em/gtk_box_pack_start/, utilisez alors <em/gtk_container_add/ pour
1653 placer la boîte dans le bouton.
1655 Voici un exemple d'utilisation de <em/gtk_button_new()/ pour créer un
1656 bouton contenant une image et un label. J'ai séparé du reste le code
1657 qui crée une boîte pour que vous puissiez l'utiliser dans vos
1662 #include <gtk/gtk.h>
1665 /* Création d'une hbox avec une image et un label. Cette fonction
1666 * retourne la boîte... */
1668 GtkWidget *xpm_label_box (GtkWidget *parent, gchar *xpm_filename,
1673 GtkWidget *pixmapwid;
1678 /* Création de la boite pour un xpm et un label */
1680 box1 = gtk_hbox_new (FALSE, 0);
1681 gtk_container_border_width (GTK_CONTAINER (box1), 2);
1683 /* Choix d'un style de bouton... Je suppose que c'est pour obtenir
1684 * la couleur du fond. Si quelqu'un connaît la vraie raison, qu'il
1685 * m'éclaire sur ce point. */
1687 style = gtk_widget_get_style(parent);
1689 /* Chargement de xpm pour créer une image */
1691 pixmap = gdk_pixmap_create_from_xpm (parent->window, &mask,
1692 &style->bg[GTK_STATE_NORMAL],
1694 pixmapwid = gtk_pixmap_new (pixmap, mask);
1696 /* Création d'un label */
1698 label = gtk_label_new (label_text);
1700 /* placement de l'image et du label dans la boîte */
1702 gtk_box_pack_start (GTK_BOX (box1),
1703 pixmapwid, FALSE, FALSE, 3);
1705 gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 3);
1707 gtk_widget_show(pixmapwid);
1708 gtk_widget_show(label);
1713 /* Notre fonction de rappel habituelle */
1715 void callback (GtkWidget *widget, gpointer *data)
1717 g_print ("Bonjour - %s a été pressé\n", (char *) data);
1721 int main (int argc, char *argv[])
1723 /* GtkWidget est le type utilisé pour déclarer les widgets */
1729 gtk_init (&argc, &argv);
1731 /* Création d'une fenêtre */
1733 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1735 gtk_window_set_title (GTK_WINDOW (window), "Pixmap'd Buttons!");
1737 /* Il est préférable de faire cela pour toutes les fenêtres */
1739 gtk_signal_connect (GTK_OBJECT (window), "destroy",
1740 GTK_SIGNAL_FUNC (gtk_exit), NULL);
1743 /* Configuration du bord de la fenêtre */
1745 gtk_container_border_width (GTK_CONTAINER (window), 10);
1747 /* Création d'un bouton */
1749 button = gtk_button_new ();
1751 /* Vous devriez être habitué à voir ces fonctions maintenant */
1753 gtk_signal_connect (GTK_OBJECT (button), "clicked",
1754 GTK_SIGNAL_FUNC (callback), (gpointer) "cool button");
1756 /* Appel de notre fonction de création de boîte */
1758 box1 = xpm_label_box(window, "info.xpm", "cool button");
1760 /* Placement et affichage de tous nos widgets */
1762 gtk_widget_show(box1);
1764 gtk_container_add (GTK_CONTAINER (button), box1);
1766 gtk_widget_show(button);
1768 gtk_container_add (GTK_CONTAINER (window), button);
1770 gtk_widget_show (window);
1772 /* Le reste est dans gtk_main */
1779 La fonction <em/xpm_label_box()/ peut être utilisée pour placer des
1780 xpms et des labels sur tout widget qui peut être container.
1782 <sect1> Boutons commutateurs
1784 Les boutons commutateurs ressemblent beaucoup aux boutons normaux, sauf
1785 qu'ils seront toujours alternativement dans un état ou dans un
1786 autre. Le changement d'état s'effectue par un click. Ils peuvent être
1787 enfoncés et, lorsqu'on clique dessus, ils se relèvent. Re-cliquez,
1788 et ils se renfoncent.
1790 Les boutons commutateurs sont la base des cases à cocher ou des boutons
1791 radio, donc la plupart des appels utilisés pour les boutons commutateurs
1792 sont hérités par les cases à cocher et les boutons radio. J'insisterai
1793 là dessus quand nous les aborderons.
1795 Création d'un bouton commutateur :
1798 GtkWidget* gtk_toggle_button_new (void);
1800 GtkWidget* gtk_toggle_button_new_with_label (gchar *label);
1803 Comme vous pouvez l'imaginer, elles fonctionnent comme celles des
1804 boutons normaux. La première crée un bouton commutateur vide et la
1805 deuxième un bouton commutateur contenant déjà un label.
1807 Pour récupérer l'état d'un commutateur et cela comprend aussi les
1808 cases à cocher et les boutons radio, on utilise une macro comme nous
1809 le montrons dans l'exemple qui suit et qui teste l'état du commutateur
1810 dans une fonction de rappel. Le signal qui nous intéresse et qui est
1811 émis par les boutons commutateurs (ce qui comprend aussi les cases à
1812 cocher et les boutons radio), est le signal "toggled". Pour vérifier
1813 l'état de ces boutons, on configure un gestionnaire de signal qui
1814 capture "toggled" et utilise la macro pour déterminer l'état. La
1815 fonction de rappel ressemblera à ceci :
1818 void rappel_bouton_commutateur (GtkWidget *widget, gpointer data)
1820 if (GTK_TOGGLE_BUTTON(widget)->active)
1822 /* Si l'on est ici, c'est que le bouton est relâché. */
1826 /* le bouton est enfoncé */
1836 guint gtk_toggle_button_get_type (void);
1839 No idea... they all have this, but I dunno what it is :)
1843 void gtk_toggle_button_set_mode (GtkToggleButton *toggle_button,
1844 gint draw_indicator);
1851 L'appel qui suit peut être utilisé pour configurer l'état d'un bouton
1852 commutateur et de ses descendants, les cases à cocher et les boutons
1853 radio. On lui passe notre bouton en premier paramètre et TRUE ou
1854 FALSE pour spécifier s'il doit être relâché ou enfoncé. Par défaut, il
1855 est relâché (FALSE).
1858 void gtk_toggle_button_set_state (GtkToggleButton *toggle_button,
1862 On notera que lorsqu'on utilise cette fonction, et que l'état est
1863 modifié, cela force le bouton à émettre un signal "clicked".
1866 void gtk_toggle_button_toggled (GtkToggleButton *toggle_button);
1869 Cet appel ne fait que commuter le bouton et émettre le signal "toggled".
1871 <sect1> Cases à cocher
1873 Les cases à cocher héritent de nombreuses propriétés et fonctions des
1874 boutons commutateurs, mais ont un aspect différent. Au lieu d'être des
1875 boutons contenant du texte, ce sont de petits carrés avec un texte sur
1876 leur droite. Il sont souvent utilisés pour valider ou non des options
1877 dans les applications.
1879 Les deux fonctions de création sont identiques à celles des boutons normaux.
1882 GtkWidget* gtk_check_button_new (void);
1884 GtkWidget* gtk_check_button_new_with_label (gchar *label);
1887 La fonction <em/new_with_label/ crée une case à cocher avec un texte à
1890 La vérification de l'état d'une case à cocher est identique à celle
1891 des boutons commutateurs.
1893 <sect1> Boutons radio
1895 Les boutons radio ressemblent aux cases à cocher sauf qu'ils sont
1896 groupés de façon à ce qu'un seul d'entre-eux puisse être sélectionné à
1897 un moment donné. Ils sont utilisés par les applications lorsqu'il
1898 s'agit d'effectuer un choix dans une liste d'options.
1900 La création d'un bouton radio s'effectue grâce à l'un des appels
1904 GtkWidget* gtk_radio_button_new (GSList *group);
1906 GtkWidget* gtk_radio_button_new_with_label (GSList *group,
1910 On notera le paramètre supplémentaire de ces fonctions. Elles
1911 nécessitent un groupe pour réaliser correctement leur tâche. Le
1912 premier appel doit passer NULL au premier paramètre puis on peut créer un
1913 groupe en utilisant :
1916 GSList* gtk_radio_button_group (GtkRadioButton *radio_button);
1920 On passe alors ce groupe en premier paramètre des appels suivants aux fonctions de création. Il est préférable, aussi, de préciser quel bouton doit être choisi par défaut avec la fonction :
1923 void gtk_toggle_button_set_state (GtkToggleButton *toggle_button,
1927 Celle-ci est décrite dans la section sur les boutons commutateurs et fonctionne
1928 exactement de la même façon.
1930 [Mettre ici un exemple d'utilisation de tout cela car je crois que cela ferait
1931 beaucoup de bien...]
1934 <sect> Widgets divers
1938 Les labels sont très utilisés dans GTK et sont relativement
1939 simples. Ils n'émettent pas de signaux car ils n'ont pas de fenêtre X
1940 qui leur est associée. Si vous avez besoin de capturer des signaux ou
1941 de faire des coupures (« clippings »), utilisez un widget EventBox.
1943 Pour créer un label, on utilise :
1946 GtkWidget* gtk_label_new (char *str);
1949 Où l'unique paramètre est la chaîne de caractères que l'on veut que le
1952 Pour changer le texte d'un label après sa création, on utilise la fonction :
1955 void gtk_label_set (GtkLabel *label,
1959 où le premier paramètre est le label que l'on veut modifier, que l'on
1960 convertit en utilisant la macro GTK_LABEL(), et le second est la
1963 L'espace nécessaire à la nouvelle chaîne sera automatiquement ajusté
1966 Pour récupérer la chaîne courante, on utilise la fonction :
1969 void gtk_label_get (GtkLabel *label,
1973 où le premier paramètre est le label dont on veut récupérer la chaîne
1974 et le second sert à retourner cette chaîne.
1976 <sect1>Le widget bulle d'aide
1978 Ce sont les petits textes qui surgissent lorsque vous laissez votre
1979 pointeur sur un bouton ou un autre widget pendant quelques secondes.
1980 Ils sont faciles à utiliser, on ne donnera donc pas d'exemple. Si vous
1981 voulez voir du code, consultez le programme <em/testgtk.c/ distribué
1984 Certains widgets (comme les labels) ne fonctionnent pas avec les
1987 Le premier appel que vous utiliserez sera pour créer une nouvelle
1988 bulle d'aide. Vous n'avez besoin que de le faire une fois dans une
1989 fonction donnée. Le <em/GtkTooltip/ que cette fonction retourne peut
1990 être utilisé pour créer plusieurs bulles d'aide.
1993 GtkTooltips *gtk_tooltips_new (void);
1996 Lorsque vous avez créé une nouvelle bulle d'aide et le widget sur lequel vous
1997 voulez l'utiliser, vous n'avez qu'à faire cet appel pour la configurer :
2000 void gtk_tooltips_set_tips (GtkTooltips *tooltips,
2005 Les paramètres sont la bulle d'aide déjà créée, suivi du widget pour
2006 lequel vous voulez voir apparaître cette bulle et le texte que vous
2007 voulez qu'elle contienne.
2009 Voici un petit exemple :
2012 GtkTooltips *tooltips;
2015 tooltips = gtk_tooltips_new ();
2016 button = gtk_button_new_with_label ("bouton 1");
2018 gtk_tooltips_set_tips (tooltips, button, "C'est le bouton 1");
2022 D'autres fonctions peuvent être utilisées avec les bulles d'aide. Je ne ferais que les énumérer et les décrire brièvement.
2025 void gtk_tooltips_destroy (GtkTooltips *tooltips);
2028 Destruction de bulles d'aide.
2031 void gtk_tooltips_enable (GtkTooltips *tooltips);
2034 Activation d'un ensemble de bulles d'aide désactivées.
2037 void gtk_tooltips_disable (GtkTooltips *tooltips);
2040 Désactivation d'un ensemble de bulles d'aide activées.
2043 void gtk_tooltips_set_delay (GtkTooltips *tooltips,
2046 </verb></tscreen> Configure le nombre de millisecondes pendant lequel
2047 le pointeur soit se trouver sur le widget avant que la bulle d'aide
2048 n'apparaisse. Par défaut, ce délai est de 1000 millisecondes, soit 1
2052 void gtk_tooltips_set_tips (GtkTooltips *tooltips,
2057 Change le texte d'une bulle d'aide déjà créée.
2060 void gtk_tooltips_set_colors (GtkTooltips *tooltips,
2061 GdkColor *background,
2062 GdkColor *foreground);
2065 Configure les couleurs de fond et de premier plan des bulles
2066 d'aides. Je ne sais toujours pas comment spécifier les couleurs...
2068 Et c'est tout concernant les fonctions associées aux bulles
2069 d'aide. C'est plus que vous ne vouliez sûrement en savoir :)
2071 <sect1> Barres de progression
2073 Les barres de progression sont utilisées pour afficher la progression
2074 d'une opération. Elles sont très simple à utiliser comme vous pourrez
2075 le constater en étudiant le code ci-dessous. Commençons d'abord par
2076 l'appel permettant de créer une nouvelle barre.
2079 GtkWidget *gtk_progress_bar_new (void);
2082 Maintenant que la barre est créée, nous pouvons l'utiliser.
2085 void gtk_progress_bar_update (GtkProgressBar *pbar, gfloat percentage);
2088 Le premier paramètre est la barre de progression sur laquelle on veut
2089 agir, et le second est le pourcentage « effectué », signifiant le
2090 remplissage de la barres de 0 à 100 % (réel compris entre 0 et 1).
2092 Les barres de progression sont généralement utilisées avec les délais
2093 d'expiration ou autres fonctions identiques (voir la section sur <ref
2094 id="sec_timeouts" name="Expirations, fonctions d'E/S et d'attente">)
2095 pour donner l'illusion du multi-tâches. Toutes emploient la fonction
2096 <em/gtk_progress_bar_update/ de la même façon.
2098 Voici un exemple de barre de progression mise à jour par des
2099 expirations. Ce code montre aussi comment réinitialiser une barre.
2102 #include <gtk/gtk.h>
2104 static int ptimer = 0;
2107 /* Cette fonction incrémente et met à jour la barre de progression,
2108 * elle la réinitialise si pstat vaut FALSE */
2110 gint progress (gpointer data)
2114 /* récupération de la valeur courante de la barre */
2116 pvalue = GTK_PROGRESS_BAR (data)->percentage;
2118 if ((pvalue >= 1.0) || (pstat == FALSE)) {
2124 gtk_progress_bar_update (GTK_PROGRESS_BAR (data), pvalue);
2129 /* Cette fonction signale une réinitialisation de la barre */
2131 void progress_r (void)
2136 void destroy (GtkWidget *widget, gpointer *data)
2141 int main (int argc, char *argv[])
2149 gtk_init (&argc, &argv);
2151 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2153 gtk_signal_connect (GTK_OBJECT (window), "delete_event",
2154 GTK_SIGNAL_FUNC (destroy), NULL);
2156 gtk_container_border_width (GTK_CONTAINER (window), 10);
2158 table = gtk_table_new(3,2,TRUE);
2159 gtk_container_add (GTK_CONTAINER (window), table);
2161 label = gtk_label_new ("Exemple de barre de progression");
2162 gtk_table_attach_defaults(GTK_TABLE(table), label, 0,2,0,1);
2163 gtk_widget_show(label);
2165 /* Crée une barre, la place dans la table et l'affiche */
2167 pbar = gtk_progress_bar_new ();
2168 gtk_table_attach_defaults(GTK_TABLE(table), pbar, 0,2,1,2);
2169 gtk_widget_show (pbar);
2171 /* Configure le délai d'expiration pour gérer automatiquement la
2172 * mise à jour de la barre */
2174 ptimer = gtk_timeout_add (100, progress, pbar);
2176 /* Ce bouton indique à la barre qu'elle doit se réinitialiser */
2178 button = gtk_button_new_with_label ("Reset");
2179 gtk_signal_connect (GTK_OBJECT (button), "clicked",
2180 GTK_SIGNAL_FUNC (progress_r), NULL);
2181 gtk_table_attach_defaults(GTK_TABLE(table), button, 0,1,2,3);
2182 gtk_widget_show(button);
2184 button = gtk_button_new_with_label ("Annuler");
2185 gtk_signal_connect (GTK_OBJECT (button), "clicked",
2186 GTK_SIGNAL_FUNC (destroy), NULL);
2188 gtk_table_attach_defaults(GTK_TABLE(table), button, 1,2,2,3);
2189 gtk_widget_show (button);
2191 gtk_widget_show(table);
2192 gtk_widget_show(window);
2200 Dans ce petit programme, il y a quatre parties concernant le
2201 fonctionnement général des barres de progression, nous les étudierons
2202 dans l'ordre de leurs appels.
2205 pbar = gtk_progress_bar_new ();
2208 Cet appel crée une nouvelle barre, nommée <em/pbar/.
2211 ptimer = gtk_timeout_add (100, progress, pbar);
2214 Cet appel utilise des délais d'expiration pour permettre un intervalle
2215 de temps constant. ces délais ne sont pas nécessaires à l'utilisation
2216 des barres de progression.
2219 pvalue = GTK_PROGRESS_BAR (data)->percentage;
2222 Ce code assigne à <em/pvalue/ la valeur du pourcentage de la barre.
2225 gtk_progress_bar_update (GTK_PROGRESS_BAR (data), pvalue);
2228 Finalement, ce code met à jour la barre avec la valeur de <em/pvalue/.
2230 Et c'est tout ce qu'il y a à savoir sur les barres de
2231 progression. Amusez-vous bien.
2233 <sect1> Boîtes de dialogue
2236 Les widgets boîtes de dialogue sont très simples : ce sont
2237 simplement des fenêtres avec plusieurs choses déjà placées dedans. La
2238 structure d'une boîte de dialogue est :
2246 GtkWidget *action_area;
2250 Comme vous le voyez, cela crée simplement une fenêtre et la place dans
2251 une vbox suivie d'un séparateur et d'une hbox pour la « zone d'action ».
2253 Le widget boîte de dialogue peut servir à produire des messages pour
2254 l'utilisateur ainsi qu'à d'autres tâches. Il est vraiment rudimentaire
2255 et il n'y a qu'une seule fonction pour les boîtes de dialogue :
2258 GtkWidget* gtk_dialog_new (void);
2261 Ainsi, pour créer un nouveau dialogue, on utilise :
2265 window = gtk_dialog_new ();
2268 Ceci créera la boîte de dialogue et c'est maintenant à vous de
2269 l'utiliser. Vous pouvez, par exemple, placer un bouton dans la zone
2270 d'action en faisant quelque chose comme :
2274 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->action_area), button,
2276 gtk_widget_show (button);
2279 Et vous pouvez aussi ajouter un label à la zone de la vboxb :
2282 label = gtk_label_new ("Les boîtes de dialogues sont pratiques");
2283 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->vbox), label, TRUE,
2285 gtk_widget_show (label);
2288 Comme exemple d'utilisation d'une boîte de dialogue, vous pourriez
2289 mettre deux boutons dans la zone d'action (un bouton « Annuler » et un
2290 bouton « Ok ») et un label dans la zone de la vbox posant une question
2291 à l'utilisateur ou signalant une erreur, etc. Vous pouvez alors
2292 attacher un signal différent à chacun des boutons et réaliser
2293 l'opération que l'utilisateur a choisie.
2298 Les pixmaps sont des structures de données contenant des
2299 images. Celles-ci peuvent être utilisées à différents endroits, mais
2300 le plus souvent comme icônes dans le bureau X Window. Un bitmap est un
2301 pixmap de 2 couleurs.
2303 Pour utiliser des pixmaps avec GTK, on doit d'abord construire une
2304 structure <em/GdkPixmap/ en utilisant les fonctions de la couche
2305 GDK. Les pixmaps peuvent soit être créés à partir de données en
2306 memoire, ou à partir de données lues dans un fichier. Nous utiliserons
2307 chacun des appels pour créer un pixmap.
2310 GdkPixmap *gdk_bitmap_create_from_data( GdkWindow *window,
2316 Cette fonction sert à créer un pixmap mono-plan (2 couleurs) à partir
2317 de données en mémoire. Chaque bit de la donnée <em/data/. <em/width/
2318 et <em/height/ sont exprimés en pixels. Le pointeur vers un
2319 <em/GdkWindow/ pointe sur la fenêtre courante car les ressources d'un
2320 pixmap n'ont de signification que dans le contexte de l'écran où il
2324 GdkPixmap* gdk_pixmap_create_from_data( GdkWindow *window,
2333 Cette fonction est utilisée pour créer un pixmap d'une profondeur
2334 donnée (nombre de couleurs) à partir de la donnée spécifiée pas
2335 <em/data/. <em/fg/ et <em/bg/ sont les couleurs à utiliser pour
2336 l'avant et l'arrière-plan.
2339 GdkPixmap* gdk_pixmap_create_from_xpm( GdkWindow *window,
2341 GdkColor *transparent_color,
2342 const gchar *filename );
2345 Le format XPM est une représentation des pixmaps reconnue par le système X Window. Il est largement utilisé et de nombreux utilitaires pour créer des fichiers d'images à ce format sont disponibles. Le fichier <em/filename/ doit contenir une image dans ce format qui sera chargée dans la structure pixmap. Le masque <em/mask/ indique quels sont les bits opaques du pixmap. Tous les autres bits sont colorisés en utilisant la couleur spécifiée par <em/transparent_color/. Un exemple d'utilisation est présenté ci-dessous.
2348 GdkPixmap* gdk_pixmap_create_from_xpm_d (GdkWindow *window,
2350 GdkColor *transparent_color,
2354 De petites images peuvent être intégrées dans un programme sous la forme de
2355 données <em/data/ au format XPM. Un pixmap est créé en utilisant ces données au
2356 lieu de les lire dans un fichier. Un exemple de telles données est :
2360 static const char * xpm_data[] = {
2363 ". c #000000000000",
2364 "X c #FFFFFFFFFFFF",
2384 void gdk_pixmap_destroy( GdkPixmap *pixmap );
2387 Lorsqu'on a utilisé un pixmap et que l'on en a plus besoin tout de suite, il est préférable de libérer la ressource en utilisant un appel à <em/gdk_pixmap_destroy/. Les pixmaps doivent être considérées comme une ressource précieuse.
2389 Quand le pixmap est créé, on peut l'afficher comme un widget GTK. On
2390 doit créer un widget pixmap qui contiendra le pixmap GDK. Ceci est
2391 réalisé de la façon suivante :
2394 GtkWidget* gtk_pixmap_new( GdkPixmap *pixmap,
2398 Les autres fonctions pour les widgets pixmap sont :
2401 guint gtk_pixmap_get_type( void );
2402 void gtk_pixmap_set( GtkPixmap *pixmap,
2405 void gtk_pixmap_get( GtkPixmap *pixmap,
2410 <em/gtk_pixmap_set/ sert à changer le pixmap pris en charge par le widget. <em/val/ est le pixmap créé par le GDK.
2412 Voici un exemple illustrant l'utilisation d'un pixmap dans un bouton :
2416 #include <gtk/gtk.h>
2419 /* données XPM d'une icône "Ouvrir fichier" */
2420 static const char * xpm_data[] = {
2423 ". c #000000000000",
2424 "X c #FFFFFFFFFFFF",
2443 /* Termine l'application lorsqu'elle est appelée
2444 * via le signal "delete_event" */
2446 void close_application( GtkWidget *widget, GdkEvent *event, gpointer *data )
2452 /* Invoquée lorsque le bouton est cliqué. Affiche simplement
2455 void button_clicked( GtkWidget *widget, gpointer *data )
2457 printf( "bouton cliqué\n" );
2461 int main( int argc, char *argv[] )
2463 /* GtkWidget est le type pour déclarer les widgets */
2465 GtkWidget *window, *pixmapwid, *button;
2470 /* Crée la fenêtre principale et attache le signal "delete_event" pour
2471 * terminer l'application */
2473 gtk_init( &argc, &argv );
2474 window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
2475 gtk_signal_connect( GTK_OBJECT (window), "delete_event",
2476 GTK_SIGNAL_FUNC (close_application), NULL );
2477 gtk_container_border_width( GTK_CONTAINER (window), 10 );
2478 gtk_widget_show( window );
2480 /* Utilisation de GDK pour créer le pixmap */
2482 style = gtk_widget_get_style( window );
2483 pixmap = gdk_pixmap_create_from_xpm_d( window->window, &mask,
2484 &style->bg[GTK_STATE_NORMAL],
2485 (gchar **)xpm_data );
2487 /* Création d'un widget pixmap GTK pour contenir le pixmap GDK */
2489 pixmapwid = gtk_pixmap_new( pixmap, mask );
2490 gtk_widget_show( pixmapwid );
2492 /* Création d'un bouton pour contenir le widget pixmap */
2494 button = gtk_button_new();
2495 gtk_container_add( GTK_CONTAINER(button), pixmapwid );
2496 gtk_container_add( GTK_CONTAINER(window), button );
2497 gtk_widget_show( button );
2499 gtk_signal_connect( GTK_OBJECT(button), "clicked",
2500 GTK_SIGNAL_FUNC(button_clicked), NULL );
2502 /* Affichage de la fenêtre */
2510 Pour charger un fichier à partir d'un fichier XPM appelé
2511 <em/icon0.xpm/ se trouvant dans le répertoire courant, on aurait créé
2512 le pixmap ainsi :
2515 /* Charge un pixmap à partir d'un fichier */
2517 pixmap = gdk_pixmap_create_from_xpm( window->window, &mask,
2518 &style->bg[GTK_STATE_NORMAL],
2520 pixmapwid = gtk_pixmap_new( pixmap, mask );
2521 gtk_widget_show( pixmapwid );
2522 gtk_container_add( GTK_CONTAINER(window), pixmapwid );
2526 Utilisation des formes
2528 Un désavantage de l'utilisation des pixmaps est que l'objet affiché
2529 est toujours rectangulaire, quelle que soit l'image. On voudrait
2530 pouvoir créer des bureaux et des applications avec des icônes ayant
2531 des formes plus naturelles. Par exemple, pour une interface de jeu, on
2532 aimerait avoir des boutons ronds à pousser. Pour faire cela, on doit
2533 utiliser des fenêtres avec des formes.
2535 Une fenêtre avec forme est simplement un pixmap dont les pixels du
2536 fond sont transparents. Ainsi, lorsque l'image d'arrière-plan est
2537 multicolore, on ne la cache pas avec le bord de notre icône. L'exemple
2538 suivant affiche une image de brouette sur le bureau.
2542 #include <gtk/gtk.h>
2545 static char * WheelbarrowFull_xpm[] = {
2548 ". c #DF7DCF3CC71B",
2549 "X c #965875D669A6",
2550 "o c #71C671C671C6",
2551 "O c #A699A289A699",
2552 "+ c #965892489658",
2553 "@ c #8E38410330C2",
2554 "# c #D75C7DF769A6",
2555 "$ c #F7DECF3CC71B",
2556 "% c #96588A288E38",
2557 "& c #A69992489E79",
2558 "* c #8E3886178E38",
2559 "= c #104008200820",
2560 "- c #596510401040",
2561 "; c #C71B30C230C2",
2562 ": c #C71B9A699658",
2563 "> c #618561856185",
2564 ", c #20811C712081",
2565 "< c #104000000000",
2566 "1 c #861720812081",
2567 "2 c #DF7D4D344103",
2568 "3 c #79E769A671C6",
2569 "4 c #861782078617",
2570 "5 c #41033CF34103",
2571 "6 c #000000000000",
2572 "7 c #49241C711040",
2573 "8 c #492445144924",
2574 "9 c #082008200820",
2575 "0 c #69A618611861",
2576 "q c #B6DA71C65144",
2577 "w c #410330C238E3",
2578 "e c #CF3CBAEAB6DA",
2579 "r c #71C6451430C2",
2580 "t c #EFBEDB6CD75C",
2581 "y c #28A208200820",
2582 "u c #186110401040",
2583 "i c #596528A21861",
2584 "p c #71C661855965",
2585 "a c #A69996589658",
2586 "s c #30C228A230C2",
2587 "d c #BEFBA289AEBA",
2588 "f c #596545145144",
2589 "g c #30C230C230C2",
2590 "h c #8E3882078617",
2591 "j c #208118612081",
2592 "k c #38E30C300820",
2593 "l c #30C2208128A2",
2594 "z c #38E328A238E3",
2595 "x c #514438E34924",
2596 "c c #618555555965",
2597 "v c #30C2208130C2",
2598 "b c #38E328A230C2",
2599 "n c #28A228A228A2",
2600 "m c #41032CB228A2",
2601 "M c #104010401040",
2602 "N c #492438E34103",
2603 "B c #28A2208128A2",
2604 "V c #A699596538E3",
2605 "C c #30C21C711040",
2606 "Z c #30C218611040",
2607 "A c #965865955965",
2608 "S c #618534D32081",
2609 "D c #38E31C711040",
2610 "F c #082000000820",
2619 "ty> 459@>+&& ",
2621 "%$;=* *3:.Xa.dfg> ",
2622 "Oh$;ya *3d.a8j,Xe.d3g8+ ",
2623 " Oh$;ka *3d$a8lz,,xxc:.e3g54 ",
2624 " Oh$;kO *pd$%svbzz,sxxxxfX..&wn> ",
2625 " Oh$@mO *3dthwlsslszjzxxxxxxx3:td8M4 ",
2626 " Oh$@g& *3d$XNlvvvlllm,mNwxxxxxxxfa.:,B* ",
2627 " Oh$@,Od.czlllllzlmmqV@V#V@fxxxxxxxf:%j5& ",
2628 " Oh$1hd5lllslllCCZrV#r#:#2AxxxxxxxxxcdwM* ",
2629 " OXq6c.%8vvvllZZiqqApA:mq:Xxcpcxxxxxfdc9* ",
2630 " 2r<6gde3bllZZrVi7S@SV77A::qApxxxxxxfdcM ",
2631 " :,q-6MN.dfmZZrrSS:#riirDSAX@Af5xxxxxfevo",
2632 " +A26jguXtAZZZC7iDiCCrVVii7Cmmmxxxxxx%3g",
2633 " *#16jszN..3DZZZZrCVSA2rZrV7Dmmwxxxx&en",
2634 " p2yFvzssXe:fCZZCiiD7iiZDiDSSZwwxx8e*>",
2635 " OA1<jzxwwc:$d%NDZZZZCCCZCCZZCmxxfd.B ",
2636 " 3206Bwxxszx%et.eaAp77m77mmmf3&eeeg* ",
2637 " @26MvzxNzvlbwfpdettttttttttt.c,n& ",
2638 " *;16=lsNwwNwgsvslbwwvccc3pcfu<o ",
2639 " p;<69BvwwsszslllbBlllllllu<5+ ",
2640 " OS0y6FBlvvvzvzss,u=Blllj=54 ",
2641 " c1-699Blvlllllu7k96MMMg4 ",
2642 " *10y8n6FjvllllB<166668 ",
2643 " S-kg+>666<M<996-y6n<8* ",
2644 " p71=4 m69996kD8Z-66698&& ",
2645 " &i0ycm6n4 ogk17,0<6666g ",
2646 " N-k-<> >=01-kuu666> ",
2647 " ,6ky& &46-10ul,66, ",
2648 " Ou0<> o66y<ulw<66& ",
2649 " *kk5 >66By7=xu664 ",
2650 " <<M4 466lj<Mxu66o ",
2651 " *>> +66uv,zN666* ",
2661 /* Termine l'application lorsqu'elle est appelée
2662 * (via le signal "delete_event"). */
2664 void close_application( GtkWidget *widget, GdkEvent *event, gpointer *data )
2670 int main (int argc, char *argv[])
2672 GtkWidget *window, *pixmap, *fixed;
2673 GdkPixmap *gdk_pixmap;
2678 /* crée la fenêtre principale et attache le signal "delete_event"
2679 * pour terminer l'application. On notera que la fenêtre principale
2680 * n'a pas de barre de titre car nous la faisons surgir. */
2682 gtk_init (&argc, &argv);
2683 window = gtk_window_new( GTK_WINDOW_POPUP );
2684 gtk_signal_connect (GTK_OBJECT (window), "delete_event",
2685 GTK_SIGNAL_FUNC (close_application), NULL);
2686 gtk_widget_show (window);
2688 /* Création du pixmap et du widget pixmap */
2690 style = gtk_widget_get_default_style();
2691 gc = style->black_gc;
2692 gdk_pixmap = gdk_pixmap_create_from_xpm_d( window->window, &mask,
2693 &style->bg[GTK_STATE_NORMAL],
2694 WheelbarrowFull_xpm );
2695 pixmap = gtk_pixmap_new( gdk_pixmap, mask );
2696 gtk_widget_show( pixmap );
2698 /* Pour afficher le pixmap, on utilise un widget fixe pour placer
2701 fixed = gtk_fixed_new();
2702 gtk_widget_set_usize( fixed, 200, 200 );
2703 gtk_fixed_put( GTK_FIXED(fixed), pixmap, 0, 0 );
2704 gtk_container_add( GTK_CONTAINER(window), fixed );
2705 gtk_widget_show( fixed );
2707 /* On masque tout sauf l'image elle-même */
2709 gtk_widget_shape_combine_mask( window, mask, 0, 0 );
2711 /* Affichage de la fenêtre */
2713 gtk_widget_set_uposition( window, 20, 400 );
2714 gtk_widget_show( window );
2721 Pour rendre l'image de la brouette sensible aux clics, on peut lui
2722 attacher le signal "button_press_event" pour lui faire faire quelque
2723 chose. Les quelques lignes suivantes font que l'image sera sensible à
2724 un clic souris qui provoquera l'arrêt de l'application.
2727 gtk_widget_set_events( window,
2728 gtk_widget_get_events( window ) |
2729 GDK_BUTTON_PRESS_MASK );
2731 gtk_signal_connect( GTK_OBJECT(window), "button_press_event",
2732 GTK_SIGNAL_FUNC(close_application), NULL );
2736 <sect> Widgets containers
2740 Le widget bloc-notes est un ensemble de « pages » qui se
2741 chevauchent. Chaque page contient des informations
2742 différentes. Récemment, ce widget est devenu plus commun dans la
2743 programmation des interfaces graphiques et c'est un bon moyen de
2744 montrer des blocs d'information similaires qui justifient une
2745 séparation de leur affichage.
2747 Le premier appel de fonction que l'on doit connaître est, vous
2748 l'aviez deviné, celui qui crée un widget bloc-notes.
2751 GtkWidget* gtk_notebook_new (void);
2754 Lorsque le bloc-notes a été créé, il y a 12 fonctions permettant de
2755 travailler sur les blocs-notes. Étudions-les séparément.
2757 La première permet de positionner les indicateurs de pages. Ceux-ci
2758 (désignés par le mot « tab » (signet)), peuvent se trouver en haut, en
2759 bas, à gauche ou à droite des pages.
2762 void gtk_notebook_set_tab_pos (GtkNotebook *notebook, GtkPositionType pos);
2765 <em/GtkPositionType/ peut prendre les valeurs suivantes qu'il n'est
2766 pas nécessaire d'expliquer :
2769 <item> GTK_POS_RIGHT
2771 <item> GTK_POS_BOTTOM
2774 GTK_POS_TOP est la valeur par défaut.
2776 La fonction suivante permet d'ajouter des pages à un bloc-notes. Il y
2777 a trois façons d'ajouter des pages. Regardons les deux premières qui
2778 sont très semblables.
2781 void gtk_notebook_append_page (GtkNotebook *notebook, GtkWidget *child,
2782 GtkWidget *tab_label);
2784 void gtk_notebook_prepend_page (GtkNotebook *notebook, GtkWidget *child,
2785 GtkWidget *tab_label);
2788 Ces fonctions ajoutent des pages au bloc-notes<em/*notebook/ en les
2789 insérant à la fin (<em/append/) ou au début
2790 (<em/prepend/). <em/*child/ est le widget qui est placé dans la page
2791 du bloc-notes, et <em/*tab_label/ est le label de la page qui est
2794 La troisième fonction ajoutant une page à un bloc-notes conserve
2795 toutes les propriétés des deux précédentes, mais elle nous permet en
2796 plus de spécifier la position où l'on désire insérer cette page.
2799 void gtk_notebook_insert_page (GtkNotebook *notebook, GtkWidget *child,
2800 GtkWidget *tab_label, gint position);
2803 Les paramètres sont les mêmes que <em/_append_/ et <em/_prepend_/ sauf
2804 qu'il y en a un de plus : <em/position/. Celui-ci sert à
2805 spécifier l'endroit où cette page sera insérée.
2807 Maintenant que nous savons insérer une page, voyons comment en supprimer une.
2810 void gtk_notebook_remove_page (GtkNotebook *notebook, gint page_num);
2813 Cette fonction ôte la page spécifiée par <em/page_num/ du widget
2816 Pour connaître la page courante d'un bloc-notes, on dispose de la
2820 gint gtk_notebook_current_page (GtkNotebook *notebook);
2823 Les deux fonctions suivantes permettent de passer à la page suivante
2824 ou précédente d'un bloc-notes. Il suffit de faire l'appel de la
2825 fonction adéquate avec le widget sur lequel on veut
2826 opérer. Remarque : lorsqu'on est sur la dernière page du
2827 bloc-notes et que l'on appelle <em/gtk_notebook_next_page/, on revient
2828 à la première page. De même, si l'on est sur la première page et que
2829 l'onappelle <em/gtk_notebook_prev_page/, on se retrouve sur sa
2833 void gtk_notebook_next_page (GtkNoteBook *notebook);
2834 void gtk_notebook_prev_page (GtkNoteBook *notebook);
2837 La fonction qui suit permet de choisir la page « active ». Si vous
2838 voulez ouvrir le bloc-notes à la page 5, par exemple, vous utiliserez
2839 cette fonction. Sans elle, le bloc-notes s'ouvre sur sa première page
2843 void gtk_notebook_set_page (GtkNotebook *notebook, gint page_num);
2846 Les deux fonctions suivantes ajoutent ou ôtent les indicateurs de page
2847 et le contour du bloc-notes, respectivement.
2850 void gtk_notebook_set_show_tabs (GtkNotebook *notebook, gint show_tabs);
2851 void gtk_notebook_set_show_border (GtkNotebook *notebook, gint show_border);
2854 <em/show_tabs/ et <em/show_border/ peuvent valoir TRUE ou FALSE (0 ou 1).
2856 Voyons maintenant un exemple, il est tiré du code de <em/testgtk.c/ de
2857 la distribution GTK et montre l'utilisation des 13 fonctions. Ce petit
2858 programme crée une fenêtre contenant un bloc-notes et six boutons. Le
2859 bloc-notes contient 11 pages, ajoutées par trois moyens
2860 différents : à la fin, au milieu et au début. Les boutons
2861 permettent de faire tourner les indicateurs de page, ajouter/ôter les
2862 indicateurs et le contour, ôter une page, passer à la page suivante et
2863 précédente, et sortir du programme.
2868 #include <gtk/gtk.h>
2870 /* Rotation des indicateurs de page */
2872 void rotate_book (GtkButton *button, GtkNotebook *notebook)
2874 gtk_notebook_set_tab_pos (notebook, (notebook->tab_pos +1) %4);
2877 /* Ajout/Suppression des indicateurs de pages et des contours */
2879 void tabsborder_book (GtkButton *button, GtkNotebook *notebook)
2883 if (notebook->show_tabs == 0)
2885 if (notebook->show_border == 0)
2888 gtk_notebook_set_show_tabs (notebook, tval);
2889 gtk_notebook_set_show_border (notebook, bval);
2892 /* Suppression d'une page */
2894 void remove_book (GtkButton *button, GtkNotebook *notebook)
2898 page = gtk_notebook_current_page(notebook);
2899 gtk_notebook_remove_page (notebook, page);
2901 /* Il faut rafraîchir le widget --
2902 * ce qui force le widget à se redessiner. */
2904 gtk_widget_draw(GTK_WIDGET(notebook), NULL);
2907 void delete (GtkWidget *widget, GdkEvent *event, gpointer *data)
2912 int main (int argc, char *argv[])
2917 GtkWidget *notebook;
2920 GtkWidget *checkbutton;
2925 gtk_init (&argc, &argv);
2927 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2929 gtk_signal_connect (GTK_OBJECT (window), "delete_event",
2930 GTK_SIGNAL_FUNC (delete), NULL);
2932 gtk_container_border_width (GTK_CONTAINER (window), 10);
2934 table = gtk_table_new(2,6,TRUE);
2935 gtk_container_add (GTK_CONTAINER (window), table);
2937 /* Création d'un bloc-notes, placement des indicateurs de page. */
2939 notebook = gtk_notebook_new ();
2940 gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_TOP);
2941 gtk_table_attach_defaults(GTK_TABLE(table), notebook, 0,6,0,1);
2942 gtk_widget_show(notebook);
2944 /* Ajoute un groupe de pages à la fin du bloc-notes. */
2946 for (i=0; i < 5; i++) {
2947 sprintf(bufferf, "Append Frame %d", i+1);
2948 sprintf(bufferl, "Page %d", i+1);
2950 frame = gtk_frame_new (bufferf);
2951 gtk_container_border_width (GTK_CONTAINER (frame), 10);
2952 gtk_widget_set_usize (frame, 100, 75);
2953 gtk_widget_show (frame);
2955 label = gtk_label_new (bufferf);
2956 gtk_container_add (GTK_CONTAINER (frame), label);
2957 gtk_widget_show (label);
2959 label = gtk_label_new (bufferl);
2960 gtk_notebook_append_page (GTK_NOTEBOOK (notebook), frame, label);
2964 /* Ajoute une page à un endroit précis. */
2966 checkbutton = gtk_check_button_new_with_label ("Cochez moi !");
2967 gtk_widget_set_usize(checkbutton, 100, 75);
2968 gtk_widget_show (checkbutton);
2970 label = gtk_label_new ("Emplacement de la nouvelle page");
2971 gtk_container_add (GTK_CONTAINER (checkbutton), label);
2972 gtk_widget_show (label);
2973 label = gtk_label_new ("Ajout de page");
2974 gtk_notebook_insert_page (GTK_NOTEBOOK (notebook), checkbutton, label, 2);
2976 /* Ajout de pages au début du bloc-notes */
2978 for (i=0; i < 5; i++) {
2979 sprintf(bufferf, "Prepend Frame %d", i+1);
2980 sprintf(bufferl, "Page %d", i+1);
2982 frame = gtk_frame_new (bufferf);
2983 gtk_container_border_width (GTK_CONTAINER (frame), 10);
2984 gtk_widget_set_usize (frame, 100, 75);
2985 gtk_widget_show (frame);
2987 label = gtk_label_new (bufferf);
2988 gtk_container_add (GTK_CONTAINER (frame), label);
2989 gtk_widget_show (label);
2991 label = gtk_label_new (bufferl);
2992 gtk_notebook_prepend_page (GTK_NOTEBOOK(notebook), frame, label);
2995 /* Configuration de la page de départ (page 4) */
2997 gtk_notebook_set_page (GTK_NOTEBOOK(notebook), 3);
3000 /* Création des boutons */
3002 button = gtk_button_new_with_label ("Fermer");
3003 gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
3004 GTK_SIGNAL_FUNC (delete), NULL);
3005 gtk_table_attach_defaults(GTK_TABLE(table), button, 0,1,1,2);
3006 gtk_widget_show(button);
3008 button = gtk_button_new_with_label ("Page suivante");
3009 gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
3010 (GtkSignalFunc) gtk_notebook_next_page,
3011 GTK_OBJECT (notebook));
3012 gtk_table_attach_defaults(GTK_TABLE(table), button, 1,2,1,2);
3013 gtk_widget_show(button);
3015 button = gtk_button_new_with_label ("Page précédente");
3016 gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
3017 (GtkSignalFunc) gtk_notebook_prev_page,
3018 GTK_OBJECT (notebook));
3019 gtk_table_attach_defaults(GTK_TABLE(table), button, 2,3,1,2);
3020 gtk_widget_show(button);
3022 button = gtk_button_new_with_label ("Position des indicateurs");
3023 gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
3024 (GtkSignalFunc) rotate_book, GTK_OBJECT(notebook));
3025 gtk_table_attach_defaults(GTK_TABLE(table), button, 3,4,1,2);
3026 gtk_widget_show(button);
3028 button = gtk_button_new_with_label ("Indicateurs/Contours oui/non");
3029 gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
3030 (GtkSignalFunc) tabsborder_book,
3031 GTK_OBJECT (notebook));
3032 gtk_table_attach_defaults(GTK_TABLE(table), button, 4,5,1,2);
3033 gtk_widget_show(button);
3035 button = gtk_button_new_with_label ("Oter page");
3036 gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
3037 (GtkSignalFunc) remove_book,
3038 GTK_OBJECT(notebook));
3039 gtk_table_attach_defaults(GTK_TABLE(table), button, 5,6,1,2);
3040 gtk_widget_show(button);
3042 gtk_widget_show(table);
3043 gtk_widget_show(window);
3051 En espérant que ceci vous aide à créer des blocs-notes pour vos
3055 <sect1> Fenêtres avec barres de défilement
3057 Les fenêtres avec barres de défilement servent à créer des zones
3058 défilantes à l'intérieur d'une vraie fenêtre. On peut insérer
3059 n'importe quel widget dans ces fenêtres, ils seront accessibles quelle
3060 que soit leur taille en utilisant les barres de défilement.
3063 La fonction suivante sert à créer une fenêtre avec barre de
3067 GtkWidget* gtk_scrolled_window_new (GtkAdjustment *hadjustment,
3068 GtkAdjustment *vadjustment);
3071 Le premier paramètre est l'ajustement horizontal, et le second
3072 l'ajustement vertical. Ils sont presque toujours positionnés à NULL.
3075 void gtk_scrolled_window_set_policy (GtkScrolledWindow *scrolled_window,
3076 GtkPolicyType hscrollbar_policy,
3077 GtkPolicyType vscrollbar_policy);
3080 Cela permet de configurer le fonctionnement des barres de
3081 défilement. Le premier paramètre est la fenêtre à défilement que l'on
3082 veut modifier, le second configure le fonctionnement de la barre
3083 horizontale et le troisième celui de la barre verticale.
3085 Ce fonctionnement peut être GTK_POLICY AUTOMATIC ou GTK_POLICY_ALWAYS.
3086 GTK_POLICY_AUTOMATIC décidera automatiquement de votre besoin en
3087 barres de défilement, alors que GTK_POLICY_ALWAYS mettra toujours
3090 Voici un exemple simple qui place 100 boutons commutateurs dans une
3091 fenêtre à défilement. Je n'ai commenté que les parties qui sont
3092 nouvelles pour vous.
3095 #include <gtk/gtk.h>
3097 void destroy(GtkWidget *widget, gpointer *data)
3102 int main (int argc, char *argv[])
3104 static GtkWidget *window;
3105 GtkWidget *scrolled_window;
3111 gtk_init (&argc, &argv);
3113 /* Création d'une boîte de dialogue pour y placer la fenêtre à défilement.
3114 * Une boîte de dialogue est une fenêtre comme les autres sauf qu'elle contient
3115 * une vbox et un séparateur horizontal. Ce n'est qu'un raccourci pour créer des
3116 * zones de dialogue. */
3118 window = gtk_dialog_new ();
3119 gtk_signal_connect (GTK_OBJECT (window), "destroy",
3120 (GtkSignalFunc) destroy, NULL);
3121 gtk_window_set_title (GTK_WINDOW (window), "dialog");
3122 gtk_container_border_width (GTK_CONTAINER (window), 0);
3124 /* Création d'une fenêtre à défilement. */
3126 scrolled_window = gtk_scrolled_window_new (NULL, NULL);
3128 gtk_container_border_width (GTK_CONTAINER (scrolled_window), 10);
3130 /* La gestion des barres est soit GTK_POLICY AUTOMATIC, soit GTK_POLICY_ALWAYS.
3131 * GTK_POLICY_AUTOMATIC décide automatiquement s'il faut ou non des barres,
3132 * GTK_POLICY_ALWAYS met toujours des barres
3133 * Le premier paramètre correspond à la barre horizontale,
3134 * le second à la barre verticale. */
3136 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
3137 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
3139 /* Création d'une boîte de dialogue */
3141 gtk_box_pack_start (GTK_BOX (GTK_DIALOG(window)->vbox), scrolled_window,
3143 gtk_widget_show (scrolled_window);
3145 /* Création d'une table de 10x10 cases. */
3147 table = gtk_table_new (10, 10, FALSE);
3149 /* Configure l'espace des lignes et des colonnes de 10 pixels */
3151 gtk_table_set_row_spacings (GTK_TABLE (table), 10);
3152 gtk_table_set_col_spacings (GTK_TABLE (table), 10);
3154 /* Place la table fans la fenêtre à défilement */
3156 gtk_container_add (GTK_CONTAINER (scrolled_window), table);
3157 gtk_widget_show (table);
3159 /* Crée une grille de boutons commutateurs dans la table */
3161 for (i = 0; i < 10; i++)
3162 for (j = 0; j < 10; j++) {
3163 sprintf (buffer, "bouton (%d,%d)\n", i, j);
3164 button = gtk_toggle_button_new_with_label (buffer);
3165 gtk_table_attach_defaults (GTK_TABLE (table), button,
3167 gtk_widget_show (button);
3170 /* Ajoute un bouton « Fermer » en bas de la boîte de dialogue */
3172 button = gtk_button_new_with_label ("Fermer");
3173 gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
3174 (GtkSignalFunc) gtk_widget_destroy,
3175 GTK_OBJECT (window));
3177 /* On met ce bouton en « bouton par défaut ». */
3179 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
3180 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->action_area), button, TRUE, TRUE, 0);
3182 /* Récupère le bouton par défaut. Le fait de presser la touche « Entrée »
3183 * activera le bouton. */
3185 gtk_widget_grab_default (button);
3186 gtk_widget_show (button);
3188 gtk_widget_show (window);
3196 Essayez de changer la taille de la fenêtre et faites attention aux
3197 réactions des barres de défilement. On peut aussi utiliser la fonction
3198 <em/gtk_widget_set_usize()/ pour configurer la taille par défaut de la
3199 fenêtre et des autres widgets.
3202 <sect>Widgets listes
3204 Le widget <em/GtkList/ sert de container vertical pour des widgets <em/GtkListItem/.
3206 Un widget <em/GtkList/ possède sa propre fenêtre pour recevoir les
3207 événements et sa propre couleur de fond qui est habituellement
3208 blanche. Comme il est directement dérivé de <em/GtkContainer/, il peut
3209 être traité comme tel en utilisant la macro GTK_CONTAINER(List) :
3210 voir le widget <em/GtkContainer/ pour en savoir plus.
3212 On doit d'abord connaître l'utilisation des <em/GList/ et des
3213 fonctions <em/g_list_*()/ qui leur sont liées pour pouvoir utiliser
3214 pleinement le widget <em/GtkList/.
3216 Un champ de la structure d'un widget <em/GtkList/ nous intéresse particulièrement :
3223 guint selection_mode;
3228 Le champ <em/selection/ d'un <em/GtkList/ pointe sur une liste chaînée
3229 de tous les items qui sont sélectionnés, ou vaut NULL si aucune
3230 sélection n'est faite. Ainsi, pour connaître la sélection courante, on
3231 consulte le champ <em/GTK_LIST()->selection/ mais on ne doit pas le
3232 modifier car ses champs internes sont gérés par les fonctions
3235 Le champ <em/selection_mode/ détermine les options de sélection d'un
3236 <em/GtkList/ et donc le contenu du champ du
3237 <em/GTK_LIST()->selection/ :
3239 <em/selection_mode/ peut avoir l'une des valeurs suivantes :
3241 <item> GTK_SELECTION_SINGLE - <em/selection/ vaut NULL ou contient un
3242 pointeur vers un seul item sélectionné.
3244 <item> GTK_SELECTION_BROWSE - <em/selection/ vaut NULL si la liste ne
3245 contient aucun widget ou seulement des widgets non sensitifs. Sinon, ce
3246 champ contient un pointeur vers une seule structure Glist, et donc
3247 vers exactement un item.
3249 <item> GTK_SELECTION_MULTIPLE - <em/selection/ vaut NULL si aucun item
3250 n'est sélectionné ou pointe vers le premier item sélectionné. Ce
3251 dernier point à son tour vers le second item, etc.
3253 <item> GTK_SELECTION_EXTENDED - <em/selection/ vaut toujours NULL.
3256 La valeur par défaut est GTK_SELECTION_MULTIPLE.
3261 void GtkList::selection_changed (GtkList *LIST)
3264 Ce signal sera invoqué à chaque fois que le champ sélection d'un
3265 GtkList a changé. Cela arrive lorsqu'un fils d'un GtkList a été
3266 sélectionné ou désélectionné.
3269 void GtkList::select_child (GtkList *LIST, GtkWidget *CHILD)
3272 Ce signal est invoqué lorsqu'un fils du GtkList va être
3273 sélectionné. Ceci arrive principalement lors d'appels à
3274 <em/gtk_list_select_item(), gtk_list_select_child()/ et lors d'appuis
3275 de boutons. Quelques fois, il est indirectement déclenché lorsque des
3276 fils sont ajoutés ou supprimés du GtkList.
3279 void GtkList::unselect_child (GtkList *LIST, GtkWidget *CHILD)
3282 Ce signal est invoqué lorsqu'un fils du GtkList va être
3283 désélectionné. Cela arrive principalement lors d'appels à
3284 <em/gtk_list_unselect_item(), gtk_list_unselect_child()/, et lors
3285 d'appuis de boutons. Quelques fois, il est indirectement déclenché
3286 lorsque des fils sont ajoutés ou supprimés du GtkList.
3292 guint gtk_list_get_type (void)
3295 Retourne l'identificateur de type « GtkList ».
3298 GtkWidget* gtk_list_new (void)
3301 Crée un nouvel objet « GtkList ». Le nouveau widget est retourné sous
3302 la forme d'un pointeur vers un objet « GtkWidget ». NULL est retourné
3306 void gtk_list_insert_items (GtkList *LIST, GList *ITEMS, gint POSITION)
3309 Insère des items dans <em/LIST/, à partir de <em/POSITION/.
3310 <em/ITEMS/ est une liste doublement chaînée où chaque noeud doit
3311 pointer vers un nouveau <em/GtkListItem/. Les noeuds <em/GList/ de
3312 <em/ITEMS/ sont pris en charge par <em/LIST/.
3316 void gtk_list_append_items (GtkList *LIST, GList *ITEMS)
3319 Insère des items à la fin de <em/LIST/ selon le même principe que
3320 <em/gtk_list_insert_items/. Les noeuds <em/GList/ de <em/ITEMS/ sont
3321 pris en charge par <em/LIST/.
3324 void gtk_list_prepend_items (GtkList *LIST, GList *ITEMS)
3327 Insère des items au début de <em/LIST/ selon le même principe que
3328 <em/gtk_list_insert_items/. Les noeuds <em/GList/ de <em/ITEMS/ sont
3329 pris en charge par <em/LIST/.
3332 void gtk_list_remove_items (GtkList *LIST, GList *ITEMS)
3335 Ôte des items de <em/LIST/. <em/ITEMS/ est une liste doublement
3336 chaînée dont chaque noeud pointe vers un fils direct de <em/LIST/. Il
3337 est de la responsabilité de l'appelant de faire un appel à
3338 <em/g_list_free(ITEMS)/ après cela. L'appelant doit aussi détruire
3342 void gtk_list_clear_items (GtkList *LIST, gint START, gint END)
3345 Ôte et détruit des items de <em/LIST/. Un widget est concerné si sa
3346 position courante dans <em/LIST/ est dans l'intervalle spécifié par
3347 <em/START/ et <em/END/.
3350 void gtk_list_select_item (GtkList *LIST, gint ITEM)
3353 Invoque le signal <em/GtkList::select_child/ pour un item spécifié par
3354 sa position courante dans <em/LIST/.
3357 void gtk_list_unselect_item (GtkList *LIST, gint ITEM)
3360 Invoque le signal <em/GtkList::unselect_child/ pour un item spécifié par
3361 sa position courante dans <em/LIST/.
3364 void gtk_list_select_child (GtkList *LIST, GtkWidget *CHILD)
3367 Invoque le signal <em/GtkList::select_child/ pour le fils <em/CHILD/ spécifié.
3370 void gtk_list_unselect_child (GtkList *LIST, GtkWidget *CHILD)
3373 Invoque le signal <em/GtkList::unselect_child/ pour le fils <em/CHILD/ spécifié.
3376 gint gtk_list_child_position (GtkList *LIST, GtkWidget *CHILD)
3379 Retourne la position de <em/CHILD/ dans <em/LIST/. Retourne -1 en cas d'erreur.
3382 void gtk_list_set_selection_mode (GtkList *LIST, GtkSelectionMode MODE)
3385 Configure <em/LIST/ dans le mode de sélection <em/MODE/ qui peut être
3386 GTK_SELECTION_SINGLE, GTK_SELECTION_BROWSE, GTK_SELECTION_MULTIPLE ou
3387 GTK_SELECTION_EXTENDED.
3390 GtkList* GTK_LIST (gpointer OBJ)
3393 Convertit un pointeur générique en « <\em GtkList*\ ». Voir
3394 <em/Standard Macros::/, pour plus d'informations.
3397 GtkListClass* GTK_LIST_CLASS (gpointer CLASS)
3400 Convertit un pointeur générique en « GtkListClass* ». Voir
3401 <em/Standard Macros::/, pour plus d'informations.
3404 gint GTK_IS_LIST (gpointer OBJ)
3407 Détermine si un pointeur générique référence un objet « GtkList ». Voir
3408 <em/Standard Macros::/, pour plus d'informations.
3412 Voici un programme affichant les changements de sélection dans une
3413 <em/GtkList/ et permettant d'« emprisonner » des items en les
3414 sélectionnant avec le bouton droit de la souris.
3417 /* Compilez ce programme avec :
3418 * $ gcc -L/usr/X11R6/lib/ -I/usr/local/include/ -lgtk -lgdk -lglib -lX11 -lm -Wall main.c
3420 #include <gtk/gtk.h>
3423 /* Chaîne pour stocker les données dans les items de la liste. */
3425 const gchar *list_item_data_key="list_item_data";
3428 /* prototypes des gestionnaires de signaux que l'on connectera au widget GtkList. */
3430 static void sigh_print_selection (GtkWidget *gtklist,
3431 gpointer func_data);
3432 static void sigh_button_event (GtkWidget *gtklist,
3433 GdkEventButton *event,
3437 /* fonction principale pour configurer l'interface utilisateur */
3439 gint main (int argc, gchar *argv[])
3441 GtkWidget *separator;
3444 GtkWidget *scrolled_window;
3448 GtkWidget *list_item;
3454 /* initialise gtk (et donc gdk) */
3456 gtk_init(&argc, &argv);
3459 /* Création d'une fenêtre pour placer tous les widgets.
3460 * Connexion de gtk_main_quit() à l'événement "destroy" de
3461 * la fenêtre afin de prendre en charge les événements « fermeture d'une
3462 * fenêtre » du gestionnaire de fenêtre. */
3464 window=gtk_window_new(GTK_WINDOW_TOPLEVEL);
3465 gtk_window_set_title(GTK_WINDOW(window), "Exemple de widget GtkList");
3466 gtk_signal_connect(GTK_OBJECT(window),
3468 GTK_SIGNAL_FUNC(gtk_main_quit),
3472 /* À l'intérieur de la fenêtre, on a besoin d'une boîte pour placer
3473 * verticalement les widgets. */
3475 vbox=gtk_vbox_new(FALSE, 5);
3476 gtk_container_border_width(GTK_CONTAINER(vbox), 5);
3477 gtk_container_add(GTK_CONTAINER(window), vbox);
3478 gtk_widget_show(vbox);
3480 /* Fenêtre à défilement pour placer le widget GtkList à l'intérieur. */
3482 scrolled_window=gtk_scrolled_window_new(NULL, NULL);
3483 gtk_widget_set_usize(scrolled_window, 250, 150);
3484 gtk_container_add(GTK_CONTAINER(vbox), scrolled_window);
3485 gtk_widget_show(scrolled_window);
3487 /* Création du widget GtkList
3488 * Connexion du gestionnaire de signal sigh_print_selection() au signal
3489 * "selection_changed" du GtkList pour afficher les items sélectionnés
3490 * à chaque fois que la sélection change. */
3492 gtklist=gtk_list_new();
3493 gtk_container_add(GTK_CONTAINER(scrolled_window), gtklist);
3494 gtk_widget_show(gtklist);
3495 gtk_signal_connect(GTK_OBJECT(gtklist),
3496 "selection_changed",
3497 GTK_SIGNAL_FUNC(sigh_print_selection),
3500 /* Création d'une « Prison » pour y mettre un item. */
3502 frame=gtk_frame_new("Prison");
3503 gtk_widget_set_usize(frame, 200, 50);
3504 gtk_container_border_width(GTK_CONTAINER(frame), 5);
3505 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
3506 gtk_container_add(GTK_CONTAINER(vbox), frame);
3507 gtk_widget_show(frame);
3509 /* Connexion du gestionnaire de signal sigh_button_event() au signal
3510 * « mise au arrêts » des items du GtkList. */
3512 gtk_signal_connect(GTK_OBJECT(gtklist),
3513 "button_release_event",
3514 GTK_SIGNAL_FUNC(sigh_button_event),
3517 /* Création d'un séparateur. */
3519 separator=gtk_hseparator_new();
3520 gtk_container_add(GTK_CONTAINER(vbox), separator);
3521 gtk_widget_show(separator);
3523 /* Création d'un bouton et connexion de son signal "clicked" à la
3524 * destruction de la fenêtre. */
3526 button=gtk_button_new_with_label("Fermeture");
3527 gtk_container_add(GTK_CONTAINER(vbox), button);
3528 gtk_widget_show(button);
3529 gtk_signal_connect_object(GTK_OBJECT(button),
3531 GTK_SIGNAL_FUNC(gtk_widget_destroy),
3532 GTK_OBJECT(window));
3535 /* Création de 5 items, chacun ayant son propre label.
3536 * Ajout de ceux-ci au GtkList en utilisant gtk_container_add().
3537 * On interroge le texte du label et on l'associe avec
3538 * list_item_data_key à chaque item. */
3540 for (i=0; i<5; i++) {
3544 sprintf(buffer, "ListItemContainer avec Label #%d", i);
3545 label=gtk_label_new(buffer);
3546 list_item=gtk_list_item_new();
3547 gtk_container_add(GTK_CONTAINER(list_item), label);
3548 gtk_widget_show(label);
3549 gtk_container_add(GTK_CONTAINER(gtklist), list_item);
3550 gtk_widget_show(list_item);
3551 gtk_label_get(GTK_LABEL(label), &string);
3552 gtk_object_set_data(GTK_OBJECT(list_item),
3556 /* Création de 5 autres labels. Cette fois-ci, on utilise
3557 * gtk_list_item_new_with_label(). On ne peut interroger la chaîne
3558 * des labels car on n'a pas les pointeurs de labels et on associe
3559 * donc simplement le list_item_data_key de chaque item ayant la même
3560 * chaîne de texte pour l'ajouter au items que l'on place dans une liste
3561 * doublement chaînée (GList). On les ajoute alors par un simple appel à
3562 * gtk_list_append_items().
3563 * Comme on utilise g_list_prepend() pour mettre les items dans la liste
3564 * doublement chaînée, leur ordre sera décroissant (au lieu d'être croissant si
3565 * on utilisait g_list_append()). */
3569 sprintf(buffer, "Item avec le label %d", i);
3570 list_item=gtk_list_item_new_with_label(buffer);
3571 dlist=g_list_prepend(dlist, list_item);
3572 gtk_widget_show(list_item);
3573 gtk_object_set_data(GTK_OBJECT(list_item),
3575 "Item avec label intégré");
3577 gtk_list_append_items(GTK_LIST(gtklist), dlist);
3579 /* Enfin, on veut voir la fenêtre... */
3581 gtk_widget_show(window);
3583 /* Lancement de la boucle principale de gtk */
3587 /* On arrive ici après que gtk_main_quit() ait été appelée lorsque
3588 * la fenêtre principale a été détruite. */
3592 /* Gestionnaire de signal connecté aux événements boutons presser/relâcher
3596 sigh_button_event (GtkWidget *gtklist,
3597 GdkEventButton *event,
3600 /* On ne fait quelque chose que si le troisième bouton (celui de droite) a été
3603 if (event->type==GDK_BUTTON_RELEASE &&
3605 GList *dlist, *free_list;
3606 GtkWidget *new_prisoner;
3608 /* On recherche l'item sélectionné à ce moment précis.
3609 * Ce sera notre prisonnier ! */
3611 dlist=GTK_LIST(gtklist)->selection;
3613 new_prisoner=GTK_WIDGET(dlist->data);
3617 /* On recherche les items déjà prisonniers et on les
3618 * remet dans la liste.
3619 * Il ne faut pas oublier de libérer la liste doublement
3620 * chaînée que gtk_container_children() retourne. */
3622 dlist=gtk_container_children(GTK_CONTAINER(frame));
3625 GtkWidget *list_item;
3627 list_item=dlist->data;
3629 gtk_widget_reparent(list_item, gtklist);
3633 g_list_free(free_list);
3635 /* Si l'on a un nouveau prisonnier, on l'ôte du GtkList et on le place
3636 * dans le cadre « Prison ». On doit désélectionner l'item avant.
3641 static_dlist.data=new_prisoner;
3642 static_dlist.next=NULL;
3643 static_dlist.prev=NULL;
3645 gtk_list_unselect_child(GTK_LIST(gtklist),
3647 gtk_widget_reparent(new_prisoner, frame);
3652 /* Gestionnaire de signal appelé lorsque le GtkList
3653 * émet le signal "selection_changed". */
3656 sigh_print_selection (GtkWidget *gtklist,
3661 /* Recherche dans la liste doublement chaînée des items sélectionnés
3662 * du GtkList, à faire en lecture seulement ! */
3664 dlist=GTK_LIST(gtklist)->selection;
3666 /* S'il n'y a pas d'items sélectionné, il n'y a rien d'autre à faire que
3667 * de le dire à l'utilisateur. */
3670 g_print("Sélection nettoyée\n");
3673 /* Ok, on a une sélection et on l'affiche. */
3675 g_print("La sélection est ");
3677 /* On récupère l'item dans la liste doublement chaînée
3678 * puis on interroge la donnée associée par list_item_data_key
3679 * et on l'affiche. */
3682 GtkObject *list_item;
3683 gchar *item_data_string;
3685 list_item=GTK_OBJECT(dlist->data);
3686 item_data_string=gtk_object_get_data(list_item,
3687 list_item_data_key);
3688 g_print("%s ", item_data_string);
3696 <sect1>Widget item de liste
3698 Le widget <em/GtkListItem/ sert de container pour contenir un fils,
3699 lui fournissant des fonctions de sélection/déséselection exactement
3700 comme le widget GtkList les demande pour ses fils.
3702 Un <em/GtkListItem/ a sa propre fenêtre pour recevoir les événements et a
3703 sa propre couleur de fond, habituellement blanche.
3705 Comme il est directement dérivé d'un <em/GtkItem/, il peut être traité
3706 comme tel en utilisant la macro GTK_ITEM(ListItem), reportez-vous à la
3707 section sur le widget GtkItem pour plus de détail sur celui-ci.
3708 Habituellement, un <em/GtkListItem/ contient juste un label pour
3709 identifier, par exemple, un nom de fichier dans un <em/GtkList/ -- la
3710 fonction appropriée <em/gtk_list_item_new_with_label()/ est donc
3711 fournie. Le même effet peut être obtenu en créant un <em/GtkLabel/ à
3712 part, en configurant son alignement avec <em/xalign/=0 et
3713 <em/yalign/=0.5 suivi d'un ajout ultérieur au <em/GtkListItem/.
3715 Tout comme on n'est pas forcé d'ajouter un <em/GtkLabel/ à un
3716 <em/GtkListItem/, on peut aussi ajouter un <em/GtkVBox/ ou un
3717 <em/GtkArrow/ etc. à un <em/GtkListItem/.
3721 Un <em/GtkListItem/ ne crée pas de nouveaux signaux par lui-même, mais
3722 hérite de ceux d'un <em/GtkItem/. Voir <em/GtkItem::/, pour plus
3730 guint gtk_list_item_get_type (void)
3733 Retourne l'identificateur du type « GtkListItem ».
3736 GtkWidget* gtk_list_item_new (void)
3739 Création d'un objet <em/GtkListItem/. Le nouveau widget est retourné
3740 sous la forme d'un pointeur vers un objet <em/GtkWidget/. NULL est
3741 retourné en cas d'erreur.
3744 GtkWidget* gtk_list_item_new_with_label (gchar *LABEL)
3747 Création d'un objet <em/GtkListItem/ ayant un simple <em/GtkLabel/
3748 comme seul fils. Le nouveau widget est retourné sous la forme d'un
3749 pointeur vers un objet <em/GtkWidget/. NULL est retourné en cas
3753 void gtk_list_item_select (GtkListItem *LIST_ITEM)
3756 Cette fonction est surtout un emballage de <em/gtk_item_select
3757 (GTK_ITEM (list_item))/ qui émettra le signal <em/GtkItem::select/.
3758 Voir <em/GtkItem::/, pour plus d'informations.
3761 void gtk_list_item_deselect (GtkListItem *LIST_ITEM)
3764 Cette fonction est surtout un emballage de <em/gtk_item_deselect
3765 (GTK_ITEM (list_item))/ qui émettra le signal
3766 <em/GtkItem::deselect/. Voir <em/GtkItem::/, pour plus d'informations.
3769 GtkListItem* GTK_LIST_ITEM (gpointer OBJ)
3772 Convertit un pointeur générique en <em/GtkListItem*/. Voir
3773 <em/Standard Macros::/ pour plus d'informations.
3776 GtkListItemClass* GTK_LIST_ITEM_CLASS (gpointer CLASS)
3779 Convertit un pointeur générique en <em/GtkListItemClass*/. Voir
3780 <em/Standard Macros::/ pour plus d'informations.
3783 gint GTK_IS_LIST_ITEM (gpointer OBJ)
3786 Détermine si un pointeur générique se réfère à un objet
3787 <em/GtkListItem/. Voir <em/Standard Macros::/ pour plus
3792 L'exemple des GtkList couvre aussi l'utilisation des
3793 GtkListItem. Étudiez-le attentivement.
3796 <sect>Widgets sélections de fichiers
3798 Le widget sélection de fichier est un moyen simple et rapide pour
3799 afficher un fichier dans une boîte de dialogue. Il est complet, avec
3800 des boutons Ok, Annuler et Aide. C'est un bon moyen de raccourcir les
3801 temps de programmation.
3803 Pour créer une boîte de sélection de fichier, on utilise :
3806 GtkWidget* gtk_file_selection_new (gchar *title);
3809 Pour configurer le nom de fichier, par exemple pour aller dans un
3810 répertoire précis ou donner un nom de fichier par défaut, on utilise
3814 void gtk_file_selection_set_filename (GtkFileSelection *filesel, gchar *filename);
3817 Pour récupérer le texte que l'utilisateur a entré, ou sur lequel il a
3818 cliqué, on utilisera la fonction :
3821 gchar* gtk_file_selection_get_filename (GtkFileSelection *filesel);
3824 Des pointeurs permettent d'accéder aux widgets contenus dans la widget
3825 de sélection de fichiers. Ce sont :
3830 <item>selection_entry
3831 <item>selection_text
3838 Le plus souvent, on utilise les pointeurs <em/ok_button, cancel_button/, et
3839 <em/help_button/ pour préciser leurs utilisations.
3841 Voici un exemple emprunté à <em/testgtk.c/ et modifié pour fonctionner
3842 tout seul. Comme vous le verrez, il n'y a pas grand chose à faire pour
3843 créer un wigdget de sélection de fichier. Cependant, dans cet exemple,
3844 si le bouton Aide apparaît à l'écran, il ne fait rien car aucun signal
3848 #include <gtk/gtk.h>
3850 /* Récupère le nom de fichier sélectionné et l'affiche sur la console. */
3852 void file_ok_sel (GtkWidget *w, GtkFileSelection *fs)
3854 g_print ("%s\n", gtk_file_selection_get_filename (GTK_FILE_SELECTION (fs)));
3857 void destroy (GtkWidget *widget, gpointer *data)
3862 int main (int argc, char *argv[])
3866 gtk_init (&argc, &argv);
3868 /* Création d'un widget de sélection de fichier. */
3870 filew = gtk_file_selection_new ("File selection");
3872 gtk_signal_connect (GTK_OBJECT (filew), "destroy",
3873 (GtkSignalFunc) destroy, &filew);
3875 /* Connexion de ok_button à la fonction file_ok_sel() */
3877 gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (filew)->ok_button),
3878 "clicked", (GtkSignalFunc) file_ok_sel, filew );
3880 /* Connexion de cancel_button pour détruire le widget */
3882 gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION (filew)->cancel_button),
3883 "clicked", (GtkSignalFunc) gtk_widget_destroy,
3884 GTK_OBJECT (filew));
3886 /* Configuration du nom de fichier, comme s'il s'agissait d'un dialogue de
3887 * sauvegarde et que nous donnions un nom de fichier par défaut. */
3889 gtk_file_selection_set_filename (GTK_FILE_SELECTION(filew),
3892 gtk_widget_show(filew);
3900 Il y a deux façons de créer des menus, la facile et la compliquée. Les
3901 deux ont leur utilité, mais on peut généralement utiliser l'<em/usine
3902 à menus/ (c'est la méthode facile...). La méthode « compliquée »
3903 consiste à créer tous les menus en utilisant directement les
3904 appels. La méthode facile consiste à utiliser les appels
3905 <em/gtk_menu_factory/. C'est beaucoup plus simple, mais chaque
3906 approche a ses avantages et inconvénients.
3908 L'usine à menus est beaucoup plus facile à utiliser, elle facilite
3909 aussi l'ajout d'autres menus. Par contre, écrire quelques fonctions
3910 permettant de créer des menus en utilisant la méthode manuelle peut
3911 être le début d'un long chemin avant une quelconque utilisation. Avec
3912 l'usine à menus, il n'est pas possible d'ajouter des images ou des « /
3915 <sect1>Création manuelle de menus
3917 Selon la tradition pédagogique, nous commencerons par le plus compliqué <tt/:)/
3919 Regardons les fonctions utilisées pour créer les menus. La première sert à créer un nouveau menu.
3922 GtkWidget *gtk_menu_bar_new()
3925 Cette fonction crée une nouvelle barre de menu. On utilise la fonction
3926 <em/gtk_container_add/ pour la placer dans une fenêtre, ou les
3927 fonctions <em/box_pack/ pour la placer dans une boîte - la même que
3931 GtkWidget *gtk_menu_new();
3934 Cette fonction retourne un pointeur vers un nouveau menu, il n'est
3935 jamais montré (avec <em/gtk_widget_show/), il ne fait que contenir les
3936 items du menu. Ceci deviendra plus clair lorsque nous étudierons
3937 l'exemple ci-dessous.
3939 Les deux appels suivants servent à créer des items de menu qui seront
3940 placés dans le menu.
3943 GtkWidget *gtk_menu_item_new()
3949 GtkWidget *gtk_menu_item_new_with_label(const char *label)
3952 Ces appels servent à créer les menus qui doivent être affichés. On
3953 doit bien faire la différence entre un « menu » qui est créé avec
3954 <em/gtk_menu_new()/ et un « item de menu » créé avec les fonctions
3955 <em/gtk_menu_item_new()/. L'item de menu sera un véritable bouton avec
3956 une action associée alors qu'un menu sera un container contenant les
3961 gtk_menu_item_append()
3963 gtk_menu_item_set_submenu()
3966 Les fonctions <em/gtk_menu_new_with_label()/ et <em/gtk_menu_new()/
3967 sont telles que vous les attendiez après avoir étudié les
3968 boutons. L'une crée un nouvel item de menu contenant déjà un label, et
3969 l'autre ne fait que créer un item de menu vide.
3971 Voici les étapes pour créer une barre de menu avec des menus attachés :
3973 <item>Créer un nouveau menu avec <em/gtk_menu_new()/ <item>Créer un
3974 item de menu avec <em/gtk_menu_item_new()/. Ce sera la racine du
3975 menu, le texte apparaissant ici sera aussi sur la barre de menu.
3976 <item>Utiliser plusieurs appels à <em/gtk_menu_item_new()/ pour
3977 chaque item que l'on désire dans le menu. Utiliser
3978 <em/gtk_menu_item_append()/ pour placer chacun de ces items les uns
3979 après les autres. Cela crée une liste d'items de menu.
3980 <item>Utiliser <em/gtk_menu_item_set_submenu()/ pour attacher les
3981 items de menus nouvellement créés à l'item de menu racine (celui créé
3982 à la seconde étape).
3983 <item>Créer une nouvelle barre de menu avec
3984 <em/gtk_menu_bar_new()/. Cette étape ne doit être faite qu'une fois
3985 lorsque l'on crée une série de menu sur une seule barre de menus.
3986 <item>Utiliser <em/gtk_menu_bar_append()/ pour placer le menu racine
3987 dans la barre de menu.
3990 La création d'un menu surgissant est presque identique. La différence
3991 est que le menu n'est pas posté « automatiquement » par une barre de
3992 menu, mais explicitement en appelant la fonction <em/gtk_menu_popup()/
3993 par un événement « bouton pressé ».
3995 Suivez ces étapes
3997 <item>Créer une fonction de gestion d'événement. Elle doit avoir le prototype
3999 static gint handler(GtkWidget *widget, GdkEvent *event);
4001 et elle utilisera l'événement <em/event/ pour savoir où faire surgir
4003 <item>Ce gestionnaire, si l'événement est un appui sur un
4004 bouton souris, traite <em/event/ comme un événement bouton (ce qu'il
4005 est) et l'utilise, de la façon indiquée dans le code d'exemple, pour
4006 passer l'information à <em/gtk_menu_popup()/.
4008 <item> Lier ce gestionnaire à un widget avec :
4010 gtk_signal_connect_object(GTK_OBJECT(widget), "event",
4011 GTK_SIGNAL_FUNC (handler), GTK_OBJECT(menu));
4013 où <em/widget/ est le widget auquel vous le liez, <em/handler/ est
4014 le gestionnaire, et <em/menu/ est un menu créé avec
4015 <em/gtk_menu_new()/. Cela peut être un menu qui est aussi posté par
4016 une barre de menu, comme le montre l'exemple.
4019 <sect1>Exemple de menu manuel
4023 #include <gtk/gtk.h>
4025 static gint button_press (GtkWidget *, GdkEvent *);
4026 static void menuitem_response (GtkWidget *, gchar *);
4029 int main (int argc, char *argv[])
4034 GtkWidget *menu_bar;
4035 GtkWidget *root_menu;
4036 GtkWidget *menu_items;
4042 gtk_init (&argc, &argv);
4044 /* Création d'un fenêtre */
4046 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
4047 gtk_window_set_title(GTK_WINDOW (window), "Test de Menu GTK");
4048 gtk_signal_connect(GTK_OBJECT (window), "delete_event",
4049 (GtkSignalFunc) gtk_exit, NULL);
4051 /* Initialise le widget menu -- Attention : n'appelez jamais
4052 * gtk_show_widget() pour le widget menu !!!
4053 * C'est le menu qui contient les items de menu, celui qui surgira
4054 * lorsque vous cliquez sur le « menu racine » de l'application. */
4056 menu = gtk_menu_new();
4058 /* Voici le menu racine dont le label sera le nom du menu affiché sur la barre
4059 * de menu. Il n'a pas de gestionnaire de signal attaché car il ne fait
4060 * qu'afficher le reste du menu lorsqu'il est pressé. */
4062 root_menu = gtk_menu_item_new_with_label("Menu racine");
4064 gtk_widget_show(root_menu);
4066 /* Puis, on crée une petite boucle créant trois entrées pour « menu test »
4067 * Notez l'appel à gtk_menu_append(). Ici, on ajoute une liste d'items à
4068 * notre menu. Normalement, on devrait aussi capturer le signal "clicked"
4069 * pour chacun des items et configurer une fonction de rappel pour lui,
4070 * mais on l'a omis ici pour gagner de la place. */
4072 for(i = 0; i < 3; i++)
4074 /* Copie des noms dans buf. */
4076 sprintf(buf, "Sous-menu Test - %d", i);
4078 /* Création d'un nouveau item de menu avec un nom... */
4080 menu_items = gtk_menu_item_new_with_label(buf);
4082 /* ...et ajout de celui-ci dans le menu. */
4084 gtk_menu_append(GTK_MENU (menu), menu_items);
4086 /* On fait quelque chose d'intéressant lorsque l'item est
4089 gtk_signal_connect (GTK_OBJECT(menu_items), "activate",
4090 GTK_SIGNAL_FUNC(menuitem_response), (gpointer)
4093 /* Affichage du widget. */
4095 gtk_widget_show(menu_items);
4098 /* Maintenant, on spécifié que nous voulons que notre nouveau « menu »
4099 * soit le menu du « menu racine ». */
4101 gtk_menu_item_set_submenu(GTK_MENU_ITEM (root_menu), menu);
4103 /* Création d'une vbox pour y mettre un menu et un bouton. */
4105 vbox = gtk_vbox_new(FALSE, 0);
4106 gtk_container_add(GTK_CONTAINER(window), vbox);
4107 gtk_widget_show(vbox);
4109 /* Création d'une barre de menus pour contenir les menus. Puis, on
4110 * l'ajoute à notre fenêtre principale. */
4112 menu_bar = gtk_menu_bar_new();
4113 gtk_box_pack_start(GTK_BOX(vbox), menu_bar, FALSE, FALSE, 2);
4114 gtk_widget_show(menu_bar);
4116 /* Création d'un bouton pour y attacher le menu. */
4118 button = gtk_button_new_with_label("Pressez moi");
4119 gtk_signal_connect_object(GTK_OBJECT(button), "event",
4120 GTK_SIGNAL_FUNC (button_press), GTK_OBJECT(menu));
4121 gtk_box_pack_end(GTK_BOX(vbox), button, TRUE, TRUE, 2);
4122 gtk_widget_show(button);
4124 /* Finalement, on ajoute l'item de menu à la barre de menu --
4125 * c'est l'item de menu racine sur lequel je me suis déchaîné ;-) */
4127 gtk_menu_bar_append(GTK_MENU_BAR (menu_bar), root_menu);
4129 /* Affichage de la fenêtre. */
4131 gtk_widget_show(window);
4140 /* On répond à un appui sur le bouton en postant un nouveau menu passé comme
4143 * On remarque que le paramètre "widget" est le menu à poster, PAS le bouton
4144 * qui a été pressé. */
4147 static gint button_press (GtkWidget *widget, GdkEvent *event)
4150 if (event->type == GDK_BUTTON_PRESS) {
4151 GdkEventButton *bevent = (GdkEventButton *) event;
4152 gtk_menu_popup (GTK_MENU(widget), NULL, NULL, NULL, NULL,
4153 bevent->button, bevent->time);
4155 /* On indique à l'appelant que l'on a géré cet événement. */
4160 /* On indique à l'appelant que l'on n'a pas géré cet événement. */
4166 /* Affiche une chaîne lorsqu'un item de menu est choisi. */
4168 static void menuitem_response (GtkWidget *widget, gchar *string)
4170 printf("%s\n", string);
4174 Vous pouvez aussi configurer un item de menu pour qu'il ne soit pas
4175 sélectionnable et, en utilisant une table de raccourcis clavier, lier
4176 des touches aux fonctions du menu.
4178 <sect1>Utilisation de GtkMenuFactory
4180 Maintenant que nous avons exploré la voie difficile, nous allons voir
4181 l'utilisation des appels <em/gtk_menu_factory./
4183 <sect1>Exemple d'usine à menu
4185 Voici un exemple utilisant l'usine à menu de GTK. Le premier
4186 fichier est <em/menus.h/. Nous séparerons <em/menus.c/ et <em/main.c/ à
4187 cause des variables globales utilisées dans le fichier <em/menus.c/.
4195 #endif /* __cplusplus */
4197 void get_main_menu (GtkWidget **menubar, GtkAcceleratorTable **table);
4198 void menus_create(GtkMenuEntry *entries, int nmenu_entries);
4202 #endif /* __cplusplus */
4204 #endif /* __MENUS_H__ */
4207 Voici le fichier <em/menus.c/ :
4211 #include <gtk/gtk.h>
4212 #include <strings.h>
4217 static void menus_remove_accel(GtkWidget * widget, gchar * signal_name, gchar * path);
4218 static gint menus_install_accel(GtkWidget * widget, gchar * signal_name, gchar key, gchar modifiers, gchar * path);
4219 void menus_init(void);
4220 void menus_create(GtkMenuEntry * entries, int nmenu_entries);
4223 /* Structure GtkMenuEntry utilisée pour créer les menus. Le premier champ
4224 * est la chaîne de définition du menu. Le second, la touche de raccourci
4225 * utilisée pour accéder à cette fonction du menu avec le clavier.
4226 * Le troisième est la fonction de rappel à utiliser lorsque l'item de menu
4227 * est choisi (par la touche de raccourci ou avec la souris). Le dernier
4228 * élément est la donnée à passer à la fonction de rappel. */
4231 static GtkMenuEntry menu_items[] =
4233 {"<Main>/Fichier/Nouveau", "<control>N", NULL, NULL},
4234 {"<Main>/Fichier/Ouvrir", "<control>O", NULL, NULL},
4235 {"<Main>/Fichier/Sauver", "<control>S", NULL, NULL},
4236 {"<Main>/Fichier/Sauver sous", NULL, NULL, NULL},
4237 {"<Main>/Fichier/<separator>", NULL, NULL, NULL},
4238 {"<Main>/Fichier/Quitter", "<control>Q", file_quit_cmd_callback, "OK, c'est fini"},
4239 {"<Main>/Options/Test", NULL, NULL, NULL}
4242 /* Calcul du nombre d'éléments de menu_item */
4244 static int nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
4246 static int initialize = TRUE;
4247 static GtkMenuFactory *factory = NULL;
4248 static GtkMenuFactory *subfactory[1];
4249 static GHashTable *entry_ht = NULL;
4251 void get_main_menu(GtkWidget ** menubar, GtkAcceleratorTable ** table)
4257 *menubar = subfactory[0]->widget;
4259 *table = subfactory[0]->table;
4262 void menus_init(void)
4267 factory = gtk_menu_factory_new(GTK_MENU_FACTORY_MENU_BAR);
4268 subfactory[0] = gtk_menu_factory_new(GTK_MENU_FACTORY_MENU_BAR);
4270 gtk_menu_factory_add_subfactory(factory, subfactory[0], "<Main>");
4271 menus_create(menu_items, nmenu_items);
4275 void menus_create(GtkMenuEntry * entries, int nmenu_entries)
4284 for (i = 0; i < nmenu_entries; i++) {
4285 accelerator = g_hash_table_lookup(entry_ht, entries[i].path);
4287 if (accelerator[0] == '\0')
4288 entries[i].accelerator = NULL;
4290 entries[i].accelerator = accelerator;
4293 gtk_menu_factory_add_entries(factory, entries, nmenu_entries);
4295 for (i = 0; i < nmenu_entries; i++)
4296 if (entries[i].widget) {
4297 gtk_signal_connect(GTK_OBJECT(entries[i].widget), "install_accelerator",
4298 (GtkSignalFunc) menus_install_accel,
4300 gtk_signal_connect(GTK_OBJECT(entries[i].widget), "remove_accelerator",
4301 (GtkSignalFunc) menus_remove_accel,
4306 static gint menus_install_accel(GtkWidget * widget, gchar * signal_name, gchar key, gchar modifiers, gchar * path)
4312 if (modifiers & GDK_CONTROL_MASK)
4313 strcat(accel, "<control>");
4314 if (modifiers & GDK_SHIFT_MASK)
4315 strcat(accel, "<shift>");
4316 if (modifiers & GDK_MOD1_MASK)
4317 strcat(accel, "<alt>");
4324 t1 = g_hash_table_lookup(entry_ht, path);
4327 entry_ht = g_hash_table_new(g_string_hash, g_string_equal);
4329 g_hash_table_insert(entry_ht, path, g_strdup(accel));
4334 static void menus_remove_accel(GtkWidget * widget, gchar * signal_name, gchar * path)
4339 t = g_hash_table_lookup(entry_ht, path);
4342 g_hash_table_insert(entry_ht, path, g_strdup(""));
4346 void menus_set_sensitive(char *path, int sensitive)
4348 GtkMenuPath *menu_path;
4353 menu_path = gtk_menu_factory_find(factory, path);
4355 gtk_widget_set_sensitive(menu_path->widget, sensitive);
4357 g_warning("Impossible de configurer la sensitivité d'un menu qui n'existe pas : %s", path);
4362 Voici <em/main.h/ :
4371 #endif /* __cplusplus */
4373 void file_quit_cmd_callback(GtkWidget *widget, gpointer data);
4377 #endif /* __cplusplus */
4379 #endif /* __MAIN_H__ */
4383 Et, enfin, <em/main.c/ :
4386 #include <gtk/gtk.h>
4392 int main(int argc, char *argv[])
4395 GtkWidget *main_vbox;
4398 GtkAcceleratorTable *accel;
4400 gtk_init(&argc, &argv);
4402 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
4403 gtk_signal_connect(GTK_OBJECT(window), "destroy",
4404 GTK_SIGNAL_FUNC(file_quit_cmd_callback),
4406 gtk_window_set_title(GTK_WINDOW(window), "Usine à menu");
4407 gtk_widget_set_usize(GTK_WIDGET(window), 300, 200);
4409 main_vbox = gtk_vbox_new(FALSE, 1);
4410 gtk_container_border_width(GTK_CONTAINER(main_vbox), 1);
4411 gtk_container_add(GTK_CONTAINER(window), main_vbox);
4412 gtk_widget_show(main_vbox);
4414 get_main_menu(&menubar, &accel);
4415 gtk_window_add_accelerator_table(GTK_WINDOW(window), accel);
4416 gtk_box_pack_start(GTK_BOX(main_vbox), menubar, FALSE, TRUE, 0);
4417 gtk_widget_show(menubar);
4419 gtk_widget_show(window);
4425 /* Juste une démonstration du fonctionnement des fonctions de rappel
4426 * lorsqu'on utilise l'usine à menus. Souvent, on met tous les rappels
4427 * des menus dans un fichier séparé, ce qui assure une meilleure
4430 void file_quit_cmd_callback (GtkWidget *widget, gpointer data)
4432 g_print ("%s\n", (char *) data);
4437 Un <em/makefile/ pour que cela soit plus facile à compiler :
4442 C_FLAGS = -Wall $(PROF) -L/usr/local/include -DDEBUG
4443 L_FLAGS = $(PROF) -L/usr/X11R6/lib -L/usr/local/lib
4444 L_POSTFLAGS = -lgtk -lgdk -lglib -lXext -lX11 -lm
4447 O_FILES = menus.o main.o
4449 $(PROGNAME): $(O_FILES)
4451 $(CC) $(L_FLAGS) -o $(PROGNAME) $(O_FILES) $(L_POSTFLAGS)
4454 $(CC) -c $(C_FLAGS) $<
4457 rm -f core *.o $(PROGNAME) nohup.out
4462 Pour l'instant, il n'y a que cet exemple. Une explication et de nombreux commentaires seront intégrés plus tard.
4465 <sect>Widgets non documentés
4467 On a besoin de leurs auteurs! :). Participez à notre didacticiel.
4469 Si vous devez utiliser un de ces widgets non documentés, je vous recommande fortement de consulter leurs fichiers en-têtes respectifs dans la distribution GTK. Les noms de fonctions du GTK sont très parlantes. Lorsque vous avez compris comment les choses fonctionnent, il n'est pas difficile de savoir comment utiliser un widget à partir des déclarations de ses fonctions. Cela, avec quelques exemples de codes pris ailleurs, devrait ne pas poser de problème.
4471 Lorsque vous avez compris toutes les fonctions d'un nouveau widget non documenté, pensez à écrire un didacticiel pour que les autres puissent bénéficier du temps que vous y avez passé.
4473 <sect1>Entrées de texte
4476 <sect1>Sélections de couleurs
4479 <sect1>Contrôle d'intervalle
4485 <sect1>Boîtes de texte
4488 <sect1>Prévisualisation
4490 (Ceci peut devoir être réécrit pour suivre le style du reste de ce
4495 Les prévisualisateurs servent à plusieurs choses dans GIMP/GTK. La
4496 plus importante est celle-ci : les images de haute qualité peuvent
4497 occuper des dizaines de mega-octets en mémoire - facilement ! Toute
4498 opération sur une image aussi grosse implique un temps de traitement
4499 élevé. Si cela vous prend 5 à 10 essais (i.e. 10 à 20 étapes puisque
4500 vous devez recommencer lorsque vous avez fait une erreur) pour choisir
4501 la bonne modification, cela prendra littéralement des heures pour
4502 produire la bonne image - pour peu que vous ne manquiez pas de mémoire
4503 avant. Ceux qui on passé des heures dans les chambres noires de
4504 développement couleur connaissent cette sensation. Les
4505 prévisualisations sont notre planche de salut !
4507 L'aspect pénible de l'attente n'est pas le seul problème. souvent, il
4508 est utile de comparer les versions « Avant » et « Après » côte à côte
4509 ou, au pire l'une après l'autre. Si vous travaillez avec de grosses
4510 images et des attentes de 10 secondes, l'obtention des versions «
4511 Avant » et « Après » est, pour le moins, difficile. Pour des images de
4512 30Mo (4"x6", 600dpi, 24 bits), la comparaison côte à côte est
4513 impossible pour la plupart des gens, et la comparaison séquentielle
4514 n'est guère mieux. Les prévisualisations sont notre planche de salut !
4516 Mais il y a plus. Les prévisualisations permettent les
4517 pré-prévisualisations côte à côte. En d'autres termes, vous écrivez un
4518 plug-in (par exemple la simulation filterpack) qui aurait plusieurs
4519 prévisualisations de ce-que-ce-serait-si-vous-faisiez-ceci. Une approche
4520 comme celle ci agit comme une sorte de palette de prévisualisation et
4521 est très pratique pour les petites modifications. Utilisons les
4524 Encore plus : pour certains plug-ins une intervention humaine en
4525 temps réel, spécifique aux images, peut s'avérer nécessaire. Dans le
4526 plug-in SuperNova, par exemple, on demande à l'utilisateur d'entrer
4527 les coordonnées du centre de la future supernova. La façon la plus
4528 simple de faire cela, vraiment, est de présenter une prévisualisation
4529 à l'utilisateur et de lui demander de choisir interactivement le
4530 point. Utilisons les prévisualisations !
4532 Enfin, quelques utilisations diverses : on peut utiliser les
4533 prévisualisations, même lorsqu'on ne travaille pas avec de grosses
4534 images. Elles sont utiles, par exemple, lorsqu'on veut avoir un rendu
4535 de motifs complexes. (Testez le vénérable plug-in Diffraction et
4536 d'autres !). Comme autre exemple, regardez le plug-in de rotation de
4537 couleur (travail en cours). Vous pouvez aussi utiliser les
4538 prévisualisations pour des petits logos dans vos plug-ins et même pour
4539 une photo de vous, l'Auteur. Utilisons les prévisualisations !
4541 Quand ne pas utiliser les prévisualisations
4543 N'utilisez pas les prévisualisations pour les graphes, les tracés,
4544 etc. GDK est bien plus rapide pour ça. N'utilisez les que pour les
4547 Utilisons les prévisualisations !
4549 Vous pouvez mettre une prévisualisation dans à peu près n'importe
4550 quoi. Dans une vbox, une hbox, un bouton, etc. Mais elles donnent leur
4551 meilleur d'elles-mêmes dans des cadres resserrés autour d'elles. Les
4552 prévisualisations n'ont, par elles-mêmes, aucun contour et semblent
4553 plates sans eux. (Bien sûr, si c'est cet aspect que vous
4554 voulez...). Les cadres serrés fournissent les bordures nécessaires.
4558 Les prévisualisations sont, à bien des égards, comme tous les autres
4559 widgets de GTK (avec tout ce que cela implique) sauf qu'il disposent
4560 d'une fonctionnalité supplémentaire : ils doivent être remplis
4561 avec une image ! Nous traiterons d'abord exclusivement de l'aspect GTK
4562 des prévisualisations, puis nous verrons comment les remplir.
4565 /* Création d'un widget prévisualisation,
4566 * configuration de sa taille et affichage */
4568 preview=gtk_preview_new(GTK_PREVIEW_COLOR)
4570 GTK_PREVIEW_GRAYSCALE);*/
4572 gtk_preview_size (GTK_PREVIEW (preview), WIDTH, HEIGHT);
4573 gtk_widget_show(preview);
4574 my_preview_rendering_function(preview);
4576 Ah oui, comme je le disais, les prévisualisations rendent mieux dans
4579 GtkWidget *create_a_preview(int Width,
4586 frame = gtk_frame_new(NULL);
4587 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
4588 gtk_container_border_width (GTK_CONTAINER(frame),0);
4589 gtk_widget_show(frame);
4591 preview=gtk_preview_new (Colorfulness?GTK_PREVIEW_COLOR
4592 :GTK_PREVIEW_GRAYSCALE);
4593 gtk_preview_size (GTK_PREVIEW (preview), Width, Height);
4594 gtk_container_add(GTK_CONTAINER(frame),preview);
4595 gtk_widget_show(preview);
4597 my_preview_rendering_function(preview);
4601 Ceci est ma prévisualisation de base. Cette fonction retourne le cadre
4602 « père », on peut ainsi le placer ailleurs dans notre interface. Bien
4603 sûr, on peut passer le cadre « père » en paramètre à cette
4604 fonction. Dans de nombreuses situations, toutefois, le contenu de la
4605 prévisualisation est changée continuellement par notre application. En
4606 ce cas, on peut passer un pointeur vers une prévisualisation à la
4607 fonction <em/create_a_preview()/ et avoir ainsi un contrôle sur elle
4610 Un point plus important qui pourra un jour vous faire économiser
4611 beaucoup de temps. Quelques fois, il est souhaitable de mettre un
4612 label à votre prévisualisation. Par exemple, on peut nommer la
4613 prévisualisation contenant l'image originale « Original » et celle
4614 contenant l'image modifiée « Moins Originale ». Il peut vous arriver
4615 de placer la prévisualisation avec le label approprié dans une
4616 vbox. L'effet inattendu est que si le label est plus large que la
4617 prévisualisation (taille de cette dernière, taille de la fonte du
4618 label, etc), le cadre s'élargit et ne convient plus à la
4619 prévisualisation. Le même problème se passera probablement dans
4620 d'autres situations aussi.
4624 La solution consiste à placer la prévisualisation et le label dans une
4625 table de 2x2 en les attachant avec les paramètres suivants (c'est
4626 l'une des possibilités, bien sûr. La clé consiste à ne pas mettre
4627 GTK_FILL dans le second attachement)«nbsp;:
4629 gtk_table_attach(GTK_TABLE(table),label,0,1,0,1,
4631 GTK_EXPAND|GTK_FILL,
4633 gtk_table_attach(GTK_TABLE(table),frame,0,1,1,2,
4639 Et voici le résultat :
4645 Rendre une prévisualisation cliquable se fait très facilement en la plaçant dans un bouton. Cela ajoute aussi une bordure agréable autour de la prévisualisation et vous n'avez même pas besoin de la mettre dans un cadre. Voir le plug-in Filter Pack Simulation comme exemple.
4647 Remplir une prévisualisation
4649 Afin de nous familiariser avec les bases de ce remplissage, créons le motif suivant :
4654 my_preview_rendering_function(GtkWidget *preview)
4657 #define HALF (SIZE/2)
4659 guchar *row=(guchar *) malloc(3*SIZE); /* 3 bits par point */
4660 gint i, j; /* Coordonnées */
4661 double r, alpha, x, y;
4663 if (preview==NULL) return; /* J'ajoute généralement ceci quand je */
4664 /* veux éviter des plantages stupides */
4665 /* Vous devez vous assurer que tout a */
4666 /* été correctement initialisé ! */
4668 for (j=0; j < ABS(cos(2*alpha)) ) { /* Sommes-nous dans la forme ? */
4669 /* glib.h contient ABS(x). */
4670 row[i*3+0] = sqrt(1-r)*255; /* Definit rouge */
4671 row[i*3+1] = 128; /* Definit vert */
4672 row[i*3+2] = 224; /* Definit bleu */
4673 } /* "+0" est pour l'alignement ! */
4676 row[i*3+1] = ABS(sin((float)i/SIZE*2*PI))*255;
4677 row[i*3+2] = ABS(sin((float)j/SIZE*2*PI))*255;
4680 gtk_preview_draw_row( GTK_PREVIEW(preview),row,0,j,SIZE);
4682 /* Insère "row" dans "preview" en partant du point de */
4683 /* coordonnées (0,j) première colonne, j_ième ligne allant de SIZE */
4684 /* pixels vers la droite */
4687 free(row); /* on récupère un peu d'espace */
4688 gtk_widget_draw(preview,NULL); /* qu'est-ce que ça fait ? */
4689 gdk_flush(); /* et ça ? */
4692 Ceux qui n'utilisent pas GIMP en ont suffisamment vu pour faire
4693 déjà beaucoup de choses. Pour ceux qui l'utilisent, j'ai quelques
4694 précisions à ajouter.
4696 Prévisualisation d'image
4698 Il est pratique de conserver une version réduite de l'image ayant
4699 juste assez de pixels pour remplir la prévisualisation. Ceci est
4700 possible en choisissant chaque énième pixel où n est le ratio de la
4701 taille de l'image par rapport à la taille de la visualisation. Toutes
4702 les opérations suivantes (y compris le remplissage des
4703 prévisualisations) sont alors réalisées seulement sur le nombre réduit
4704 de pixels. Ce qui suit est mon implantation de la réduction d'image
4705 (Gardez à l'esprit que je n'ai que quelques notions de base en C !).
4707 (ATTENTION : CODE NON TESTÉ !!!)
4719 SELCTION_IN_CONTEXT,
4723 ReducedImage *Reduce_The_Image(GDrawable *drawable,
4728 /* Cette fonction réduit l'image à la taille de prévisualisation choisie */
4729 /* La taille de la prévisualisation est déterminée par LongerSize, i.e. */
4730 /* la plus grande des deux dimensions. Ne fonctionne qu'avec des images */
4733 gint RH, RW; /* Hauteur et Largeur réduites */
4734 gint width, height; /* Largeur et Hauteur de la surface à réduire */
4735 gint bytes=drawable->bpp;
4736 ReducedImage *temp=(ReducedImage *)malloc(sizeof(ReducedImage));
4738 guchar *tempRGB, *src_row, *tempmask, *src_mask_row,R,G,B;
4739 gint i, j, whichcol, whichrow, x1, x2, y1, y2;
4740 GPixelRgn srcPR, srcMask;
4741 gint NoSelectionMade=TRUE; /* Suppose que l'on traite l'image entière */
4743 gimp_drawable_mask_bounds (drawable->id, &x1, &y1, &x2, &y2);
4746 /* S'il y a une SELECTION, on récupère ses frontières ! */
4748 if (width != drawable->width && height != drawable->height)
4749 NoSelectionMade=FALSE;
4750 /* On vérifie si l'utilisateur a rendu une sélection active */
4751 /* Ceci sera important plus tard, lorsqu'on créera un masque réduit */
4753 /* Si on veut prévisualiser l'image entière, supprimer ce qui suit ! */
4754 /* Bien sûr, s'il n'y a pas de sélection, cela n'a aucun effet ! */
4755 if (Selection==ENTIRE_IMAGE) {
4759 y2=drawable->height;
4762 /* Si on veut prévisualiser une sélection avec une surface qui l'entoure, */
4763 /* on doit l'agrandir un petit peu. Considérez ça comme une devinette. */
4765 if (Selection==SELECTION_IN_CONTEXT) {
4766 x1=MAX(0, x1-width/2.0);
4767 x2=MIN(drawable->width, x2+width/2.0);
4768 y1=MAX(0, y1-height/2.0);
4769 y2=MIN(drawable->height, y2+height/2.0);
4772 /* Calcul de la largeur et de la hauteur de la surface à réduire. */
4777 /* Les lignes ci-dessous déterminent la dimension qui sera le coté */
4778 /* le plus long. Cette idée est empruntée au plug-in Supernova. */
4779 /* Je soupçonne que j'aurais pu y penser moi-même, mais la vérité */
4780 /* doit être dite. Le plagiat pue ! */
4784 RH=(float) height * (float) LongerSize/ (float) width;
4788 RW=(float)width * (float) LongerSize/ (float) height;
4791 /* L'image entière est réduite dans une chaîne ! */
4793 tempRGB = (guchar *) malloc(RW*RH*bytes);
4794 tempmask = (guchar *) malloc(RW*RH);
4796 gimp_pixel_rgn_init (&srcPR, drawable, x1, y1, width, height, FALSE, FALSE);
4797 gimp_pixel_rgn_init (&srcMask, mask, x1, y1, width, height, FALSE, FALSE);
4799 /* Réservation pour sauver une ligne d'image et une ligne du masque */
4800 src_row = (guchar *) malloc (width*bytes);
4801 src_mask_row = (guchar *) malloc (width);
4803 for (i=0; i < RH; i++) {
4804 whichrow=(float)i*(float)height/(float)RH;
4805 gimp_pixel_rgn_get_row (&srcPR, src_row, x1, y1+whichrow, width);
4806 gimp_pixel_rgn_get_row (&srcMask, src_mask_row, x1, y1+whichrow, width);
4808 for (j=0; j < RW; j++) {
4809 whichcol=(float)j*(float)width/(float)RW;
4811 /* Pas de sélection = chaque point est complètement sélectionné ! */
4813 if (NoSelectionMade)
4814 tempmask[i*RW+j]=255;
4816 tempmask[i*RW+j]=src_mask_row[whichcol];
4818 /* Ajout de la ligne à la longue chaîne qui contient maintenant */
4821 tempRGB[i*RW*bytes+j*bytes+0]=src_row[whichcol*bytes+0];
4822 tempRGB[i*RW*bytes+j*bytes+1]=src_row[whichcol*bytes+1];
4823 tempRGB[i*RW*bytes+j*bytes+2]=src_row[whichcol*bytes+2];
4825 /* On s'accroche aussi à l'alpha */
4827 tempRGB[i*RW*bytes+j*bytes+3]=src_row[whichcol*bytes+3];
4834 temp->mask=tempmask;
4838 La suite est une fonction de prévisualisation qui utilise le même type
4839 <em/ReducedImage/ ! On remarque qu'elle utilise une fausse
4840 transparence (au moyen de <em/fake_transparancy/ qui est défini comme
4843 gint fake_transparency(gint i, gint j)
4845 if ( ((i%20)- 10) * ((j%20)- 10)>0 )
4851 Voici maintenant la fonction de prévisualisation«nbsp;:
4854 my_preview_render_function(GtkWidget *preview,
4858 gint Inten, bytes=drawable->bpp;
4861 gint RW=reduced->width;
4862 gint RH=reduced->height;
4863 guchar *row=malloc(bytes*RW);;
4866 for (i=0; i < RH; i++) {
4867 for (j=0; j < RW; j++) {
4869 row[j*3+0] = reduced->rgb[i*RW*bytes + j*bytes + 0];
4870 row[j*3+1] = reduced->rgb[i*RW*bytes + j*bytes + 1];
4871 row[j*3+2] = reduced->rgb[i*RW*bytes + j*bytes + 2];
4874 for (k=0; k<3; k++) {
4875 float transp=reduced->rgb[i*RW*bytes+j*bytes+3]/255.0;
4876 row[3*j+k]=transp*a[3*j+k]+(1-transp)*fake_transparency(i,j);
4879 gtk_preview_draw_row( GTK_PREVIEW(preview),row,0,i,RW);
4883 gtk_widget_draw(preview,NULL);
4887 Fonctions applicables
4889 guint gtk_preview_get_type (void);
4891 void gtk_preview_uninit (void);
4893 GtkWidget* gtk_preview_new (GtkPreviewType type);
4894 /* Décrite ci-dessous */
4895 void gtk_preview_size (GtkPreview *preview,
4898 /* Permet de changer la taille d'une prévisualisation existante */
4899 /* Apparamment, il y a un bug dans GTK qui rend ce traitement */
4900 /* hasardeux. Une méthode pour corriger ce problème consiste à */
4901 /* changer manuellement la taille de la fenêtre contenant la */
4902 /* prévisualisation après avoir changé la taille de la */
4903 /* prévisualisation. */
4905 void gtk_preview_put (GtkPreview *preview,
4916 void gtk_preview_put_row (GtkPreview *preview,
4924 void gtk_preview_draw_row (GtkPreview *preview,
4929 /* Décrite dans le texte */
4931 void gtk_preview_set_expand (GtkPreview *preview,
4935 /* Aucune piste pour celles qui suivent mais devrait être */
4936 /* un standard pour la plupart des widgets. */
4938 void gtk_preview_set_gamma (double gamma);
4939 void gtk_preview_set_color_cube (guint nred_shades,
4940 guint ngreen_shades,
4942 guint ngray_shades);
4943 void gtk_preview_set_install_cmap (gint install_cmap);
4944 void gtk_preview_set_reserved (gint nreserved);
4945 GdkVisual* gtk_preview_get_visual (void);
4946 GdkColormap* gtk_preview_get_cmap (void);
4947 GtkPreviewInfo* gtk_preview_get_info (void);
4958 <sect>Widget EventBox <label id="sec_The_EventBox_Widget">
4960 Il n'est disponible que dans <em/gtk+970916.tar.gz/ et les
4961 distributions ultérieures. <p> Certains widgets GTK n'ont pas de
4962 fenêtre X associée, il se dessinent donc sur leurs parents. À cause de
4963 cela, ils ne peuvent recevoir d'événements et, s'ils ont une taille
4964 incorrecte, ils ne peuvent pas se mettre en place correctement : on peut
4965 alors avoir des surimpressions douteuses, etc. Si vous avez besoin de
4966 ces widgets, <em/EventBox/ est fait pour vous.
4968 Au premier abord, le widget <em/EventBox/ peut apparaître comme
4969 totalement dénué d'intérêt. Il ne dessine rien à l'écran et ne répond
4970 à aucun évenement. Cependant, il joue un rôle - il fournit une fenêtre
4971 X pour son widget fils. Ceci est important car de nombreux widgets GTK
4972 n'ont pas de fenêtre X associée. Ne pas avoir de fenêtre permet
4973 d'économiser de la mémoire mais a aussi quelques inconvénients. Un
4974 widget sans fenêtre ne peut recevoir d'événement, et ne réalise aucune
4975 mise en place de ce qu'il contient. Bien que le nom « EventBox »
4976 insiste sur la fonction de gestion d'événement, le widget peut aussi
4977 être utilisé pour la mise en place (et plus... voir l'exemple
4981 Pour créer un widget EventBox, on utilise :
4984 GtkWidget* gtk_event_box_new (void);
4988 Un widget fils peut alors être ajouté à cet <em/EventBox/ :
4991 gtk_container_add (GTK_CONTAINER(event_box), widget);
4995 L'exemple suivant montre l'utilisation d'un <em/EventBox/ - un label
4996 est créé et mis en place sur une petite boîte, et configuré pour qu'un
4997 clic souris sur le label provoque la fin du programme.
5000 #include <gtk/gtk.h>
5003 main (int argc, char *argv[])
5006 GtkWidget *event_box;
5009 gtk_init (&argc, &argv);
5011 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
5013 gtk_window_set_title (GTK_WINDOW (window), "Event Box");
5015 gtk_signal_connect (GTK_OBJECT (window), "destroy",
5016 GTK_SIGNAL_FUNC (gtk_exit), NULL);
5018 gtk_container_border_width (GTK_CONTAINER (window), 10);
5020 /* Création d'un EventBox et ajout de celui-ci dans la fenêtre. */
5022 event_box = gtk_event_box_new ();
5023 gtk_container_add (GTK_CONTAINER(window), event_box);
5024 gtk_widget_show (event_box);
5026 /* Création d'un long label */
5028 label = gtk_label_new ("Cliquez ici pour quitter, quitter, quitter, quitter, quitter");
5029 gtk_container_add (GTK_CONTAINER (event_box), label);
5030 gtk_widget_show (label);
5032 /* Placement serré. */
5034 gtk_widget_set_usize (label, 110, 20);
5036 /* Attachement d'une action à celui-ci. */
5038 gtk_widget_set_events (event_box, GDK_BUTTON_PRESS_MASK);
5039 gtk_signal_connect (GTK_OBJECT(event_box), "button_press_event",
5040 GTK_SIGNAL_FUNC (gtk_exit), NULL);
5042 /* Encore une fois, vous avez besoin d'une fenêtre X pour... */
5044 gtk_widget_realize (event_box);
5045 gdk_window_set_cursor (event_box->window, gdk_cursor_new (GDK_HAND1));
5047 gtk_widget_show (window);
5055 <sect>Configuration des attributs de widget<label
5056 id="sec_setting_widget_attributes">
5058 Cette section décrit les fonctions opérant sur les widgets. Elles
5059 peuvent être utilisées pour configurer le style, l'espacement, la
5062 (Je devrais peut être faire une section uniquement consacrée aux
5063 raccourcis clavier.)
5066 void gtk_widget_install_accelerator (GtkWidget *widget,
5067 GtkAcceleratorTable *table,
5072 void gtk_widget_remove_accelerator (GtkWidget *widget,
5073 GtkAcceleratorTable *table,
5074 gchar *signal_name);
5076 void gtk_widget_activate (GtkWidget *widget);
5078 void gtk_widget_set_name (GtkWidget *widget,
5080 gchar* gtk_widget_get_name (GtkWidget *widget);
5082 void gtk_widget_set_sensitive (GtkWidget *widget,
5085 void gtk_widget_set_style (GtkWidget *widget,
5088 GtkStyle* gtk_widget_get_style (GtkWidget *widget);
5090 GtkStyle* gtk_widget_get_default_style (void);
5092 void gtk_widget_set_uposition (GtkWidget *widget,
5095 void gtk_widget_set_usize (GtkWidget *widget,
5099 void gtk_widget_grab_focus (GtkWidget *widget);
5101 void gtk_widget_show (GtkWidget *widget);
5103 void gtk_widget_hide (GtkWidget *widget);
5108 <sect>Temporisations, fonctions d'E/S et d'attente<label id="sec_timeouts">
5110 <sect1>Temporisations
5112 Vous pouvez vous demander comment faire pour que GTK fasse quelque
5113 chose d'utile lorsqu'il est dans <em/gtk_main/. En fait, on a
5114 plusieurs options. L'utilisation des fonctions suivantes permet de
5115 créer une temporisation qui sera appelée tous les
5116 <em/interval/ millisecondes.
5119 gint gtk_timeout_add (guint32 interval,
5120 GtkFunction function,
5124 Le premier paramètre est le nombre de millisecondes entre les appels à
5125 notre fonction. Le deuxième est la fonction à appeler et le troisième
5126 est la donnée passée à cette fonction de rappel. La valeur retournée
5127 est un « marqueur » de type entier qui pourra être utilisé pour
5128 arrêter la temporisation en appelant :
5131 void gtk_timeout_remove (gint tag);
5134 On peut aussi stopper la fonction de temporisation en faisant
5135 retourner zéro ou FALSE à notre fonction de rappel. Évidemment, cela
5136 veut dire que si vous voulez que votre fonction continue à être
5137 appelée, elle doit retourner une valeur non nulle, ou TRUE.
5139 La déclaration de votre fonction de rappel doit ressembler à ça :
5142 gint timeout_callback (gpointer data);
5145 <sect1>Surveillance des E/S
5147 Une autre caractéristique intéressante du GTK est la possibilité de
5148 vérifier les données d'un descripteur de fichier (celles retournées
5149 par <em/open/(2) ou <em/socket/(2)). C'est particulièrement pratique pour les
5150 applications réseau. La fonction suivante permet cette
5154 gint gdk_input_add (gint source,
5155 GdkInputCondition condition,
5156 GdkInputFunction function,
5160 Le premier paramètre est le descripteur de fichier que l'on veut
5161 étudier, le second spécifie ce qu'on veut que le GDK recherche. Cela
5164 GDK_INPUT_READ - Appel <em/function/ lorsqu'il y a une donnée prête à
5165 être lue dans le descripteur de fichier.
5167 GDK_INPUT_WRITE - Appel de <em/function/ lorsque le descripteur de
5168 fichier est prêt pour une écriture.
5170 Je suis sûr que vous vous doutez, maintenant, que le troisième
5171 paramètre est la fonction que l'on veut appeler lorsque les conditions
5172 ci-dessus sont satisfaites. Le dernier paramètre est la donnée à
5173 passer à cette fonction.
5175 La valeur retournée est un marqueur qui pourra être utilisé pour dire
5176 au GDK de cesser de surveiller ce descripteur à l'aide de la fonction :
5179 void gdk_input_remove (gint tag);
5182 La fonction de rappel doit être déclarée de la façon suivante :
5185 void input_callback (gpointer data, gint source,
5186 GdkInputCondition condition);
5190 <sect1>Fonctions d'attente
5192 Que se passe-t'il si vous avez une fonction qui doit être appelée
5193 lorsque rien d'autre ne se passe ? On utilise la fonction suivante qui force
5194 GTK à appeler <em/function/ lorsqu'on est en phase d'inaction ;
5197 gint gtk_idle_add (GtkFunction function,
5202 void gtk_idle_remove (gint tag);
5205 Je n'expliquerai pas la signification des paramètres car ils
5206 ressemblent beaucoup à ceux déjà vus ci-dessus. La fonction pointée
5207 par le premier paramètre de <em/gtk_idle_add()/ sera appelée à chaque
5208 occasion. Comme pour les autres, retourner FALSE empêchera la fonction
5209 d'attente d'être appelée.
5211 <sect>Gestion des sélections
5215 Un type de communication inter-processus gérée par GTK est les
5216 <em>sélections</em>. Une sélection identifie un morceau de données,
5217 par exemple une portion de texte sélectionnée par l'utilisateur avec
5218 la souris. Seule une application sur un écran (le <em/propriétaire/)
5219 peut posséder une sélection particulière à un moment donné, ainsi
5220 lorsqu'une sélection est réclamée par une application, le propriétaire
5221 précédent doit indiquer à l'utilisateur que la sélection a été
5222 abandonnée. Les autres applications peuvent demander le contenu d'une
5223 sélection sous différentes formes appelées <em/cibles/. Il peut y
5224 avoir un nombre quelconque de sélections, mais la plupart des
5225 applications X n'en gèrent qu'une, la <em/sélection primaire/.
5228 Dans la plupart des cas, une application GTK n'a pas besoin de gérer elle-même
5229 les sélections. Les widgets standards, comme le widget Entrée de texte,
5230 possèdent déjà la capacité de réclamer la sélection lorsqu'il le faut (par
5231 exemple, lorsque l'utilisateur glisse au dessus d'un texte) et de récupérer le
5232 contenu de la sélection détenue par un autre widget ou une autre application
5233 (par exemple, lorsque l'utilisateur clique avec le deuxième bouton de la
5234 souris). Cependant, il peut il y avoir des cas dans lesquels vous voulez donner
5235 aux autres widgets la possibilité de fournir la sélection, ou vous désirez
5236 récupérer des cibles non supportées par défaut.
5239 Un concept fondamental dans la compréhension du fonctionnement des
5240 sélections est celui d'<em/atome/. Un atome est un entier qui définit
5241 de façon unique une chaîne (sur un affichage particulier). Certains
5242 atomes sont prédéfinis par le serveur X et, dans certains cas, des
5243 constantes définies dans <em/gtk.h/ correspondent à ces atomes. Par
5244 exemple, la constante GDK_PRIMARY_SELECTION correspond à la chaîne
5245 "PRIMARY". Dans d'autres cas, on doit utiliser les fonctions
5246 <em/gdk_atom_intern()/, pour obtenir l'atome correspondant à une
5247 chaîne, et <em/gdk_atom_name()/, pour obtenir le nom d'un atome. Les
5248 sélections et les cibles sont identifiés par des atomes.
5250 <sect1>Récupération de la sélection
5252 La récupération de la sélection est un processus asynchrone. Pour démarrer le processus, on appelle :
5255 gint gtk_selection_convert (GtkWidget *widget,
5261 Cela <em>convertit</em> la sélection dans la forme spécifiée par
5262 <em/target/. Si tout est possible, le paramètre <em/time/ sera le
5263 moment de l'événement qui a déclenché la sélection. Ceci aide à
5264 s'assurer que les événements arrivent dans l'ordre où l'utilisateur
5265 les a demandé. Cependant, si cela n'est pas possible (par exemple,
5266 lorsque la conversion a été déclenchée par un signal "clicked"), alors
5267 on peut utiliser la macro GDK_CURRENT_TIME.
5270 Quand le propriétaire de la sélection répond à la requête, un signal
5271 "selection_received" est envoyé à notre application. Le gestionnaire
5272 de ce signal reçoit un pointeur vers une structure
5273 <tt/GtkSelectionData/ définie ainsi :
5276 struct _GtkSelectionData
5287 <em/selection/ et <em/target/ sont les valeurs que l'on a donné dans
5288 notre appel <em/gtk_selection_convert()/. <em/type/ est un atome qui
5289 identifie le type de données retourné par le propriétaire de la
5290 sélection. Quelques valeurs possibles sont : "STRING", une chaîne
5291 de caractères latin-1, "ATOM", une série d'atomes, "INTEGER", un
5292 entier, etc. La plupart des cibles ne peuvent retourner qu'un
5293 type. <em/format/ donne la longueur des unités (les caractères, par
5294 exemple) en bits. Habituellement, on ne se préoccupe pas de cela
5295 lorsqu'on reçoit des données. <em/data/ est un pointeur vers la donnée
5296 retournée et <em/length/ donne la longueur en octets de la donnée
5297 retournée. Si <em/length/ est négative, cela indique qu'une erreur est
5298 survenue et que la sélection ne peut être récupérée. Ceci peut arriver
5299 si aucune application n'est propriétaire de la sélection, ou si vous
5300 avez demandé une cible que l'application ne sait pas gérer. Le tampon
5301 est garanti d'être un octet plus long que <em/length/ ; l'octet
5302 supplémentaire sera toujours zéro, et il n'est donc pas nécessaire de
5303 faire une copie de chaîne simplement pour qu'elle soit terminée par
5304 zéro (comme doivent l'être toutes les chaînes C).
5307 Dans l'exemple qui suit, on récupère la cible spéciale "TARGETS", qui
5308 est une liste de toutes les cibles en lesquelles la sélection peut
5312 #include <gtk/gtk.h>
5314 void selection_received (GtkWidget *widget,
5315 GtkSelectionData *selection_data,
5318 /* Gestionnaire de signal invoqué lorsque l'utilisateur clique sur
5319 * le bouton « Obtenir les cibles ». */
5321 void get_targets (GtkWidget *widget, gpointer data)
5323 static GdkAtom targets_atom = GDK_NONE;
5325 /* Obtention de l'atome correspondant à la chaîne "TARGETS" */
5327 if (targets_atom == GDK_NONE)
5328 targets_atom = gdk_atom_intern ("TARGETS", FALSE);
5330 /* Demande de la cible "TARGETS" pour la sélection primaire */
5332 gtk_selection_convert (widget, GDK_SELECTION_PRIMARY, targets_atom,
5336 /* Gestionnaire de signal appelé quand le propriétaire des sélections
5337 * retourne la donnée. */
5339 void selection_received (GtkWidget *widget, GtkSelectionData *selection_data,
5346 /* **** IMPORTANT **** On vérifie si la récupération s'est bien passée. */
5348 if (selection_data->length < 0)
5350 g_print ("Selection retrieval failed\n");
5354 /* On s'assure que l'on a obtenu la donnée sous la forme attendue. */
5356 if (selection_data->type != GDK_SELECTION_TYPE_ATOM)
5358 g_print ("La sélection \"TARGETS\" n'a pas été retournée sous la forme d'atomes !\n");
5362 /* Affichage des atomes reçus. */
5364 atoms = (GdkAtom *)selection_data->data;
5367 for (i=0; i<selection_data->length/sizeof(GdkAtom); i++)
5370 name = gdk_atom_name (atoms[i]);
5372 g_print ("%s\n",name);
5374 g_print ("(atome incorrect)\n");
5381 main (int argc, char *argv[])
5386 gtk_init (&argc, &argv);
5388 /* Création de la fenêtre de l'application. */
5390 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
5391 gtk_window_set_title (GTK_WINDOW (window), "Sélections");
5392 gtk_container_border_width (GTK_CONTAINER (window), 10);
5394 gtk_signal_connect (GTK_OBJECT (window), "destroy",
5395 GTK_SIGNAL_FUNC (gtk_exit), NULL);
5397 /* Création d'un bouton pour obtenir les cibles */
5399 button = gtk_button_new_with_label ("Obtenir les cibles");
5400 gtk_container_add (GTK_CONTAINER (window), button);
5402 gtk_signal_connect (GTK_OBJECT(button), "clicked",
5403 GTK_SIGNAL_FUNC (get_targets), NULL);
5404 gtk_signal_connect (GTK_OBJECT(button), "selection_received",
5405 GTK_SIGNAL_FUNC (selection_received), NULL);
5407 gtk_widget_show (button);
5408 gtk_widget_show (window);
5416 <sect1>Fournir la sélection
5419 Fournir la sélection est un peu plus compliqué. On doit enregistrer
5420 les gestionnaires qui seront appelés lorsque notre sélection est
5421 demandée. Pour chaque paire sélection/cible que l'on gèrera, on fera
5425 void gtk_selection_add_handler (GtkWidget *widget,
5428 GtkSelectionFunction function,
5429 GtkRemoveFunction remove_func,
5433 <em/widget/, <em/selection/ et <em/target/ identifient les requêtes
5434 que ce gestionnaire gèrera. S'il ne vaut pas NULL, <em/remove_func/
5435 sera appelé lorsque le gestionnaire de signal est supprimé. Ceci est
5436 utile, par exemple, pour des langages interprétés qui doivent garder
5437 une trace du nombre de références à <em/data/.
5440 La fonction de rappel <em/function/ doit avoir la signature suivante :
5443 typedef void (*GtkSelectionFunction) (GtkWidget *widget,
5444 GtkSelectionData *selection_data,
5449 Le <em/GtkSelectionData/ est le même qu'au dessus, mais, cette fois,
5450 nous sommes responsables de l'initialisation de ses champs <em/type/,
5451 <em/format/, <em/data/, et <em/length/. (Le champ <em/format/ est
5452 important ici - le serveur X l'utilise pour savoir si la donnée doit
5453 être échangée par octet ou non. Habituellement, ce sera 8 (un
5454 caractère), ou 32 (un entier)). Cette initialisation est faite en
5455 utilisant l'appel :
5458 void gtk_selection_data_set (GtkSelectionData *selection_data,
5465 Cette fonction s'occupe de faire une copie correcte des données afin
5466 que l'on n'ait pas à se soucier du reste. (On ne doit pas remplir ces
5470 Lorsque cela est demandé par l'utilisateur, on réclame la possession
5471 de la sélection en appelant :
5474 gint gtk_selection_owner_set (GtkWidget *widget,
5479 Si une autre application réclame la possession de la sélection, on
5480 recevra un "selection_clear_event".
5482 Comme exemple de fourniture de sélection, l'exemple suivant ajoute une
5483 fonctionnalité de sélection à un bouton commutateur. Lorsque ce bouton
5484 est appuyé, le programme réclame la sélection primaire. La seule cible
5485 supportée (à part certaines cibles fournies par GTK lui-même, comme
5486 « TARGETS ») est « STRING ». Lorsque celle-ci est demandée, on
5487 retourne une représentation de l'heure sous forme de chaîne.
5490 #include <gtk/gtk.h>
5493 /* Fonction de rappel appelée lorsque l'utilisateur commute la sélection. */
5495 void selection_toggled (GtkWidget *widget, gint *have_selection)
5497 if (GTK_TOGGLE_BUTTON(widget)->active)
5499 *have_selection = gtk_selection_owner_set (widget,
5500 GDK_SELECTION_PRIMARY,
5502 /* Si la demande de sélection échoue, on remet le bouton en position sortie. */
5504 if (!*have_selection)
5505 gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON(widget), FALSE);
5509 if (*have_selection)
5511 /* Avant de nettoyer la selection en mettant son propriétaire à NULL,
5512 * on vérifie que nous sommes bien son propriétaire actuel. */
5514 if (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == widget->window)
5515 gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY,
5517 *have_selection = FALSE;
5522 /* Appelée lorsqu'une autre application demande la sélection. */
5524 gint selection_clear (GtkWidget *widget, GdkEventSelection *event,
5525 gint *have_selection)
5527 *have_selection = FALSE;
5528 gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON(widget), FALSE);
5533 /* Fournit l'heure comme sélection. */
5535 void selection_handle (GtkWidget *widget,
5536 GtkSelectionData *selection_data,
5540 time_t current_time;
5542 current_time = time (NULL);
5543 timestr = asctime (localtime(&current_time));
5545 /* Lorsqu'on retourne une chaîne, elle ne doit pas se terminer par
5546 * 0, ce sera fait pour nous. */
5548 gtk_selection_data_set (selection_data, GDK_SELECTION_TYPE_STRING,
5549 8, timestr, strlen(timestr));
5552 int main (int argc, char *argv[])
5556 GtkWidget *selection_button;
5558 static int have_selection = FALSE;
5560 gtk_init (&argc, &argv);
5562 /* Création de la fenêtre principale. */
5564 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
5565 gtk_window_set_title (GTK_WINDOW (window), "Event Box");
5566 gtk_container_border_width (GTK_CONTAINER (window), 10);
5568 gtk_signal_connect (GTK_OBJECT (window), "destroy",
5569 GTK_SIGNAL_FUNC (gtk_exit), NULL);
5571 /* Création d'un bouton commutateur pour qu'il agisse comme une sélection. */
5573 selection_button = gtk_toggle_button_new_with_label ("Demande de sélection");
5574 gtk_container_add (GTK_CONTAINER (window), selection_button);
5575 gtk_widget_show (selection_button);
5577 gtk_signal_connect (GTK_OBJECT(selection_button), "toggled",
5578 GTK_SIGNAL_FUNC (selection_toggled), &have_selection);
5579 gtk_signal_connect (GTK_OBJECT(selection_button), "selection_clear_event",
5580 GTK_SIGNAL_FUNC (selection_clear), &have_selection);
5582 gtk_selection_add_handler (selection_button, GDK_SELECTION_PRIMARY,
5583 GDK_SELECTION_TYPE_STRING,
5584 selection_handle, NULL, NULL);
5586 gtk_widget_show (selection_button);
5587 gtk_widget_show (window);
5597 <sect>glib<label id="sec_glib">
5599 La <em/glib/ fournit de nombreuses fonctions et définitions utiles,
5600 prêtes à être utilisées lorsqu'on crée des applications GDK et GTK. Je
5601 les énumèrerais toutes avec une brève explication. Beaucoup sont des
5602 répliques des fonctions standards de la <em/libc/, et je ne les
5603 détaillerais donc pas trop. Ceci doit surtout servir de référence afin
5604 de savoir ce qui est disponible pour être utilisé.
5608 Les définitions pour les bornes de la plupart des types standards sont :
5623 Voici aussi les redéfinitions de types. Celles qui ne sont pas
5624 spécifiées sont configurées dynamiquement selon l'architecture. Évitez
5625 surtout de compter sur la taille d'un pointeur si vous voulez un
5626 programme portable ! Un pointeur sur un Alpha fait 8 octets, mais il
5627 en fait 4 sur un Intel.
5636 unsigned char guchar;
5637 unsigned short gushort;
5638 unsigned long gulong;
5643 long double gldouble;
5655 <sect1>Listes doublement chaînées
5657 Les fonctions suivantes servent à créer, gérer et détruire des listes
5658 doublement chaînées. Je suppose que vous savez ce qu'est une liste
5659 chaînée car leur explication n'entre pas dans le cadre de ce
5660 document. Bien sûr, il n'y a pas besoin de les connaître pour une
5661 utilisation générale de GTK, mais c'est bien de savoir comment elles
5665 GList* g_list_alloc (void);
5667 void g_list_free (GList *list);
5669 void g_list_free_1 (GList *list);
5671 GList* g_list_append (GList *list,
5674 GList* g_list_prepend (GList *list,
5677 GList* g_list_insert (GList *list,
5681 GList* g_list_remove (GList *list,
5684 GList* g_list_remove_link (GList *list,
5687 GList* g_list_reverse (GList *list);
5689 GList* g_list_nth (GList *list,
5692 GList* g_list_find (GList *list,
5695 GList* g_list_last (GList *list);
5697 GList* g_list_first (GList *list);
5699 gint g_list_length (GList *list);
5701 void g_list_foreach (GList *list,
5703 gpointer user_data);
5707 <sect1>Listes simplement chaînées
5709 La plupart des fonctions pour les listes simplement chaînées
5710 ci-dessous sont identiques à celles vues plus haut. Voici une liste
5714 GSList* g_slist_alloc (void);
5716 void g_slist_free (GSList *list);
5718 void g_slist_free_1 (GSList *list);
5720 GSList* g_slist_append (GSList *list,
5723 GSList* g_slist_prepend (GSList *list,
5726 GSList* g_slist_insert (GSList *list,
5730 GSList* g_slist_remove (GSList *list,
5733 GSList* g_slist_remove_link (GSList *list,
5736 GSList* g_slist_reverse (GSList *list);
5738 GSList* g_slist_nth (GSList *list,
5741 GSList* g_slist_find (GSList *list,
5744 GSList* g_slist_last (GSList *list);
5746 gint g_slist_length (GSList *list);
5748 void g_slist_foreach (GSList *list,
5750 gpointer user_data);
5754 <sect1>Gestion de la mémoire
5757 gpointer g_malloc (gulong size);
5760 Remplace <em/malloc()/. On n'a pas besoin de vérifier la valeur de
5761 retour car cela est fait pour nous dans cette fonction.
5764 gpointer g_malloc0 (gulong size);
5767 Identique à la précédente, mais initialise la mémoire à zéro avant de
5768 retourner un pointeur vers la zone réservée.
5771 gpointer g_realloc (gpointer mem,
5775 Réalloue <em/size/ octets de mémoire à partir de <em/mem/. Évidemment,
5776 la mémoire doit avoir été allouée auparavant.
5779 void g_free (gpointer mem);
5782 Libère la mémoire. Facile.
5785 void g_mem_profile (void);
5788 Produit un profil de la mémoire utilisée, mais requiert l'ajout de
5789 <em/#define MEM_PROFILE/ au début de <em>glib/gmem.c</em>,
5790 de refaire un <em/make/ et un <em/make install/.
5793 void g_mem_check (gpointer mem);
5796 Vérifie qu'un emplacement mémoire est valide. Nécessite que l'on
5797 ajoute <em/#define MEM_CHECK/ au début de <em/gmem.c/ que l'on refasse
5798 un <em/make/ et un <em/make install/.
5802 Fonctions des timers...
5805 GTimer* g_timer_new (void);
5807 void g_timer_destroy (GTimer *timer);
5809 void g_timer_start (GTimer *timer);
5811 void g_timer_stop (GTimer *timer);
5813 void g_timer_reset (GTimer *timer);
5815 gdouble g_timer_elapsed (GTimer *timer,
5816 gulong *microseconds);
5819 <sect1>Gestion des chaînes
5821 Un ensemble complet de fonction de gestion des chaînes. Elles semblent
5822 toutes très intéressantes et sont sûrement meilleures, à bien des
5823 égards, que les fonctions C standards, mais elle nécessitent de la
5827 GString* g_string_new (gchar *init);
5828 void g_string_free (GString *string,
5831 GString* g_string_assign (GString *lval,
5834 GString* g_string_truncate (GString *string,
5837 GString* g_string_append (GString *string,
5840 GString* g_string_append_c (GString *string,
5843 GString* g_string_prepend (GString *string,
5846 GString* g_string_prepend_c (GString *string,
5849 void g_string_sprintf (GString *string,
5853 void g_string_sprintfa (GString *string,
5858 <sect1>Utilitaires et fonctions d'erreurs
5861 gchar* g_strdup (const gchar *str);
5864 Remplace la fonction <em/strdup/. Elle copie le contenu de la chaîne
5865 d'origine dans la mémoire venant d'être allouée et retourne un
5866 pointeur sur cette zone.
5869 gchar* g_strerror (gint errnum);
5872 Je recommande de l'utiliser pour tous les messages d'erreur. Elle est
5873 beaucoup plus propre et plus portable que <em/perror()/ ou les
5874 autres. La sortie est habituellement de la forme :
5877 nom du programme:fonction qui a échoué:fichier ou autre descripteur:strerror
5880 Voici un exemple d'appel utilisé dans le programme « Bonjour tout le monde ! » :
5883 g_print("bonjour_monde:open:%s:%s\n", filename, g_strerror(errno));
5887 void g_error (gchar *format, ...);
5890 Affiche un message d'erreur. Le format est comme <em/printf/, mais il
5891 ajoute « ** ERROR **: » au début du message et sort du programme. À
5892 n'utiliser que pour les erreurs fatales.
5895 void g_warning (gchar *format, ...);
5898 Comme au dessus, mais ajoute « ** WARNING **: », et ne termine pas le
5902 void g_message (gchar *format, ...);
5904 Affiche « message: » avant la chaîne passée en paramètre.
5907 void g_print (gchar *format, ...);
5910 Remplace <em/printf()/.
5912 Enfin la dernière fonction :
5915 gchar* g_strsignal (gint signum);
5918 Affiche le nom du signal système Unix correspondant au numéro de
5919 signal. Utile pour les fonctions génériques de gestion de signaux.
5921 Tout ce qui est ci-dessus est plus ou moins volé à <em/glib.h/. Si
5922 quelqu'un s'occupe de documenter une fonction, qu'il m'envoit un
5926 <sect>Fichiers rc de GTK
5928 GTK a sa propre méthode pour gérer les configurations par défaut des
5929 applications, en utilisant des fichiers <tt/rc/. Ceux-ci peuvent être
5930 utilisés pour configurer les couleurs de presque tous les widgets, et
5931 pour mettre des pixmaps sur le fond de certains widgets.
5933 <sect1>Fonctions pour les fichiers rc
5935 Au démarrage de votre application, ajoutez un appel à :
5937 void gtk_rc_parse (char *filename);
5940 en lui passant le nom de votre fichier rc. Ceci forcera GTK à analyser
5941 ce fichier et à utiliser les configurations de styles pour les types
5942 de widgets qui y sont définis.
5944 Si vous voulez avoir un ensemble particulier de widgets qui prenne le
5945 pas sur le style des autres, ou une autre division logique de widgets,
5946 utilisez un appel à :
5948 void gtk_widget_set_name (GtkWidget *widget,
5952 En lui passant comme premier paramètre le widget que vous avez créé,
5953 et le nom que vous voulez lui donner comme second paramètre. Ceci vous
5954 permettra de changer les attributs de ce widget par son nom dans le
5957 Si vous utilisez un appel comme celui-ci :
5960 button = gtk_button_new_with_label ("Bouton Spécial");
5961 gtk_widget_set_name (button, "bouton special");
5964 Ce bouton s'appelle « bouton special » et peut être accédé par son nom
5965 dans le fichier rc en tant que « bouton special.GtkButton ». [<---
5968 Le fichier rc ci-dessous configure les propriétés de la fenêtre
5969 principale et fait hériter tous les fils de celle-ci du style décrit
5970 par « bouton_principal ». Le code utilisé dans l'application
5974 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
5975 gtk_widget_set_name (window, "fenetre principale");
5978 Et le style est défini dans le fichier rc avec :
5981 widget "fenetre principale.*GtkButton*" style "bouton_principal"
5984 Ce qui configure tous les widgets <em/GtkButton/ de « fenêtre
5985 principale » avec le style « bouton_principal » défini dans le fichier
5988 Ainsi que vous pouvez le voir, il s'agit d'un système puissant et
5989 flexible. Utilisez votre imagination pour en tirer le meilleur.
5991 <sect1>Format des fichiers rc de GTK
5993 Le format du fichier GTK est illustré dans l'exemple suivant. Il
5994 s'agit du fichier <em/testgtkrc/ de la distribution GTK mais j'ai
5995 ajouté quelques commentaires et autres choses. Vous pouvez inclure
5996 cette explication à votre application pour permettre à l'utilisateur
5997 de régler finement son application.
5999 Il y a plusieurs directives pour changer les attributs d'un widget.
6001 <item>fg - configure la couleur de premier plan d'un widget.
6002 <item>bg - configure la couleur d'arrière plan d'un widget.
6003 <item>bg_pixmap - configure l'arrière plan d'un widget avec un pixmap.
6004 <item>font - configure la fonte à utiliser pour un widget.
6007 De plus, un widget peut se trouver dans différents états et l'on peut
6008 configurer des couleurs, pixmaps et fontes différentes pour chacun
6009 d'eux. Ces états sont :
6011 <item>NORMAL - L'état normal d'un widget, sans la souris au dessus de
6012 lui, non pressé, etc.
6013 <item>PRELIGHT - Lorsque la souris se trouve au dessus du widget, les
6014 couleurs définies pour cet état sont actives.
6015 <item>ACTIVE - Lorsque le widget est pressé ou cliqué, il devient
6016 actif et les attributs associés à cet état sont appliqués.
6017 <item>INSENSITIVE - Quand un widget est configuré pour être
6018 insensible et qu'il ne peut être activé, il prend ces attributs.
6019 <item>SELECTED - Lorsqu'un objet est choisi, il prend ces attributs.
6022 Lorsqu'on utilise les mots-clés « <em/fg/ » et « <em/bg/ » pour
6023 configurer les couleurs des widgets, le format est :
6025 <tscreen><verb> fg[<STATE>] = { Red, Green, Blue } </verb></tscreen>
6027 Où STATE est l'un des états vus plus haut (PRELIGHT, ACTIVE etc), et
6028 où <em/Red/, <em/Green/ et <em/Blue/ sont des valeurs comprises entre
6029 0 et 1.0. { 1.0, 1.0, 1.0 } représente la couleur blanche. Ces
6030 valeurs doivent être de type réel ou elles seront considérées comme
6031 valant 0, ainsi un simple « 1 » ne marchera pas, il faut mettre « 1.0
6032 ». Un « 0 » simple convient car ce n'est pas un problème s'il n'est
6033 pas reconnu puisque toutes les valeurs non reconnues sont mises à 0.
6035 <em/bg_pixmap/ est très similaire, sauf que les couleurs sont
6036 remplacées par un nom de fichier.
6038 <em/pixmap_path/ est une liste de chemins séparés par des « : ». Ces
6039 chemins seront parcourus pour chaque pixmap que l'on spécifie.
6042 La directive <em/font/ est simplement :
6044 font = "<font name>"
6047 Où la seule partie difficile est d'arriver à comprendre la chaîne
6048 contenant le nom de la fonte. L'utilisation de <em/xfontsel/ ou d'un
6049 autre utilitaire semblable peut aider.
6051 « <em/widget_class/ » configure le style d'une classe de widgets. Ces
6052 classes sont listées dans la section sur la hiérarchie des widgets.
6054 La directive « <em/widget/ » configure un ensemble spécifique de
6055 widgets selon un style donné, annulant tout style de configuration
6056 pour la classe de widget donnée. Ces widgets sont enregistrés dans
6057 l'application en utilisant l'appel <em/gtk_widget_set_name()/. Ceci
6058 vous permet de spécifier les attributs d'un widget, widget par widget,
6059 au lieu de configurer les attributs d'une classe entière de
6060 widgets. Je vous demande instamment de documenter tous ces widgets
6061 spéciaux pour que les utilisateurs puisse les adapter à leurs besoins.
6063 Lorsque le mot-clé « <em/parent/ » est utilisé comme attribut, le
6064 widget prendra les attributs de son parent dans l'application.
6066 Lorsqu'on définit un style, on peut assigner les attributs d'un style
6067 déjà défini à ce nouveau style.
6070 style "bouton_principal" = "button"
6072 font = "-adobe-helvetica-medium-r-normal--*-100-*-*-*-*-*-*"
6073 bg[PRELIGHT] = { 0.75, 0, 0 }
6077 Cet exemple prend le style "button" et crée un nouveau style
6078 "bouton_principal"en changeant simplement la fonte et la couleur de
6079 fond pour l'état PRELIGHT.
6081 Bien sûr, un bon nombre de ces attributs ne s'applique pas à tous les
6082 widgets. C'est une question de bon sens. Tout ce qui peut s'appliquer
6085 <sect1>Exemple de fichier rc
6089 # pixmap_path "<dir 1>:<dir 2>:<dir 3>:..."
6091 pixmap_path "/usr/include/X11R6/pixmaps:/home/imain/pixmaps"
6093 # style <name> [= <name>]
6098 # widget <widget_set> style <style_name>
6099 # widget_class <widget_class_set> style <style_name>
6102 # Voici une liste des états possibles. Remarquez que certains ne s'appliquent
6103 # pas à certains widgets.
6105 # NORMAL - L'état normal d'un widget, sans la souris au dessus de lui,
6108 # PRELIGHT - Lorsque la souris se trouve au dessus du widget, les couleurs
6109 # définies pour cet état sont actives.
6111 # ACTIVE - Lorsque le widget est pressé ou cliqué, il devient actif et les
6112 # attributs associés à cet état sont appliqués.
6114 # INSENSITIVE - Quand un widget est configuré pour être insensible, et qu'il
6115 # ne peut être activé, il prend ces attributs.
6117 # SELECTED - Lorsqu'un objet est choisi, il prend ces attributs.
6119 # Avec ces états, on peut configurer les attributs des widgets dans chacun
6120 # de ces états en utilisant les directives suivantes.
6122 # fg - configure la couleur de premier plan d'un widget.
6123 # bg - configure la couleur d'arrière plan d'un widget.
6124 # bg_pixmap - configure l'arrière plan d'un widget avec un pixmap.
6125 # font - configure la fonte à utiliser pour un widget.
6127 # Configuration d'un style appelé "button". Le nom n'est pas important
6128 # car il est assigné aux widgets réels à la fin du fichier.
6132 #Configure l'espace autour de la fenêtre avec le pixmap spécifié.
6133 #bg_pixmap[<STATE>] = "<pixmap filename>"
6134 bg_pixmap[NORMAL] = "warning.xpm"
6139 #Configure la couleur de premier plan (celle de la fonte) à rouge
6140 #lorsqu'on est dans l'état "NORMAL".
6142 fg[NORMAL] = { 1.0, 0, 0 }
6144 #Configure le pixmap d'arrière plan de ce widget à celui de son parent.
6145 bg_pixmap[NORMAL] = "<parent>"
6150 # Voici tous les états possibles pour un bouton. Le seul qui ne peut
6151 # s'appliquer est l'état SELECTED.
6153 fg[PRELIGHT] = { 0, 1.0, 1.0 }
6154 bg[PRELIGHT] = { 0, 0, 1.0 }
6155 bg[ACTIVE] = { 1.0, 0, 0 }
6156 fg[ACTIVE] = { 0, 1.0, 0 }
6157 bg[NORMAL] = { 1.0, 1.0, 0 }
6158 fg[NORMAL] = { .99, 0, .99 }
6159 bg[INSENSITIVE] = { 1.0, 1.0, 1.0 }
6160 fg[INSENSITIVE] = { 1.0, 0, 1.0 }
6163 # Dans cet exemple, on hérite des attributs du style "button" puis on
6164 # écrase la fonte et la couleur de fond pour créer un nouveau style
6167 style "main_button" = "button"
6169 font = "-adobe-helvetica-medium-r-normal--*-100-*-*-*-*-*-*"
6170 bg[PRELIGHT] = { 0.75, 0, 0 }
6173 style "toggle_button" = "button"
6175 fg[NORMAL] = { 1.0, 0, 0 }
6176 fg[ACTIVE] = { 1.0, 0, 0 }
6178 # Configure le pixmap de fond du toggle_button à celui de son widget
6179 # parent (comme défini dans l'application).
6180 bg_pixmap[NORMAL] = "<parent>"
6185 bg_pixmap[NORMAL] = "marble.xpm"
6186 fg[NORMAL] = { 1.0, 1.0, 1.0 }
6191 font = "-adobe-helvetica-medium-r-normal--*-80-*-*-*-*-*-*"
6194 # pixmap_path "~/.pixmaps"
6196 # Configuration des types de widget pour utiliser les styles définis
6198 # Les types de widget sont listés dans la hiérarchie des classes, mais
6199 # peut probablement être listée dans ce document pour que l'utilisateur
6200 # puisse s'y référer.
6202 widget_class "GtkWindow" style "window"
6203 widget_class "GtkDialog" style "window"
6204 widget_class "GtkFileSelection" style "window"
6205 widget_class "*Gtk*Scale" style "scale"
6206 widget_class "*GtkCheckButton*" style "toggle_button"
6207 widget_class "*GtkRadioButton*" style "toggle_button"
6208 widget_class "*GtkButton*" style "button"
6209 widget_class "*Ruler" style "ruler"
6210 widget_class "*GtkText" style "text"
6212 # Configure tous les boutons fils de la "main window" avec le style
6213 # main_button. Ceci doit être documenté pour en tirer profit.
6214 widget "main window.*GtkButton*" style "main_button"
6217 <sect>Écriture de vos propres widgets
6219 <sect1>Vue d'ensemble
6221 Bien que la distribution GTK fournisse de nombreux types de widgets
6222 qui devraient couvrir la plupart des besoins de base, il peut arriver
6223 un moment où vous aurez besoin de créer votre propre type de
6224 widget. Comme GTK utilise l'héritage de widget de façon intensive et
6225 qu'il y a déjà un widget ressemblant à celui que vous voulez, il est
6226 souvent possible de créer un nouveau type de widget en seulement
6227 quelques lignes de code. Mais, avant de travailler sur un nouveau
6228 widget, il faut vérifier d'abord que quelqu'un ne l'a pas déjà
6229 écrit. Ceci éviter la duplication des efforts et maintient au minimum
6230 le nombre de widgets, ce qui permet de garder la cohérence du code et
6231 de l'interface des différentes applications. Un effet de bord est que,
6232 lorsque l'on a créé un nouveau widget, il faut l'annoncer afin que les
6233 autres puissent en bénéficier. Le meilleur endroit pour faire cela
6234 est, sans doute, la <tt>gtk-list</tt>.
6236 <sect1>Anatomie d'un widget
6239 Afin de créer un nouveau widget, il importe de comprendre comment
6240 fonctionnent les objets GTK. Cette section ne se veut être qu'un
6241 rapide survol. Consultez la documentation de référence pour plus de
6245 Les widgets sont implantés selon une méthode orientée
6246 objet. Cependant, ils sont écrits en C standard. Ceci améliore
6247 beaucoup la portabilité et la stabilité, par contre cela signifie que
6248 celui qui écrit des widgets doit faire attention à certains détails
6249 d'implantation. Les informations communes à toutes les instances d'une
6250 classe de widget (tous les widgets boutons, par exemple) sont stockées
6251 dans la <em/structure de la classe/. Il n'y en a qu'une copie dans
6252 laquelle sont stockées les informations sur les signaux de la
6253 classe (fonctionnement identique aux fonctions virtuelles en C). Pour
6254 permettre l'héritage, le premier champ de la structure de classe doit
6255 être une copie de la structure de classe du père. La déclaration de la
6256 structure de classe de <em/GtkButton/ ressemble à ceci :
6259 struct _GtkButtonClass
6261 GtkContainerClass parent_class;
6263 void (* pressed) (GtkButton *button);
6264 void (* released) (GtkButton *button);
6265 void (* clicked) (GtkButton *button);
6266 void (* enter) (GtkButton *button);
6267 void (* leave) (GtkButton *button);
6272 Lorsqu'un bouton est traité comme un container (par exemple, lorsqu'il
6273 change de taille), sa structure de classe peut être convertie en
6274 <em/GtkContainerClass/ et les champs adéquats utilisés pour gérer les
6278 Il y a aussi une structure pour chaque widget créé sur une base
6279 d'instance. Cette structure a des champs pour stocker les informations
6280 qui sont différentes pour chaque instance du widget. Nous l'appelerons
6281 <em/structure d'objet/. Pour la classe <em/Button/, elle ressemble
6287 GtkContainer container;
6291 guint in_button : 1;
6292 guint button_down : 1;
6297 Notez que, comme pour la structure de classe, le premier champ est la
6298 structure d'objet de la classe parente, cette structure peut donc être
6299 convertie dans la structure d'objet de la classe parente si besoin
6302 <sect1> Création d'un widget composé
6304 <sect2> Introduction
6307 Un type de widget qui peut être intéressant à créer est un widget qui
6308 est simplement un agrégat d'autres widgets GTK. Ce type de widget ne
6309 fait rien qui ne pourrait être fait sans créer de nouveaux widgets,
6310 mais offre une méthode pratique pour empaqueter les éléments d'une
6311 interface utilisateur afin de la réutiliser facilement. Les widgets
6312 <em/FileSelection/ et <em/ColorSelection/ de la distribution standard
6313 sont des exemples de ce type de widget.
6316 L'exemple de widget que nous allons créer dans cette section créera un
6317 widget <em/Tictactoe/, un tableau de 3x3 boutons commutateurs qui
6318 déclenche un signal lorsque tous les boutons d'une ligne, d'une
6319 colonne, ou d'une diagonale sont pressés.
6321 <sect2> Choix d'une classe parent
6324 La classe parent d'un widget composé est, typiquement, la classe
6325 container contenant tous les éléments du widget composé. Par exemple,
6326 la classe parent du widget <em/FileSelection/ est la classe
6327 <em/Dialog/. Comme nos boutons seront mis sous la forme d'un tableau,
6328 il semble naturel d'utiliser la classe <em/GtkTable/ comme
6329 parent. Malheureusement, cela ne peut marcher. La création d'un widget
6330 est divisée en deux fonctions -- <em/WIDGETNAME_new()/ que
6331 l'utilisateur appelle, et <em/WIDGETNAME_init()/ qui réalise le
6332 travail d'initialisation du widget indépendamment des paramètre passés
6333 à la fonction <tt/_new()/. Les widgets fils n'appellent que la
6334 fonction <em/_init/ de leur widget parent. Mais cette division du
6335 travail ne fonctionne pas bien avec les tableaux qui, lorsqu'ils sont
6336 créés, ont besoin de connaître leue nombre de lignes et de
6337 colonnes. Sauf à dupliquer la plupart des fonctionnalités de
6338 <em/gtk_table_new()/ dans notre widget <em/Tictactoe/, nous ferions
6339 mieux d'éviter de le dériver de <em/GtkTable/. Pour cette raison, nous
6340 la dériverons plutôt de <em/GtkVBox/ et nous placerons notre table
6343 <sect2> The header file
6346 Chaque classe de widget possède un fichier en-tête qui déclare les
6347 structures d'objet et de classe pour ce widget, en plus de fonctions
6348 publiques. Quelques caractéristiques méritent d'être indiquées. Afin
6349 d'éviter des définitions multiples, on enveloppe le fichier en-tête
6353 #ifndef __TICTACTOE_H__
6354 #define __TICTACTOE_H__
6358 #endif /* __TICTACTOE_H__ */
6361 Et, pour faire plaisir aux programmes C++ qui inclueront ce fichier, on l'enveloppe aussi dans :
6366 #endif /* __cplusplus */
6372 #endif /* __cplusplus */
6375 En plus des fonctions et structures, nous déclarons trois macros
6376 standard, <tt/TICTACTOE(obj)/, <tt/TICTACTOE_CLASS(class)/, et
6377 <tt/IS_TICTACTOE(obj)/, qui, respectivement, convertissent un pointeur
6378 en un pointeur vers une structure d'objet ou de classe, et vérifient
6379 si un objet est un widget Tictactoe.
6382 Voici le fichier en-tête complet :
6386 #ifndef __TICTACTOE_H__
6387 #define __TICTACTOE_H__
6389 #include <gdk/gdk.h>
6390 #include <gtk/gtkvbox.h>
6394 #endif /* __cplusplus */
6396 #define TICTACTOE(obj) GTK_CHECK_CAST (obj, tictactoe_get_type (), Tictactoe)
6397 #define TICTACTOE_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, tictactoe_get_type (), TictactoeClass)
6398 #define IS_TICTACTOE(obj) GTK_CHECK_TYPE (obj, tictactoe_get_type ())
6401 typedef struct _Tictactoe Tictactoe;
6402 typedef struct _TictactoeClass TictactoeClass;
6408 GtkWidget *buttons[3][3];
6411 struct _TictactoeClass
6413 GtkVBoxClass parent_class;
6415 void (* tictactoe) (Tictactoe *ttt);
6418 guint tictactoe_get_type (void);
6419 GtkWidget* tictactoe_new (void);
6420 void tictactoe_clear (Tictactoe *ttt);
6424 #endif /* __cplusplus */
6426 #endif /* __TICTACTOE_H__ */
6430 <sect2>La fonction <tt/_get_type()/
6433 Continuons maintenant avec l'implantation de notre widget. La fonction
6434 centrale pour chaque widget est <em/WIDGETNAME_get_type()/. Cette
6435 fonction, lorsqu'elle est appelée pour la première fois, informe le
6436 GTK de la classe et récupère un ID permettant d'identifier celle-ci de
6437 façon unique. Lors des appels suivants, elle ne fait que retourner cet
6442 tictactoe_get_type ()
6444 static guint ttt_type = 0;
6448 GtkTypeInfo ttt_info =
6452 sizeof (TictactoeClass),
6453 (GtkClassInitFunc) tictactoe_class_init,
6454 (GtkObjectInitFunc) tictactoe_init,
6458 ttt_type = gtk_type_unique (gtk_vbox_get_type (), &ttt_info);
6466 La structure <em/GtkTypeInfo/ est définie de la façon suivante :
6474 GtkClassInitFunc class_init_func;
6475 GtkObjectInitFunc object_init_func;
6476 GtkArgFunc arg_func;
6481 Les champs de cette structure s'expliquent d'eux-mêmes. Nous
6482 ignorerons le champ <em/arg_func/ ici : il a un rôle important
6483 permettant aux options des widgets d'être correctement initialisées à
6484 partir des langages interprétés, mais cette fonctionnalité est encore
6485 très peu implantée. Lorsque GTK dispose d'une copie correctement
6486 remplie de cette structure, il sait comment créer des objets d'un type
6487 particulier de widget.
6489 <sect2>La fonction <em/_class_init()/
6492 La fonction <em/WIDGETNAME_class_init()/ initialise les champs de la
6493 structure de classe du widget et configure tous les signaux de cette
6494 classe. Pour notre widget Tictactoe, cet appel est :
6503 static gint tictactoe_signals[LAST_SIGNAL] = { 0 };
6506 tictactoe_class_init (TictactoeClass *class)
6508 GtkObjectClass *object_class;
6510 object_class = (GtkObjectClass*) class;
6512 tictactoe_signals[TICTACTOE_SIGNAL] = gtk_signal_new ("tictactoe",
6515 GTK_SIGNAL_OFFSET (TictactoeClass, tictactoe),
6516 gtk_signal_default_marshaller, GTK_ARG_NONE, 0);
6519 gtk_object_class_add_signals (object_class, tictactoe_signals, LAST_SIGNAL);
6521 class->tictactoe = NULL;
6526 Notre widget n'a qu'un signal : "tictactoe", invoqué lorsqu'une
6527 ligne, une colonne ou une diagonale est complètement remplie. Tous les
6528 widgets composés n'ont pas besoin de signaux. Si vous lisez ceci pour
6529 la première fois, vous pouvez passer directement à la section suivante
6530 car les choses vont se compliquer un peu
6535 gint gtk_signal_new (gchar *name,
6536 GtkSignalRunType run_type,
6538 gint function_offset,
6539 GtkSignalMarshaller marshaller,
6540 GtkArgType return_val,
6545 crée un nouveau signal. Les paramètres sont :
6548 <item> <em/name/ : Le nom du signal signal.
6549 <item> <em/run_type/ : Indique si le gestionnaire par défaut doit être
6550 lancé avant ou après le gestionnaire de l'utilisateur. Le plus
6551 souvent, ce sera <tt/GTK_RUN_FIRST/, ou <tt/GTK_RUN_LAST/, bien qu'il
6552 y ait d'autres possibilités.
6554 <item> <em/object_type/ : L'ID de l'objet auquel s'applique ce signal
6555 (il s'appliquera aussi au descendants de l'objet).
6557 <item> <em/function_offset/ : L'offset d'un pointeur vers le
6558 gestionnaire par défaut dans la structure de classe.
6560 <item> <em/marshaller/ : Fonction utilisée pour invoquer le
6561 gestionnaire de signal. Pour les gestionnaires de signaux n'ayant pas
6562 d'autres paramètres que l'objet émetteur et les données utilisateur,
6563 on peut utiliser la fonction prédéfinie
6564 <em/gtk_signal_default_marshaller()/.
6566 <item> <em/return_val/ : Type de la valeur retournée.
6568 <item> <em/nparams/ : Nombre de paramètres du gestionnaire de signal
6569 (autres que les deux par défaut mentionnés plus haut).
6571 <item> <em/.../ : Types des paramètres.
6574 Lorsque l'on spécifie les types, on utilise l'énumération
6575 <em/GtkArgType/ :
6594 <em/gtk_signal_new()/ retourne un identificateur entier pour le
6595 signal, que l'on stocke dans le tableau <em/tictactoe_signals/, indicé
6596 par une énumération (conventionnellement, les éléments de
6597 l'énumération sont le nom du signal, en majuscules, mais, ici, il y
6598 aurait un conflit avec la macro <tt/TICTACTOE()/, nous l'appellerons
6599 donc <tt/TICTACTOE_SIGNAL/ à la place.
6601 Après avoir créé nos signaux, nous devons demander à GTK d'associer
6602 ceux-ci à la classe Tictactoe. Ceci est fait en appelant
6603 <em/gtk_object_class_add_signals()/. Puis nous configurons le pointeur
6604 qui pointe sur le gestionnaire par défaut du signal "tictactoe" à
6605 NULL, pour indiquer qu'il n'y a pas d'action par défaut.
6607 <sect2>La fonction <em/_init()/
6610 Chaque classe de widget a aussi besoin d'une fonction pour initialiser
6611 la structure d'objet. Habituellement, cette fonction a le rôle, plutôt
6612 limité, d'initialiser les champs de la structure avec des valeurs par
6613 défaut. Cependant, pour les widgets composés, cette fonction crée
6614 aussi les widgets composants.
6619 tictactoe_init (Tictactoe *ttt)
6624 table = gtk_table_new (3, 3, TRUE);
6625 gtk_container_add (GTK_CONTAINER(ttt), table);
6626 gtk_widget_show (table);
6631 ttt->buttons[i][j] = gtk_toggle_button_new ();
6632 gtk_table_attach_defaults (GTK_TABLE(table), ttt->buttons[i][j],
6634 gtk_signal_connect (GTK_OBJECT (ttt->buttons[i][j]), "toggled",
6635 GTK_SIGNAL_FUNC (tictactoe_toggle), ttt);
6636 gtk_widget_set_usize (ttt->buttons[i][j], 20, 20);
6637 gtk_widget_show (ttt->buttons[i][j]);
6642 <sect2> Et le reste...
6645 Il reste une fonction que chaque widget (sauf pour les types widget de
6646 base, comme <em/GtkBin/, qui ne peuvent être instanciés) à besoin
6647 d'avoir -- celle que l'utilisateur appelle pour créer un objet de ce
6648 type. Elle est conventionnellement appelée <em/WIDGETNAME_new()/. Pour
6649 certains widgets, par pour ceux de Tictactoe, cette fonction prend des
6650 paramètres et réalise certaines initialisations dépendantes des
6651 paramètres. Les deux autres fonctions sont spécifiques au widget
6655 <em/tictactoe_clear()/ est une fonction publique qui remet tous les
6656 boutons du widget en position relâchée. Notez l'utilisation de
6657 <em/gtk_signal_handler_block_by_data()/ pour empêcher notre
6658 gestionnaire de signaux des boutons commutateurs d'être déclenché sans
6662 <em/tictactoe_toggle()/ est le gestionnaire de signal invoqué
6663 lorsqu'on clique sur un bouton. Il vérifie s'il y a des combinaisons
6664 gagnantes concernant le bouton qui vient d'être commuté et, si c'est
6665 le cas, émet le signal "tictactoe".
6671 return GTK_WIDGET ( gtk_type_new (tictactoe_get_type ()));
6675 tictactoe_clear (Tictactoe *ttt)
6682 gtk_signal_handler_block_by_data (GTK_OBJECT(ttt->buttons[i][j]), ttt);
6683 gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (ttt->buttons[i][j]),
6685 gtk_signal_handler_unblock_by_data (GTK_OBJECT(ttt->buttons[i][j]), ttt);
6690 tictactoe_toggle (GtkWidget *widget, Tictactoe *ttt)
6694 static int rwins[8][3] = { { 0, 0, 0 }, { 1, 1, 1 }, { 2, 2, 2 },
6695 { 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 },
6696 { 0, 1, 2 }, { 0, 1, 2 } };
6697 static int cwins[8][3] = { { 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 },
6698 { 0, 0, 0 }, { 1, 1, 1 }, { 2, 2, 2 },
6699 { 0, 1, 2 }, { 2, 1, 0 } };
6710 success = success &&
6711 GTK_TOGGLE_BUTTON(ttt->buttons[rwins[k][i]][cwins[k][i]])->active;
6713 ttt->buttons[rwins[k][i]][cwins[k][i]] == widget;
6716 if (success && found)
6718 gtk_signal_emit (GTK_OBJECT (ttt),
6719 tictactoe_signals[TICTACTOE_SIGNAL]);
6728 Enfin, un exemple de programme utilisant notre widget Tictactoe
6731 #include <gtk/gtk.h>
6732 #include "tictactoe.h"
6734 /* Invoqué lorsqu'une ligne, une colonne ou une diagonale est complète */
6736 void win (GtkWidget *widget, gpointer data)
6738 g_print ("Ouais !\n");
6739 tictactoe_clear (TICTACTOE (widget));
6742 int main (int argc, char *argv[])
6747 gtk_init (&argc, &argv);
6749 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
6751 gtk_window_set_title (GTK_WINDOW (window), "Aspect Frame");
6753 gtk_signal_connect (GTK_OBJECT (window), "destroy",
6754 GTK_SIGNAL_FUNC (gtk_exit), NULL);
6756 gtk_container_border_width (GTK_CONTAINER (window), 10);
6758 /* Création d'un widget Tictactoe */
6759 ttt = tictactoe_new ();
6760 gtk_container_add (GTK_CONTAINER (window), ttt);
6761 gtk_widget_show (ttt);
6763 /* On lui attache le signal "tictactoe" */
6764 gtk_signal_connect (GTK_OBJECT (ttt), "tictactoe",
6765 GTK_SIGNAL_FUNC (win), NULL);
6767 gtk_widget_show (window);
6776 <sect1> Création d'un widget à partir de zéro
6778 <sect2> Introduction
6782 Dans cette section, nous en apprendrons plus sur la façon dont les
6783 widgets s'affichent eux-mêmes à l'écran et comment ils interagissent
6784 avec les événements. Comme exemple, nous créerons un widget d'appel
6785 télephonique interactif avec un pointeur que l'utilisateur pourra
6786 déplacer pour initialiser la valeur.
6788 <sect2>Afficher un widget à l'écran
6790 Il y a plusieurs étapes mises en jeu lors de l'affichage. Lorsque le widget est
6791 créé par l'appel <em/WIDGETNAME_new()/, plusieurs autres fonctions
6792 supplémentaires sont requises.
6795 <item> <em/WIDGETNAME_realize()/ s'occupe de créer une fenêtre X pour le
6796 widget, s'il en a une.
6797 <item> <em/WIDGETNAME_map()/ est invoquée après l'appel de
6798 <em/gtk_widget_show()/. Elle s'assure que le widget est bien tracé à l'écran
6799 (<em/mappé/). Dans le cas d'une classe container, elle doit aussi appeler des
6800 fonctions <em/map()/> pour chaque widget fils.
6801 <item> <em/WIDGETNAME_draw()/ est invoquée lorsque <em/gtk_widget_draw()/ est
6802 appelé pour le widget ou l'un de ces ancêtres. Elle réalise les véritables
6803 appels aux fonctions de dessin pour tracer le widget à l'écran. Pour les
6804 widgets containers, cette fonction doit appeler <em/gtk_widget_draw()/ pour ses
6806 <item> <em/WIDGETNAME_expose()/ est un gestionnaire pour les événements
6807 d'exposition du widget. Il réalise les appels nécessaires aux fonctions de
6808 dessin pour tracer la partie exposée à l'écran. Pour les widgets containers,
6809 cette fonction doit générer les événements d'exposition pour ses widgets
6810 enfants n'ayant pas leurs propres fenêtres (s'ils ont leurs propres fenêtres, X
6811 génèrera les événements d'exposition nécessaires).
6815 Vous avez pu noter que les deux dernières fonctions sont assez similaires --
6816 chacune se charge de tracer le widget à l'écran. En fait, de nombreux types de
6817 widgets ne se préoccupent pas vraiment de la différence entre les deux. La
6818 fonction <em/draw()/ par défaut de la classe widget génère simplement un
6819 événement d'exposition synthétique pour la zone à redessiner. Cependant,
6820 certains types de widgets peuvent économiser du travail en faisant la
6821 différence entre les deux fonctions. Par exemple, si un widget a plusieurs
6822 fenêtres X et puisque les événements d'exposition identifient la fenêtre
6823 exposée, il peut redessiner seulement la fenêtre concernée, ce qui n'est pas
6824 possible avec des appels à <em/draw()/.
6827 Les widgets container, même s'ils ne se soucient pas eux-mêmes de la
6828 différence, ne peuvent pas utiliser simplement la fonction <em/draw()/ car
6829 leurs widgets enfants tiennent compte de cette différence. Cependant, ce serait
6830 du gaspillage de dupliquer le code de tracé pour les deux
6831 fonctions. Conventionnellement, de tels widgets possèdent une fonction nommée
6832 <em/WIDGETNAME_paint()/ qui réalise le véritable travail de tracé du widget et
6833 qui est appelée par les fonctions <tt/draw()/ et <tt/expose()/.
6836 Dans notre exemple, comme le widget d'appel n'est pas un widget container et
6837 n'a qu'une fenêtre, nous pouvons utiliser l'approche la plus simple :
6838 utiliser la fonction <em/draw()/ par défaut et n'implanter que la fonction
6841 <sect2>Origines du widget Dial
6844 Exactement comme les animaux terrestres ne sont que des variantes des premiers
6845 amphibiens qui rampèrent hors de la boue, les widgets GTK sont des variantes
6846 d'autres widgets, déjà écrits. Ainsi, bien que cette section s'appelle « créer
6847 un widget à partir de zéro », le widget Dial commence réellement avec le code
6848 source du widget Range. Celui-ci a été pris comme point de départ car ce serait
6849 bien que notre Dial ait la même interface que les widgets Scale qui ne sont que
6850 des descendants spécialisés du widget Range. Par conséquent, bien que le code
6851 source soit présenté ci-dessous sous une forme achevée, cela n'implique pas
6852 qu'il a été écrit <em/deus ex machina/. De plus, si vous ne savez pas comment
6853 fonctionnent les widgets Scale du point de vue du programmeur de l'application,
6854 il est préférable de les étudier avant de continuer.
6859 Un petite partie de notre widget devrait ressembler au widget Tictactoe. Nous
6860 avons d'abord le fichier en-tête :
6863 /* GTK - The GIMP Toolkit
6864 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
6866 * This library is free software; you can redistribute it and/or
6867 * modify it under the terms of the GNU Library General Public
6868 * License as published by the Free Software Foundation; either
6869 * version 2 of the License, or (at your option) any later version.
6871 * This library is distributed in the hope that it will be useful,
6872 * but WITHOUT ANY WARRANTY; without even the implied warranty of
6873 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
6874 * Library General Public License for more details.
6876 * You should have received a copy of the GNU Library General Public
6877 * License along with this library; if not, write to the Free
6878 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
6881 #ifndef __GTK_DIAL_H__
6882 #define __GTK_DIAL_H__
6884 #include <gdk/gdk.h>
6885 #include <gtk/gtkadjustment.h>
6886 #include <gtk/gtkwidget.h>
6891 #endif /* __cplusplus */
6894 #define GTK_DIAL(obj) GTK_CHECK_CAST (obj, gtk_dial_get_type (), GtkDial)
6895 #define GTK_DIAL_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, gtk_dial_get_type (), GtkDialClass)
6896 #define GTK_IS_DIAL(obj) GTK_CHECK_TYPE (obj, gtk_dial_get_type ())
6899 typedef struct _GtkDial GtkDial;
6900 typedef struct _GtkDialClass GtkDialClass;
6906 /* politique de mise à jour
6907 (GTK_UPDATE_[CONTINUOUS/DELAYED/DISCONTINUOUS]) */
6911 /* Le bouton qui est pressé, 0 si aucun */
6914 /* Dimensions des composants de dial */
6918 /* ID du timer de mise à jour, 0 si aucun */
6924 /* Anciennes valeurs d'ajustement stockées. On sait donc quand quelque
6930 /* L'objet ajustment qui stocke les données de cet appel */
6931 GtkAdjustment *adjustment;
6934 struct _GtkDialClass
6936 GtkWidgetClass parent_class;
6940 GtkWidget* gtk_dial_new (GtkAdjustment *adjustment);
6941 guint gtk_dial_get_type (void);
6942 GtkAdjustment* gtk_dial_get_adjustment (GtkDial *dial);
6943 void gtk_dial_set_update_policy (GtkDial *dial,
6944 GtkUpdateType policy);
6946 void gtk_dial_set_adjustment (GtkDial *dial,
6947 GtkAdjustment *adjustment);
6950 #endif /* __cplusplus */
6953 #endif /* __GTK_DIAL_H__ */
6957 Comme il y a plus de choses à faire avec ce widget par rapport à l'autre, nous
6958 avons plus de champs dans la structure de données, mais à part ça, les choses
6959 sont plutôt similaires.
6963 Puis, après avoir inclus les fichiers en-tête et déclaré quelques constantes,
6964 nous devons fournir quelques fonctions pour donner des informations sur le
6965 widget et pour l'initialiser :
6970 #include <gtk/gtkmain.h>
6971 #include <gtk/gtksignal.h>
6973 #include "gtkdial.h"
6975 #define SCROLL_DELAY_LENGTH 300
6976 #define DIAL_DEFAULT_SIZE 100
6978 /* Déclararations des prototypes */
6980 [ omis pour gagner de la place ]
6982 /* Données locales */
6984 static GtkWidgetClass *parent_class = NULL;
6987 gtk_dial_get_type ()
6989 static guint dial_type = 0;
6993 GtkTypeInfo dial_info =
6997 sizeof (GtkDialClass),
6998 (GtkClassInitFunc) gtk_dial_class_init,
6999 (GtkObjectInitFunc) gtk_dial_init,
7003 dial_type = gtk_type_unique (gtk_widget_get_type (), &dial_info);
7010 gtk_dial_class_init (GtkDialClass *class)
7012 GtkObjectClass *object_class;
7013 GtkWidgetClass *widget_class;
7015 object_class = (GtkObjectClass*) class;
7016 widget_class = (GtkWidgetClass*) class;
7018 parent_class = gtk_type_class (gtk_widget_get_type ());
7020 object_class->destroy = gtk_dial_destroy;
7022 widget_class->realize = gtk_dial_realize;
7023 widget_class->expose_event = gtk_dial_expose;
7024 widget_class->size_request = gtk_dial_size_request;
7025 widget_class->size_allocate = gtk_dial_size_allocate;
7026 widget_class->button_press_event = gtk_dial_button_press;
7027 widget_class->button_release_event = gtk_dial_button_release;
7028 widget_class->motion_notify_event = gtk_dial_motion_notify;
7032 gtk_dial_init (GtkDial *dial)
7035 dial->policy = GTK_UPDATE_CONTINUOUS;
7038 dial->pointer_width = 0;
7040 dial->old_value = 0.0;
7041 dial->old_lower = 0.0;
7042 dial->old_upper = 0.0;
7043 dial->adjustment = NULL;
7047 gtk_dial_new (GtkAdjustment *adjustment)
7051 dial = gtk_type_new (gtk_dial_get_type ());
7054 adjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
7056 gtk_dial_set_adjustment (dial, adjustment);
7058 return GTK_WIDGET (dial);
7062 gtk_dial_destroy (GtkObject *object)
7066 g_return_if_fail (object != NULL);
7067 g_return_if_fail (GTK_IS_DIAL (object));
7069 dial = GTK_DIAL (object);
7071 if (dial->adjustment)
7072 gtk_object_unref (GTK_OBJECT (dial->adjustment));
7074 if (GTK_OBJECT_CLASS (parent_class)->destroy)
7075 (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
7079 Notez que cette fonction <em/init()/ fait moins de choses que pour le widget
7080 Tictactoe car ce n'est pas un widget composé et que la fonction <em/new()/ en
7081 fait plus car elle a maintenant un paramètre. Notez aussi que lorsque nous
7082 stockons un pointeur vers l'objet Adjustement, nous incrémentons son nombre de
7083 références (et nous le décrémentons lorsque nous ne l'utilisons plus) afin que
7084 GTK puisse savoir quand il pourra être détruit sans danger.
7087 Il y a aussi quelques fonctions pour manipuler les options du widget :
7091 gtk_dial_get_adjustment (GtkDial *dial)
7093 g_return_val_if_fail (dial != NULL, NULL);
7094 g_return_val_if_fail (GTK_IS_DIAL (dial), NULL);
7096 return dial->adjustment;
7100 gtk_dial_set_update_policy (GtkDial *dial,
7101 GtkUpdateType policy)
7103 g_return_if_fail (dial != NULL);
7104 g_return_if_fail (GTK_IS_DIAL (dial));
7106 dial->policy = policy;
7110 gtk_dial_set_adjustment (GtkDial *dial,
7111 GtkAdjustment *adjustment)
7113 g_return_if_fail (dial != NULL);
7114 g_return_if_fail (GTK_IS_DIAL (dial));
7116 if (dial->adjustment)
7118 gtk_signal_disconnect_by_data (GTK_OBJECT (dial->adjustment), (gpointer) dial);
7119 gtk_object_unref (GTK_OBJECT (dial->adjustment));
7122 dial->adjustment = adjustment;
7123 gtk_object_ref (GTK_OBJECT (dial->adjustment));
7125 gtk_signal_connect (GTK_OBJECT (adjustment), "changed",
7126 (GtkSignalFunc) gtk_dial_adjustment_changed,
7128 gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed",
7129 (GtkSignalFunc) gtk_dial_adjustment_value_changed,
7132 dial->old_value = adjustment->value;
7133 dial->old_lower = adjustment->lower;
7134 dial->old_upper = adjustment->upper;
7136 gtk_dial_update (dial);
7140 <sect2> <em/gtk_dial_realize()/
7143 Nous arrivons maintenant à quelques nouveaux types de fonctions. D'abord, nous
7144 avons une fonction qui réalise la création de la fenêtre X. Notez que l'on
7145 passe un masque à la fonction <em/gdk_window_new()/ pour spécifier quels sont
7146 les champs de la structure GdkWindowAttr qui contiennent des données (les
7147 autres recevront des valeurs par défaut). Notez aussi la façon dont est créé le
7148 masque d'événement du widget. On appelle <em/gtk_widget_get_events()/ pour
7149 récupérer le masque d'événement que l'utilisateur a spécifié pour ce widget
7150 (avec <em/gtk_widget_set_events()/) et ajouter les événements qui nous
7154 Après avoir créé la fenêtre, nous configurons son style et son fond et mettons
7155 un pointeur vers le widget dans le champ user de la GdkWindow. Cette dernière
7156 étape permet à GTK de distribuer les événements pour cette fenêtre au widget
7161 gtk_dial_realize (GtkWidget *widget)
7164 GdkWindowAttr attributes;
7165 gint attributes_mask;
7167 g_return_if_fail (widget != NULL);
7168 g_return_if_fail (GTK_IS_DIAL (widget));
7170 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
7171 dial = GTK_DIAL (widget);
7173 attributes.x = widget->allocation.x;
7174 attributes.y = widget->allocation.y;
7175 attributes.width = widget->allocation.width;
7176 attributes.height = widget->allocation.height;
7177 attributes.wclass = GDK_INPUT_OUTPUT;
7178 attributes.window_type = GDK_WINDOW_CHILD;
7179 attributes.event_mask = gtk_widget_get_events (widget) |
7180 GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK |
7181 GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK |
7182 GDK_POINTER_MOTION_HINT_MASK;
7183 attributes.visual = gtk_widget_get_visual (widget);
7184 attributes.colormap = gtk_widget_get_colormap (widget);
7186 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
7187 widget->window = gdk_window_new (widget->parent->window, &attributes, attributes_mask);
7189 widget->style = gtk_style_attach (widget->style, widget->window);
7191 gdk_window_set_user_data (widget->window, widget);
7193 gtk_style_set_background (widget->style, widget->window, GTK_STATE_ACTIVE);
7197 <sect2>Négotiation de la taille
7200 Avant le premier affichage de la fenêtre contenant un widget et à chaque fois
7201 que la forme de la fenêtre change, GTK demande à chaque widget fils la taille
7202 qu'il désire avoir. Cette requête est gérée par la fonction
7203 <em/gtk_dial_size_request()/. Comme notre widget n'est pas un widget container,
7204 et n'a pas de contraintes réelles sur sa taille, nous ne faisons que retourner
7205 une valeur raisonnable par défaut.
7209 gtk_dial_size_request (GtkWidget *widget,
7210 GtkRequisition *requisition)
7212 requisition->width = DIAL_DEFAULT_SIZE;
7213 requisition->height = DIAL_DEFAULT_SIZE;
7218 Lorsque tous les widgets on demandé une taille idéale, le forme de la fenêtre
7219 est calculée et chaque widget fils est averti de sa taille. Habituellement, ce
7220 sera autant que la taille requise, mais si, par exemple, l'utilisateur a
7221 redimensionné la fenêtre, cette taille peut occasionnellement être plus petite
7222 que la taille requise. La notification de la taille est gérée par la fonction
7223 <em/gtk_dial_size_allocate()/. Notez qu'en même temps qu'elle calcule les
7224 tailles de certains composants pour une utilisation future, cette routine fait
7225 aussi le travail de base consistant à déplacer les widgets X Window dans leur
7226 nouvelles positions et tailles.
7230 gtk_dial_size_allocate (GtkWidget *widget,
7231 GtkAllocation *allocation)
7235 g_return_if_fail (widget != NULL);
7236 g_return_if_fail (GTK_IS_DIAL (widget));
7237 g_return_if_fail (allocation != NULL);
7239 widget->allocation = *allocation;
7240 if (GTK_WIDGET_REALIZED (widget))
7242 dial = GTK_DIAL (widget);
7244 gdk_window_move_resize (widget->window,
7245 allocation->x, allocation->y,
7246 allocation->width, allocation->height);
7248 dial->radius = MAX(allocation->width,allocation->height) * 0.45;
7249 dial->pointer_width = dial->radius / 5;
7254 <sect2> <em/gtk_dial_expose()/
7257 Comme cela est mentionné plus haut, tout le dessin de ce widget est réalisé
7258 dans le gestionnaire pour les événements d'exposition. Il n'y a pas grand chose
7259 de plus à dire là dessus, sauf constater l'utilisation de la fonction
7260 <em/gtk_draw_polygon/ pour dessiner le pointeur avec une forme en trois
7261 dimensions selon les couleurs stockées dans le style du widget.
7266 gtk_dial_expose (GtkWidget *widget,
7267 GdkEventExpose *event)
7277 g_return_val_if_fail (widget != NULL, FALSE);
7278 g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
7279 g_return_val_if_fail (event != NULL, FALSE);
7281 if (event->count > 0)
7284 dial = GTK_DIAL (widget);
7286 gdk_window_clear_area (widget->window,
7288 widget->allocation.width,
7289 widget->allocation.height);
7291 xc = widget->allocation.width/2;
7292 yc = widget->allocation.height/2;
7296 for (i=0; i<25; i++)
7298 theta = (i*M_PI/18. - M_PI/6.);
7302 tick_length = (i%6 == 0) ? dial->pointer_width : dial->pointer_width/2;
7304 gdk_draw_line (widget->window,
7305 widget->style->fg_gc[widget->state],
7306 xc + c*(dial->radius - tick_length),
7307 yc - s*(dial->radius - tick_length),
7308 xc + c*dial->radius,
7309 yc - s*dial->radius);
7314 s = sin(dial->angle);
7315 c = cos(dial->angle);
7318 points[0].x = xc + s*dial->pointer_width/2;
7319 points[0].y = yc + c*dial->pointer_width/2;
7320 points[1].x = xc + c*dial->radius;
7321 points[1].y = yc - s*dial->radius;
7322 points[2].x = xc - s*dial->pointer_width/2;
7323 points[2].y = yc - c*dial->pointer_width/2;
7325 gtk_draw_polygon (widget->style,
7336 <sect2>Gestion des événements
7339 Le reste du code du widget gère différents types d'événements et n'est pas
7340 trop différent de ce que l'on trouve dans la plupart des applications GTK. Deux
7341 types d'événements peuvent survenir -- l'utilisateur peut cliquer sur le widget
7342 avec la souris et faire glisser pour déplacer le pointeur, ou bien la valeur de
7343 l'objet Adjustment peut changer à cause d'une circonstance extérieure.
7346 Lorsque l'utilisateur clique sur le widget, on vérifie si le clic s'est bien
7347 passé près du pointeur et si c'est le cas, on stocke alors le bouton avec
7348 lequel l'utilisateur a cliqué dans le champ <em/button/ de la structure du
7349 widget et on récupère tous les événements souris avec un appel à
7350 <em/gtk_grab_add()/. Un déplacement ultérieur de la souris provoque le recalcul
7351 de la valeur de contrôle (par la fonction <em/gtk_dial_update_mouse/). Selon la
7352 politique qui a été choisie, les événements "value_changed" sont, soit générés
7353 instantanément (<em/GTK_UPDATE_CONTINUOUS/), après un délai ajouté au timer
7354 avec <em/gtk_timeout_add()/ (<em/GTK_UPDATE_DELAYED/), ou seulement lorsque le
7355 bouton est relâché (<em/GTK_UPDATE_DISCONTINUOUS/).
7359 gtk_dial_button_press (GtkWidget *widget,
7360 GdkEventButton *event)
7366 double d_perpendicular;
7368 g_return_val_if_fail (widget != NULL, FALSE);
7369 g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
7370 g_return_val_if_fail (event != NULL, FALSE);
7372 dial = GTK_DIAL (widget);
7374 /* Détermine si le bouton pressé est dans la région du pointeur.
7375 On fait cela en calculant les distances parallèle et perpendiculaire
7376 du point où la souris a été pressée par rapport à la ligne passant
7379 dx = event->x - widget->allocation.width / 2;
7380 dy = widget->allocation.height / 2 - event->y;
7382 s = sin(dial->angle);
7383 c = cos(dial->angle);
7385 d_parallel = s*dy + c*dx;
7386 d_perpendicular = fabs(s*dx - c*dy);
7388 if (!dial->button &&
7389 (d_perpendicular < dial->pointer_width/2) &&
7390 (d_parallel > - dial->pointer_width))
7392 gtk_grab_add (widget);
7394 dial->button = event->button;
7396 gtk_dial_update_mouse (dial, event->x, event->y);
7403 gtk_dial_button_release (GtkWidget *widget,
7404 GdkEventButton *event)
7408 g_return_val_if_fail (widget != NULL, FALSE);
7409 g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
7410 g_return_val_if_fail (event != NULL, FALSE);
7412 dial = GTK_DIAL (widget);
7414 if (dial->button == event->button)
7416 gtk_grab_remove (widget);
7420 if (dial->policy == GTK_UPDATE_DELAYED)
7421 gtk_timeout_remove (dial->timer);
7423 if ((dial->policy != GTK_UPDATE_CONTINUOUS) &&
7424 (dial->old_value != dial->adjustment->value))
7425 gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
7432 gtk_dial_motion_notify (GtkWidget *widget,
7433 GdkEventMotion *event)
7436 GdkModifierType mods;
7439 g_return_val_if_fail (widget != NULL, FALSE);
7440 g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
7441 g_return_val_if_fail (event != NULL, FALSE);
7443 dial = GTK_DIAL (widget);
7445 if (dial->button != 0)
7450 if (event->is_hint || (event->window != widget->window))
7451 gdk_window_get_pointer (widget->window, &x, &y, &mods);
7453 switch (dial->button)
7456 mask = GDK_BUTTON1_MASK;
7459 mask = GDK_BUTTON2_MASK;
7462 mask = GDK_BUTTON3_MASK;
7470 gtk_dial_update_mouse (dial, x,y);
7477 gtk_dial_timer (GtkDial *dial)
7479 g_return_val_if_fail (dial != NULL, FALSE);
7480 g_return_val_if_fail (GTK_IS_DIAL (dial), FALSE);
7482 if (dial->policy == GTK_UPDATE_DELAYED)
7483 gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
7489 gtk_dial_update_mouse (GtkDial *dial, gint x, gint y)
7494 g_return_if_fail (dial != NULL);
7495 g_return_if_fail (GTK_IS_DIAL (dial));
7497 xc = GTK_WIDGET(dial)->allocation.width / 2;
7498 yc = GTK_WIDGET(dial)->allocation.height / 2;
7500 old_value = dial->adjustment->value;
7501 dial->angle = atan2(yc-y, x-xc);
7503 if (dial->angle < -M_PI/2.)
7504 dial->angle += 2*M_PI;
7506 if (dial->angle < -M_PI/6)
7507 dial->angle = -M_PI/6;
7509 if (dial->angle > 7.*M_PI/6.)
7510 dial->angle = 7.*M_PI/6.;
7512 dial->adjustment->value = dial->adjustment->lower + (7.*M_PI/6 - dial->angle) *
7513 (dial->adjustment->upper - dial->adjustment->lower) / (4.*M_PI/3.);
7515 if (dial->adjustment->value != old_value)
7517 if (dial->policy == GTK_UPDATE_CONTINUOUS)
7519 gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
7523 gtk_widget_draw (GTK_WIDGET(dial), NULL);
7525 if (dial->policy == GTK_UPDATE_DELAYED)
7528 gtk_timeout_remove (dial->timer);
7530 dial->timer = gtk_timeout_add (SCROLL_DELAY_LENGTH,
7531 (GtkFunction) gtk_dial_timer,
7540 Les changements de l'Adjustement par des moyens extérieurs sont communiqués à
7541 notre widget par les signaux "changed" et "value_changed". Les gestionnaires
7542 pour ces fonctions appellent <em/gtk_dial_update()/ pour valider les
7543 paramètres, calculer le nouvel angle du pointeur et redessiner le widget (en
7544 appelant <em/gtk_widget_draw()/).
7548 gtk_dial_update (GtkDial *dial)
7552 g_return_if_fail (dial != NULL);
7553 g_return_if_fail (GTK_IS_DIAL (dial));
7555 new_value = dial->adjustment->value;
7557 if (new_value < dial->adjustment->lower)
7558 new_value = dial->adjustment->lower;
7560 if (new_value > dial->adjustment->upper)
7561 new_value = dial->adjustment->upper;
7563 if (new_value != dial->adjustment->value)
7565 dial->adjustment->value = new_value;
7566 gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
7569 dial->angle = 7.*M_PI/6. - (new_value - dial->adjustment->lower) * 4.*M_PI/3. /
7570 (dial->adjustment->upper - dial->adjustment->lower);
7572 gtk_widget_draw (GTK_WIDGET(dial), NULL);
7576 gtk_dial_adjustment_changed (GtkAdjustment *adjustment,
7581 g_return_if_fail (adjustment != NULL);
7582 g_return_if_fail (data != NULL);
7584 dial = GTK_DIAL (data);
7586 if ((dial->old_value != adjustment->value) ||
7587 (dial->old_lower != adjustment->lower) ||
7588 (dial->old_upper != adjustment->upper))
7590 gtk_dial_update (dial);
7592 dial->old_value = adjustment->value;
7593 dial->old_lower = adjustment->lower;
7594 dial->old_upper = adjustment->upper;
7599 gtk_dial_adjustment_value_changed (GtkAdjustment *adjustment,
7604 g_return_if_fail (adjustment != NULL);
7605 g_return_if_fail (data != NULL);
7607 dial = GTK_DIAL (data);
7609 if (dial->old_value != adjustment->value)
7611 gtk_dial_update (dial);
7613 dial->old_value = adjustment->value;
7618 <sect2>Améliorations possibles
7622 Le widget Dial décrit jusqu'à maintenant exécute à peu près 670 lignes de
7623 code. Bien que cela puisse sembler beaucoup, nous en avons vraiment fait
7624 beaucoup avec ce code, notamment parce que la majeure partie de cette longueur
7625 est due aux en-têtes et à la préparation. Cependant, certaines améliorations
7626 peuvent être apportées à ce widget :
7629 <item> Si vous testez ce widget, vous vous apercevrez qu'il y a un peu de
7630 scintillement lorsque le pointeur est déplacé. Ceci est dû au fait que le
7631 widget entier est effacé, puis redessiné à chaque mouvement du
7632 pointeur. Souvent, la meilleure façon de gérer ce problème est de dessiner sur
7633 un pixmap non affiché, puis de copier le résultat final sur l'écran en une
7634 seule étape (le widget <em/ProgressBar/ se dessine de cette façon).
7636 <item> L'utilisateur devrait pouvoir utiliser les flèches du curseur vers le
7637 haut et vers le bas pour incrémenter et décrémenter la valeur.
7639 <item> Ce serait bien si le widget avait des boutons pour augmenter et diminuer
7640 la valeur dans de petites ou de grosses proportions. Bien qu'il serait possible
7641 d'utiliser les widgets <em/Button/ pour cela, nous voudrions aussi que les
7642 boutons s'auto-répètent lorsqu'ils sont maintenus appuyés, comme font les
7643 flèches d'une barre de défilement. La majeure partie du code pour implanter ce
7644 type de comportement peut se trouver dans le widget <em/GtkRange/.
7646 <item> Le widget Dial pourrait être fait dans un widget container avec un seul
7647 widget fils positionnée en bas, entre les boutons mentionnés
7648 ci-dessus. L'utilisateur pourrait alors ajouter au choix, un widget label ou
7649 entrée pour afficher la valeur courante de l'appel.
7653 <sect1>En savoir plus
7656 Seule une petite partie des nombreux détails de la création des widgets a pu
7657 être décrite. Si vous désirez écrire vos propres widgets, la meilleure source
7658 d'exemples est le source de GTK lui-même. Posez-vous quelques questions sur les
7659 widgets que vous voulez écrire : est-ce un widget container ? possède-t-il
7660 sa propre fenêtre ? est-ce une modification d'un widget existant ? Puis,
7661 trouvez un widget identique et commencez à faire les modifications. Bonne
7664 <sect>Scribble, un programme simple de dessin
7669 Dans cette section, nous construirons un programme simple de dessin. Ce
7670 faisant, nous examinerons comment gérer les événements souris, comment dessiner
7671 dans une fenêtre, et comment mieux dessiner en utilisant un pixmap en arrière
7672 plan. Après avoir créé ce programme, nous l'étendrons en lui ajoutant le
7673 support des périphériques <em/Xinput/, comme les tables de tracé. GTK dispose
7674 de routines de support qui facilitent beaucoup l'obtention des informations
7675 étendues (comme la pression et l'inclinaison du stylet) à partir de tels
7678 <sect1>Gestion d'événement
7681 Les signaux GTK que nous avons déjà vus concernent les actions de haut niveau,
7682 comme la sélection d'un choix d'un menu. Cependant, il est quelques fois utile
7683 de connaître les cas de bas niveau, comme le déplacement de la souris, ou la
7684 pression d'une touche. Il existe aussi des signaux GTK correspondant à ces
7685 <em/événements/ bas niveau. Les gestionnaires de ces signaux ont un paramètre
7686 supplémentaire qui est un pointeur vers une structure contenant des
7687 informations sur l'événement. Par exemple, les gestionnaires des événements de
7688 déplacement recoivent un paramètre vers une structure <em/GdkEventMotion/ qui
7689 ressemble (en partie) à ceci :
7692 struct _GdkEventMotion
7705 <em/type/ sera initialisé avec le type de l'événement, ici
7706 <em/GDK_MOTION_NOTIFY/, <em/window/ est la fenêtre dans laquelle l'événement
7707 est survenu. <em/x/ et <em/y/ donnent les coordonnées de l'événement et
7708 <em/state/ spécifie l'état du modificateur lorsque l'événement s'est produit
7709 (c'est-à-dire quelles sont les touches de modification et les boutons souris
7710 qui ont été pressés). Il s'agit d'un OU bit à bit de l'une des valeurs
7730 Comme pour les autres signaux, on appelle <em/gtk_signal_connect()/ pour
7731 déterminer ce qui se passe lorsqu'un événement survient. Mais nous devons aussi
7732 faire en sorte que GTK sache de quels événements nous voulons être
7733 avertis. Pour ce faire, on appelle la fonction :
7736 void gtk_widget_set_events (GtkWidget *widget,
7740 Le deuxième champ spécifie les événements qui nous intéressent. Il s'agit d'un
7741 OU bit à bit de constantes qui indiquent différent types d'événements. Pour
7742 référence ultérieure, les types d'événements sont :
7746 GDK_POINTER_MOTION_MASK
7747 GDK_POINTER_MOTION_HINT_MASK
7748 GDK_BUTTON_MOTION_MASK
7749 GDK_BUTTON1_MOTION_MASK
7750 GDK_BUTTON2_MOTION_MASK
7751 GDK_BUTTON3_MOTION_MASK
7752 GDK_BUTTON_PRESS_MASK
7753 GDK_BUTTON_RELEASE_MASK
7755 GDK_KEY_RELEASE_MASK
7756 GDK_ENTER_NOTIFY_MASK
7757 GDK_LEAVE_NOTIFY_MASK
7758 GDK_FOCUS_CHANGE_MASK
7760 GDK_PROPERTY_CHANGE_MASK
7761 GDK_PROXIMITY_IN_MASK
7762 GDK_PROXIMITY_OUT_MASK
7765 Il y a quelques points subtils qui doivent être observés lorsqu'on appelle
7766 <em/gtk_widget_set_events()/. D'abord, elle doit être appelée avant que la
7767 fenêtre X d'un widget GTK soit créée. En pratique, cela signifie que l'on doit
7768 l'appeler immédiatement après avoir créé le widget. Ensuite, le widget doit
7769 avoir une fenêtre X associée. Pour des raisons d'efficacité, de nombreux types
7770 de widgets n'ont pas de fenêtre propre, mais se dessinent dans la fenêtre de
7771 leur parent. Ces widgets sont :
7797 Pour capturer les événements pour ces widgets, on doit utiliser un widget
7798 <em/EventBox/. Voir la section sur <ref id="sec_The_EventBox_Widget" name="Le
7799 widget EventBox"> pour plus de détails.
7802 Pour notre programme de dessin, on veut savoir quand le bouton de la souris est
7803 pressé et quand la souris est déplacée, nous indiquons donc
7804 <em/GDK_POINTER_MOTION_MASK/ et <em/GDK_BUTTON_PRESS_MASK/. On veut aussi
7805 savoir quand il est nécessaire de redessiner notre fenêtre, on indique donc
7806 <em/GDK_EXPOSURE_MASK/. Bien que nous voulions être avertis via un événement
7807 <em/Configure/ lorsque la taille de notre fenêtre change, on n'a pas besoin de
7808 préciser le flag <em/GDK_STRUCTURE_MASK/ correspondant car il est
7809 automatiquement spécifié pour chaque fenêtre.
7812 Il arrive cependant qu'il puisse y avoir un problème en indiquant seulement
7813 <em/GDK_POINTER_MOTION_MASK/. Cela fera que le serveur ajoutera un nouvel
7814 événement de déplacement à la file des événements à chaque fois que
7815 l'utilisateur déplace la souris. Si cela nous prend 0,1 seconde pour gérer un
7816 événement de déplacement, si le serveur X n'ajoute un nouvel événement de
7817 déplacement dans la queue que toutes les 0,05 secondes, nous serons vite à la
7818 traîne de l'utilisateur. Si l'utilisateur dessine pendant 5 secondes, cela nous
7819 prendra 5 secondes de plus pour le traiter après qu'il ait relâché le bouton de
7820 la souris ! Ce que l'on voudrait, c'est ne récupérer qu'un événement de
7821 déplacement pour chaque événement que l'on traite. Pour cela, il faut préciser
7822 <em/GDK_POINTER_MOTION_HINT_MASK/.
7825 Avec <em/GDK_POINTER_MOTION_HINT_MASK/, le serveur nous envoit un événement de
7826 déplacement la première fois que la pointeur se déplace après être entré dans
7827 la fenêtre, ou après un événement d'appui ou de relâchement d'un bouton. Les
7828 événements de déplacement suivants seront supprimés jusqu'à ce que l'on demande
7829 explicitement la position du pointeur en utilisant la fonction :
7832 GdkWindow* gdk_window_get_pointer (GdkWindow *window,
7835 GdkModifierType *mask);
7838 (Il existe une autre fonction, <em/gtk_widget_get_pointer()/ qui possède une
7839 interface simple, mais n'est pas très utile car elle ne fait que récupérer la
7840 position de la souris et ne se préoccupe pas de savoir si les boutons sont
7844 Le code pour configurer les événements pour notre fenêtre ressemble alors
7848 gtk_signal_connect (GTK_OBJECT (drawing_area), "expose_event",
7849 (GtkSignalFunc) expose_event, NULL);
7850 gtk_signal_connect (GTK_OBJECT(drawing_area),"configure_event",
7851 (GtkSignalFunc) configure_event, NULL);
7852 gtk_signal_connect (GTK_OBJECT (drawing_area), "motion_notify_event",
7853 (GtkSignalFunc) motion_notify_event, NULL);
7854 gtk_signal_connect (GTK_OBJECT (drawing_area), "button_press_event",
7855 (GtkSignalFunc) button_press_event, NULL);
7857 gtk_widget_set_events (drawing_area, GDK_EXPOSURE_MASK
7858 | GDK_LEAVE_NOTIFY_MASK
7859 | GDK_BUTTON_PRESS_MASK
7860 | GDK_POINTER_MOTION_MASK
7861 | GDK_POINTER_MOTION_HINT_MASK);
7864 Nous garderons les gestionnaires de "expose_event" et "configure_event" pour
7865 plus tard. Les gestionnaires de "motion_notify_event" et "button_press_event"
7866 sont très simples :
7870 button_press_event (GtkWidget *widget, GdkEventButton *event)
7872 if (event->button == 1 && pixmap != NULL)
7873 draw_brush (widget, event->x, event->y);
7879 motion_notify_event (GtkWidget *widget, GdkEventMotion *event)
7882 GdkModifierType state;
7885 gdk_window_get_pointer (event->window, &x, &y, &state);
7890 state = event->state;
7893 if (state & GDK_BUTTON1_MASK && pixmap != NULL)
7894 draw_brush (widget, x, y);
7901 <sect1>Le widget DrawingArea et le dessin
7904 Revenons au processus de dessin sur l'écran. Le widget que l'on utilise pour
7905 ceci est le widget <em/DrawingArea/. Un tel widget est essentiellement une
7906 fenêtre X et rien de plus. Il s'agit d'une toile vide sur laquelle nous pouvons
7907 dessiner ce que nous voulons.
7909 Une zone de dessin est créée avec l'appel :
7912 GtkWidget* gtk_drawing_area_new (void);
7915 Une taille par défaut peut être donnée au widget par l'appel :
7918 void gtk_drawing_area_size (GtkDrawingArea *darea,
7923 Cette taille par défaut peu être surchargée en appelant
7924 <em/gtk_widget_set_usize()/ et celle-ci, à son tour, peut être surchargée si
7925 l'utilisateur modifie manuellement la taille de la fenêtre contenant la zone de
7929 Il faut noter que lorsque l'on crée un widget <em/DrawingArea/, nous sommes
7930 <em>complètement</em> responsable du dessin du contenu. Si notre fenêtre est
7931 cachée puis redécouverte, nous recevrons un événement d'exposition et devrons
7932 redessiner ce qui a été caché auparavant.
7935 Devoir se rappeler tout ce qui a été dessiné à l'écran pour pouvoir
7936 correctement le redessiner peut s'avérer, c'est le moins que l'on puisse dire,
7937 pénible. De plus, cela peut être visible si des portions de la fenêtre sont
7938 effacées puis redessinées étape par étape. La solution à ce problème est
7939 d'utiliser un <em/pixmap d'arrière-plan/ qui n'est pas sur l'écran. Au lieu de
7940 dessiner directement à l'écran, on dessine sur une image stockée dans la
7941 mémoire du serveur et qui n'est pas affichée, puis, lorsque l'image change ou
7942 lorsque de nouvelles portions de l'image sont affichées, on copie les parties
7943 adéquates sur l'écran.
7946 Pour créer un pixmap mémoire, on appelle la fonction :
7949 GdkPixmap* gdk_pixmap_new (GdkWindow *window,
7955 Le paramètre <em/windows/ indique une fenêtre GTK de laquelle ce pixmap tire
7956 certaines de ses propriétés. <em/width/ et <em/height/ précisent la taille du
7957 pixmap. <em/depth/ précise la <em/profondeur de couleur/, c'est-à-dire le
7958 nombre de bits par pixel, de la nouvelle fenêtre. Si cette profondeur vaut
7959 <em/-1/, elle correspondra à celle de <em/window/.
7962 Nous créons le pixmap dans notre gestionnaire "configure_event". Cet événement
7963 est généré à chaque fois que la fenêtre change de taille, y compris lorsqu'elle
7967 /* Pixmap d'arrière-plan pour la zone de dessin */
7968 static GdkPixmap *pixmap = NULL;
7970 /* Création d'un nouveau pixmap d'arrière-plan de la taille voulue */
7972 configure_event (GtkWidget *widget, GdkEventConfigure *event)
7976 gdk_pixmap_destroy(pixmap);
7978 pixmap = gdk_pixmap_new(widget->window,
7979 widget->allocation.width,
7980 widget->allocation.height,
7982 gdk_draw_rectangle (pixmap,
7983 widget->style->white_gc,
7986 widget->allocation.width,
7987 widget->allocation.height);
7993 L'appel à <em/gdk_draw_rectangle()/ remet le pixmap à blanc. Nous en dirons un
7994 peu plus dans un moment.
7997 Notre gestionnaire d'événement d'exposition copie alors simplement la partie
7998 concernées du pixmap sur l'écran (on détermine la zone qu'il faut redessiner en
7999 utilisant le champ <em/event->area/ de l'événement d'exposition) :
8002 /* Remplit l'écran à partir du pixmap d'arrière-plan */
8004 expose_event (GtkWidget *widget, GdkEventExpose *event)
8006 gdk_draw_pixmap(widget->window,
8007 widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
8009 event->area.x, event->area.y,
8010 event->area.x, event->area.y,
8011 event->area.width, event->area.height);
8017 Nous avons vu comment garder l'écran à jour avec notre pixmap, mais comment
8018 dessine-t-on réellement ce que l'on veut dans le pixmap ? Il existe un grand
8019 nombre d'appels dans la bibliothèque GDK de GTK pour dessiner sur des
8020 <em>dessinables</em>. Un dessinable est simplement quelque chose sur lequel on
8021 peut dessiner. Cela peut être une fenêtre, un pixmap, ou un bitmap (une image
8022 en noir et blanc). Nous avons déjà vu plus haut deux de ces appels,
8023 <em/gdk_draw_rectangle()/ et <em/gdk_draw_pixmap()/. La liste complète
8028 gdk_draw_rectangle ()
8037 gdk_draw_segments ()
8040 Consultez la documentation de référence ou le fichier en-tête
8041 <em/<gdk/gdk.h>/ pour plus de détails sur ces fonctions. Celles-ci
8042 partagent toutes les mêmes deux paramêtres. Le premier est le dessinable sur
8043 lequel dessiner, le second est un <em/contexte graphique/ (GC).
8046 Un contexte graphique encapsule l'information sur des choses comme les couleurs
8047 de premier et d'arrière plan et la largeur de ligne. GDK possède un ensemble
8048 complet de fonctions pour créer et manipuler les contextes graphiques, mais,
8049 pour simplifier, nous n'utiliserons que les contextes graphiques
8050 prédéfinis. Chaque widget a un style associé (qui peut être modifié dans un
8051 fichier gtkrc, voir la section sur les fichiers rc de GTK). Celui-ci, entre
8052 autres choses, stocke plusieurs contextes graphiques. Quelques exemples d'accès
8053 à ces contextes sont :
8056 widget->style->white_gc
8057 widget->style->black_gc
8058 widget->style->fg_gc[GTK_STATE_NORMAL]
8059 widget->style->bg_gc[GTK_WIDGET_STATE(widget)]
8062 Les champs <em/fg_gc, bg_gc, dark_gc/ et <em/light_gc/ sont indexés par un
8063 paramètre de type <em/GtkStateType/ qui peut prendre les valeurs :
8070 GTK_STATE_INSENSITIVE
8073 Par exemple, pour <em/GTK_STATE_SELECTED/, la couleur de premier plan par
8074 défaut est blanc et la couleur d'arrière plan par défaut est bleu foncé.
8077 Notre fonction <em/draw_brush()/, qui réalise le dessin à l'écran est alors :
8080 /* Dessine un rectangle à l'écran */
8082 draw_brush (GtkWidget *widget, gdouble x, gdouble y)
8084 GdkRectangle update_rect;
8086 update_rect.x = x - 5;
8087 update_rect.y = y - 5;
8088 update_rect.width = 10;
8089 update_rect.height = 10;
8090 gdk_draw_rectangle (pixmap,
8091 widget->style->black_gc,
8093 update_rect.x, update_rect.y,
8094 update_rect.width, update_rect.height);
8095 gtk_widget_draw (widget, &update_rect);
8099 Après avoir dessiné le rectangle représentant la brosse sur le pixmap, nous
8100 appelons la fonction :
8103 void gtk_widget_draw (GtkWidget *widget,
8104 GdkRectangle *area);
8107 qui indique à X que la zone donnée par le paramètre <em/area/ a besoin d'être
8108 mise à jour. X génèrera éventuellement un événement d'exposition (en combinant
8109 peut-être les zones passés dans plusieurs appels à <em/gtk_widget_draw()/) ce
8110 qui forcera notre gestionnaire d'événement d'exposition à copier les parties
8111 adéquates à l'écran.
8114 Nous avons maintenant couvert entièrement le programme de dessin, sauf quelques
8115 détails banals comme la création de la fenêtre principale. Le code source
8116 complet est disponible à l'endroit où vous avez obtenu ce didacticiel.
8118 <sect1>Ajouter le support XInput
8121 Il est maintenant possible d'acheter des périphériques bon marché, comme les
8122 tablettes graphiques qui permettent d'exprimer beaucoup plus facilement son
8123 talent qu'avec une souris. La façon la plus simple pour utiliser de tels
8124 périphériques est simplement de le faire comme un remplacement de la souris,
8125 mais cela ne tire pas partie des nombreux avantages de ces périphériques,
8129 <item> Sensibilité à la pression ;
8130 <item> rapport d'inclinaison ;
8131 <item> positionnement au dessous du pixel ;
8132 <item> entrées multiples (par exemple, un stylet avec pointe et gomme).
8135 Pour des informations sur l'extension XInput, voir <url
8136 url="http://www.msc.cornell.edu/~otaylor/xinput/XInput-HOWTO.html"
8137 name="XInput-HOWTO">.
8140 Si l'on examine la définition complète de, par exemple, la structure
8141 <em/GdkEventMotion/, on voit qu'elle possède des champs pour supporter des
8142 informations étendues sur les périphériques.
8145 struct _GdkEventMotion
8157 GdkInputSource source;
8162 <em/pressure/ indique la pression comme un nombre réel compris entre 0 et 1.
8163 <em/xtilt/ et <em/ytilt/ peuvent prendre des valeurs entre -1 et 1,
8164 correspondant au degré d'inclinaison dans chaque direction, <em/source/ et
8165 <em/deviceid/ précisent de deux façons différentes le périphérique pour lequel
8166 l'événement est survenus. <em/source/ donne une simple information sur le type
8167 du périphérique. Il peut prendre l'une des valeurs suivantes :
8176 <tt/deviceid/ précise un ID numérique unique pour le périphérique. Il peut être
8177 utilisé pour trouver des informations supplémentaires sur le périphérique en
8178 utilisant l'appel <em/gdk_input_list_devices()/ (voir ci-dessous). La valeur
8179 spéciale <em/GDK_CORE_POINTER/ sert à désigner le périphérique de pointage
8180 principal (habituellement la souris).
8182 <sect2>Valider l'information supplémentaire sur un périphérique
8185 Pour indiquer à GTK que l'on désire obtenir des informations supplémentaires
8186 sur le périphérique, on a simplement besoin d'ajouter une ligne à nos
8190 gtk_widget_set_extension_events (drawing_area, GDK_EXTENSION_EVENTS_CURSOR);
8193 En donnant la valeur <em/GDK_EXTENSION_EVENTS_CURSOR/, on indique que nous
8194 désirons les événements d'extension, mais seulement si l'on ne doit pas
8195 dessiner notre propre curseur. Voir la section <ref
8196 id="sec_Further_Sophistications" name="Sophistications supplémentaires">
8197 ci-dessous pour des plus de détails sur le dessin du curseur. Nous pourrions
8198 aussi donner les valeurs <em/GDK_EXTENSION_EVENTS_ALL/ si nous voulons dessiner
8199 notre propre curseur, ou <tt/GDK_EXTENSION_EVENTS_NONE/ pour revenir à la
8200 situation par défaut.
8203 Toutefois, nous ne sommes pas complètement à la fin de l'histoire. Par défaut,
8204 aucun périphérique d'extension n'est autorisé. Nous avons besoin d'un mécanisme
8205 pour que les utilisateurs puissent autoriser et configurer leur extensions. GTK
8206 dispose du widget <em/InputDialog/ pour automatiser cette tâche. La procédure
8207 suivante gère un widget InputDialog. Elle crée le dialogue s'il n'est pas
8208 présent et le place au premier plan sinon.
8212 input_dialog_destroy (GtkWidget *w, gpointer data)
8214 *((GtkWidget **)data) = NULL;
8218 create_input_dialog ()
8220 static GtkWidget *inputd = NULL;
8224 inputd = gtk_input_dialog_new();
8226 gtk_signal_connect (GTK_OBJECT(inputd), "destroy",
8227 (GtkSignalFunc)input_dialog_destroy, &inputd);
8228 gtk_signal_connect_object (GTK_OBJECT(GTK_INPUT_DIALOG(inputd)->close_button),
8230 (GtkSignalFunc)gtk_widget_hide,
8231 GTK_OBJECT(inputd));
8232 gtk_widget_hide ( GTK_INPUT_DIALOG(inputd)->save_button);
8234 gtk_widget_show (inputd);
8238 if (!GTK_WIDGET_MAPPED(inputd))
8239 gtk_widget_show(inputd);
8241 gdk_window_raise(inputd->window);
8246 (vous pouvez remarquer la façon dont nous gérons ce dialogue. En le connectant
8247 au signal "destroy", nous nous assurons que nous ne garderons pas un pointeur
8248 sur le dialogue après l'avoir détruit -- cela pourrait provoquer une erreur de
8253 InputDialog a deux boutons "Close" et "Save", qui n'ont pas d'actions qui leur
8254 sont assignées par défaut. Dans la fonction ci-dessus, nous associons à "Close"
8255 le masquage du dialogue et nous cachons le bouton "Save" car nous n'implantons
8256 pas la sauvegarde des options XInput dans ce programme.
8258 <sect2>Utiliser l'information supplémentaire d'un périphérique
8261 Lorsque l'on a validé le périphérique, on peut simplement utiliser
8262 l'information supplémentaire des champs des structures d'événement. En fait, il
8263 est toujours prident d'utiliser ces informations car ces champs auront des
8264 valeurs par défaut judicieuses même lorsque les événements supplémentaires ne
8268 La seule modification consiste à appeler <em/gdk_input_window_get_pointer()/ au
8269 lieu de <em/gdk_window_get_pointer/. Cela est nécessaire car
8270 <em/gdk_window_get_pointer/ ne retourne pas l'information supplémentaire.
8273 void gdk_input_window_get_pointer (GdkWindow *window,
8280 GdkModifierType *mask);
8283 Lorsque l'on appelle cette fonction, on doit préciser l'ID du périphérique
8284 ainsi que la fenêtre. Habituellement, on aura obtenu cet ID par le champ
8285 <em/deviceid/ d'une structure d'événement. Cette fonction retournera une valeur
8286 cohérente lorsque les événements ne sont pas autorisés (dans ce cas,
8287 <em/event->deviceid/ aura la valeur <em/GDK_CORE_POINTER/).
8289 La structure de base des gestionnaires d'événements de déplacement et de bouton
8290 pressé ne change donc pas trop -- il faut juste ajouter le code permettant de
8291 traiter l'information supplémentaire.
8295 button_press_event (GtkWidget *widget, GdkEventButton *event)
8297 print_button_press (event->deviceid);
8299 if (event->button == 1 && pixmap != NULL)
8300 draw_brush (widget, event->source, event->x, event->y, event->pressure);
8306 motion_notify_event (GtkWidget *widget, GdkEventMotion *event)
8310 GdkModifierType state;
8313 gdk_input_window_get_pointer (event->window, event->deviceid,
8314 &x, &y, &pressure, NULL, NULL, &state);
8319 pressure = event->pressure;
8320 state = event->state;
8323 if (state & GDK_BUTTON1_MASK && pixmap != NULL)
8324 draw_brush (widget, event->source, x, y, pressure);
8330 On doit aussi faire quelquechose de cette nouvelle information. Notre nouvelle
8331 fonction <em/draw_brush()/ dessine avec une couleur différente pour chaque
8332 <em/event->source/ et change la taille du pinceau selon la pression.
8335 /* Dessine un rectangle à l'écran, la taille dépend de la pression,
8336 et la couleur dépend du type de périphérique */
8338 draw_brush (GtkWidget *widget, GdkInputSource source,
8339 gdouble x, gdouble y, gdouble pressure)
8342 GdkRectangle update_rect;
8346 case GDK_SOURCE_MOUSE:
8347 gc = widget->style->dark_gc[GTK_WIDGET_STATE (widget)];
8349 case GDK_SOURCE_PEN:
8350 gc = widget->style->black_gc;
8352 case GDK_SOURCE_ERASER:
8353 gc = widget->style->white_gc;
8356 gc = widget->style->light_gc[GTK_WIDGET_STATE (widget)];
8359 update_rect.x = x - 10 * pressure;
8360 update_rect.y = y - 10 * pressure;
8361 update_rect.width = 20 * pressure;
8362 update_rect.height = 20 * pressure;
8363 gdk_draw_rectangle (pixmap, gc, TRUE,
8364 update_rect.x, update_rect.y,
8365 update_rect.width, update_rect.height);
8366 gtk_widget_draw (widget, &update_rect);
8370 <sect2>En savoir plus sur un périphérique
8373 Notre programme affichera le nom du périphérique qui a généré chaque appui de
8374 bouton. Pour trouver le nom d'un périphérique, nous appelons la fonction :
8377 GList *gdk_input_list_devices (void);
8380 qui retourne une GList (un type de liste chaînée de la bibliothèque glib) de
8381 structures GdkDeviceInfo. La structure GdkDeviceInfo est définie de la façon
8385 struct _GdkDeviceInfo
8389 GdkInputSource source;
8399 La plupart de ces champs sont des informations de configuration que l'on peut
8400 ignorer sauf si l'on implante la sauvegarde de la configuration XInput. Celui
8401 qui nous intéresse est <em/name/ qui est, tout simplement, le nom que X donne
8402 au périphérique. L'autre champ, qui n'est pas une information de configuration,
8403 est <em/has_cursor/. Si <em/has_cursor/ est faux, on doit dessiner notre propre
8404 curseur, mais puisque nous avons précisé <em/GDK_EXTENSION_EVENTS_CURSOR/, nous
8405 n'avons pas à nous en préoccuper.
8408 Notre fonction <em/print_button_press()/ ne fait parcourir la liste retournée
8409 jusqu'à trouver une correspondance, puis affiche le nom du périphérique.
8413 print_button_press (guint32 deviceid)
8417 /* gdk_input_list_devices retourne une liste interne, nous ne devons donc
8418 pas la libérer après */
8419 tmp_list = gdk_input_list_devices();
8423 GdkDeviceInfo *info = (GdkDeviceInfo *)tmp_list->data;
8425 if (info->deviceid == deviceid)
8427 printf("Bouton pressé sur le périphérique '%s'\n", info->name);
8431 tmp_list = tmp_list->next;
8436 Ceci termine les modifications de notre programme « XInputize ». Comme pour la
8437 première version, le code complet est disponible à l'endroit où vous avez
8438 obtenu ce didacticiel.
8440 <sect2>Sophistications supplémentaires<label id="sec_Further_Sophistications">
8443 Bien que notre programme supporte maintenant XInput, il y manque des
8444 caractéristiques que l'on souhaite trouver dans les applications
8445 complètes. D'abord, l'utilisateur ne veut probablement pas avoir à configurer
8446 ses périphériques à chaque fois qu'il lance le programme et nous devons donc
8447 lui permettre de sauvegarder la configuration du périphérique. Ceci est réalisé
8448 en parcourant ce que retourne <em/gdk_input_list_devices()/ et en écrivant la
8449 configuration dans un fichier.
8452 Pour restaurer un état au prochain démarrage du programme, GDK dispose de
8453 fonctions pour changer la configuration des périphériques :
8456 gdk_input_set_extension_events()
8457 gdk_input_set_source()
8458 gdk_input_set_mode()
8459 gdk_input_set_axes()
8463 (La liste retournée par <em/gdk_input_list_devices()/ ne doit pas être modifiée
8464 directement). Un exemple est donné dans le programme de dessin <em/gsumi/
8465 (disponible à l'adresse <htmlurl
8466 url="http://www.msc.cornell.edu/~otaylor/gsumi/"
8467 name="http://www.msc.cornell.edu/~otaylor/gsumi/">). De plus, ce serait
8468 pratique d'avoir une méthode standard pour faire cela pour toutes les
8469 applications. Ceci appartient probablement à un niveau légèrement supérieur à
8470 GTK, peut-être dans la bibliothèque GNOME.
8473 Une autre grosse omission que nous avons mentionnée plus haut est l'absence de
8474 dessin du curseur. Les plates-formes autres qu'XFree86 n'autorisent pas encore
8475 l'utilisation simultanée d'un périphérique comme pointeur de base et comme
8476 pointeur d'une application. Lisez le <url
8477 url="http://www.msc.cornell.edu/~otaylor/xinput/XInput-HOWTO.html"
8478 name="XInput-HOWTO"> pour plus d'informations là-dessus. Ceci signifie que les
8479 applications qui veulent atteindre le plus de monde possible doivent dessiner
8480 leur propre curseur.
8483 Une application qui dessine son propre curseur doit faire deux choses :
8484 déterminer si le périphérique courant a besoin ou non d'un curseur dessiné et
8485 déterminer si le périphérique courant est à proximité (si celui-ci est une
8486 tablette de dessin, il est pratique de faire disparaître le curseur lorsque le
8487 stylet est en dehors de la tablette. Lorsque le périphérique voit le stylet, on
8488 dit qu'il est « à proximité »). La première vérification est faite en
8489 recherchant dans la liste des périphériques, comme nous l'avons fait pour
8490 obtenir le nom du périphérique. La deuxième est réalisée en choisissant des
8491 événements "proximity_out". Une exemple de dessin d'un curseur personnel est
8492 donné dans le programme <em/testinput/ de la distribution GTK.
8495 <sect>Conseils pour l'écriture d'applications GTK
8497 Cette section est simplement un regroupement de lignes de conduites générales
8498 et sages, ainsi que d'astuces pour créer des applications GTK correctes. Elle
8499 est totalement inutile pour l'instant car il ne s'agit que d'une phrase :)
8501 Utilisez <em/autoconf/ et <em/automake/ de GNU ! Ce sont vos amis :) J'ai en
8502 projet de les présenter brièvement ici.
8507 Ce document, comme beaucoup d'autres beaux logiciels, a été créé (NdT : et
8509 volontaires bénévoles. Si vous vous y connaissez sur certains aspects de GTK
8510 qui ne sont pas encore documentés, soyez gentils de contribuer à ce document.
8513 Si vous décidez de contribuer, envoyez-moi (Ian Main) votre texte à
8514 <tt><htmlurl url="mailto:slow@intergate.bc.ca"
8515 name="slow@intergate.bc.ca"></tt>. La totalité de ce document est libre et
8516 tout ajout que vous pourriez y faire doit l'être également. Ainsi, tout le
8517 monde peut utiliser n'importe quelle partie de vos exemples dans les
8518 programmes, les copies de ce document peuvent être distribuées à volonté, etc.
8525 Je voudrai remercier les personnes suivantes pour leurs contributions à ce texte :
8528 <item>Bawer Dagdeviren, <tt><htmlurl url="mailto:chamele0n@geocities.com"
8529 name="chamele0n@geocities.com"></tt> pour le didacticiel sur les menus.
8531 <item>Raph Levien, <tt><htmlurl url="mailto:raph@acm.org"
8532 name="raph@acm.org"></tt>
8533 pour <em/bonjour tout le monde/ à la GTK, le placement des widgets et pour sa
8534 sagesse. Il a aussi généreusement donné un abri à ce didacticiel.
8536 <item>Peter Mattis, <tt><htmlurl url="mailto:petm@xcf.berkeley.edu"
8537 name="petm@xcf.berkeley.edu"></tt> pour le programme GTK le plus simple et pour
8538 sa capacité à le faire :)
8540 <item>Werner Koch <tt><htmlurl url="mailto:werner.koch@guug.de"
8541 name="werner.koch@guug.de"></tt> pour la conversion du texte original en SGML,
8542 et pour la hiérarchie des classes de widget.
8544 <item>Mark Crichton <tt><htmlurl url="mailto:crichton@expert.cc.purdue.edu"
8545 name="crichton@expert.cc.purdue.edu"></tt> pour le code de l'usine à menus et
8546 pour le didacticiel de placement des tables.
8548 <item>Owen Taylor <tt><htmlurl url="mailto:owt1@cornell.edu"
8549 name="owt1@cornell.edu"></tt> pour la section sur le widget EventBox (et le
8550 patch de la distribution). Il est aussi responsable des sections sur l'écriture
8551 de vos propres widgets GTK et de l'application exemple. Merci beaucoup à Owen
8552 pour toute son aide !
8554 <item>Mark VanderBoom <tt><htmlurl url="mailto:mvboom42@calvin.edu"
8555 name="mvboom42@calvin.edu"></tt> pour son merveilleux travail sur les widgets
8556 Notebook, Progress Bar, Dialog et File selection. Merci beaucoup, Mark ! Ton
8557 aide a été très précieuse.
8559 <item>Tim Janik <tt><htmlurl url="mailto:timj@gtk.org"
8560 name="timj@gtk.org"></tt> pour son beau travail sur le widget Lists.
8563 <item>Rajat Datta <tt><htmlurl url="mailto:rajat@ix.netcom.com"
8564 name="rajat@ix.netcom.com"</tt> pour son excellent travail sur le didacticiel
8567 <item>Michael K. Johnson <tt><htmlurl url="mailto:johnsonm@redhat.com"
8568 name="johnsonm@redhat.com"></tt> pour ses infos et le code pour les menus.
8572 Et à tous ceux d'entre vous qui ont commenté et aidé à améliorer ce document.
8578 Ce didacticiel est Copyright (C) 1997 Ian Main
8580 Ce programme est un logiciel libre ; vous pouvez le redistribuer et/ou le
8581 modifier sous les termes de la licence publique générale GNU telle qu'elle est
8582 publiée par la Free Software Foundation ; soit la version 2 de la licence, ou
8583 (comme vous voulez) toute version ultérieure.
8585 Ce programme est distribué dans l'espoir qu'il sera utile, mais SANS AUCUNE
8586 GARANTIE ; même sans la garantie de COMMERCIALITÉ ou d'ADÉQUATION A UN BUT
8587 PARTICULIER. Voir la licence publique générale GNU pour plus de détails.
8589 Vous devriez avoir reçu une copie de la licence publique générale GNU avec ce
8590 programme ; si ce n'est pas le cas, écrivez à la Free Software
8591 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
8593 <sect1>Notes du traducteur
8594 Ce document a été adapté en Français par <url url="mailto:jaco@dotcom.fr"
8595 name="Éric Jacoboni">. Toute remarque sur cette adaptation sera la bienvenue.
8597 Merci à <url url="mailto:kheops@linux-kheops.com" name="Joël Bernier"> pour
8598 avoir initié cette adaptation, et à <url url="mailto:vincent@debian.org"
8599 name="Vincent Renardias"> pour sa relecture.