]> Pileus Git - ~andy/gtk/blob - docs/tutorial/gtk_tut_fr.sgml
Revert name change
[~andy/gtk] / docs / tutorial / gtk_tut_fr.sgml
1 <!doctype linuxdoc system>
2 <article>
3 <title>Didacticiel
4 <author>Ian Main, <tt><htmlurl url="mailto:slow@intergate.bc.ca"
5                               name="slow@intergate.bc.ca"></tt>
6 <date>January 24, 1998.
7
8    
9 <sect>Introduction
10 <p>
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&nbsp;:
17 <itemize>
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>
24 </itemize>
25
26 <p>
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
30 de fonctions).
31 <p>
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&nbsp;: g_malloc(), par exemple, facilite le
39 débuggage.<p>
40  
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.
51 <p>
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.
55
56 <sect>Bien débuter
57 <p>
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.
65 <p>
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
69 shell.
70
71 <tscreen><verb>
72 #include <gtk/gtk.h>
73
74 int main (int argc, char *argv[])
75 {
76     GtkWidget *window;
77     
78     gtk_init (&amp;argc, &amp;argv);
79     
80     window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
81     gtk_widget_show (window);
82     
83     gtk_main ();
84     
85     return 0;
86 }
87 </verb></tscreen>
88
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.
92 <p>
93 La ligne &nbsp;:
94
95 <tscreen><verb>
96 gtk_init (&amp;argc, &amp;argv);
97 </verb></tscreen>
98
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&nbsp;:
107
108 <itemize>
109 <item> <tt/--display/
110 <item> <tt/--debug-level/
111 <item> <tt/--no-xshm/
112 <item> <tt/--sync/
113 <item> <tt/--show-events/
114 <item> <tt/--no-show-events/
115 </itemize>
116 <p>
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.
121 <p>
122 Les deux lignes de code suivantes créent et affichent une fenêtre.
123
124 <tscreen><verb>
125   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
126   gtk_widget_show (window);
127 </verb></tscreen>
128
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&nbsp;: on peut
133 ainsi la manipuler facilement.
134 <p>
135 La fonction <em/gtk_widget_show()/ informe GTK que l'on a configuré
136 le widget et qu'il peut l'afficher.
137 <p>
138 La ligne suivante lance la boucle principale de traitement de GTK.
139
140 <tscreen><verb>
141 gtk_main ();
142 </verb></tscreen>
143
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.
149
150
151 <sect1>« Bonjour tout le monde » en GTK
152 <p>
153 OK, écrivons un programme avec un widget (bouton). C'est le classique « Bonjour tout le monde » à la sauce GTK.
154
155 <tscreen><verb>
156
157 #include <gtk/gtk.h>
158
159     /* fonction de rappel. Dans cet exemple, les paramètres sont ignorés...
160      *  Les fonctions de rappel sont détaillées plus loin. */
161
162 void hello (GtkWidget *widget, gpointer data)
163 {
164     g_print ("Bonjour tout le monde.\n");
165 }
166
167 gint delete_event(GtkWidget *widget, GdkEvent *event, gpointer data)
168 {
169     g_print ("le signal delete_event est survenu.\n");
170
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 ? » */
176     
177     /* Remplacez FALSE par TRUE et la fenêtre principale sera détruite par
178      * un signal « delete_event ». */
179     
180     return (FALSE); 
181 }
182
183 /* Autre fonction de rappel */
184
185 void destroy (GtkWidget *widget, gpointer data)
186 {
187     gtk_main_quit ();
188 }
189
190 int main (int argc, char *argv[])
191 {
192     /* GtkWidget est le type pour déclarer les widgets. */
193
194     GtkWidget *window;
195     GtkWidget *button;
196     
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. */
200
201     gtk_init (&amp;argc, &amp;argv);
202     
203     /* Création d'une nouvelle fenêtre. */
204
205     window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
206     
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
212      * rappel. */
213
214     gtk_signal_connect (GTK_OBJECT (window), "delete_event",
215                         GTK_SIGNAL_FUNC (delete_event), NULL);
216     
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". */
220
221     gtk_signal_connect (GTK_OBJECT (window), "destroy",
222                         GTK_SIGNAL_FUNC (destroy), NULL);
223     
224     /* Configuration de la largeur du contour de la fenêtre. */
225
226     gtk_container_border_width (GTK_CONTAINER (window), 10);
227     
228     /* Création d'un nouveau bouton portant le label 
229      * "Bonjour tout le monde". */
230
231     button = gtk_button_new_with_label ("Bonjour tout le monde");
232     
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. */
235
236     gtk_signal_connect (GTK_OBJECT (button), "clicked",
237                         GTK_SIGNAL_FUNC (hello), NULL);
238     
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. */
243
244     gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
245                                GTK_SIGNAL_FUNC (gtk_widget_destroy),
246                                GTK_OBJECT (window));
247     
248     /* Insertion du bouton dans la fenêtre (container gtk). */
249
250     gtk_container_add (GTK_CONTAINER (window), button);
251     
252     /* L'étape finale consiste à afficher ce nouveau widget... */
253
254     gtk_widget_show (button);
255     
256     /* ... et la fenêtre. */
257
258     gtk_widget_show (window);
259     
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). */
263
264     gtk_main ();
265     
266     return 0;
267 }
268 </verb></tscreen>
269
270 <sect1>Compilation de « Bonjour tout le monde »
271 <p>
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
274 suivante&nbsp;:
275
276 <tscreen><verb>
277 gcc -Wall -g bonjour.c -o bonjour_monde -L/usr/X11R6/lib \
278     -lgtk -lgdk -lglib -lXext -lX11 -lm
279 </verb></tscreen>
280 <p>
281 Les bibliothèques invoquées ci-dessus doivent toutes être dans vos
282 chemins de recherche par défaut, sinon, ajoutez <tt/-L&lt;library
283 directory&gt;/ 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&nbsp;: et c'est pareil sur mon système Red Hat
287 Linux...).
288 <p>
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
291 les traiter.
292 <p>
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
296 cas.
297 <p>
298 Les bibliothèques que l'on utilise sont&nbsp;:
299 <itemize>
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.
305  
306 <item>La bibliothèque GDK (<tt/-lgdk/), l'enveloppe de Xlib.
307
308 <item>La bibliothèque GTK (<tt/-lgtk/), la bibliothèque des widgets,
309 construite au dessus de GDK.
310
311 <item>La bibliothèque Xlib (<tt/-lX11/ utilisée par GDK.
312
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.
315
316 <item>La bibliothèque mathématique (<tt/-lm/).  Elle est utilisée pour
317 différentes raisons par GTK.
318 </itemize>
319
320 <sect1>Théorie des signaux et des rappels
321 <p>
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.
327 <p>
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&nbsp;:
335
336 <tscreen><verb>
337 gint gtk_signal_connect (GtkObject *object,
338                          gchar *name,
339                          GtkSignalFunc func,
340                          gpointer func_data);
341 </verb></tscreen>
342 <p>
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.
348 <p>
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&nbsp;:
351
352 <tscreen><verb>
353 void callback_func(GtkWidget *widget, gpointer *callback_data);
354 </verb></tscreen>
355 <p>
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
359 plus haut.
360 <p>
361 Un autre appel utilisé dans l'exemple « Bonjour tout le monde » est&nbsp;:
362
363 <tscreen><verb>
364 gint gtk_signal_connect_object (GtkObject *object,
365                                 gchar  *name,
366                                 GtkSignalFunc func,
367                                 GtkObject *slot_object);
368 </verb></tscreen>
369 <p>
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&nbsp;: un pointeur vers un objet GTK. Lorsqu'on utilise
373 cette fonction pour connecter des signaux, le rappel doit être de
374 cette forme&nbsp;:
375
376 <tscreen><verb>
377 void callback_func (GtkObject *object);
378 </verb></tscreen>
379 <p>
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.
384
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.
393
394 <sect1>« Bonjour tout le monde » pas à pas
395 <p>
396 Maintenant que nous connaissons la théorie, clarifions un peu en progressant à travers le programme « Bonjour tout le monde ».
397 <p>
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é
402 pressé.
403
404 <tscreen><verb>
405 void hello (GtkWidget *widget, gpointer *data)
406 {
407     g_print ("Bonjour tout le monde\n");
408 }
409 </verb></tscreen>
410
411 <p>
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.
417
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
423 signal "destroy".
424
425 <tscreen><verb>
426 gint delete_event(GtkWidget *widget, GdkEvent *event, gpointer data)
427 {
428     g_print ("le signal delete_event est survenu.\n");
429
430     return (FALSE); 
431 }
432 </verb></tscreen>
433
434 <p>
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&nbsp;:
438
439 <tscreen><verb>
440 void destroy (GtkWidget *widget, gpointer *data)
441 {
442     gtk_main_quit ();
443 }
444 </verb></tscreen>
445 <p>
446 Je suppose que vous connaissez la fonction <em/main()/... oui, comme
447 les autres programmes C, toutes les applications GTK en ont une.
448
449 <tscreen><verb> 
450 int main (int argc, char *argv[]) 
451
452 </verb></tscreen>
453 <p>
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.
457
458 <tscreen><verb>
459     GtkWidget *window;
460     GtkWidget *button;
461 </verb></tscreen>
462 <p>
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.
468
469 <tscreen><verb>
470     gtk_init (&amp;argc, &amp;argv);
471 </verb></tscreen>
472 <p>
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.
478
479 <tscreen><verb>
480     window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
481 </verb></tscreen>
482 <p>
483 Voici maintenant un exemple de connexion d'un gestionnaire de signal à
484 un objet&nbsp;: 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
491 nous.
492 <p>
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.
496
497 <tscreen><verb>
498     gtk_signal_connect (GTK_OBJECT (window), "destroy",
499                         GTK_SIGNAL_FUNC (destroy), NULL);
500 </verb></tscreen>
501 <p>
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">
509 <p>
510 À nouveau, <tt/GTK_CONTAINER/ est une macro réalisant la conversion de type.
511 <tscreen><verb>
512     gtk_container_border_width (GTK_CONTAINER (window), 10);
513 </verb></tscreen>
514 <p>
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é.
519
520 <tscreen><verb>
521     button = gtk_button_new_with_label ("Bonjour tout le monde");
522 </verb></tscreen>
523 <p>
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.
530
531 <tscreen><verb>
532     gtk_signal_connect (GTK_OBJECT (button), "clicked",
533                         GTK_SIGNAL_FUNC (hello), NULL);
534 </verb></tscreen>
535 <p>
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()/.
547
548 <tscreen><verb>
549     gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
550                                GTK_SIGNAL_FUNC (gtk_widget_destroy),
551                                GTK_OBJECT (window));
552 </verb></tscreen>
553 <p>
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.
557
558 <tscreen><verb> gtk_container_add (GTK_CONTAINER (window), button);
559 </verb></tscreen>
560 <p>
561 Maintenant, nous avons tout configuré comme on le souhaitait&nbsp;:
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.  
569 <tscreen><verb> 
570     gtk_widget_show(button);
571
572     gtk_widget_show (window);
573 </verb></tscreen>
574 <p>
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.
578 <tscreen><verb>
579     gtk_main ();
580 </verb></tscreen>
581 Enfin, le <em/return/ final. Il est exécuté lorsque <em/gtk_quit()/ est appelé.
582 <tscreen><verb>
583     return 0;
584 </verb></tscreen>
585 <p>
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
597 simplement GTK.
598 <p>
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.
609
610
611 <sect>Continuons
612 <p>
613 <sect1>Types de données
614 <p>
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
625 par <em/gtk.h/).
626 <p>
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.
630
631 <sect1>Compléments sur les gestionnaires de signaux
632 <p>
633 Regardons à nouveau la déclaration de <em/gtk_signal_connect/.
634
635 <tscreen><verb>
636 gint gtk_signal_connect (GtkObject *object, gchar *name,
637                          GtkSignalFunc func, gpointer func_data);
638 </verb></tscreen>
639
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&nbsp&;: 
646
647 <tscreen><verb>
648       void gtk_signal_disconnect (GtkObject *object, gint id);
649 </verb></tscreen> 
650
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.
654 <p>
655 Une autre fonction permettant de supprimer tous les gestionnaires de
656 signaux pour un objet est&nbsp;: 
657
658 <tscreen><verb>
659       gtk_signal_handlers_destroy (GtkObject *object); 
660 </verb></tscreen>
661 <p>
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.
664
665
666 <sect1>Un « Bonjour tout le monde » amélioré
667 <p>
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&nbsp;: le placement des wigdets.
671
672 <tscreen><verb>
673 #include <gtk/gtk.h>
674
675 /* Notre nouveau rappel amélioré. La donnée passée à cette fonction est
676  * imprimée sur stdout. */
677
678 void rappel (GtkWidget *widget, gpointer *data)
679 {
680     g_print ("Re-Bonjour - %s a été pressé\n", (char *) data);
681 }
682
683 /* Un autre rappel */
684
685 void delete_event (GtkWidget *widget, GdkEvent *event, gpointer *data)
686 {
687     gtk_main_quit ();
688 }
689
690 int main (int argc, char *argv[])
691 {
692     /* GtkWidget est le type pour déclarer les widgets */
693
694     GtkWidget *window;
695     GtkWidget *button;
696     GtkWidget *box1;
697
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. */
701
702     gtk_init (&amp;argc, &amp;argv);
703
704     /* Création d'une nouvelle fenêtre. */
705
706     window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
707
708     /* Nouvel appel qui intitule notre nouvelle fenêtre 
709      * "Salut les boutons !" */
710
711     gtk_window_set_title (GTK_WINDOW (window), "Salut les boutons !");
712
713     /* Configuration d'un gestionnaire pour "delete_event" afin de
714      * quitter immédiatement GTK. */
715
716     gtk_signal_connect (GTK_OBJECT (window), "delete_event",
717                         GTK_SIGNAL_FUNC (delete_event), NULL);
718
719
720     /* Configuration de la largeur du contour de la fenêtre. */
721     
722     gtk_container_border_width (GTK_CONTAINER (window), 10);
723
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. */
728
729     box1 = gtk_hbox_new(FALSE, 0);
730
731     /* On met la boîte dans la fenêtre principale. */
732
733     gtk_container_add (GTK_CONTAINER (window), box1);
734
735     /* On crée un nouveau bouton portant le label « Bouton 1 ». */
736
737     button = gtk_button_new_with_label ("Bouton 1");
738
739     /* Lorsque le bouton est cliqué, on appelle la fonction « rappel » 
740      * avec un pointeur sur la chaîne « Bouton 1 » comme paramètre. */
741
742     gtk_signal_connect (GTK_OBJECT (button), "clicked",
743                         GTK_SIGNAL_FUNC (rappel), (gpointer) "Bouton 1");
744
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. */
747
748     gtk_box_pack_start(GTK_BOX(box1), button, TRUE, TRUE, 0);
749
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é. */
752
753     gtk_widget_show(button);
754
755     /* On fait la même chose pour créer un deuxième bouton. */
756
757     button = gtk_button_new_with_label ("Bouton 2");
758
759     /* On appelle la même fonction de rappel avec un paramètre différent,
760      * un pointeur sur la chaîne « Bouton 2 ». */
761
762     gtk_signal_connect (GTK_OBJECT (button), "clicked",
763                         GTK_SIGNAL_FUNC (rappel), (gpointer) "Bouton 2");
764
765     gtk_box_pack_start(GTK_BOX(box1), button, TRUE, TRUE, 0);
766
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. */
770
771     gtk_widget_show(button);
772
773     gtk_widget_show(box1);
774
775     gtk_widget_show (window);
776
777     /* Le reste est dans gtk_main et on attend que la fête commence ! */
778
779     gtk_main ();
780
781     return 0;
782 }
783 </verb></tscreen>
784 <p>
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&nbsp;: 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
793 son comportement.
794 <p>
795 Juste une remarque&nbsp;: 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.
800
801 <sect>Placement des widgets
802 <p>
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
809 entre en jeu.
810
811 <sect1>Théorie des boîtes de placement
812 <p>
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&nbsp;:
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
822 désiré.
823 <p>
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.
838 <p>
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
844 créer les widgets.
845
846 <sect1>Détails sur les boîtes
847 <p>
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
852 différents.
853
854 <p>
855 <?  
856 <IMG ALIGN="center" SRC="packbox1.gif"
857 VSPACE="15" HSPACE="10" ALT="Box Packing Example Image" WIDTH="528"
858 HEIGHT="235">
859 >
860
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()/).
866 <p>
867 Voici la déclaration de la fonction <em/gtk_box_pack_start/.
868
869 <tscreen><verb>
870 void gtk_box_pack_start (GtkBox    *box,
871                          GtkWidget *child,
872                          gint       expand,
873                          gint       fill,
874                          gint       padding);
875 </verb></tscreen>
876
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.
880 <p>
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/.
890 <p>
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/
895 vaut TRUE.
896 <p>
897 Lorsque l'on crée une nouvelle boîte, on utilise une fonction comme&nbsp;:
898
899 <tscreen><verb>
900 GtkWidget * gtk_hbox_new (gint homogeneous,
901                           gint spacing);
902 </verb></tscreen>
903
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.
909 <p>
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&nbsp;:
915
916 <?
917 <IMG ALIGN="center" SRC="packbox2.gif"
918 VSPACE="15" HSPACE="10" ALT="Box Packing Example Image" WIDTH="509"
919 HEIGHT="213">
920 >
921
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.
925
926 <sect1>Programme de démonstration des placements
927 <p>
928
929 <tscreen><verb>
930 #include "gtk/gtk.h"
931
932 void
933 delete_event (GtkWidget *widget, GdkEvent *event, gpointer *data)
934 {
935     gtk_main_quit ();
936 }
937
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. */
941
942 GtkWidget *make_box (gint homogeneous, gint spacing,
943                      gint expand, gint fill, gint padding) 
944 {
945     GtkWidget *box;
946     GtkWidget *button;
947     char padstr[80];
948     
949     /* Création d'une hbox avec les paramètres homogeneous et spacing
950      * voulus. */
951
952     box = gtk_hbox_new (homogeneous, spacing);
953     
954     /* Création d'une série de boutons configurés de façon appropriée */
955
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);
959     
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);
963     
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);
967     
968     /* Création d'un bouton portant un label dépendant de la valeur
969      * du paramètre expand. */
970
971     if (expand == TRUE)
972             button = gtk_button_new_with_label ("TRUE,");
973     else
974             button = gtk_button_new_with_label ("FALSE,");
975     
976     gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
977     gtk_widget_show (button);
978     
979     /* Même chose que ci-dessus mais sous forme abrégée. */
980
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);
984     
985     /* Récupération du paramètre padding sous forme de chaîne. */
986
987     sprintf (padstr, "%d);", padding);
988     
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);
992     
993     return box;
994 }
995
996 int main (int argc, char *argv[])
997 {
998     GtkWidget *window;
999     GtkWidget *button;
1000     GtkWidget *box1;
1001     GtkWidget *box2;
1002     GtkWidget *separator;
1003     GtkWidget *label;
1004     GtkWidget *quitbox;
1005     int which;
1006     
1007     /* Initialisation, à ne jamais oublier ! :) */
1008
1009     gtk_init (&amp;argc, &amp;argv);
1010    
1011     if (argc != 2) {
1012         fprintf (stderr, "usage : %s num, où num vaut 1, 2, ou 3.\n", *argv);
1013
1014         /* Nettoyage dans GTK et sortie avec un code d'erreur de 1 */
1015         gtk_exit (1);
1016     }
1017     
1018     which = atoi (argv[1]);
1019
1020     /* Création de notre fenêtre. */
1021
1022     window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1023
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. */
1027
1028     gtk_signal_connect (GTK_OBJECT (window), "delete_event",
1029                         GTK_SIGNAL_FUNC (delete_event), NULL);
1030
1031     gtk_container_border_width (GTK_CONTAINER (window), 10);
1032     
1033
1034     /* Création d'une boîte verticale (vbox) pour y placer les boîtes
1035      * horizontales.
1036      * Ceci permet de placer les boîtes horizontales contenant les boutons
1037      * les unes au dessus des autres dans cette vbox. */
1038
1039     box1 = gtk_vbox_new (FALSE, 0);
1040     
1041     /* L'exemple à afficher. Ils correspondent aux images ci-dessus. */
1042
1043     switch (which) {
1044     case 1:
1045         /* Création d'un label. */
1046
1047         label = gtk_label_new ("gtk_hbox_new (FALSE, 0);");
1048         
1049         /* Alignement du label à gauche.  On précisera cette fonction ainsi
1050          * que les autres dans la section sur les attributs des widgets. */
1051
1052         gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
1053
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. */
1057
1058         gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0);
1059         
1060         /* Affichage du label */
1061
1062         gtk_widget_show (label);
1063         
1064         /* On appelle notre fonction de construction de boîte :
1065          * homogeneous = FALSE, spacing = 0,
1066          * expand = FALSE, fill = FALSE, padding = 0 */
1067
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);
1071
1072         /* On appelle notre fonction de construction de boîte :
1073          * homogeneous = FALSE, spacing = 0,
1074          * expand = FALSE, fill = FALSE, padding = 0 */
1075
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);
1079         
1080         /* Paramètres : homogeneous = FALSE, spacing = 0, 
1081          * expand = TRUE, fill = TRUE, padding = 0 */
1082
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);
1086         
1087         /* Création d'un séparateur, on verra cela plus tard, mais ils sont
1088          * simples à utiliser. */
1089
1090         separator = gtk_hseparator_new ();
1091         
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 
1094          * verticalement. */
1095
1096         gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
1097         gtk_widget_show (separator);
1098         
1099         /* Création d'un nouveau label et affichage de celui-ci. */
1100
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);
1105         
1106         /* Paramètres : homogeneous = TRUE, spacing = 0, 
1107          * expand = TRUE, fill = FALSE, padding = 0 */
1108
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);
1112         
1113         /* Paramètres : homogeneous = TRUE, spacing = 0, 
1114          * expand = TRUE, fill = TRUE, padding = 0 */
1115
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);
1119         
1120         /* Un autre séparateur */
1121
1122         separator = gtk_hseparator_new ();
1123
1124         /* Les 3 derniers paramètres de gtk_box_pack_start sont : 
1125          * expand = FALSE, fill = TRUE, padding = 5. */
1126
1127         gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
1128         gtk_widget_show (separator);
1129         
1130         break;
1131
1132     case 2:
1133
1134         /* Création d'un label, box1 est une vbox identique à 
1135          * celle créée au début de main() */
1136
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);
1141         
1142         /* Paramètres : homogeneous = FALSE, spacing = 10, 
1143          * expand = TRUE, fill = FALSE, padding = 0 */
1144
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);
1148         
1149         /* Paramètres : homogeneous = FALSE, spacing = 10, 
1150          * expand = TRUE, fill = TRUE, padding = 0 */
1151
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);
1155         
1156         separator = gtk_hseparator_new ();
1157
1158         /* Les 3 derniers paramètres de gtk_box_pack_start sont : 
1159          * expand = FALSE, fill = TRUE, padding = 5. */
1160
1161         gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
1162         gtk_widget_show (separator);
1163         
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);
1168         
1169         /* Paramètres : homogeneous = FALSE, spacing = 0, 
1170          * expand = TRUE, fill = FALSE, padding = 10 */
1171
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);
1175         
1176         /* Paramètres : homogeneous = FALSE, spacing = 0, 
1177          * expand = TRUE, fill = TRUE, padding = 10 */
1178
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);
1182         
1183         separator = gtk_hseparator_new ();
1184
1185         /* Les 3 derniers paramètres de gtk_box_pack_start sont : 
1186          * expand = FALSE, fill = TRUE, padding = 5. */
1187         
1188         gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
1189         gtk_widget_show (separator);
1190         break;
1191     
1192     case 3:
1193
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. */
1197
1198         box2 = make_box (FALSE, 0, FALSE, FALSE, 0);
1199
1200         /* On crée le label qui sera mis à la fin. */
1201
1202         label = gtk_label_new ("end");
1203
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(). */
1206
1207         gtk_box_pack_end (GTK_BOX (box2), label, FALSE, FALSE, 0);
1208
1209         /* Affichage du label. */
1210
1211         gtk_widget_show (label);
1212         
1213         /* Placement de box2 dans box1 (la vbox, vous vous rappelez ? :) */
1214
1215         gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
1216         gtk_widget_show (box2);
1217         
1218         /* Séparateur pour le bas. */
1219
1220         separator = gtk_hseparator_new ();
1221
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. */
1227
1228         gtk_widget_set_usize (separator, 400, 5);
1229
1230         /* Placement du séparateur dans la vbox (box1) 
1231          * créée au debut de main(). */
1232
1233         gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
1234         gtk_widget_show (separator);    
1235     }
1236     
1237     /* Création d'une nouvelle hbox.. vous pouvez en utiliser autant que
1238      * que vous en avez besoin ! */
1239
1240     quitbox = gtk_hbox_new (FALSE, 0);
1241     
1242     /* Notre bouton pour quitter. */
1243
1244     button = gtk_button_new_with_label ("Quit");
1245     
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. */
1249
1250     gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
1251                                GTK_SIGNAL_FUNC (gtk_widget_destroy),
1252                                GTK_OBJECT (window));
1253
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. */
1257
1258     gtk_box_pack_start (GTK_BOX (quitbox), button, TRUE, FALSE, 0);
1259
1260     /* Placement de la quitbox dans la vbox (box1) */
1261
1262     gtk_box_pack_start (GTK_BOX (box1), quitbox, FALSE, FALSE, 0);
1263     
1264     /* Placement de la vbox (box1), qui contient maintenant tous nos
1265      * widgets, dans la fenêtre principale. */
1266
1267     gtk_container_add (GTK_CONTAINER (window), box1);
1268     
1269     /* Affichage */
1270
1271     gtk_widget_show (button);
1272     gtk_widget_show (quitbox);
1273     
1274     gtk_widget_show (box1);
1275
1276     /* Affichage de la fenêtre en dernier */
1277
1278     gtk_widget_show (window);
1279     
1280     /* Ne pas oublier notre fonction principale. */
1281
1282     gtk_main ();
1283
1284     /* Le contrôle revient ici lorsque gtk_main_quit() est appelée,
1285      * jusqu'à ce que  gtk_exit() soitutilisée. */
1286     
1287     return 0;
1288 }
1289 </verb></tscreen>
1290
1291 <p>
1292 <sect1>Placement avec les tables
1293 <p>
1294 Étudions une autre méthode de placement&nbsp;: les tables. Elles
1295 peuvent s'avérer très utiles dans certaines situations.
1296
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
1299 désire.
1300
1301 La première chose à faire est, bien sûr, d'étudier la fonction
1302 <em/gtk_table_new/&nbsp;:
1303
1304 <tscreen><verb>
1305 GtkWidget* gtk_table_new (gint rows,
1306                           gint columns,
1307                           gint homogeneous);
1308 </verb></tscreen>
1309 <p>
1310 Le premier paramètre est le nombre de lignes de la table et le
1311 deuxième, le nombre de colonnes.
1312
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.
1318
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&nbsp;:
1322
1323 <tscreen><verb>
1324  0          1          2
1325 0+----------+----------+
1326  |          |          |
1327 1+----------+----------+
1328  |          |          |
1329 2+----------+----------+
1330 </verb></tscreen>
1331 <p>
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
1334 suivante&nbsp;:
1335
1336 <tscreen><verb>
1337 void gtk_table_attach (GtkTable      *table,
1338                        GtkWidget     *child,
1339                        gint           left_attach,
1340                        gint           right_attach,
1341                        gint           top_attach,
1342                        gint           bottom_attach,
1343                        gint           xoptions,
1344                        gint           yoptions,
1345                        gint           xpadding,
1346                        gint           ypadding);
1347 </verb></tscreen>                                      
1348 <p>
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
1351 table.
1352
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.
1359
1360 Si on veut un widget occupant toute la ligne supérieure de notre table, on utilisera
1361 les valeurs 0, 2, 0, 1.
1362
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.
1366
1367 Ces options sont&nbsp;:
1368 <itemize>
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
1371 disponible.
1372
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.
1378
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.
1381 </itemize>
1382
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.
1385
1386 gtk_table_attach() a BEAUCOUP d'options.  Voici donc une fonction-raccourci&nbsp;:
1387
1388 <tscreen><verb>
1389 void gtk_table_attach_defaults (GtkTable   *table,
1390                                 GtkWidget  *widget,
1391                                 gint        left_attach,
1392                                 gint        right_attach,
1393                                 gint        top_attach,
1394                                 gint        bottom_attach);
1395 </verb></tscreen>
1396
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.
1400
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.
1404
1405 <tscreen><verb>
1406 void gtk_table_set_row_spacing (GtkTable      *table,
1407                                 gint           row,
1408                                 gint           spacing);
1409 </verb></tscreen>
1410 et
1411 <tscreen><verb>
1412 void gtk_table_set_col_spacing  (GtkTable      *table,
1413                                  gint           column,
1414                                  gint           spacing);
1415 </verb></tscreen>
1416
1417 Pour les colonnes, l'espace est ajouté à droite de la colonne et pour
1418 les lignes, il est ajouté en dessous.
1419
1420 On peut aussi configurer un espacement pour toutes les lignes et/ou
1421 colonnes avec&nbsp;:
1422
1423 <tscreen><verb>
1424 void gtk_table_set_row_spacings (GtkTable *table,
1425                                  gint      spacing);
1426 </verb></tscreen>
1427 <p>
1428 Et,
1429 <tscreen><verb>
1430 void gtk_table_set_col_spacings (GtkTable  *table,
1431                                  gint       spacing);
1432 </verb></tscreen>
1433 <p>
1434 Avec ces appels, la dernière ligne et la dernière colonne n'ont pas
1435 d'espace supplémentaire.
1436
1437 <sect1>Exemple de placement avec table
1438 <p>
1439 Pour le moment, étudiez l'exemple sur les tables (testgtk.c) distribué
1440 avec les sources de GTK.
1441
1442 <sect>Vue d'ensemble des widgets
1443 <p>
1444 <p>
1445 Les étapes pour créer un widget en GTK sont&nbsp;:
1446 <enum>
1447 <item> <em/gtk_*_new()/ - une des fonctions disponibles pour créer un
1448 nouveau widget. Ces fonctions sont décrites dans cette section.
1449
1450 <item> Connexion de tous les signaux que l'on souhaite utiliser avec
1451 les gestionnaires adéquats.
1452
1453 <item> Configuration des attributs du widget.
1454
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()/.
1457
1458 <item> Affichage du widget grâce à <em/gtk_widget_show()/.
1459 </enum>
1460 <p>
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()/.
1471
1472 <sect1> Conversions de types
1473 <p>
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&nbsp;:
1479
1480 <itemize>
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)
1486 <item> GTK_BOX(box)
1487 </itemize>
1488
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.
1493
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().
1499
1500 Par exemple&nbsp;:
1501
1502 <tscreen><verb>
1503 gtk_signal_connect(GTK_OBJECT(button), "clicked",
1504                    GTK_SIGNAL_FUNC(fonction_rappel), donnee_de_rappel);
1505 </verb></tscreen> 
1506
1507 Cet appel convertit le bouton en objet et fournit une conversion pour
1508 le pointeur de fonction vers la fonction de rappel.
1509
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.
1515
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&nbsp;: 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
1521 fonctions.
1522
1523 <p>
1524 <sect1>La hiérarchie des widgets
1525 <p>
1526 Voici l'arbre de la hiérarchie de classes utilisées pour implanter les widgets.
1527
1528 <tscreen><verb>
1529     GtkObject
1530     +-- GtkData
1531     |   \-- GtkAdjustment
1532     |
1533     \-- GtkWidget
1534         +-- GtkContainer
1535         |   +-- GtkBin
1536         |   |   +-- GtkAlignment
1537         |   |   +-- GtkFrame
1538         |   |   |   *-- GtkAspectFrame
1539         |   |   |
1540         |   |   +-- GtkItem
1541         |   |   |   +-- GtkListItem
1542         |   |   |   +-- GtkMenuItem
1543         |   |   |   |   +-- GtkCheckMenuItem
1544         |   |   |   |       *-- GtkRadioMenuItem
1545         |   |   |   |
1546         |   |   |   *-- GtkTreeItem
1547         |   |   |
1548         |   |   +-- GtkViewport
1549         |   |   \-- GtkWindow
1550         |   |       +-- GtkDialog
1551         |   |       \-- GtkFileSelection
1552         |   |
1553         |   +-- GtkBox
1554         |   |   +-- GtkHBox
1555         |   |   \-- GtkVBox
1556         |   |       +-- GtkColorSelection
1557         |   |       \-- GtkCurve
1558         |   |
1559         |   +-- GtkButton
1560         |   |   +-- GtkOptionMenu
1561         |   |   \-- GtkToggleButton
1562         |   |       \-- GtkCheckButton
1563         |   |           \-- GtkRadioButton
1564         |   |
1565         |   +-- GtkList
1566         |   +-- GtkMenuShell
1567         |   |   +-- GtkMenu
1568         |   |   \-- GtkMenuBar
1569         |   |
1570         |   +-- GtkNotebook
1571         |   +-- GtkScrolledWindow
1572         |   +-- GtkTable
1573         |   \-- GtkTree
1574         |
1575         +-- GtkDrawingArea
1576         +-- GtkEntry
1577         +-- GtkMisc
1578         |   +-- GtkArrow
1579         |   +-- GtkImage
1580         |   +-- GtkLabel
1581         |   \-- GtkPixmap
1582         |
1583         +-- GtkPreview
1584         +-- GtkProgressBar
1585         +-- GtkRange
1586         |   +-- GtkScale
1587         |   |   +-- GtkHScale
1588         |   |   \-- GtkVScale
1589         |   |
1590         |   \-- GtkScrollbar
1591         |       +-- GtkHScrollbar
1592         |       \-- GtkVScrollbar
1593         |
1594         +-- GtkRuler
1595         |   +-- GtkHRuler
1596         |   \-- GtkVRuler
1597         |
1598         \-- GtkSeparator
1599             +-- GtkHSeparator
1600             \-- GtkVSeparator
1601
1602 </verb></tscreen>
1603 <p>
1604
1605 <sect1>Widgets sans fenêtre
1606 <p>
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">
1611
1612 <tscreen><verb>
1613 GtkAlignment
1614 GtkArrow
1615 GtkBin
1616 GtkBox
1617 GtkImage
1618 GtkItem
1619 GtkLabel
1620 GtkPaned
1621 GtkPixmap
1622 GtkScrolledWindow
1623 GtkSeparator
1624 GtkTable
1625 GtkViewport
1626 GtkAspectFrame
1627 GtkFrame
1628 GtkVPaned
1629 GtkHPaned
1630 GtkVBox
1631 GtkHBox
1632 GtkVSeparator
1633 GtkHSeparator
1634 </verb></tscreen>
1635 <p>
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>
1640
1641 <sect>Widgets boutons
1642 <p>
1643 <sect1>Boutons normaux
1644 <p>
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.
1654 <p>
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
1658 programmes.
1659
1660
1661 <tscreen><verb>
1662 #include <gtk/gtk.h>
1663
1664
1665 /* Création d'une hbox avec une image et un label. Cette fonction 
1666  * retourne la boîte... */
1667
1668 GtkWidget *xpm_label_box (GtkWidget *parent, gchar *xpm_filename, 
1669                           gchar *label_text)
1670 {
1671     GtkWidget *box1;
1672     GtkWidget *label;
1673     GtkWidget *pixmapwid;
1674     GdkPixmap *pixmap;
1675     GdkBitmap *mask;
1676     GtkStyle *style;
1677
1678     /* Création de la boite pour un xpm et un label */
1679
1680     box1 = gtk_hbox_new (FALSE, 0);
1681     gtk_container_border_width (GTK_CONTAINER (box1), 2);
1682
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. */
1686
1687     style = gtk_widget_get_style(parent);
1688
1689     /* Chargement de xpm pour créer une image */
1690
1691     pixmap = gdk_pixmap_create_from_xpm (parent->window, &amp;mask,
1692                                          &amp;style->bg[GTK_STATE_NORMAL],
1693                                          xpm_filename);
1694     pixmapwid = gtk_pixmap_new (pixmap, mask);
1695
1696     /* Création d'un label  */
1697
1698     label = gtk_label_new (label_text);
1699
1700     /* placement de l'image et du label dans la boîte */
1701
1702     gtk_box_pack_start (GTK_BOX (box1),
1703                         pixmapwid, FALSE, FALSE, 3);
1704
1705     gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 3);
1706
1707     gtk_widget_show(pixmapwid);
1708     gtk_widget_show(label);
1709
1710     return (box1);
1711 }
1712
1713 /* Notre fonction de rappel habituelle */
1714
1715 void callback (GtkWidget *widget, gpointer *data)
1716 {
1717     g_print ("Bonjour - %s a été pressé\n", (char *) data);
1718 }
1719
1720
1721 int main (int argc, char *argv[])
1722 {
1723     /* GtkWidget est le type utilisé pour déclarer les widgets */
1724
1725     GtkWidget *window;
1726     GtkWidget *button;
1727     GtkWidget *box1;
1728
1729     gtk_init (&amp;argc, &amp;argv);
1730
1731     /* Création d'une fenêtre */
1732
1733     window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1734
1735     gtk_window_set_title (GTK_WINDOW (window), "Pixmap'd Buttons!");
1736
1737     /* Il est préférable de faire cela pour toutes les fenêtres */
1738
1739     gtk_signal_connect (GTK_OBJECT (window), "destroy",
1740                         GTK_SIGNAL_FUNC (gtk_exit), NULL);
1741
1742
1743     /* Configuration du bord de la fenêtre */
1744
1745     gtk_container_border_width (GTK_CONTAINER (window), 10);
1746
1747     /* Création d'un bouton */
1748
1749     button = gtk_button_new ();
1750
1751     /* Vous devriez être habitué à voir ces fonctions maintenant */
1752
1753     gtk_signal_connect (GTK_OBJECT (button), "clicked",
1754                         GTK_SIGNAL_FUNC (callback), (gpointer) "cool button");
1755
1756     /* Appel de notre fonction de création de boîte */
1757
1758     box1 = xpm_label_box(window, "info.xpm", "cool button");
1759
1760     /* Placement et affichage de tous nos widgets */
1761
1762     gtk_widget_show(box1);
1763
1764     gtk_container_add (GTK_CONTAINER (button), box1);
1765
1766     gtk_widget_show(button);
1767
1768     gtk_container_add (GTK_CONTAINER (window), button);
1769
1770     gtk_widget_show (window);
1771
1772     /* Le reste est dans gtk_main */
1773     gtk_main ();
1774
1775     return 0;
1776 }
1777 </verb></tscreen>
1778
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.
1781
1782 <sect1> Boutons commutateurs
1783 <p>
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.
1789
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.
1794
1795 Création d'un bouton commutateur : 
1796
1797 <tscreen><verb>
1798 GtkWidget* gtk_toggle_button_new (void);
1799
1800 GtkWidget* gtk_toggle_button_new_with_label (gchar *label);
1801 </verb></tscreen>
1802 <p>
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.
1806 <p>
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&nbsp;:
1816
1817 <tscreen><verb>
1818 void rappel_bouton_commutateur (GtkWidget *widget, gpointer data)
1819 {
1820     if (GTK_TOGGLE_BUTTON(widget)->active) 
1821     {
1822         /* Si l'on est ici, c'est que le bouton est relâché. */
1823     
1824     } else {
1825     
1826         /* le bouton est enfoncé */
1827     }
1828 }
1829 </verb></tscreen>
1830
1831 <!--
1832
1833 COMMENTED!
1834
1835 <tscreen><verb>
1836 guint gtk_toggle_button_get_type (void);
1837 </verb></tscreen>
1838 <p>
1839 No idea... they all have this, but I dunno what it is :)
1840
1841
1842 <tscreen><verb>
1843 void gtk_toggle_button_set_mode (GtkToggleButton *toggle_button,
1844                                  gint draw_indicator);
1845 </verb></tscreen>
1846 <p>
1847 No idea.
1848 -->
1849
1850 <p>
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).
1856
1857 <tscreen><verb>
1858 void gtk_toggle_button_set_state (GtkToggleButton *toggle_button,
1859                                   gint state);
1860 </verb></tscreen>
1861 <p>
1862 On notera que lorsqu'on utilise cette fonction, et que l'état est
1863 modifié, cela force le bouton à émettre un signal "clicked".
1864
1865 <tscreen><verb>
1866 void gtk_toggle_button_toggled (GtkToggleButton *toggle_button);
1867 </verb></tscreen>
1868 <p>
1869 Cet appel ne fait que commuter le bouton et émettre le signal "toggled".
1870
1871 <sect1> Cases à cocher
1872 <p>
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.
1878
1879 Les deux fonctions de création sont identiques à celles des boutons normaux.
1880
1881 <tscreen><verb>
1882 GtkWidget* gtk_check_button_new (void);
1883
1884 GtkWidget* gtk_check_button_new_with_label (gchar *label);
1885 </verb></tscreen>
1886
1887 La fonction <em/new_with_label/ crée une case à cocher avec un texte à
1888 coté d'elle.
1889
1890 La vérification de l'état d'une case à cocher est identique à celle
1891 des boutons commutateurs.
1892
1893 <sect1> Boutons radio
1894 <p>
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.
1899
1900 La création d'un bouton radio s'effectue grâce à l'un des appels
1901 suivants&nbsp;:
1902
1903 <tscreen><verb>
1904 GtkWidget* gtk_radio_button_new (GSList *group);
1905
1906 GtkWidget* gtk_radio_button_new_with_label (GSList *group,
1907                                             gchar *label);
1908 </verb></tscreen>
1909 <p>
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&nbsp;:
1914
1915 <tscreen><verb>
1916 GSList* gtk_radio_button_group (GtkRadioButton *radio_button);
1917 </verb></tscreen>
1918
1919 <p>
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&nbsp;:
1921
1922 <tscreen><verb>
1923 void gtk_toggle_button_set_state (GtkToggleButton *toggle_button,
1924                                   gint state);
1925 </verb></tscreen>
1926 <p>
1927 Celle-ci est décrite dans la section sur les boutons commutateurs et fonctionne
1928 exactement de la même façon.
1929 <p>
1930 [Mettre ici un exemple d'utilisation de tout cela car je crois que cela ferait
1931 beaucoup de bien...]
1932
1933
1934 <sect> Widgets divers
1935 <p>
1936 <sect1> Labels
1937 <p>
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.
1942
1943 Pour créer un label, on utilise&nbsp;:
1944
1945 <tscreen><verb>
1946 GtkWidget* gtk_label_new (char *str);
1947 </verb></tscreen>
1948
1949 Où l'unique paramètre est la chaîne de caractères que l'on veut que le
1950 label affiche.
1951
1952 Pour changer le texte d'un label après sa création, on utilise la fonction&nbsp;:
1953
1954 <tscreen><verb>
1955 void gtk_label_set (GtkLabel  *label,
1956                     char      *str);
1957 </verb></tscreen>
1958 <p>
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
1961 nouvelle chaîne.
1962
1963 L'espace nécessaire à la nouvelle chaîne sera automatiquement ajusté
1964 si nécessaire.
1965
1966 Pour récupérer la chaîne courante, on utilise la fonction&nbsp;:
1967
1968 <tscreen><verb>
1969 void gtk_label_get (GtkLabel  *label,
1970                     char     **str);
1971 </verb></tscreen>
1972
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.
1975
1976 <sect1>Le widget bulle d'aide
1977 <p>
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é
1982 avec GTK.
1983 <p>
1984 Certains widgets (comme les labels) ne fonctionnent pas avec les
1985 bulles d'aide.
1986 <p>
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.
1991
1992 <tscreen><verb>
1993 GtkTooltips *gtk_tooltips_new (void);
1994 </verb></tscreen>
1995
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&nbsp;:
1998
1999 <tscreen><verb>
2000 void gtk_tooltips_set_tips   (GtkTooltips *tooltips,
2001                               GtkWidget   *widget,
2002                               gchar       *tips_text);
2003 </verb></tscreen>
2004
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.
2008 <p>
2009 Voici un petit exemple&nbsp;:
2010
2011 <tscreen><verb>
2012 GtkTooltips *tooltips;
2013 GtkWidget *button;
2014 ...
2015 tooltips = gtk_tooltips_new ();
2016 button = gtk_button_new_with_label ("bouton 1");
2017 ...
2018 gtk_tooltips_set_tips (tooltips, button, "C'est le bouton 1");
2019 </verb></tscreen>
2020
2021
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.
2023
2024 <tscreen><verb>
2025 void gtk_tooltips_destroy    (GtkTooltips *tooltips);
2026 </verb></tscreen>
2027
2028 Destruction de bulles d'aide.
2029
2030 <tscreen><verb>
2031 void gtk_tooltips_enable     (GtkTooltips *tooltips);
2032 </verb></tscreen>
2033
2034 Activation d'un ensemble de bulles d'aide désactivées.
2035
2036 <tscreen><verb>
2037 void gtk_tooltips_disable    (GtkTooltips *tooltips);
2038 </verb></tscreen>
2039
2040 Désactivation d'un ensemble de bulles d'aide activées.
2041
2042 <tscreen><verb>
2043 void gtk_tooltips_set_delay  (GtkTooltips *tooltips,
2044                               gint         delay);
2045
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
2049 seconde.
2050
2051 <tscreen><verb>
2052 void      gtk_tooltips_set_tips (GtkTooltips *tooltips,
2053                                  GtkWidget   *widget,
2054                                  gchar    *tips_text);
2055 </verb></tscreen>
2056
2057 Change le texte d'une bulle d'aide déjà créée.
2058
2059 <tscreen><verb>
2060 void gtk_tooltips_set_colors (GtkTooltips *tooltips,
2061                               GdkColor    *background,
2062                               GdkColor    *foreground);
2063 </verb></tscreen>
2064
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...
2067 <p>
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 :)
2070
2071 <sect1> Barres de progression
2072 <p>
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.
2077
2078 <tscreen><verb>
2079 GtkWidget *gtk_progress_bar_new (void);
2080 </verb></tscreen>
2081
2082 Maintenant que la barre est créée, nous pouvons l'utiliser. 
2083
2084 <tscreen><verb>
2085 void gtk_progress_bar_update (GtkProgressBar *pbar, gfloat percentage);
2086 </verb></tscreen>
2087
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).
2091
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.
2097
2098 Voici un exemple de barre de progression mise à jour par des
2099 expirations. Ce code montre aussi comment réinitialiser une barre.
2100
2101 <tscreen><verb>
2102 #include <gtk/gtk.h>
2103
2104 static int ptimer = 0;
2105 int pstat = TRUE;
2106
2107 /* Cette fonction incrémente et met à jour la barre de progression,
2108  * elle la réinitialise si pstat vaut FALSE */
2109
2110 gint progress (gpointer data)
2111 {
2112     gfloat pvalue;
2113     
2114     /* récupération de la valeur courante de la barre */
2115
2116     pvalue = GTK_PROGRESS_BAR (data)->percentage;
2117     
2118     if ((pvalue >= 1.0) || (pstat == FALSE)) {
2119         pvalue = 0.0;
2120         pstat = TRUE;
2121     }
2122     pvalue += 0.01;
2123     
2124     gtk_progress_bar_update (GTK_PROGRESS_BAR (data), pvalue);
2125     
2126     return TRUE;
2127 }
2128
2129 /* Cette fonction signale une réinitialisation de la barre */
2130
2131 void progress_r (void)
2132 {  
2133     pstat = FALSE;  
2134 }
2135
2136 void destroy (GtkWidget *widget, gpointer *data)
2137 {
2138     gtk_main_quit ();
2139 }
2140
2141 int main (int argc, char *argv[])
2142 {
2143     GtkWidget *window;
2144     GtkWidget *button;
2145     GtkWidget *label;
2146     GtkWidget *table;
2147     GtkWidget *pbar;
2148     
2149     gtk_init (&amp;argc, &amp;argv);
2150     
2151     window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2152     
2153     gtk_signal_connect (GTK_OBJECT (window), "delete_event",
2154                         GTK_SIGNAL_FUNC (destroy), NULL);
2155     
2156     gtk_container_border_width (GTK_CONTAINER (window), 10);
2157     
2158     table = gtk_table_new(3,2,TRUE);
2159     gtk_container_add (GTK_CONTAINER (window), table);
2160     
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);
2164     
2165     /* Crée une barre, la place dans la table et l'affiche */
2166
2167     pbar = gtk_progress_bar_new ();
2168     gtk_table_attach_defaults(GTK_TABLE(table), pbar, 0,2,1,2);
2169     gtk_widget_show (pbar);
2170     
2171     /* Configure le délai d'expiration pour gérer automatiquement la
2172      *  mise à jour de la barre */ 
2173
2174     ptimer = gtk_timeout_add (100, progress, pbar);
2175     
2176     /* Ce bouton indique à la barre qu'elle doit se réinitialiser */
2177
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);
2183     
2184     button = gtk_button_new_with_label ("Annuler");
2185     gtk_signal_connect (GTK_OBJECT (button), "clicked",
2186                         GTK_SIGNAL_FUNC (destroy), NULL);
2187     
2188     gtk_table_attach_defaults(GTK_TABLE(table), button, 1,2,2,3);
2189     gtk_widget_show (button);
2190     
2191     gtk_widget_show(table);
2192     gtk_widget_show(window);
2193     
2194     gtk_main ();
2195     
2196     return 0;
2197 }
2198 </verb></tscreen>
2199
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.
2203
2204 <tscreen><verb>
2205 pbar = gtk_progress_bar_new ();
2206 </verb></tscreen>
2207
2208 Cet appel crée une nouvelle barre, nommée <em/pbar/.
2209
2210 <tscreen><verb>
2211 ptimer = gtk_timeout_add (100, progress, pbar);
2212 </verb></tscreen>
2213
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.
2217
2218 <tscreen><verb>
2219 pvalue = GTK_PROGRESS_BAR (data)->percentage;
2220 </verb></tscreen>
2221
2222 Ce code assigne à <em/pvalue/ la valeur du pourcentage de la barre.
2223
2224 <tscreen><verb>
2225 gtk_progress_bar_update (GTK_PROGRESS_BAR (data), pvalue);
2226 </verb></tscreen>
2227
2228 Finalement, ce code met à jour la barre avec la valeur de <em/pvalue/.
2229
2230 Et c'est tout ce qu'il y a à savoir sur les barres de
2231 progression. Amusez-vous bien.
2232
2233 <sect1> Boîtes de dialogue
2234 <p>
2235
2236 Les widgets boîtes de dialogue sont très simples&nbsp;: 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&nbsp;:
2239
2240 <tscreen><verb>
2241 struct GtkDialog
2242 {
2243       GtkWindow window;
2244     
2245       GtkWidget *vbox;
2246       GtkWidget *action_area;
2247 };
2248 </verb></tscreen>
2249
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 ».
2252
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&nbsp;:
2256
2257 <tscreen><verb>
2258 GtkWidget* gtk_dialog_new (void);
2259 </verb></tscreen>
2260
2261 Ainsi, pour créer un nouveau dialogue, on utilise&nbsp;:
2262
2263 <tscreen><verb>
2264 GtkWidget window;
2265 window = gtk_dialog_new ();
2266 </verb></tscreen>
2267
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&nbsp;:
2271
2272 <tscreen><verb>
2273 button = ...
2274 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->action_area), button,
2275                     TRUE, TRUE, 0);
2276 gtk_widget_show (button);
2277 </verb></tscreen>
2278
2279 Et vous pouvez aussi ajouter un label à la zone de la vboxb&nbsp;:
2280
2281 <tscreen><verb>
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,
2284                     TRUE, 0);
2285 gtk_widget_show (label);
2286 </verb></tscreen>
2287
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.
2294
2295
2296 <sect1> Pixmaps
2297 <p>
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.
2302
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.
2308
2309 <tscreen><verb>
2310 GdkPixmap *gdk_bitmap_create_from_data( GdkWindow *window,
2311                                         gchar     *data,
2312                                         gint      width,
2313                                         gint      height );
2314 </verb></tscreen>
2315 <p>
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
2321 doit s'afficher.
2322
2323 <tscreen><verb>
2324 GdkPixmap* gdk_pixmap_create_from_data( GdkWindow  *window,
2325                                         gchar      *data,
2326                                         gint        width,
2327                                         gint        height,
2328                                         gint        depth,
2329                                         GdkColor   *fg,
2330                                         GdkColor   *bg );
2331 </verb></tscreen>
2332
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.
2337
2338 <tscreen><verb>
2339 GdkPixmap* gdk_pixmap_create_from_xpm( GdkWindow  *window,
2340                                        GdkBitmap **mask,
2341                                        GdkColor   *transparent_color,
2342                                        const gchar *filename );
2343 </verb></tscreen>
2344
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.
2346
2347 <tscreen><verb>
2348 GdkPixmap* gdk_pixmap_create_from_xpm_d (GdkWindow  *window,
2349                                          GdkBitmap **mask,
2350                                          GdkColor   *transparent_color,
2351                                          gchar     **data);
2352 </verb></tscreen>
2353
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&nbsp;:
2357
2358 <tscreen><verb>
2359 /* XPM */
2360 static const char * xpm_data[] = {
2361 "16 16 3 1",
2362 "       c None",
2363 ".      c #000000000000",
2364 "X      c #FFFFFFFFFFFF",
2365 "                ",
2366 "   ......       ",
2367 "   .XXX.X.      ",
2368 "   .XXX.XX.     ",
2369 "   .XXX.XXX.    ",
2370 "   .XXX.....    ",
2371 "   .XXXXXXX.    ",
2372 "   .XXXXXXX.    ",
2373 "   .XXXXXXX.    ",
2374 "   .XXXXXXX.    ",
2375 "   .XXXXXXX.    ",
2376 "   .XXXXXXX.    ",
2377 "   .XXXXXXX.    ",
2378 "   .........    ",
2379 "                ",
2380 "                "};
2381 </verb></tscreen>
2382
2383 <tscreen><verb>
2384 void gdk_pixmap_destroy( GdkPixmap  *pixmap );
2385 </verb></tscreen>
2386 <p>
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.
2388
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&nbsp;:
2392
2393 <tscreen><verb>
2394 GtkWidget* gtk_pixmap_new( GdkPixmap  *pixmap,
2395                            GdkBitmap  *mask );
2396 </verb></tscreen>
2397 <p>
2398 Les autres fonctions pour les widgets pixmap sont&nbsp;:
2399
2400 <tscreen><verb>
2401 guint gtk_pixmap_get_type( void );
2402 void  gtk_pixmap_set( GtkPixmap  *pixmap,
2403                       GdkPixmap  *val,
2404                       GdkBitmap  *mask);
2405 void  gtk_pixmap_get( GtkPixmap  *pixmap,
2406                       GdkPixmap **val,
2407                       GdkBitmap **mask);
2408 </verb></tscreen>
2409 <p>
2410 <em/gtk_pixmap_set/ sert à changer le pixmap pris en charge par le widget. <em/val/ est le pixmap créé par le GDK.
2411
2412 Voici un exemple illustrant l'utilisation d'un pixmap dans un bouton&nbsp;:
2413
2414 <tscreen><verb>
2415
2416 #include <gtk/gtk.h>
2417
2418
2419 /* données XPM d'une icône "Ouvrir fichier" */
2420 static const char * xpm_data[] = {
2421 "16 16 3 1",
2422 "       c None",
2423 ".      c #000000000000",
2424 "X      c #FFFFFFFFFFFF",
2425 "                ",
2426 "   ......       ",
2427 "   .XXX.X.      ",
2428 "   .XXX.XX.     ",
2429 "   .XXX.XXX.    ",
2430 "   .XXX.....    ",
2431 "   .XXXXXXX.    ",
2432 "   .XXXXXXX.    ",
2433 "   .XXXXXXX.    ",
2434 "   .XXXXXXX.    ",
2435 "   .XXXXXXX.    ",
2436 "   .XXXXXXX.    ",
2437 "   .XXXXXXX.    ",
2438 "   .........    ",
2439 "                ",
2440 "                "};
2441
2442
2443 /* Termine l'application lorsqu'elle est appelée 
2444  * via le signal "delete_event" */
2445
2446 void close_application( GtkWidget *widget, GdkEvent *event, gpointer *data ) 
2447 {
2448     gtk_main_quit();
2449 }
2450
2451
2452 /* Invoquée lorsque le bouton est cliqué. Affiche simplement 
2453  * un message. */
2454
2455 void button_clicked( GtkWidget *widget, gpointer *data ) 
2456 {
2457     printf( "bouton cliqué\n" );
2458 }
2459
2460
2461 int main( int argc, char *argv[] )
2462 {
2463     /* GtkWidget est le type pour déclarer les widgets */
2464
2465     GtkWidget *window, *pixmapwid, *button;
2466     GdkPixmap *pixmap;
2467     GdkBitmap *mask;
2468     GtkStyle *style;
2469     
2470     /* Crée la fenêtre principale et attache le signal "delete_event" pour 
2471      * terminer l'application */
2472
2473     gtk_init( &amp;argc, &amp;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 );
2479
2480     /* Utilisation de GDK pour créer le pixmap */
2481
2482     style = gtk_widget_get_style( window );
2483     pixmap = gdk_pixmap_create_from_xpm_d( window->window,  &amp;mask,
2484                                            &amp;style->bg[GTK_STATE_NORMAL],
2485                                            (gchar **)xpm_data );
2486
2487     /* Création d'un widget pixmap GTK pour contenir le pixmap GDK */
2488
2489     pixmapwid = gtk_pixmap_new( pixmap, mask );
2490     gtk_widget_show( pixmapwid );
2491
2492     /* Création d'un bouton pour contenir le widget pixmap */
2493
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 );
2498
2499     gtk_signal_connect( GTK_OBJECT(button), "clicked",
2500                         GTK_SIGNAL_FUNC(button_clicked), NULL );
2501
2502     /* Affichage de la fenêtre */
2503     gtk_main ();
2504           
2505     return 0;
2506 }
2507 </verb></tscreen>
2508
2509
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&nbsp;:
2513
2514 <tscreen><verb>
2515     /* Charge un pixmap à partir d'un fichier */
2516
2517     pixmap = gdk_pixmap_create_from_xpm( window->window, &amp;mask,
2518                                          &amp;style->bg[GTK_STATE_NORMAL],
2519                                          "./icon0.xpm" );
2520     pixmapwid = gtk_pixmap_new( pixmap, mask );
2521     gtk_widget_show( pixmapwid );
2522     gtk_container_add( GTK_CONTAINER(window), pixmapwid );
2523 </verb></tscreen>
2524
2525
2526 Utilisation des formes
2527 <p>
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.
2534
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.
2539
2540 <tscreen><verb>
2541
2542 #include <gtk/gtk.h>
2543
2544 /* XPM */
2545 static char * WheelbarrowFull_xpm[] = {
2546 "48 48 64 1",
2547 "       c None",
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 "&amp;      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",
2611 "                                                ",
2612 "          .XoO                                  ",
2613 "         +@#$%o&amp;                                ",
2614 "         *=-;#::o+                              ",
2615 "           >,<12#:34                            ",
2616 "             45671#:X3                          ",
2617 "               +89<02qwo                        ",
2618 "e*                >,67;ro                       ",
2619 "ty>                 459@>+&amp;&amp;                    ",
2620 "$2u+                  ><ipas8*                  ",
2621 "%$;=*                *3:.Xa.dfg>                ",
2622 "Oh$;ya             *3d.a8j,Xe.d3g8+             ",
2623 " Oh$;ka          *3d$a8lz,,xxc:.e3g54           ",
2624 "  Oh$;kO       *pd$%svbzz,sxxxxfX..&amp;wn>         ",
2625 "   Oh$@mO    *3dthwlsslszjzxxxxxxx3:td8M4       ",
2626 "    Oh$@g&amp; *3d$XNlvvvlllm,mNwxxxxxxxfa.:,B*     ",
2627 "     Oh$@,Od.czlllllzlmmqV@V#V@fxxxxxxxf:%j5&amp;   ",
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&amp;en",
2634 "           p2yFvzssXe:fCZZCiiD7iiZDiDSSZwwxx8e*>",
2635 "           OA1<jzxwwc:$d%NDZZZZCCCZCCZZCmxxfd.B ",
2636 "            3206Bwxxszx%et.eaAp77m77mmmf3&amp;eeeg* ",
2637 "             @26MvzxNzvlbwfpdettttttttttt.c,n&amp;  ",
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&amp;&amp;        ",
2645 "                &amp;i0ycm6n4 ogk17,0<6666g         ",
2646 "                 N-k-<>     >=01-kuu666>        ",
2647 "                 ,6ky&amp;      &amp;46-10ul,66,        ",
2648 "                 Ou0<>       o66y<ulw<66&amp;       ",
2649 "                  *kk5       >66By7=xu664       ",
2650 "                   <<M4      466lj<Mxu66o       ",
2651 "                   *>>       +66uv,zN666*       ",
2652 "                              566,xxj669        ",
2653 "                              4666FF666>        ",
2654 "                               >966666M         ",
2655 "                                oM6668+         ",
2656 "                                  *4            ",
2657 "                                                ",
2658 "                                                "};
2659
2660
2661 /* Termine l'application lorsqu'elle est appelée 
2662  * (via le signal "delete_event"). */
2663
2664 void close_application( GtkWidget *widget, GdkEvent *event, gpointer *data ) 
2665 {
2666     gtk_main_quit();
2667 }
2668
2669
2670 int main (int argc, char *argv[])
2671 {
2672     GtkWidget *window, *pixmap, *fixed;
2673     GdkPixmap *gdk_pixmap;
2674     GdkBitmap *mask;
2675     GtkStyle *style;
2676     GdkGC *gc;
2677     
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. */
2681
2682     gtk_init (&amp;argc, &amp;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);
2687
2688     /* Création du pixmap et du widget pixmap */
2689
2690     style = gtk_widget_get_default_style();
2691     gc = style->black_gc;
2692     gdk_pixmap = gdk_pixmap_create_from_xpm_d( window->window, &amp;mask,
2693                                              &amp;style->bg[GTK_STATE_NORMAL],
2694                                              WheelbarrowFull_xpm );
2695     pixmap = gtk_pixmap_new( gdk_pixmap, mask );
2696     gtk_widget_show( pixmap );
2697
2698     /* Pour afficher le pixmap, on utilise un widget fixe pour placer
2699      * le pixmap */
2700
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 );
2706
2707     /* On masque tout sauf l'image elle-même */
2708
2709     gtk_widget_shape_combine_mask( window, mask, 0, 0 );
2710     
2711     /* Affichage de la fenêtre */
2712
2713     gtk_widget_set_uposition( window, 20, 400 );
2714     gtk_widget_show( window );
2715     gtk_main ();
2716           
2717     return 0;
2718 }
2719 </verb></tscreen>
2720 <p>
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.
2725
2726 <tscreen><verb>
2727 gtk_widget_set_events( window,
2728                        gtk_widget_get_events( window ) |
2729                        GDK_BUTTON_PRESS_MASK );
2730
2731 gtk_signal_connect( GTK_OBJECT(window), "button_press_event",
2732                     GTK_SIGNAL_FUNC(close_application), NULL );
2733 </verb></tscreen>
2734
2735
2736 <sect> Widgets containers
2737
2738 <sect1> Bloc-notes
2739 <p>
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.
2746
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.
2749
2750 <tscreen><verb>
2751 GtkWidget* gtk_notebook_new (void);
2752 </verb></tscreen>
2753
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.
2756
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.
2760
2761 <tscreen><verb>
2762 void gtk_notebook_set_tab_pos (GtkNotebook *notebook, GtkPositionType pos);
2763 </verb></tscreen>
2764
2765 <em/GtkPositionType/ peut prendre les valeurs suivantes qu'il n'est
2766 pas nécessaire d'expliquer&nbsp;:
2767 <itemize>
2768 <item> GTK_POS_LEFT
2769 <item> GTK_POS_RIGHT
2770 <item> GTK_POS_TOP
2771 <item> GTK_POS_BOTTOM
2772 </itemize>
2773
2774 GTK_POS_TOP est la valeur par défaut.
2775
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.
2779
2780 <tscreen><verb>
2781 void gtk_notebook_append_page (GtkNotebook *notebook, GtkWidget *child, 
2782                                GtkWidget *tab_label);
2783
2784 void gtk_notebook_prepend_page (GtkNotebook *notebook, GtkWidget *child, 
2785                                 GtkWidget *tab_label);
2786 </verb></tscreen>
2787
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
2792 ajoutée.
2793
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.
2797
2798 <tscreen><verb>
2799 void gtk_notebook_insert_page (GtkNotebook *notebook, GtkWidget *child, 
2800                                GtkWidget *tab_label, gint position);
2801 </verb></tscreen>
2802
2803 Les paramètres sont les mêmes que <em/_append_/ et <em/_prepend_/ sauf
2804 qu'il y en a un de plus&nbsp;: <em/position/. Celui-ci sert à
2805 spécifier l'endroit où cette page sera insérée.
2806
2807 Maintenant que nous savons insérer une page, voyons comment en supprimer une.
2808
2809 <tscreen><verb>
2810 void gtk_notebook_remove_page (GtkNotebook *notebook, gint page_num);
2811 </verb></tscreen>
2812
2813 Cette fonction ôte la page spécifiée par <em/page_num/ du widget
2814 <em/*notebook/.
2815
2816 Pour connaître la page courante d'un bloc-notes, on dispose de la
2817 fonction&nbsp;:
2818
2819 <tscreen><verb>
2820 gint gtk_notebook_current_page (GtkNotebook *notebook);
2821 </verb></tscreen>
2822
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&nbsp;: 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
2830 dernière page.
2831
2832 <tscreen><verb>
2833 void gtk_notebook_next_page (GtkNoteBook *notebook);
2834 void gtk_notebook_prev_page (GtkNoteBook *notebook);
2835 </verb></tscreen>
2836
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
2840 par défaut.
2841
2842 <tscreen><verb>
2843 void gtk_notebook_set_page (GtkNotebook *notebook, gint page_num);
2844 </verb></tscreen>
2845
2846 Les deux fonctions suivantes ajoutent ou ôtent les indicateurs de page
2847 et le contour du bloc-notes, respectivement.
2848
2849 <tscreen><verb>
2850 void gtk_notebook_set_show_tabs (GtkNotebook *notebook, gint show_tabs);
2851 void gtk_notebook_set_show_border (GtkNotebook *notebook, gint show_border);
2852 </verb></tscreen>
2853
2854 <em/show_tabs/ et <em/show_border/ peuvent valoir TRUE ou FALSE (0 ou 1).
2855
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&nbsp;: à 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.
2864
2865
2866 <tscreen><verb>
2867
2868 #include <gtk/gtk.h>
2869
2870 /* Rotation des indicateurs de page  */
2871
2872 void rotate_book (GtkButton *button, GtkNotebook *notebook)
2873 {
2874     gtk_notebook_set_tab_pos (notebook, (notebook->tab_pos +1) %4);
2875 }
2876
2877 /* Ajout/Suppression des indicateurs de pages et des contours */
2878
2879 void tabsborder_book (GtkButton *button, GtkNotebook *notebook)
2880 {
2881     gint tval = FALSE;
2882     gint bval = FALSE;
2883     if (notebook->show_tabs == 0)
2884             tval = TRUE; 
2885     if (notebook->show_border == 0)
2886             bval = TRUE;
2887     
2888     gtk_notebook_set_show_tabs (notebook, tval);
2889     gtk_notebook_set_show_border (notebook, bval);
2890 }
2891
2892 /* Suppression d'une page */
2893
2894 void remove_book (GtkButton *button, GtkNotebook *notebook)
2895 {
2896     gint page;
2897     
2898     page = gtk_notebook_current_page(notebook);
2899     gtk_notebook_remove_page (notebook, page);
2900
2901     /* Il faut rafraîchir  le widget --
2902      * ce qui force le widget à se redessiner. */
2903
2904       gtk_widget_draw(GTK_WIDGET(notebook), NULL);
2905 }
2906
2907 void delete (GtkWidget *widget, GdkEvent *event, gpointer *data)
2908 {
2909     gtk_main_quit ();
2910 }
2911
2912 int main (int argc, char *argv[])
2913 {
2914     GtkWidget *window;
2915     GtkWidget *button;
2916     GtkWidget *table;
2917     GtkWidget *notebook;
2918     GtkWidget *frame;
2919     GtkWidget *label;
2920     GtkWidget *checkbutton;
2921     int i;
2922     char bufferf[32];
2923     char bufferl[32];
2924     
2925     gtk_init (&amp;argc, &amp;argv);
2926     
2927     window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2928     
2929     gtk_signal_connect (GTK_OBJECT (window), "delete_event",
2930                         GTK_SIGNAL_FUNC (delete), NULL);
2931     
2932     gtk_container_border_width (GTK_CONTAINER (window), 10);
2933     
2934     table = gtk_table_new(2,6,TRUE);
2935     gtk_container_add (GTK_CONTAINER (window), table);
2936     
2937     /* Création d'un bloc-notes, placement des indicateurs de page. */
2938
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);
2943     
2944     /* Ajoute un groupe de pages à la fin du bloc-notes. */
2945
2946     for (i=0; i < 5; i++) {
2947         sprintf(bufferf, "Append Frame %d", i+1);
2948         sprintf(bufferl, "Page %d", i+1);
2949         
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);
2954         
2955         label = gtk_label_new (bufferf);
2956         gtk_container_add (GTK_CONTAINER (frame), label);
2957         gtk_widget_show (label);
2958         
2959         label = gtk_label_new (bufferl);
2960         gtk_notebook_append_page (GTK_NOTEBOOK (notebook), frame, label);
2961     }
2962     
2963     
2964     /* Ajoute une page à un endroit précis. */
2965
2966     checkbutton = gtk_check_button_new_with_label ("Cochez moi !");
2967     gtk_widget_set_usize(checkbutton, 100, 75);
2968     gtk_widget_show (checkbutton);
2969     
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);
2975     
2976     /* Ajout de pages au début du bloc-notes */
2977
2978     for (i=0; i < 5; i++) {
2979         sprintf(bufferf, "Prepend Frame %d", i+1);
2980         sprintf(bufferl, "Page %d", i+1);
2981         
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);
2986         
2987         label = gtk_label_new (bufferf);
2988         gtk_container_add (GTK_CONTAINER (frame), label);
2989         gtk_widget_show (label);
2990         
2991         label = gtk_label_new (bufferl);
2992         gtk_notebook_prepend_page (GTK_NOTEBOOK(notebook), frame, label);
2993     }
2994     
2995     /* Configuration de la page de départ (page 4) */
2996
2997     gtk_notebook_set_page (GTK_NOTEBOOK(notebook), 3);
2998     
2999     
3000     /* Création des boutons */
3001
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);
3007     
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);
3014     
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);
3021     
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);
3027     
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);
3034     
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);
3041     
3042     gtk_widget_show(table);
3043     gtk_widget_show(window);
3044     
3045     gtk_main ();
3046     
3047     return 0;
3048 }
3049 </verb></tscreen>
3050 <p>
3051 En espérant que ceci vous aide à créer des blocs-notes pour vos
3052 applications GTK.
3053
3054
3055 <sect1> Fenêtres avec barres de défilement
3056 <p>
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.
3061
3062
3063 La fonction suivante sert à créer une fenêtre avec barre de
3064 défilement&nbsp;:
3065
3066 <tscreen><verb>
3067 GtkWidget* gtk_scrolled_window_new (GtkAdjustment *hadjustment,
3068                                     GtkAdjustment *vadjustment);
3069 </verb></tscreen>
3070 <p>
3071 Le premier paramètre est l'ajustement horizontal, et le second
3072 l'ajustement vertical. Ils sont presque toujours positionnés à NULL.
3073
3074 <tscreen><verb>
3075 void gtk_scrolled_window_set_policy      (GtkScrolledWindow *scrolled_window,
3076                                           GtkPolicyType      hscrollbar_policy,
3077                                           GtkPolicyType      vscrollbar_policy);
3078 </verb></tscreen>
3079
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.
3084
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
3088 celles-ci.
3089
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.
3093
3094 <tscreen><verb>
3095 #include <gtk/gtk.h>
3096
3097 void destroy(GtkWidget *widget, gpointer *data)
3098 {
3099     gtk_main_quit();
3100 }
3101
3102 int main (int argc, char *argv[])
3103 {
3104     static GtkWidget *window;
3105     GtkWidget *scrolled_window;
3106     GtkWidget *table;
3107     GtkWidget *button;
3108     char buffer[32];
3109     int i, j;
3110     
3111     gtk_init (&amp;argc, &amp;argv);
3112     
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. */
3117
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);
3123     
3124     /* Création d'une fenêtre à défilement. */
3125
3126     scrolled_window = gtk_scrolled_window_new (NULL, NULL);
3127     
3128     gtk_container_border_width (GTK_CONTAINER (scrolled_window), 10);
3129     
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. */
3135
3136     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
3137                                     GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
3138
3139     /* Création d'une boîte de dialogue */
3140
3141     gtk_box_pack_start (GTK_BOX (GTK_DIALOG(window)->vbox), scrolled_window, 
3142                         TRUE, TRUE, 0);
3143     gtk_widget_show (scrolled_window);
3144     
3145     /* Création d'une table de 10x10 cases. */
3146
3147     table = gtk_table_new (10, 10, FALSE);
3148     
3149     /* Configure l'espace des lignes et des colonnes de 10 pixels */
3150
3151     gtk_table_set_row_spacings (GTK_TABLE (table), 10);
3152     gtk_table_set_col_spacings (GTK_TABLE (table), 10);
3153     
3154     /* Place la table fans la fenêtre à défilement */
3155
3156     gtk_container_add (GTK_CONTAINER (scrolled_window), table);
3157     gtk_widget_show (table);
3158     
3159     /* Crée une grille de boutons commutateurs dans la table */
3160
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,
3166                                            i, i+1, j, j+1);
3167                 gtk_widget_show (button);
3168             }
3169     
3170     /* Ajoute un bouton « Fermer » en bas de la boîte de dialogue */
3171
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));
3176     
3177     /* On met ce bouton en « bouton par défaut ». */
3178     
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);
3181     
3182     /* Récupère le bouton par défaut. Le fait de presser la touche « Entrée »
3183      * activera le bouton. */
3184
3185     gtk_widget_grab_default (button);
3186     gtk_widget_show (button);
3187     
3188     gtk_widget_show (window);
3189     
3190     gtk_main();
3191     
3192     return(0);
3193 }
3194 </verb></tscreen>
3195 <p>
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.
3200
3201
3202 <sect>Widgets listes
3203 <p>
3204 Le widget <em/GtkList/ sert de container vertical pour des widgets <em/GtkListItem/.
3205
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)&nbsp;:
3210 voir le widget <em/GtkContainer/ pour en savoir plus.
3211
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/.
3215
3216 Un champ de la structure d'un widget <em/GtkList/ nous intéresse particulièrement&nbsp;:
3217
3218 <tscreen><verb>
3219 struct _GtkList
3220 {
3221   ...
3222   GList *selection;
3223   guint selection_mode;
3224   ...
3225 }; 
3226 </verb></tscreen>
3227
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
3233 <em/gtk_list_*()/.
3234
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/&nbsp;:
3238
3239 <em/selection_mode/ peut avoir l'une des valeurs suivantes&nbsp;:
3240 <itemize>
3241 <item> GTK_SELECTION_SINGLE - <em/selection/ vaut NULL ou contient un
3242 pointeur vers un seul item sélectionné.
3243
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.
3248
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.
3252
3253 <item> GTK_SELECTION_EXTENDED - <em/selection/ vaut toujours NULL.
3254 </itemize>
3255 <p>
3256 La valeur par défaut est GTK_SELECTION_MULTIPLE.
3257
3258 <sect1>Signaux
3259 <p>
3260 <tscreen><verb>
3261 void GtkList::selection_changed (GtkList *LIST)
3262 </verb></tscreen>
3263
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é.
3267
3268 <tscreen><verb>
3269 void GtkList::select_child (GtkList *LIST, GtkWidget *CHILD)
3270 </verb></tscreen>
3271
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.
3277
3278 <tscreen><verb>
3279 void GtkList::unselect_child (GtkList *LIST, GtkWidget *CHILD)
3280 </verb></tscreen>
3281
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.
3287
3288
3289 <sect1>Fonctions
3290 <p>
3291 <tscreen><verb>
3292 guint gtk_list_get_type (void)
3293 </verb></tscreen>
3294
3295 Retourne l'identificateur de type « GtkList ».
3296
3297 <tscreen><verb>
3298 GtkWidget* gtk_list_new (void)
3299 </verb></tscreen>
3300
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é
3303 en cas d'erreur.
3304
3305 <tscreen><verb>
3306 void gtk_list_insert_items (GtkList *LIST, GList *ITEMS, gint POSITION)
3307 </verb></tscreen>
3308
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/.
3313
3314
3315 <tscreen><verb>
3316 void gtk_list_append_items (GtkList *LIST, GList *ITEMS)
3317 </verb></tscreen>
3318
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/.
3322
3323 <tscreen><verb>
3324 void gtk_list_prepend_items (GtkList *LIST, GList *ITEMS)
3325 </verb></tscreen>
3326
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/.
3330
3331 <tscreen><verb>
3332 void gtk_list_remove_items (GtkList *LIST, GList *ITEMS)
3333 </verb></tscreen>
3334
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
3339 lui-même les items.
3340
3341 <tscreen><verb>
3342 void gtk_list_clear_items (GtkList *LIST, gint START, gint END)
3343 </verb></tscreen>
3344
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/.
3348
3349 <tscreen><verb>
3350 void gtk_list_select_item (GtkList *LIST, gint ITEM)
3351 </verb></tscreen>
3352
3353 Invoque le signal <em/GtkList::select_child/ pour un item spécifié par
3354 sa position courante dans <em/LIST/.
3355
3356 <tscreen><verb>
3357 void gtk_list_unselect_item (GtkList *LIST, gint ITEM)
3358 </verb></tscreen>
3359
3360 Invoque le signal <em/GtkList::unselect_child/ pour un item spécifié par
3361 sa position courante dans <em/LIST/.
3362
3363 <tscreen><verb>
3364 void gtk_list_select_child (GtkList *LIST, GtkWidget *CHILD)
3365 </verb></tscreen>
3366
3367 Invoque le signal <em/GtkList::select_child/ pour le fils <em/CHILD/ spécifié.
3368
3369 <tscreen><verb>
3370 void gtk_list_unselect_child (GtkList *LIST, GtkWidget *CHILD)
3371 </verb></tscreen>
3372
3373 Invoque le signal <em/GtkList::unselect_child/ pour le fils <em/CHILD/ spécifié.
3374
3375 <tscreen><verb>
3376 gint gtk_list_child_position (GtkList *LIST, GtkWidget *CHILD)
3377 </verb></tscreen>
3378
3379 Retourne la position de <em/CHILD/ dans <em/LIST/. Retourne -1 en cas d'erreur.
3380
3381 <tscreen><verb>
3382 void gtk_list_set_selection_mode (GtkList *LIST, GtkSelectionMode MODE)
3383 </verb></tscreen>
3384
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.
3388
3389 <tscreen><verb>
3390 GtkList* GTK_LIST (gpointer OBJ)
3391 </verb></tscreen>
3392
3393 Convertit un pointeur générique en « <\em GtkList*\ ». Voir
3394 <em/Standard Macros::/, pour plus d'informations.
3395
3396 <tscreen><verb>
3397 GtkListClass* GTK_LIST_CLASS (gpointer CLASS)
3398 </verb></tscreen>
3399
3400 Convertit un pointeur générique en « GtkListClass* ».  Voir
3401 <em/Standard Macros::/, pour plus d'informations.
3402
3403 <tscreen><verb>
3404 gint GTK_IS_LIST (gpointer OBJ)
3405 </verb></tscreen>
3406
3407 Détermine si un pointeur générique référence un objet « GtkList ». Voir
3408 <em/Standard Macros::/, pour plus d'informations.
3409
3410 <sect1>Exemple
3411 <p>
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.
3415
3416 <tscreen><verb>
3417 /* Compilez ce programme avec :
3418  * $ gcc -L/usr/X11R6/lib/ -I/usr/local/include/ -lgtk -lgdk -lglib -lX11 -lm -Wall main.c
3419  */
3420 #include        <gtk/gtk.h>
3421 #include        <stdio.h>
3422
3423 /* Chaîne pour stocker les données dans les items de la liste. */
3424
3425 const   gchar   *list_item_data_key="list_item_data";
3426
3427
3428 /* prototypes des gestionnaires de signaux que l'on connectera au widget GtkList. */
3429
3430 static  void    sigh_print_selection    (GtkWidget      *gtklist,
3431                                          gpointer       func_data);
3432 static  void    sigh_button_event       (GtkWidget      *gtklist,
3433                                          GdkEventButton *event,
3434                                          GtkWidget      *frame);
3435
3436
3437 /* fonction principale pour configurer l'interface utilisateur */
3438
3439 gint main (int argc, gchar *argv[])
3440 {                                  
3441     GtkWidget       *separator;
3442     GtkWidget       *window;
3443     GtkWidget       *vbox;
3444     GtkWidget       *scrolled_window;
3445     GtkWidget       *frame;
3446     GtkWidget       *gtklist;
3447     GtkWidget       *button;
3448     GtkWidget       *list_item;
3449     GList           *dlist;
3450     guint           i;
3451     gchar           buffer[64];
3452     
3453     
3454     /* initialise gtk (et donc gdk) */
3455
3456     gtk_init(&amp;argc, &amp;argv);
3457     
3458     
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. */
3463
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),
3467                        "destroy",
3468                        GTK_SIGNAL_FUNC(gtk_main_quit),
3469                        NULL);
3470     
3471     
3472     /* À l'intérieur de la fenêtre, on a besoin d'une boîte pour placer 
3473      * verticalement les widgets. */
3474
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);
3479     
3480     /* Fenêtre à défilement pour placer le widget GtkList à l'intérieur. */
3481
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);
3486     
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. */
3491
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),
3498                        NULL);
3499     
3500     /* Création d'une « Prison » pour y mettre un item. */
3501
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);
3508     
3509     /* Connexion du gestionnaire de signal sigh_button_event() au signal
3510      * « mise au arrêts » des items du GtkList. */
3511
3512     gtk_signal_connect(GTK_OBJECT(gtklist),
3513                        "button_release_event",
3514                        GTK_SIGNAL_FUNC(sigh_button_event),
3515                        frame);
3516     
3517     /* Création d'un séparateur. */
3518
3519     separator=gtk_hseparator_new();
3520     gtk_container_add(GTK_CONTAINER(vbox), separator);
3521     gtk_widget_show(separator);
3522     
3523     /* Création d'un bouton et connexion de son signal "clicked" à la
3524      * destruction de la fenêtre. */
3525
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),
3530                               "clicked",
3531                               GTK_SIGNAL_FUNC(gtk_widget_destroy),
3532                               GTK_OBJECT(window));
3533     
3534     
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. */
3539     
3540     for (i=0; i<5; i++) {
3541         GtkWidget       *label;
3542         gchar           *string;
3543         
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), &amp;string);
3552         gtk_object_set_data(GTK_OBJECT(list_item),
3553                             list_item_data_key,
3554                             string);
3555     }
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()). */
3566      
3567     dlist=NULL;
3568     for (; i<10; i++) {
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),
3574                             list_item_data_key,
3575                             "Item avec label intégré");
3576     }
3577     gtk_list_append_items(GTK_LIST(gtklist), dlist);
3578     
3579     /* Enfin, on veut voir la fenêtre... */
3580     
3581     gtk_widget_show(window);
3582     
3583     /* Lancement de la boucle principale de gtk */
3584     
3585     gtk_main();
3586     
3587     /* On arrive ici après que gtk_main_quit() ait été appelée lorsque
3588      * la fenêtre principale a été détruite. */
3589
3590 }
3591
3592 /* Gestionnaire de signal connecté aux événements boutons presser/relâcher
3593  * du GtkList. */
3594
3595 void
3596 sigh_button_event       (GtkWidget      *gtklist,
3597                          GdkEventButton *event,
3598                          GtkWidget      *frame)
3599 {
3600     /* On ne fait quelque chose que si le troisième bouton (celui de droite) a été
3601      * relâché. */
3602
3603     if (event->type==GDK_BUTTON_RELEASE &amp;&amp;
3604         event->button==3) {
3605         GList           *dlist, *free_list;
3606         GtkWidget       *new_prisoner;
3607         
3608         /* On recherche l'item sélectionné à ce moment précis. 
3609          * Ce sera notre prisonnier ! */
3610
3611         dlist=GTK_LIST(gtklist)->selection;
3612         if (dlist)
3613                 new_prisoner=GTK_WIDGET(dlist->data);
3614         else
3615                 new_prisoner=NULL;
3616         
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. */
3621         
3622         dlist=gtk_container_children(GTK_CONTAINER(frame));
3623         free_list=dlist;
3624         while (dlist) {
3625             GtkWidget       *list_item;
3626             
3627             list_item=dlist->data;
3628             
3629             gtk_widget_reparent(list_item, gtklist);
3630             
3631             dlist=dlist->next;
3632         }
3633         g_list_free(free_list);
3634         
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.
3637
3638         if (new_prisoner) {
3639             GList   static_dlist;
3640             
3641             static_dlist.data=new_prisoner;
3642             static_dlist.next=NULL;
3643             static_dlist.prev=NULL;
3644             
3645             gtk_list_unselect_child(GTK_LIST(gtklist),
3646                                     new_prisoner);
3647             gtk_widget_reparent(new_prisoner, frame);
3648         }
3649     }
3650 }
3651
3652 /* Gestionnaire de signal appelé lorsque le GtkList
3653  * émet le signal "selection_changed". */
3654
3655 void
3656 sigh_print_selection    (GtkWidget      *gtklist,
3657                          gpointer       func_data)
3658 {
3659     GList   *dlist;
3660     
3661     /* Recherche dans la liste doublement chaînée des items sélectionnés
3662      * du GtkList, à faire en lecture seulement ! */
3663
3664     dlist=GTK_LIST(gtklist)->selection;
3665     
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. */
3668
3669     if (!dlist) {
3670         g_print("Sélection nettoyée\n");
3671         return;
3672     }
3673     /* Ok, on a une sélection et on l'affiche. */
3674
3675     g_print("La sélection est ");
3676     
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. */
3680
3681     while (dlist) {
3682         GtkObject       *list_item;
3683         gchar           *item_data_string;
3684         
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);
3689         
3690         dlist=dlist->next;
3691     }
3692     g_print("\n");
3693 }
3694 </verb></tscreen>
3695
3696 <sect1>Widget item de liste
3697 <p>
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.
3701
3702 Un <em/GtkListItem/ a sa propre fenêtre pour recevoir les événements et a
3703 sa propre couleur de fond, habituellement blanche.
3704
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/.
3714
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/.
3718
3719 <sect1>Signaux
3720 <p>
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
3723 d'informations.
3724
3725
3726 <sect1>Fonctions
3727 <p>
3728
3729 <tscreen><verb>
3730 guint gtk_list_item_get_type (void)
3731 </verb></tscreen>
3732
3733 Retourne l'identificateur du type « GtkListItem ».
3734
3735 <tscreen><verb>
3736 GtkWidget* gtk_list_item_new (void)
3737 </verb></tscreen>
3738
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.
3742
3743 <tscreen><verb>
3744 GtkWidget* gtk_list_item_new_with_label (gchar *LABEL)
3745 </verb></tscreen>
3746
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
3750 d'erreur.
3751
3752 <tscreen><verb>
3753 void gtk_list_item_select (GtkListItem *LIST_ITEM)
3754 </verb></tscreen>
3755
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.
3759
3760 <tscreen><verb>
3761 void gtk_list_item_deselect (GtkListItem *LIST_ITEM)
3762 </verb></tscreen>
3763
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.
3767
3768 <tscreen><verb>
3769 GtkListItem* GTK_LIST_ITEM (gpointer OBJ)
3770 </verb></tscreen>
3771
3772 Convertit un pointeur générique en <em/GtkListItem*/. Voir
3773 <em/Standard Macros::/ pour plus d'informations.
3774
3775 <tscreen><verb>
3776 GtkListItemClass* GTK_LIST_ITEM_CLASS (gpointer CLASS)
3777 </verb></tscreen>
3778
3779 Convertit un pointeur générique en <em/GtkListItemClass*/. Voir
3780 <em/Standard Macros::/ pour plus d'informations.
3781
3782 <tscreen><verb>
3783 gint GTK_IS_LIST_ITEM (gpointer OBJ)
3784 </verb></tscreen>
3785
3786 Détermine si un pointeur générique se réfère à un objet
3787 <em/GtkListItem/.  Voir <em/Standard Macros::/ pour plus
3788 d'informations.
3789  
3790 <sect1>Exemple
3791 <p>
3792 L'exemple des GtkList couvre aussi l'utilisation des 
3793 GtkListItem. Étudiez-le attentivement.
3794
3795
3796 <sect>Widgets sélections de fichiers
3797 <p>
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.
3802
3803 Pour créer une boîte de sélection de fichier, on utilise&nbsp;:
3804
3805 <tscreen><verb>
3806 GtkWidget* gtk_file_selection_new (gchar *title);
3807 </verb></tscreen>
3808
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
3811 la fonction&nbsp;:
3812
3813 <tscreen><verb>
3814 void gtk_file_selection_set_filename (GtkFileSelection *filesel, gchar *filename);
3815 </verb></tscreen>
3816
3817 Pour récupérer le texte que l'utilisateur a entré, ou sur lequel il a
3818 cliqué, on utilisera la fonction&nbsp;:
3819
3820 <tscreen><verb>
3821 gchar* gtk_file_selection_get_filename (GtkFileSelection *filesel);
3822 </verb></tscreen>
3823
3824 Des pointeurs permettent d'accéder aux widgets contenus dans la widget
3825 de sélection de fichiers. Ce sont&nbsp;:
3826
3827 <itemize>
3828 <item>dir_list
3829 <item>file_list
3830 <item>selection_entry
3831 <item>selection_text
3832 <item>main_vbox
3833 <item>ok_button
3834 <item>cancel_button
3835 <item>help_button
3836 </itemize>
3837
3838 Le plus souvent, on utilise les pointeurs <em/ok_button, cancel_button/, et
3839 <em/help_button/ pour préciser leurs utilisations.
3840
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
3845 ne lui est attaché.
3846
3847 <tscreen><verb>
3848 #include <gtk/gtk.h>
3849
3850 /* Récupère le nom de fichier sélectionné et l'affiche sur la console. */
3851
3852 void file_ok_sel (GtkWidget *w, GtkFileSelection *fs)
3853 {
3854     g_print ("%s\n", gtk_file_selection_get_filename (GTK_FILE_SELECTION (fs)));
3855 }
3856
3857 void destroy (GtkWidget *widget, gpointer *data)
3858 {
3859     gtk_main_quit ();
3860 }
3861
3862 int main (int argc, char *argv[])
3863 {
3864     GtkWidget *filew;
3865     
3866     gtk_init (&amp;argc, &amp;argv);
3867     
3868     /* Création d'un widget de sélection de fichier. */
3869
3870     filew = gtk_file_selection_new ("File selection");
3871     
3872     gtk_signal_connect (GTK_OBJECT (filew), "destroy",
3873                         (GtkSignalFunc) destroy, &amp;filew);
3874
3875     /* Connexion de ok_button à la fonction file_ok_sel() */
3876
3877     gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (filew)->ok_button),
3878                         "clicked", (GtkSignalFunc) file_ok_sel, filew );
3879     
3880     /* Connexion de cancel_button pour détruire le widget */
3881
3882     gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION (filew)->cancel_button),
3883                                "clicked", (GtkSignalFunc) gtk_widget_destroy,
3884                                GTK_OBJECT (filew));
3885     
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. */
3888
3889     gtk_file_selection_set_filename (GTK_FILE_SELECTION(filew), 
3890                                      "penguin.png");
3891     
3892     gtk_widget_show(filew);
3893     gtk_main ();
3894     return 0;
3895 }
3896 </verb></tscreen>
3897
3898 <sect>Widgets Menu
3899 <p>
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.
3907
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 « /
3913 » aux menus.
3914 <p>
3915 <sect1>Création manuelle de menus
3916 <p>
3917 Selon la tradition pédagogique, nous commencerons par le plus compliqué <tt/:)/
3918 <p>
3919 Regardons les fonctions utilisées pour créer les menus. La première sert à créer un nouveau menu.
3920
3921 <tscreen><verb>
3922 GtkWidget *gtk_menu_bar_new()
3923 </verb></tscreen>
3924
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
3928 pour les boutons.
3929
3930 <tscreen><verb>
3931 GtkWidget *gtk_menu_new();
3932 </verb></tscreen>
3933
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.
3938 <p>
3939 Les deux appels suivants servent à créer des items de menu qui seront
3940 placés dans le menu.
3941
3942 <tscreen><verb>
3943 GtkWidget *gtk_menu_item_new()
3944 </verb></tscreen>
3945
3946 et
3947
3948 <tscreen><verb>
3949 GtkWidget *gtk_menu_item_new_with_label(const char *label)
3950 </verb></tscreen>
3951
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
3957 items.
3958
3959
3960 <tscreen><verb>
3961 gtk_menu_item_append()
3962
3963 gtk_menu_item_set_submenu()
3964 </verb></tscreen>
3965
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.
3970 <p>
3971 Voici les étapes pour créer une barre de menu avec des menus attachés&nbsp;:
3972 <itemize>
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.
3988 </itemize>
3989 <p>
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é ».
3994
3995 Suivez ces étapes&nbsp;
3996 <itemize>
3997 <item>Créer une fonction de gestion d'événement. Elle doit avoir le prototype
3998 <tscreen>
3999 static gint handler(GtkWidget *widget, GdkEvent *event);
4000 </tscreen>
4001 et elle utilisera l'événement <em/event/ pour savoir où faire surgir
4002 le menu.  
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()/.
4007
4008 <item> Lier ce gestionnaire à un widget avec&nbsp;:
4009 <tscreen>
4010 gtk_signal_connect_object(GTK_OBJECT(widget), "event",
4011                           GTK_SIGNAL_FUNC (handler), GTK_OBJECT(menu));
4012 </tscreen>
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.
4017 </itemize>
4018 <p>
4019 <sect1>Exemple de menu manuel
4020 <p>
4021 <tscreen><verb>
4022
4023 #include <gtk/gtk.h>
4024
4025 static gint button_press (GtkWidget *, GdkEvent *);
4026 static void menuitem_response (GtkWidget *, gchar *);
4027
4028
4029 int main (int argc, char *argv[])
4030 {
4031
4032     GtkWidget *window;
4033     GtkWidget *menu;
4034     GtkWidget *menu_bar;
4035     GtkWidget *root_menu;
4036     GtkWidget *menu_items;
4037     GtkWidget *vbox;
4038     GtkWidget *button;
4039     char buf[128];
4040     int i;
4041
4042     gtk_init (&amp;argc, &amp;argv);
4043
4044     /* Création d'un fenêtre */
4045
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);
4050
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. */
4055
4056     menu = gtk_menu_new();
4057
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é. */
4061
4062     root_menu = gtk_menu_item_new_with_label("Menu racine");
4063
4064     gtk_widget_show(root_menu);
4065
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. */
4071
4072     for(i = 0; i < 3; i++)
4073         {
4074             /* Copie des noms dans buf. */
4075
4076             sprintf(buf, "Sous-menu Test - %d", i);
4077
4078             /* Création d'un nouveau item de menu avec un nom... */
4079
4080             menu_items = gtk_menu_item_new_with_label(buf);
4081
4082             /* ...et ajout de celui-ci dans le menu. */
4083
4084             gtk_menu_append(GTK_MENU (menu), menu_items);
4085
4086             /* On fait quelque chose d'intéressant lorsque l'item est
4087              * sélectionné. */
4088
4089             gtk_signal_connect (GTK_OBJECT(menu_items), "activate",
4090                 GTK_SIGNAL_FUNC(menuitem_response), (gpointer)
4091                 g_strdup(buf));
4092                       
4093             /* Affichage du widget. */
4094
4095             gtk_widget_show(menu_items);
4096         }
4097
4098     /* Maintenant, on spécifié que nous voulons que notre nouveau « menu »
4099      * soit le menu du « menu racine ». */
4100
4101     gtk_menu_item_set_submenu(GTK_MENU_ITEM (root_menu), menu);
4102
4103     /* Création d'une vbox pour y mettre un menu et un bouton. */
4104
4105     vbox = gtk_vbox_new(FALSE, 0);
4106     gtk_container_add(GTK_CONTAINER(window), vbox);
4107     gtk_widget_show(vbox);
4108
4109     /* Création d'une barre de menus pour contenir les menus. Puis, on
4110      * l'ajoute à notre fenêtre principale. */
4111
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);
4115
4116     /* Création d'un bouton pour y attacher le menu. */
4117
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);
4123
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é ;-) */
4126
4127     gtk_menu_bar_append(GTK_MENU_BAR (menu_bar), root_menu);
4128
4129     /* Affichage de la fenêtre. */
4130
4131     gtk_widget_show(window);
4132
4133     gtk_main ();
4134
4135     return 0;
4136 }
4137
4138
4139
4140 /* On répond à un appui sur le bouton en postant un nouveau menu passé comme 
4141  * un widget.
4142  *
4143  * On remarque que le paramètre "widget" est le menu à poster, PAS le bouton
4144  * qui a été pressé. */
4145
4146
4147 static gint button_press (GtkWidget *widget, GdkEvent *event)
4148 {
4149
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);
4154
4155         /* On indique à l'appelant que l'on a géré cet événement. */
4156
4157         return TRUE;
4158     }
4159
4160     /* On indique à l'appelant que l'on n'a pas géré cet événement. */
4161
4162     return FALSE;
4163 }
4164
4165
4166 /* Affiche une chaîne lorsqu'un item de menu est choisi. */
4167
4168 static void menuitem_response (GtkWidget *widget, gchar *string)
4169 {
4170     printf("%s\n", string);
4171 }
4172 </verb></tscreen>
4173
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.
4177 <p>
4178 <sect1>Utilisation de GtkMenuFactory
4179 <p>
4180 Maintenant que nous avons exploré la voie difficile, nous allons voir
4181 l'utilisation des appels <em/gtk_menu_factory./
4182 <p>
4183 <sect1>Exemple d'usine à menu
4184 <p>
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/.
4188
4189 <tscreen><verb>
4190 #ifndef __MENUS_H__
4191 #define __MENUS_H__
4192
4193 #ifdef __cplusplus
4194 extern "C" {
4195 #endif /* __cplusplus */
4196
4197 void get_main_menu (GtkWidget **menubar, GtkAcceleratorTable **table);
4198 void menus_create(GtkMenuEntry *entries, int nmenu_entries);
4199
4200 #ifdef __cplusplus
4201 }
4202 #endif /* __cplusplus */
4203
4204 #endif /* __MENUS_H__ */
4205 </verb></tscreen>
4206 <p>
4207 Voici le fichier <em/menus.c/&nbsp;:
4208
4209 <tscreen><verb>
4210
4211 #include <gtk/gtk.h>
4212 #include <strings.h>
4213
4214 #include "main.h"
4215
4216
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);
4221
4222
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. */
4229  
4230
4231 static GtkMenuEntry menu_items[] =
4232 {
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}
4240 };
4241
4242 /* Calcul du nombre d'éléments de menu_item */
4243
4244 static int nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
4245
4246 static int initialize = TRUE;
4247 static GtkMenuFactory *factory = NULL;
4248 static GtkMenuFactory *subfactory[1];
4249 static GHashTable *entry_ht = NULL;
4250
4251 void get_main_menu(GtkWidget ** menubar, GtkAcceleratorTable ** table)
4252 {
4253     if (initialize)
4254             menus_init();
4255     
4256     if (menubar)
4257             *menubar = subfactory[0]->widget;
4258     if (table)
4259             *table = subfactory[0]->table;
4260 }
4261
4262 void menus_init(void)
4263 {
4264     if (initialize) {
4265         initialize = FALSE;
4266         
4267         factory = gtk_menu_factory_new(GTK_MENU_FACTORY_MENU_BAR);
4268         subfactory[0] = gtk_menu_factory_new(GTK_MENU_FACTORY_MENU_BAR);
4269         
4270         gtk_menu_factory_add_subfactory(factory, subfactory[0], "<Main>");
4271         menus_create(menu_items, nmenu_items);
4272     }
4273 }
4274
4275 void menus_create(GtkMenuEntry * entries, int nmenu_entries)
4276 {
4277     char *accelerator;
4278     int i;
4279     
4280     if (initialize)
4281             menus_init();
4282     
4283     if (entry_ht)
4284             for (i = 0; i < nmenu_entries; i++) {
4285                 accelerator = g_hash_table_lookup(entry_ht, entries[i].path);
4286                 if (accelerator) {
4287                     if (accelerator[0] == '\0')
4288                             entries[i].accelerator = NULL;
4289                     else
4290                             entries[i].accelerator = accelerator;
4291                 }
4292             }
4293     gtk_menu_factory_add_entries(factory, entries, nmenu_entries);
4294     
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,
4299                                    entries[i].path);
4300                 gtk_signal_connect(GTK_OBJECT(entries[i].widget), "remove_accelerator",
4301                                    (GtkSignalFunc) menus_remove_accel,
4302                                    entries[i].path);
4303             }
4304 }
4305
4306 static gint menus_install_accel(GtkWidget * widget, gchar * signal_name, gchar key, gchar modifiers, gchar * path)
4307 {
4308     char accel[64];
4309     char *t1, t2[2];
4310     
4311     accel[0] = '\0';
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>");
4318     
4319     t2[0] = key;
4320     t2[1] = '\0';
4321     strcat(accel, t2);
4322     
4323     if (entry_ht) {
4324         t1 = g_hash_table_lookup(entry_ht, path);
4325         g_free(t1);
4326     } else
4327             entry_ht = g_hash_table_new(g_string_hash, g_string_equal);
4328     
4329     g_hash_table_insert(entry_ht, path, g_strdup(accel));
4330     
4331     return TRUE;
4332 }
4333
4334 static void menus_remove_accel(GtkWidget * widget, gchar * signal_name, gchar * path)
4335 {
4336     char *t;
4337     
4338     if (entry_ht) {
4339         t = g_hash_table_lookup(entry_ht, path);
4340         g_free(t);
4341         
4342         g_hash_table_insert(entry_ht, path, g_strdup(""));
4343     }
4344 }
4345
4346 void menus_set_sensitive(char *path, int sensitive)
4347 {
4348     GtkMenuPath *menu_path;
4349     
4350     if (initialize)
4351             menus_init();
4352     
4353     menu_path = gtk_menu_factory_find(factory, path);
4354     if (menu_path)
4355             gtk_widget_set_sensitive(menu_path->widget, sensitive);
4356     else
4357             g_warning("Impossible de configurer la sensitivité d'un menu qui n'existe pas : %s", path);
4358 }
4359
4360 </verb></tscreen>
4361 <p>
4362 Voici <em/main.h/&nbsp;:
4363
4364 <tscreen><verb>
4365 #ifndef __MAIN_H__
4366 #define __MAIN_H__
4367
4368
4369 #ifdef __cplusplus
4370 extern "C" {
4371 #endif /* __cplusplus */
4372
4373 void file_quit_cmd_callback(GtkWidget *widget, gpointer data);
4374
4375 #ifdef __cplusplus
4376 }
4377 #endif /* __cplusplus */
4378
4379 #endif /* __MAIN_H__ */
4380
4381 </verb></tscreen>
4382 <p>
4383 Et, enfin, <em/main.c/&nbsp;:
4384
4385 <tscreen><verb>
4386 #include <gtk/gtk.h>
4387
4388 #include "main.h"
4389 #include "menus.h"
4390
4391
4392 int main(int argc, char *argv[])
4393 {
4394     GtkWidget *window;
4395     GtkWidget *main_vbox;
4396     GtkWidget *menubar;
4397     
4398     GtkAcceleratorTable *accel;
4399     
4400     gtk_init(&amp;argc, &amp;argv);
4401     
4402     window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
4403     gtk_signal_connect(GTK_OBJECT(window), "destroy", 
4404                        GTK_SIGNAL_FUNC(file_quit_cmd_callback), 
4405                        "WM destroy");
4406     gtk_window_set_title(GTK_WINDOW(window), "Usine à menu");
4407     gtk_widget_set_usize(GTK_WIDGET(window), 300, 200);
4408     
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);
4413     
4414     get_main_menu(&amp;menubar, &amp;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);
4418     
4419     gtk_widget_show(window);
4420     gtk_main();
4421     
4422     return(0);
4423 }
4424
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 
4428  * organisation. */ 
4429
4430 void file_quit_cmd_callback (GtkWidget *widget, gpointer data)
4431 {
4432     g_print ("%s\n", (char *) data);
4433     gtk_exit(0);
4434 }
4435 </verb></tscreen>
4436 <p>
4437 Un <em/makefile/ pour que cela soit plus facile à compiler&nbsp;:
4438
4439 <tscreen><verb>
4440 CC      = gcc
4441 PROF    = -g
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
4445 PROGNAME = at
4446
4447 O_FILES = menus.o main.o
4448
4449 $(PROGNAME): $(O_FILES)
4450         rm -f $(PROGNAME)
4451         $(CC) $(L_FLAGS) -o $(PROGNAME) $(O_FILES) $(L_POSTFLAGS)
4452
4453 .c.o: 
4454         $(CC) -c $(C_FLAGS) $<
4455
4456 clean: 
4457         rm -f core *.o $(PROGNAME) nohup.out
4458 distclean: clean 
4459         rm -f *~
4460 </verb></tscreen>
4461 <p>
4462 Pour l'instant, il n'y a que cet exemple. Une explication et de nombreux commentaires seront intégrés plus tard.
4463
4464
4465 <sect>Widgets non documentés
4466 <p>
4467 On a besoin de leurs auteurs! :). Participez à notre didacticiel.
4468
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.
4470
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é.
4472
4473 <sect1>Entrées de texte
4474 <p>
4475
4476 <sect1>Sélections de couleurs
4477 <p>
4478
4479 <sect1>Contrôle d'intervalle
4480 <p>
4481
4482 <sect1>Règles
4483 <p>
4484
4485 <sect1>Boîtes de texte
4486 <p>
4487
4488 <sect1>Prévisualisation
4489 <p>
4490 (Ceci peut devoir être réécrit pour suivre le style du reste de ce
4491 didacticiel).
4492
4493 <tscreen><verb>
4494
4495 Les prévisualisateurs servent à plusieurs choses dans GIMP/GTK. La
4496 plus importante est celle-ci&nbsp;: 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 !
4506
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 !
4515
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
4522 prévisualisations !
4523
4524 Encore plus&nbsp;: 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 !
4531
4532 Enfin, quelques utilisations diverses&nbsp;: 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 !
4540
4541 Quand ne pas utiliser les prévisualisations
4542
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
4545 images !
4546
4547 Utilisons les prévisualisations !
4548
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.
4555
4556                                [Image][Image]
4557
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&nbsp;: 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.
4563
4564
4565                               /* Création d'un widget prévisualisation,
4566                                * configuration de sa taille et affichage */
4567 GtkWidget *preview;
4568 preview=gtk_preview_new(GTK_PREVIEW_COLOR)
4569                               /* Autre option :
4570                               GTK_PREVIEW_GRAYSCALE);*/
4571
4572 gtk_preview_size (GTK_PREVIEW (preview), WIDTH, HEIGHT);
4573 gtk_widget_show(preview);
4574 my_preview_rendering_function(preview);
4575
4576 Ah oui, comme je le disais, les prévisualisations rendent mieux dans
4577 des cadres&nbsp;:
4578
4579 GtkWidget *create_a_preview(int        Width,
4580                             int        Height,
4581                             int        Colorfulness)
4582 {
4583   GtkWidget *preview;
4584   GtkWidget *frame;
4585   
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);
4590
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);
4596
4597   my_preview_rendering_function(preview);
4598   return frame;
4599 }
4600
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
4608 plus tard.
4609
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.
4621
4622                                    [Image]
4623
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;:
4628
4629 gtk_table_attach(GTK_TABLE(table),label,0,1,0,1,
4630                  0,
4631                  GTK_EXPAND|GTK_FILL,
4632                  0,0);
4633 gtk_table_attach(GTK_TABLE(table),frame,0,1,1,2,
4634                  GTK_EXPAND,
4635                  GTK_EXPAND,
4636                  0,0);
4637
4638
4639 Et voici le résultat&nbsp;:
4640
4641                                    [Image]
4642
4643 Divers
4644
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.
4646
4647 Remplir une prévisualisation
4648
4649 Afin de nous familiariser avec les bases de ce remplissage, créons le motif suivant&nbsp;:
4650
4651                                    [Image]
4652
4653 void
4654 my_preview_rendering_function(GtkWidget     *preview)
4655 {
4656 #define SIZE 100
4657 #define HALF (SIZE/2)
4658
4659   guchar *row=(guchar *) malloc(3*SIZE); /* 3 bits par point */
4660   gint i, j;                             /* Coordonnées      */
4661   double r, alpha, x, y;
4662
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é !       */
4667
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 ! */
4674       else {
4675         row[i*3+0] = r*255;
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;
4678       }
4679     }
4680     gtk_preview_draw_row( GTK_PREVIEW(preview),row,0,j,SIZE);
4681
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 */
4685   }
4686
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 ?  */
4690 }
4691
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.
4695
4696 Prévisualisation d'image
4697
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 !).
4706
4707 (ATTENTION : CODE NON TESTÉ !!!)
4708
4709 typedef struct {
4710   gint      width;
4711   gint      height;
4712   gint      bbp;
4713   guchar    *rgb;
4714   guchar    *mask;
4715 } ReducedImage;
4716
4717 enum {
4718   SELECTION_ONLY,
4719   SELCTION_IN_CONTEXT,
4720   ENTIRE_IMAGE
4721 };
4722
4723 ReducedImage *Reduce_The_Image(GDrawable *drawable,
4724                                GDrawable *mask,
4725                                gint LongerSize,
4726                                gint Selection)
4727 {
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  */
4731   /* RGB !                                                                 */
4732
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));
4737
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    */
4742
4743   gimp_drawable_mask_bounds (drawable->id, &amp;x1, &amp;y1, &amp;x2, &amp;y2);
4744   width  = x2-x1;
4745   height = y2-y1;
4746   /* S'il y a une SELECTION, on récupère ses frontières ! */
4747
4748   if (width != drawable->width &amp;&amp; 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 */
4752
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) {
4756     x1=0;
4757     x2=drawable->width;
4758     y1=0;
4759     y2=drawable->height;
4760   }
4761
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.   */
4764
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);
4770   }
4771
4772   /* Calcul de la largeur et de la hauteur de la surface à réduire. */
4773
4774   width  = x2-x1;
4775   height = y2-y1;
4776
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 !                                */
4781
4782   if (width>height) {
4783     RW=LongerSize;
4784     RH=(float) height * (float) LongerSize/ (float) width;
4785   }
4786   else {
4787     RH=LongerSize;
4788     RW=(float)width * (float) LongerSize/ (float) height;
4789   }
4790
4791   /* L'image entière est réduite dans une chaîne ! */
4792
4793   tempRGB   = (guchar *) malloc(RW*RH*bytes);
4794   tempmask  = (guchar *) malloc(RW*RH);
4795
4796   gimp_pixel_rgn_init (&amp;srcPR, drawable, x1, y1, width, height, FALSE, FALSE);
4797   gimp_pixel_rgn_init (&amp;srcMask, mask, x1, y1, width, height, FALSE, FALSE);
4798
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);
4802
4803   for (i=0; i < RH; i++) {
4804     whichrow=(float)i*(float)height/(float)RH;
4805     gimp_pixel_rgn_get_row (&amp;srcPR, src_row, x1, y1+whichrow, width);
4806     gimp_pixel_rgn_get_row (&amp;srcMask, src_mask_row, x1, y1+whichrow, width);
4807
4808     for (j=0; j < RW; j++) {
4809       whichcol=(float)j*(float)width/(float)RW;
4810
4811       /* Pas de sélection = chaque point est complètement sélectionné ! */
4812
4813       if (NoSelectionMade)
4814         tempmask[i*RW+j]=255;
4815       else
4816         tempmask[i*RW+j]=src_mask_row[whichcol];
4817
4818       /* Ajout de la ligne à la longue chaîne qui contient maintenant */
4819       /* l'image !                                                    */
4820
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];
4824
4825       /* On s'accroche aussi à l'alpha */
4826       if (bytes==4)
4827         tempRGB[i*RW*bytes+j*bytes+3]=src_row[whichcol*bytes+3];
4828     }
4829   }
4830   temp->bpp=bytes;
4831   temp->width=RW;
4832   temp->height=RH;
4833   temp->rgb=tempRGB;
4834   temp->mask=tempmask;
4835   return temp;
4836 }
4837
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
4841 suit&nbsp;:
4842
4843 gint fake_transparency(gint i, gint j)
4844 {
4845   if ( ((i%20)- 10) * ((j%20)- 10)>0   )
4846     return 64;
4847   else
4848     return 196;
4849 }
4850
4851 Voici maintenant la fonction de prévisualisation«nbsp;:
4852
4853 void
4854 my_preview_render_function(GtkWidget     *preview,
4855                            gint          changewhat,
4856                            gint          changewhich)
4857 {
4858   gint Inten, bytes=drawable->bpp;
4859   gint i, j, k;
4860   float partial;
4861   gint RW=reduced->width;
4862   gint RH=reduced->height;
4863   guchar *row=malloc(bytes*RW);;
4864
4865
4866   for (i=0; i < RH; i++) {
4867     for (j=0; j < RW; j++) {
4868
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];
4872
4873       if (bytes==4)
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);
4877         }
4878     }
4879     gtk_preview_draw_row( GTK_PREVIEW(preview),row,0,i,RW);
4880   }
4881
4882   free(a);
4883   gtk_widget_draw(preview,NULL);
4884   gdk_flush();
4885 }
4886
4887 Fonctions applicables
4888
4889 guint           gtk_preview_get_type           (void);
4890 /* Aucune idée */
4891 void            gtk_preview_uninit             (void);
4892 /* Aucune idée */
4893 GtkWidget*      gtk_preview_new                (GtkPreviewType   type);
4894 /* Décrite ci-dessous */
4895 void            gtk_preview_size               (GtkPreview      *preview,
4896                                                 gint             width,
4897                                                 gint             height);
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.                                            */
4904
4905 void            gtk_preview_put                (GtkPreview      *preview,
4906                                                 GdkWindow       *window,
4907                                                 GdkGC           *gc,
4908                                                 gint             srcx,
4909                                                 gint             srcy,
4910                                                 gint             destx,
4911                                                 gint             desty,
4912                                                 gint             width,
4913                                                 gint             height);
4914 /* Aucune idée */
4915
4916 void            gtk_preview_put_row            (GtkPreview      *preview,
4917                                                 guchar          *src,
4918                                                 guchar          *dest,
4919                                                 gint             x,
4920                                                 gint             y,
4921                                                 gint             w);
4922 /* Aucune idée */
4923
4924 void            gtk_preview_draw_row           (GtkPreview      *preview,
4925                                                 guchar          *data,
4926                                                 gint             x,
4927                                                 gint             y,
4928                                                 gint             w);
4929 /* Décrite dans le texte */
4930
4931 void            gtk_preview_set_expand         (GtkPreview      *preview,
4932                                                 gint             expand);
4933 /* Aucune idée */
4934
4935 /* Aucune piste pour celles qui suivent mais devrait être */
4936 /* un standard pour la plupart des widgets.               */
4937
4938 void            gtk_preview_set_gamma          (double           gamma);
4939 void            gtk_preview_set_color_cube     (guint            nred_shades,
4940                                                 guint            ngreen_shades,
4941                                                 guint            nblue_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);
4948
4949 That's all, folks!
4950
4951 </verb></tscreen>
4952
4953
4954 <sect1>Courbes
4955 <p>
4956
4957
4958 <sect>Widget EventBox <label id="sec_The_EventBox_Widget">
4959 <p>
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&nbsp;: on peut
4965 alors avoir des surimpressions douteuses, etc. Si vous avez besoin de
4966 ces widgets, <em/EventBox/ est fait pour vous.
4967
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
4978 ci-dessous).
4979
4980 <p>
4981 Pour créer un widget EventBox, on utilise&nbsp;:
4982
4983 <tscreen><verb>
4984 GtkWidget* gtk_event_box_new (void);
4985 </verb></tscreen>
4986
4987 <p>
4988 Un widget fils peut alors être ajouté à cet <em/EventBox/&nbsp;:
4989
4990 <tscreen><verb>
4991 gtk_container_add (GTK_CONTAINER(event_box), widget);
4992 </verb></tscreen>
4993
4994 <p>
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.
4998
4999 <tscreen><verb>
5000 #include <gtk/gtk.h>
5001
5002 int 
5003 main (int argc, char *argv[])
5004 {
5005     GtkWidget *window;
5006     GtkWidget *event_box;
5007     GtkWidget *label;
5008     
5009     gtk_init (&amp;argc, &amp;argv);
5010     
5011     window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
5012     
5013     gtk_window_set_title (GTK_WINDOW (window), "Event Box");
5014     
5015     gtk_signal_connect (GTK_OBJECT (window), "destroy",
5016                         GTK_SIGNAL_FUNC (gtk_exit), NULL);
5017     
5018     gtk_container_border_width (GTK_CONTAINER (window), 10);
5019     
5020     /* Création d'un EventBox et ajout de celui-ci dans la fenêtre. */
5021     
5022     event_box = gtk_event_box_new ();
5023     gtk_container_add (GTK_CONTAINER(window), event_box);
5024     gtk_widget_show (event_box);
5025     
5026     /* Création d'un long label */
5027     
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);
5031     
5032     /* Placement serré. */
5033
5034     gtk_widget_set_usize (label, 110, 20);
5035     
5036     /* Attachement d'une action à celui-ci. */
5037
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);
5041     
5042     /* Encore une fois, vous avez besoin d'une fenêtre X pour... */
5043     
5044     gtk_widget_realize (event_box);
5045     gdk_window_set_cursor (event_box->window, gdk_cursor_new (GDK_HAND1));
5046     
5047     gtk_widget_show (window);
5048     
5049     gtk_main ();
5050     
5051     return 0;
5052 }
5053 </verb></tscreen>
5054
5055 <sect>Configuration des attributs de widget<label
5056 id="sec_setting_widget_attributes">
5057 <p>
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
5060 taille, etc.
5061
5062 (Je devrais peut être faire une section uniquement consacrée aux
5063 raccourcis clavier.)
5064
5065 <tscreen><verb>
5066 void       gtk_widget_install_accelerator (GtkWidget           *widget,
5067                                            GtkAcceleratorTable *table,
5068                                            gchar               *signal_name,
5069                                            gchar                key,
5070                                            guint8               modifiers);
5071
5072 void       gtk_widget_remove_accelerator  (GtkWidget           *widget,
5073                                            GtkAcceleratorTable *table,
5074                                            gchar               *signal_name);
5075
5076 void       gtk_widget_activate            (GtkWidget           *widget);
5077
5078 void       gtk_widget_set_name            (GtkWidget           *widget,
5079                                            gchar               *name);
5080 gchar*     gtk_widget_get_name            (GtkWidget           *widget);
5081
5082 void       gtk_widget_set_sensitive       (GtkWidget           *widget,
5083                                            gint                 sensitive);
5084
5085 void       gtk_widget_set_style           (GtkWidget           *widget,
5086                                            GtkStyle            *style);
5087                                            
5088 GtkStyle*    gtk_widget_get_style     (GtkWidget *widget);
5089
5090 GtkStyle*    gtk_widget_get_default_style    (void);
5091
5092 void       gtk_widget_set_uposition       (GtkWidget           *widget,
5093                                            gint                 x,
5094                                            gint                 y);
5095 void       gtk_widget_set_usize           (GtkWidget           *widget,
5096                                            gint                 width,
5097                                            gint                 height);
5098
5099 void       gtk_widget_grab_focus          (GtkWidget           *widget);
5100
5101 void       gtk_widget_show                (GtkWidget           *widget);
5102
5103 void       gtk_widget_hide                (GtkWidget           *widget);
5104 </verb></tscreen>
5105
5106
5107
5108 <sect>Temporisations, fonctions d'E/S et d'attente<label id="sec_timeouts">
5109 <p>
5110 <sect1>Temporisations
5111 <p>
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.
5117
5118 <tscreen><verb>
5119 gint gtk_timeout_add (guint32 interval,
5120                       GtkFunction function,
5121                       gpointer data);
5122 </verb></tscreen>
5123
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&nbsp;:
5129
5130 <tscreen><verb>
5131 void gtk_timeout_remove (gint tag);
5132 </verb></tscreen>
5133
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.
5138
5139 La déclaration de votre fonction de rappel doit ressembler à ça&nbsp;:
5140
5141 <tscreen><verb>
5142 gint timeout_callback (gpointer data);
5143 </verb></tscreen>
5144
5145 <sect1>Surveillance des E/S
5146 <p>
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
5151 vérification&nbsp;:
5152
5153 <tscreen><verb>
5154 gint gdk_input_add (gint source,
5155                     GdkInputCondition condition,
5156                     GdkInputFunction  function,
5157                     gpointer data);
5158 </verb></tscreen>
5159
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
5162 peut être&nbsp;:
5163 <p>
5164 GDK_INPUT_READ - Appel <em/function/ lorsqu'il y a une donnée prête à
5165 être lue dans le descripteur de fichier.
5166 <p>
5167 GDK_INPUT_WRITE - Appel de <em/function/ lorsque le descripteur de
5168 fichier est prêt pour une écriture.
5169 <p>
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.
5174 <p>
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&nbsp;:
5177 <p>
5178 <tscreen><verb>
5179 void gdk_input_remove (gint tag);
5180 </verb></tscreen>
5181 <p>
5182 La fonction de rappel doit être déclarée de la façon suivante&nbsp;:
5183 <p>
5184 <tscreen><verb>
5185 void input_callback (gpointer data, gint source, 
5186                      GdkInputCondition condition);
5187 </verb></tscreen>
5188 <p>
5189
5190 <sect1>Fonctions d'attente
5191 <p>
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&nbsp;;
5195
5196 <tscreen><verb>
5197 gint gtk_idle_add (GtkFunction function,
5198                    gpointer data);
5199 </verb></tscreen>
5200
5201 <tscreen><verb>
5202 void gtk_idle_remove (gint tag);
5203 </verb></tscreen>
5204 <p>
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.
5210
5211 <sect>Gestion des sélections
5212
5213 <sect1>Introduction
5214 <p>
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/.
5226
5227 <p>
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.
5237
5238 <p>
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.
5249
5250 <sect1>Récupération de la sélection
5251 <p>
5252 La récupération de la sélection est un processus asynchrone. Pour démarrer le processus, on appelle&nbsp;:
5253
5254 <tscreen><verb>
5255 gint gtk_selection_convert   (GtkWidget           *widget, 
5256                               GdkAtom              selection, 
5257                               GdkAtom              target,
5258                               guint32              time)
5259 </verb</tscreen>
5260
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.
5268
5269 <p>
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&nbsp;:
5274
5275 <tscreen><verb>
5276 struct _GtkSelectionData
5277 {
5278   GdkAtom selection;
5279   GdkAtom target;
5280   GdkAtom type;
5281   gint    format;
5282   guchar *data;
5283   gint    length;
5284 };
5285 </verb></tscreen>
5286
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&nbsp;: "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).
5305
5306 <p>
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
5309 être convertie.
5310
5311 <tscreen><verb>
5312 #include <gtk/gtk.h>
5313
5314 void selection_received (GtkWidget *widget, 
5315                          GtkSelectionData *selection_data, 
5316                          gpointer data);
5317
5318 /* Gestionnaire de signal invoqué lorsque l'utilisateur clique sur 
5319  * le bouton « Obtenir les cibles ». */
5320
5321 void get_targets (GtkWidget *widget, gpointer data)
5322 {
5323   static GdkAtom targets_atom = GDK_NONE;
5324
5325   /* Obtention de l'atome correspondant à la chaîne "TARGETS" */
5326
5327   if (targets_atom == GDK_NONE)
5328     targets_atom = gdk_atom_intern ("TARGETS", FALSE);
5329
5330   /* Demande de la cible "TARGETS" pour la sélection primaire */
5331
5332   gtk_selection_convert (widget, GDK_SELECTION_PRIMARY, targets_atom,
5333                          GDK_CURRENT_TIME);
5334 }
5335
5336 /* Gestionnaire de signal appelé quand le propriétaire des sélections
5337  * retourne la donnée. */
5338
5339 void selection_received (GtkWidget *widget, GtkSelectionData *selection_data, 
5340                     gpointer data)
5341 {
5342   GdkAtom *atoms;
5343   GList *item_list;
5344   int i;
5345
5346   /* **** IMPORTANT **** On vérifie si la récupération s'est bien passée. */
5347
5348   if (selection_data->length < 0)
5349     {
5350       g_print ("Selection retrieval failed\n");
5351       return;
5352     }
5353
5354   /* On s'assure que l'on a obtenu la donnée sous la forme attendue. */
5355
5356   if (selection_data->type != GDK_SELECTION_TYPE_ATOM)
5357     {
5358       g_print ("La sélection \"TARGETS\" n'a pas été retournée sous la forme d'atomes !\n");
5359       return;
5360     }
5361   
5362   /* Affichage des atomes reçus. */
5363
5364   atoms = (GdkAtom *)selection_data->data;
5365
5366   item_list = NULL;
5367   for (i=0; i<selection_data->length/sizeof(GdkAtom); i++)
5368     {
5369       char *name;
5370       name = gdk_atom_name (atoms[i]);
5371       if (name != NULL)
5372         g_print ("%s\n",name);
5373       else
5374         g_print ("(atome incorrect)\n");
5375     }
5376
5377   return;
5378 }
5379
5380 int 
5381 main (int argc, char *argv[])
5382 {
5383   GtkWidget *window;
5384   GtkWidget *button;
5385   
5386   gtk_init (&amp;argc, &amp;argv);
5387
5388   /* Création de la fenêtre de l'application. */
5389
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);
5393
5394   gtk_signal_connect (GTK_OBJECT (window), "destroy",
5395                       GTK_SIGNAL_FUNC (gtk_exit), NULL);
5396
5397   /* Création d'un bouton pour obtenir les cibles */
5398
5399   button = gtk_button_new_with_label ("Obtenir les cibles");
5400   gtk_container_add (GTK_CONTAINER (window), button);
5401
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);
5406
5407   gtk_widget_show (button);
5408   gtk_widget_show (window);
5409   
5410   gtk_main ();
5411   
5412   return 0;
5413 }
5414 </verb></tscreen>
5415
5416 <sect1>Fournir la sélection 
5417 <p>
5418
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
5422 un appel à&nbsp;:
5423
5424 <tscreen><verb>
5425 void gtk_selection_add_handler (GtkWidget           *widget, 
5426                                 GdkAtom              selection,
5427                                 GdkAtom              target,
5428                                 GtkSelectionFunction function,
5429                                 GtkRemoveFunction    remove_func,
5430                                 gpointer             data);
5431 </verb></tscreen>
5432
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/.
5438
5439 <p>
5440 La fonction de rappel <em/function/ doit avoir la signature suivante&nbsp;:
5441
5442 <tscreen><verb>
5443 typedef void (*GtkSelectionFunction) (GtkWidget *widget, 
5444                                       GtkSelectionData *selection_data,
5445                                       gpointer data);
5446
5447 </verb></tscreen>
5448
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&nbsp;:
5456
5457 <tscreen><verb>
5458 void gtk_selection_data_set (GtkSelectionData *selection_data,
5459                              GdkAtom           type,
5460                              gint              format,
5461                              guchar           *data,
5462                              gint              length);
5463 </verb></tscreen>
5464
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
5467 champs à la main).
5468
5469 <p>
5470 Lorsque cela est demandé par l'utilisateur, on réclame la possession
5471 de la sélection en appelant&nbsp;:
5472
5473 <tscreen><verb>
5474 gint gtk_selection_owner_set (GtkWidget           *widget,
5475                               GdkAtom              selection,
5476                               guint32              time);
5477 </verb></tscreen>
5478
5479 Si une autre application réclame la possession de la sélection, on
5480 recevra un "selection_clear_event".
5481
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.
5488
5489 <tscreen><verb>
5490 #include <gtk/gtk.h>
5491 #include <time.h>
5492
5493 /* Fonction de rappel appelée lorsque l'utilisateur commute la sélection. */
5494
5495 void selection_toggled (GtkWidget *widget, gint *have_selection)
5496 {
5497   if (GTK_TOGGLE_BUTTON(widget)->active)
5498     {
5499       *have_selection = gtk_selection_owner_set (widget,
5500                                                  GDK_SELECTION_PRIMARY,
5501                                                  GDK_CURRENT_TIME);
5502       /* Si la demande de sélection échoue, on remet le bouton en position sortie. */
5503
5504       if (!*have_selection)
5505         gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON(widget), FALSE);
5506     }
5507   else
5508     {
5509       if (*have_selection)
5510         {
5511           /* Avant de nettoyer la selection en mettant son propriétaire à NULL, 
5512            * on vérifie que nous sommes bien son propriétaire actuel. */
5513
5514           if (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == widget->window)
5515             gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY,
5516                                      GDK_CURRENT_TIME);
5517           *have_selection = FALSE;
5518         }
5519     }
5520 }
5521
5522 /* Appelée lorsqu'une autre application demande la sélection. */
5523
5524 gint selection_clear (GtkWidget *widget, GdkEventSelection *event,
5525                  gint *have_selection)
5526 {
5527   *have_selection = FALSE;
5528   gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON(widget), FALSE);
5529
5530   return TRUE;
5531 }
5532
5533 /* Fournit l'heure comme sélection. */
5534
5535 void selection_handle (GtkWidget *widget, 
5536                   GtkSelectionData *selection_data,
5537                   gpointer data)
5538 {
5539   gchar *timestr;
5540   time_t current_time;
5541
5542   current_time = time (NULL);
5543   timestr = asctime (localtime(&amp;current_time)); 
5544
5545   /* Lorsqu'on retourne une chaîne, elle ne doit pas se terminer par
5546    * 0, ce sera fait pour nous. */
5547
5548   gtk_selection_data_set (selection_data, GDK_SELECTION_TYPE_STRING,
5549                           8, timestr, strlen(timestr));
5550 }
5551
5552 int main (int argc, char *argv[])
5553 {
5554   GtkWidget *window;
5555
5556   GtkWidget *selection_button;
5557
5558   static int have_selection = FALSE;
5559   
5560   gtk_init (&amp;argc, &amp;argv);
5561
5562   /* Création de la fenêtre principale. */
5563
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);
5567
5568   gtk_signal_connect (GTK_OBJECT (window), "destroy",
5569                       GTK_SIGNAL_FUNC (gtk_exit), NULL);
5570
5571   /* Création d'un bouton commutateur pour qu'il agisse comme une sélection. */
5572
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);
5576
5577   gtk_signal_connect (GTK_OBJECT(selection_button), "toggled",
5578                       GTK_SIGNAL_FUNC (selection_toggled), &amp;have_selection);
5579   gtk_signal_connect (GTK_OBJECT(selection_button), "selection_clear_event",
5580                       GTK_SIGNAL_FUNC (selection_clear), &amp;have_selection);
5581
5582   gtk_selection_add_handler (selection_button, GDK_SELECTION_PRIMARY,
5583                              GDK_SELECTION_TYPE_STRING,
5584                              selection_handle, NULL, NULL);
5585
5586   gtk_widget_show (selection_button);
5587   gtk_widget_show (window);
5588   
5589   gtk_main ();
5590   
5591   return 0;
5592 }
5593 </verb></tscreen>
5594
5595
5596
5597 <sect>glib<label id="sec_glib">
5598 <p>
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é.
5605
5606 <sect1>Définitions
5607 <p>
5608 Les définitions pour les bornes de la plupart des types standards sont&nbsp;:
5609
5610 <tscreen><verb>
5611 G_MINFLOAT
5612 G_MAXFLOAT
5613 G_MINDOUBLE
5614 G_MAXDOUBLE
5615 G_MINSHORT
5616 G_MAXSHORT
5617 G_MININT
5618 G_MAXINT
5619 G_MINLONG
5620 G_MAXLONG
5621 </verb></tscreen>
5622
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.
5628
5629 <tscreen><verb>
5630 char   gchar;
5631 short  gshort;
5632 long   glong;
5633 int    gint;
5634 char   gboolean;
5635
5636 unsigned char   guchar;
5637 unsigned short  gushort;
5638 unsigned long   gulong;
5639 unsigned int    guint;
5640
5641 float   gfloat;
5642 double  gdouble;
5643 long double gldouble;
5644
5645 void* gpointer;
5646
5647 gint8
5648 guint8
5649 gint16
5650 guint16
5651 gint32
5652 guint32
5653 </verb></tscreen>
5654
5655 <sect1>Listes doublement chaînées
5656 <p>
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
5662 fonctionnent.
5663
5664 <tscreen><verb>
5665 GList* g_list_alloc       (void);
5666
5667 void   g_list_free        (GList     *list);
5668
5669 void   g_list_free_1      (GList     *list);
5670
5671 GList* g_list_append      (GList     *list,
5672                            gpointer   data);
5673                            
5674 GList* g_list_prepend     (GList     *list,
5675                            gpointer   data);
5676                         
5677 GList* g_list_insert      (GList     *list,
5678                            gpointer   data,
5679                            gint       position);
5680
5681 GList* g_list_remove      (GList     *list,
5682                            gpointer   data);
5683                            
5684 GList* g_list_remove_link (GList     *list,
5685                            GList     *link);
5686
5687 GList* g_list_reverse     (GList     *list);
5688
5689 GList* g_list_nth         (GList     *list,
5690                            gint       n);
5691                            
5692 GList* g_list_find        (GList     *list,
5693                            gpointer   data);
5694
5695 GList* g_list_last        (GList     *list);
5696
5697 GList* g_list_first       (GList     *list);
5698
5699 gint   g_list_length      (GList     *list);
5700
5701 void   g_list_foreach     (GList     *list,
5702                            GFunc      func,
5703                            gpointer   user_data);
5704 </verb></tscreen>                                             
5705
5706
5707 <sect1>Listes simplement chaînées
5708 <p>
5709 La plupart des fonctions pour les listes simplement chaînées
5710 ci-dessous sont identiques à celles vues plus haut. Voici une liste
5711 complète&nbsp;:
5712
5713 <tscreen><verb>
5714 GSList* g_slist_alloc       (void);
5715
5716 void    g_slist_free        (GSList   *list);
5717
5718 void    g_slist_free_1      (GSList   *list);
5719
5720 GSList* g_slist_append      (GSList   *list,
5721                              gpointer  data);
5722                 
5723 GSList* g_slist_prepend     (GSList   *list,
5724                              gpointer  data);
5725                              
5726 GSList* g_slist_insert      (GSList   *list,
5727                              gpointer  data,
5728                              gint      position);
5729                              
5730 GSList* g_slist_remove      (GSList   *list,
5731                              gpointer  data);
5732                              
5733 GSList* g_slist_remove_link (GSList   *list,
5734                              GSList   *link);
5735                              
5736 GSList* g_slist_reverse     (GSList   *list);
5737
5738 GSList* g_slist_nth         (GSList   *list,
5739                              gint      n);
5740                              
5741 GSList* g_slist_find        (GSList   *list,
5742                              gpointer  data);
5743                              
5744 GSList* g_slist_last        (GSList   *list);
5745
5746 gint    g_slist_length      (GSList   *list);
5747
5748 void    g_slist_foreach     (GSList   *list,
5749                              GFunc     func,
5750                              gpointer  user_data);
5751         
5752 </verb></tscreen>
5753
5754 <sect1>Gestion de la mémoire
5755 <p>
5756 <tscreen><verb>
5757 gpointer g_malloc      (gulong    size);
5758 </verb></tscreen>
5759
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.
5762
5763 <tscreen><verb>
5764 gpointer g_malloc0     (gulong    size);
5765 </verb></tscreen>
5766
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.
5769
5770 <tscreen><verb>
5771 gpointer g_realloc     (gpointer  mem,
5772                         gulong    size);
5773 </verb></tscreen>
5774
5775 Réalloue <em/size/ octets de mémoire à partir de <em/mem/. Évidemment,
5776 la mémoire doit avoir été allouée auparavant.
5777
5778 <tscreen><verb>
5779 void     g_free        (gpointer  mem);
5780 </verb></tscreen>
5781
5782 Libère la mémoire. Facile.
5783  
5784 <tscreen><verb>
5785 void     g_mem_profile (void);
5786 </verb></tscreen>
5787
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/.
5791
5792 <tscreen><verb>
5793 void     g_mem_check   (gpointer  mem);
5794 </verb></tscreen>
5795
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/.
5799
5800 <sect1>Timers
5801 <p>
5802 Fonctions des timers...
5803
5804 <tscreen><verb>
5805 GTimer* g_timer_new     (void);
5806
5807 void    g_timer_destroy (GTimer  *timer);
5808
5809 void    g_timer_start   (GTimer  *timer);
5810
5811 void    g_timer_stop    (GTimer  *timer);
5812
5813 void    g_timer_reset   (GTimer  *timer);
5814
5815 gdouble g_timer_elapsed (GTimer  *timer,
5816                          gulong  *microseconds);
5817 </verb></tscreen>                        
5818
5819 <sect1>Gestion des chaînes
5820 <p>
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
5824 documentation.
5825
5826 <tscreen><verb>
5827 GString* g_string_new       (gchar   *init);
5828 void     g_string_free      (GString *string,
5829                              gint     free_segment);
5830                              
5831 GString* g_string_assign    (GString *lval,
5832                              gchar   *rval);
5833                              
5834 GString* g_string_truncate  (GString *string,
5835                              gint     len);
5836                              
5837 GString* g_string_append    (GString *string,
5838                              gchar   *val);
5839                             
5840 GString* g_string_append_c  (GString *string,
5841                              gchar    c);
5842         
5843 GString* g_string_prepend   (GString *string,
5844                              gchar   *val);
5845                              
5846 GString* g_string_prepend_c (GString *string,
5847                              gchar    c);
5848         
5849 void     g_string_sprintf   (GString *string,
5850                              gchar   *fmt,
5851                              ...);
5852         
5853 void     g_string_sprintfa  (GString *string,
5854                              gchar   *fmt,
5855                              ...);
5856 </verb></tscreen>                                                         
5857
5858 <sect1>Utilitaires et fonctions d'erreurs
5859 <p>
5860 <tscreen><verb>
5861 gchar* g_strdup    (const gchar *str);
5862 </verb></tscreen>
5863
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.
5867
5868 <tscreen><verb>
5869 gchar* g_strerror  (gint errnum);
5870 </verb></tscreen>
5871
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&nbsp;:
5875
5876 <tscreen><verb>
5877 nom du programme:fonction qui a échoué:fichier ou autre descripteur:strerror
5878 </verb></tscreen>
5879
5880 Voici un exemple d'appel utilisé dans le programme « Bonjour tout le monde ! »&nbsp;:
5881
5882 <tscreen><verb>
5883 g_print("bonjour_monde:open:%s:%s\n", filename, g_strerror(errno));
5884 </verb></tscreen>
5885
5886 <tscreen><verb>
5887 void g_error   (gchar *format, ...);
5888 </verb></tscreen>
5889
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.
5893
5894 <tscreen><verb>
5895 void g_warning (gchar *format, ...);
5896 </verb></tscreen>
5897
5898 Comme au dessus, mais ajoute « ** WARNING **: », et ne termine pas le
5899 programme.
5900
5901 <tscreen><verb>
5902 void g_message (gchar *format, ...);
5903 </verb></tscreen>
5904 Affiche  « message: » avant la chaîne passée en paramètre.
5905
5906 <tscreen><verb>
5907 void g_print   (gchar *format, ...);
5908 </verb></tscreen>
5909
5910 Remplace <em/printf()/.
5911
5912 Enfin la dernière fonction&nbsp;:
5913
5914 <tscreen><verb>
5915 gchar* g_strsignal (gint signum);
5916 </verb></tscreen>
5917
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.
5920
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
5923 courrier !
5924
5925
5926 <sect>Fichiers rc de GTK
5927 <p>
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.
5932
5933 <sect1>Fonctions pour les fichiers rc
5934 <p>
5935 Au démarrage de votre application, ajoutez un appel à&nbsp;:
5936 <tscreen><verb>
5937 void gtk_rc_parse (char *filename);
5938 </verb></tscreen>
5939 <p>
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.
5943 <p>
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 à&nbsp;:
5947 <tscreen><verb>
5948 void gtk_widget_set_name (GtkWidget *widget,
5949                           gchar *name);
5950 </verb></tscreen>
5951 <p>
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
5955 fichier rc.
5956 <p>
5957 Si vous utilisez un appel comme celui-ci&nbsp;:
5958
5959 <tscreen><verb>
5960 button = gtk_button_new_with_label ("Bouton Spécial");
5961 gtk_widget_set_name (button, "bouton special");
5962 </verb></tscreen>
5963 <p>
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 ».  [<---
5966 Vérifiez !]
5967 <p>
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
5971 est&nbsp;:
5972
5973 <tscreen><verb>
5974 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
5975 gtk_widget_set_name (window, "fenetre principale");
5976 </verb></tscreen>
5977 <p>
5978 Et le style est défini dans le fichier rc avec&nbsp;:
5979
5980 <tscreen><verb>
5981 widget "fenetre principale.*GtkButton*" style "bouton_principal"
5982 </verb></tscreen>
5983 <p>
5984 Ce qui configure tous les widgets <em/GtkButton/ de « fenêtre
5985 principale » avec le style « bouton_principal » défini dans le fichier
5986 rc.
5987 <p>
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.
5990
5991 <sect1>Format des fichiers rc de GTK
5992 <p>
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.
5998 <p>
5999 Il y a plusieurs directives pour changer les attributs d'un widget.
6000 <itemize>
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.
6005 </itemize>
6006 <p>
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&nbsp;:
6010 <itemize>
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.
6020 </itemize>
6021 <p>
6022 Lorsqu'on utilise les mots-clés « <em/fg/ » et « <em/bg/ » pour
6023 configurer les couleurs des widgets, le format est&nbsp;:
6024
6025 <tscreen><verb> fg[<STATE>] = { Red, Green, Blue } </verb></tscreen>
6026 <p>
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.
6034 <p>
6035 <em/bg_pixmap/ est très similaire, sauf que les couleurs sont
6036 remplacées par un nom de fichier.
6037
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.
6040
6041 <p>
6042 La directive <em/font/ est simplement&nbsp;:
6043 <tscreen><verb>
6044 font = "<font name>"
6045 </verb></tscreen>
6046 <p>
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.
6050 <p>
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.
6053 <p>
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.
6062 <p>
6063 Lorsque le mot-clé « <em/parent/ » est utilisé comme attribut, le
6064 widget prendra les attributs de son parent dans l'application.
6065 <p>
6066 Lorsqu'on définit un style, on peut assigner les attributs d'un style
6067 déjà défini à ce nouveau style.
6068
6069 <tscreen><verb>
6070 style "bouton_principal" = "button"
6071 {
6072   font = "-adobe-helvetica-medium-r-normal--*-100-*-*-*-*-*-*"
6073   bg[PRELIGHT] = { 0.75, 0, 0 }
6074 }
6075 </verb></tscreen>
6076 <p>
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.
6080 <p>
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
6083 s'applique.
6084
6085 <sect1>Exemple de fichier rc
6086 <p>
6087
6088 <tscreen><verb>
6089 # pixmap_path "<dir 1>:<dir 2>:<dir 3>:..."
6090 #
6091 pixmap_path "/usr/include/X11R6/pixmaps:/home/imain/pixmaps"
6092 #
6093 # style <name> [= <name>]
6094 # {
6095 #   <option>
6096 # }
6097 #
6098 # widget <widget_set> style <style_name>
6099 # widget_class <widget_class_set> style <style_name>
6100
6101
6102 # Voici une liste des états possibles. Remarquez que certains ne s'appliquent
6103 # pas à certains widgets.
6104 #
6105 # NORMAL - L'état normal d'un widget, sans la souris au dessus de lui, 
6106 # non pressé, etc.
6107 #
6108 # PRELIGHT - Lorsque la souris se trouve au dessus du widget, les couleurs 
6109 # définies pour cet état sont actives.
6110 #
6111 # ACTIVE - Lorsque le widget est pressé ou cliqué, il devient actif et les 
6112 # attributs associés à cet état sont appliqués.
6113 #
6114 # INSENSITIVE - Quand un widget est configuré pour être insensible, et qu'il 
6115 # ne peut être activé, il prend ces attributs.
6116 #
6117 # SELECTED - Lorsqu'un objet est choisi, il prend ces attributs.
6118 #
6119 # Avec ces états, on peut configurer les attributs des widgets dans chacun
6120 # de ces états en utilisant les directives suivantes.
6121 #
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.
6126
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.
6129
6130 style "window"
6131 {
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"
6135 }
6136
6137 style "scale"
6138 {
6139   #Configure la couleur de premier plan (celle de la fonte) à rouge
6140   #lorsqu'on est dans l'état "NORMAL".
6141   
6142   fg[NORMAL] = { 1.0, 0, 0 }
6143   
6144   #Configure le pixmap d'arrière plan de ce widget à celui de son parent.
6145   bg_pixmap[NORMAL] = "<parent>"
6146 }
6147
6148 style "button"
6149 {
6150   # Voici tous les états possibles pour un bouton. Le seul qui ne peut
6151   # s'appliquer est l'état SELECTED.
6152   
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 }
6161 }
6162
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
6165 # "main_button".
6166
6167 style "main_button" = "button"
6168 {
6169   font = "-adobe-helvetica-medium-r-normal--*-100-*-*-*-*-*-*"
6170   bg[PRELIGHT] = { 0.75, 0, 0 }
6171 }
6172
6173 style "toggle_button" = "button"
6174 {
6175   fg[NORMAL] = { 1.0, 0, 0 }
6176   fg[ACTIVE] = { 1.0, 0, 0 }
6177   
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>"
6181 }
6182
6183 style "text"
6184 {
6185   bg_pixmap[NORMAL] = "marble.xpm"
6186   fg[NORMAL] = { 1.0, 1.0, 1.0 }
6187 }
6188
6189 style "ruler"
6190 {
6191   font = "-adobe-helvetica-medium-r-normal--*-80-*-*-*-*-*-*"
6192 }
6193
6194 # pixmap_path "~/.pixmaps"
6195
6196 # Configuration des types de widget pour utiliser les styles définis
6197 # plus haut.
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.
6201
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"
6211
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"
6215 </verb></tscreen>
6216
6217 <sect>Écriture de vos propres widgets 
6218 <p>
6219 <sect1>Vue d'ensemble
6220 <p>
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>.
6235
6236 <sect1>Anatomie d'un widget
6237
6238 <p>
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
6242 détails.
6243
6244 <p>
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&nbsp;:
6257
6258 <tscreen><verb>
6259 struct _GtkButtonClass
6260 {
6261   GtkContainerClass parent_class;
6262
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);
6268 };
6269 </verb></tscreen>
6270
6271 <p>
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
6275 signaux.
6276
6277 <p>
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
6282 à&nbsp;:
6283
6284 <tscreen><verb>
6285 struct _GtkButton
6286 {
6287   GtkContainer container;
6288
6289   GtkWidget *child;
6290
6291   guint in_button : 1;
6292   guint button_down : 1;
6293 };
6294 </verb></tscreen>
6295
6296 <p>
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
6300 est.
6301
6302 <sect1> Création d'un widget composé
6303
6304 <sect2> Introduction
6305
6306 <p>
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.
6314
6315 <p>
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.
6320
6321 <sect2> Choix d'une classe parent
6322
6323 <p>
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
6341 dans la VBox.
6342
6343 <sect2> The header file
6344
6345 <p>
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
6350 avec&nbsp:
6351
6352 <tscreen><verb>
6353 #ifndef __TICTACTOE_H__
6354 #define __TICTACTOE_H__
6355 .
6356 .
6357 .
6358 #endif /* __TICTACTOE_H__ */
6359 </verb></tscreen>
6360
6361 Et, pour faire plaisir aux programmes C++ qui inclueront ce fichier, on l'enveloppe aussi dans&nbsp;:
6362
6363 <tscreen><verb>
6364 #ifdef __cplusplus
6365 extern "C" {
6366 #endif /* __cplusplus */
6367 .
6368 .
6369 .
6370 #ifdef __cplusplus
6371 }
6372 #endif /* __cplusplus */
6373 </verb></tscreen>
6374
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.
6380
6381 <p>
6382 Voici le fichier en-tête complet&nbsp;:
6383
6384 <tscreen><verb>
6385
6386 #ifndef __TICTACTOE_H__
6387 #define __TICTACTOE_H__
6388
6389 #include <gdk/gdk.h>
6390 #include <gtk/gtkvbox.h>
6391
6392 #ifdef __cplusplus
6393 extern "C" {
6394 #endif /* __cplusplus */
6395
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 ())
6399
6400
6401 typedef struct _Tictactoe       Tictactoe;
6402 typedef struct _TictactoeClass  TictactoeClass;
6403
6404 struct _Tictactoe
6405 {
6406   GtkVBox vbox;
6407   
6408   GtkWidget *buttons[3][3];
6409 };
6410
6411 struct _TictactoeClass
6412 {
6413   GtkVBoxClass parent_class;
6414
6415   void (* tictactoe) (Tictactoe *ttt);
6416 };
6417
6418 guint          tictactoe_get_type        (void);
6419 GtkWidget*     tictactoe_new             (void);
6420 void           tictactoe_clear           (Tictactoe *ttt);
6421
6422 #ifdef __cplusplus
6423 }
6424 #endif /* __cplusplus */
6425
6426 #endif /* __TICTACTOE_H__ */
6427
6428 </verb></tscreen>
6429
6430 <sect2>La fonction  <tt/_get_type()/
6431
6432 <p>
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
6438 ID.
6439
6440 <tscreen><verb>
6441 guint
6442 tictactoe_get_type ()
6443 {
6444   static guint ttt_type = 0;
6445
6446   if (!ttt_type)
6447     {
6448       GtkTypeInfo ttt_info =
6449       {
6450         "Tictactoe",
6451         sizeof (Tictactoe),
6452         sizeof (TictactoeClass),
6453         (GtkClassInitFunc) tictactoe_class_init,
6454         (GtkObjectInitFunc) tictactoe_init,
6455         (GtkArgFunc) NULL,
6456       };
6457
6458       ttt_type = gtk_type_unique (gtk_vbox_get_type (), &amp;ttt_info);
6459     }
6460
6461   return ttt_type;
6462 }
6463 </verb></tscreen>
6464
6465 <p>
6466 La structure <em/GtkTypeInfo/ est définie de la façon suivante&nbsp;:
6467
6468 <tscreen><verb>
6469 struct _GtkTypeInfo
6470 {
6471   gchar *type_name;
6472   guint object_size;
6473   guint class_size;
6474   GtkClassInitFunc class_init_func;
6475   GtkObjectInitFunc object_init_func;
6476   GtkArgFunc arg_func;
6477 };
6478 </verb></tscreen>
6479
6480 <p>
6481 Les champs de cette structure s'expliquent d'eux-mêmes. Nous
6482 ignorerons le champ <em/arg_func/ ici&nbsp;: 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.
6488
6489 <sect2>La fonction <em/_class_init()/
6490
6491 <p>
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&nbsp;:
6495
6496 <tscreen><verb>
6497
6498 enum {
6499   TICTACTOE_SIGNAL,
6500   LAST_SIGNAL
6501 };
6502
6503 static gint tictactoe_signals[LAST_SIGNAL] = { 0 };
6504
6505 static void
6506 tictactoe_class_init (TictactoeClass *class)
6507 {
6508   GtkObjectClass *object_class;
6509
6510   object_class = (GtkObjectClass*) class;
6511   
6512   tictactoe_signals[TICTACTOE_SIGNAL] = gtk_signal_new ("tictactoe",
6513                                          GTK_RUN_FIRST,
6514                                          object_class->type,
6515                                          GTK_SIGNAL_OFFSET (TictactoeClass, tictactoe),
6516                                          gtk_signal_default_marshaller, GTK_ARG_NONE, 0);
6517
6518
6519   gtk_object_class_add_signals (object_class, tictactoe_signals, LAST_SIGNAL);
6520
6521   class->tictactoe = NULL;
6522 }
6523 </verb></tscreen>
6524
6525 <p>
6526 Notre widget n'a qu'un signal&nbsp;: "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
6531
6532 La fonction :
6533
6534 <tscreen><verb>
6535 gint   gtk_signal_new                     (gchar               *name,
6536                                            GtkSignalRunType     run_type,
6537                                            gint                 object_type,
6538                                            gint                 function_offset,
6539                                            GtkSignalMarshaller  marshaller,
6540                                            GtkArgType           return_val,
6541                                            gint                 nparams,
6542                                            ...);
6543 </verb></tscreen>
6544
6545 crée un nouveau signal. Les paramètres sont :
6546
6547 <itemize>
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.
6553
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).
6556
6557 <item> <em/function_offset/ : L'offset d'un pointeur vers le
6558 gestionnaire par défaut dans la structure de classe.
6559
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()/.
6565
6566 <item> <em/return_val/ : Type de la valeur retournée.
6567
6568 <item> <em/nparams/ : Nombre de paramètres du gestionnaire de signal
6569 (autres que les deux par défaut mentionnés plus haut).
6570
6571 <item> <em/.../ : Types des paramètres.
6572 </itemize>
6573
6574 Lorsque l'on spécifie les types, on utilise l'énumération
6575 <em/GtkArgType/&nbsp;:
6576
6577 <tscreen><verb>
6578 typedef enum
6579 {
6580   GTK_ARG_INVALID,
6581   GTK_ARG_NONE,
6582   GTK_ARG_CHAR,
6583   GTK_ARG_SHORT,
6584   GTK_ARG_INT,
6585   GTK_ARG_LONG,
6586   GTK_ARG_POINTER,
6587   GTK_ARG_OBJECT,
6588   GTK_ARG_FUNCTION,
6589   GTK_ARG_SIGNAL
6590 } GtkArgType;
6591 </verb></tscreen>
6592
6593 <p>
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.
6600
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.
6606
6607 <sect2>La fonction <em/_init()/
6608 <p>
6609
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.
6615
6616 <tscreen><verb>
6617
6618 static void
6619 tictactoe_init (Tictactoe *ttt)
6620 {
6621   GtkWidget *table;
6622   gint i,j;
6623   
6624   table = gtk_table_new (3, 3, TRUE);
6625   gtk_container_add (GTK_CONTAINER(ttt), table);
6626   gtk_widget_show (table);
6627
6628   for (i=0;i<3; i++)
6629     for (j=0;j<3; j++)
6630       {
6631         ttt->buttons[i][j] = gtk_toggle_button_new ();
6632         gtk_table_attach_defaults (GTK_TABLE(table), ttt->buttons[i][j], 
6633                                    i, i+1, j, j+1);
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]);
6638       }
6639 }
6640 </verb></tscreen>
6641
6642 <sect2> Et le reste...
6643 <p>
6644
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
6652 Tictactoe.
6653
6654 <p>
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
6659 besoin.
6660
6661 <p>
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".
6666
6667 <tscreen><verb>  
6668 GtkWidget*
6669 tictactoe_new ()
6670 {
6671   return GTK_WIDGET ( gtk_type_new (tictactoe_get_type ()));
6672 }
6673
6674 void           
6675 tictactoe_clear (Tictactoe *ttt)
6676 {
6677   int i,j;
6678
6679   for (i=0;i<3;i++)
6680     for (j=0;j<3;j++)
6681       {
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]),
6684                                      FALSE);
6685         gtk_signal_handler_unblock_by_data (GTK_OBJECT(ttt->buttons[i][j]), ttt);
6686       }
6687 }
6688
6689 static void
6690 tictactoe_toggle (GtkWidget *widget, Tictactoe *ttt)
6691 {
6692   int i,k;
6693
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 } };
6700
6701   int success, found;
6702
6703   for (k=0; k<8; k++)
6704     {
6705       success = TRUE;
6706       found = FALSE;
6707
6708       for (i=0;i<3;i++)
6709         {
6710           success = success &amp;&amp; 
6711             GTK_TOGGLE_BUTTON(ttt->buttons[rwins[k][i]][cwins[k][i]])->active;
6712           found = found ||
6713             ttt->buttons[rwins[k][i]][cwins[k][i]] == widget;
6714         }
6715       
6716       if (success &amp;&amp; found)
6717         {
6718           gtk_signal_emit (GTK_OBJECT (ttt), 
6719                            tictactoe_signals[TICTACTOE_SIGNAL]);
6720           break;
6721         }
6722     }
6723 }
6724 </verb></tscreen>
6725
6726 <p>
6727
6728 Enfin, un exemple de programme utilisant notre widget Tictactoe&nbsp;
6729
6730 <tscreen><verb>
6731 #include <gtk/gtk.h>
6732 #include "tictactoe.h"
6733
6734 /* Invoqué lorsqu'une ligne, une colonne ou une diagonale est complète */
6735
6736 void win (GtkWidget *widget, gpointer data)
6737 {
6738   g_print ("Ouais !\n");
6739   tictactoe_clear (TICTACTOE (widget));
6740 }
6741
6742 int main (int argc, char *argv[])
6743 {
6744   GtkWidget *window;
6745   GtkWidget *ttt;
6746   
6747   gtk_init (&amp;argc, &amp;argv);
6748
6749   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
6750   
6751   gtk_window_set_title (GTK_WINDOW (window), "Aspect Frame");
6752   
6753   gtk_signal_connect (GTK_OBJECT (window), "destroy",
6754                       GTK_SIGNAL_FUNC (gtk_exit), NULL);
6755   
6756   gtk_container_border_width (GTK_CONTAINER (window), 10);
6757
6758   /* Création d'un widget Tictactoe */
6759   ttt = tictactoe_new ();
6760   gtk_container_add (GTK_CONTAINER (window), ttt);
6761   gtk_widget_show (ttt);
6762
6763   /* On lui attache le signal "tictactoe" */
6764   gtk_signal_connect (GTK_OBJECT (ttt), "tictactoe",
6765                       GTK_SIGNAL_FUNC (win), NULL);
6766
6767   gtk_widget_show (window);
6768   
6769   gtk_main ();
6770   
6771   return 0;
6772 }
6773
6774 </verb></tscreen>
6775
6776 <sect1> Création d'un widget à partir de zéro
6777
6778 <sect2> Introduction
6779
6780 <p>
6781
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.
6787
6788 <sect2>Afficher un widget à l'écran
6789 <p>
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.
6793
6794 <itemize>
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
6805 widgets fils.
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).
6812 </itemize>
6813
6814 <p>
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()/.
6825
6826 <p>
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()/.
6834
6835 <p>
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&nbsp;:
6838 utiliser la fonction <em/draw()/ par défaut et n'implanter que la fonction
6839 <em/expose()/.
6840
6841 <sect2>Origines du widget Dial
6842
6843 <p>
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.
6855
6856 <sect2>Les bases
6857
6858 <p>
6859 Un petite partie de notre widget devrait ressembler au widget Tictactoe. Nous
6860 avons d'abord le fichier en-tête&nbsp;:
6861
6862 <tscreen><verb>
6863 /* GTK - The GIMP Toolkit
6864  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
6865  *
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.
6870  *
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.
6875  *
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.
6879  */
6880
6881 #ifndef __GTK_DIAL_H__
6882 #define __GTK_DIAL_H__
6883
6884 #include <gdk/gdk.h>
6885 #include <gtk/gtkadjustment.h>
6886 #include <gtk/gtkwidget.h>
6887
6888
6889 #ifdef __cplusplus
6890 extern "C" {
6891 #endif /* __cplusplus */
6892
6893
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 ())
6897
6898
6899 typedef struct _GtkDial        GtkDial;
6900 typedef struct _GtkDialClass   GtkDialClass;
6901
6902 struct _GtkDial
6903 {
6904   GtkWidget widget;
6905
6906   /* politique de mise à jour  
6907      (GTK_UPDATE_[CONTINUOUS/DELAYED/DISCONTINUOUS]) */
6908
6909   guint policy : 2;
6910
6911   /* Le bouton qui est pressé, 0 si aucun */
6912   guint8 button;
6913
6914   /* Dimensions des composants de dial */
6915   gint radius;
6916   gint pointer_width;
6917
6918   /* ID du timer de mise à jour, 0 si aucun */
6919   guint32 timer;
6920
6921   /* Angle courant*/
6922   gfloat angle;
6923
6924   /* Anciennes valeurs d'ajustement stockées. On sait donc quand quelque
6925      chose change */
6926   gfloat old_value;
6927   gfloat old_lower;
6928   gfloat old_upper;
6929
6930   /* L'objet ajustment qui stocke les données de cet appel */
6931   GtkAdjustment *adjustment;
6932 };
6933
6934 struct _GtkDialClass
6935 {
6936   GtkWidgetClass parent_class;
6937 };
6938
6939
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);
6945
6946 void           gtk_dial_set_adjustment         (GtkDial      *dial,
6947                                                 GtkAdjustment *adjustment);
6948 #ifdef __cplusplus
6949 }
6950 #endif /* __cplusplus */
6951
6952
6953 #endif /* __GTK_DIAL_H__ */
6954
6955 </verb></tscreen>
6956
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.
6960
6961 <p>
6962
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&nbsp;:
6966
6967 <tscreen><verb>
6968 #include <math.h>
6969 #include <stdio.h>
6970 #include <gtk/gtkmain.h>
6971 #include <gtk/gtksignal.h>
6972
6973 #include "gtkdial.h"
6974
6975 #define SCROLL_DELAY_LENGTH  300
6976 #define DIAL_DEFAULT_SIZE 100
6977
6978 /* Déclararations des prototypes */
6979
6980 [ omis pour gagner de la place ]
6981
6982 /* Données locales */
6983
6984 static GtkWidgetClass *parent_class = NULL;
6985
6986 guint
6987 gtk_dial_get_type ()
6988 {
6989   static guint dial_type = 0;
6990
6991   if (!dial_type)
6992     {
6993       GtkTypeInfo dial_info =
6994       {
6995         "GtkDial",
6996         sizeof (GtkDial),
6997         sizeof (GtkDialClass),
6998         (GtkClassInitFunc) gtk_dial_class_init,
6999         (GtkObjectInitFunc) gtk_dial_init,
7000         (GtkArgFunc) NULL,
7001       };
7002
7003       dial_type = gtk_type_unique (gtk_widget_get_type (), &amp;dial_info);
7004     }
7005
7006   return dial_type;
7007 }
7008
7009 static void
7010 gtk_dial_class_init (GtkDialClass *class)
7011 {
7012   GtkObjectClass *object_class;
7013   GtkWidgetClass *widget_class;
7014
7015   object_class = (GtkObjectClass*) class;
7016   widget_class = (GtkWidgetClass*) class;
7017
7018   parent_class = gtk_type_class (gtk_widget_get_type ());
7019
7020   object_class->destroy = gtk_dial_destroy;
7021
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;
7029 }
7030
7031 static void
7032 gtk_dial_init (GtkDial *dial)
7033 {
7034   dial->button = 0;
7035   dial->policy = GTK_UPDATE_CONTINUOUS;
7036   dial->timer = 0;
7037   dial->radius = 0;
7038   dial->pointer_width = 0;
7039   dial->angle = 0.0;
7040   dial->old_value = 0.0;
7041   dial->old_lower = 0.0;
7042   dial->old_upper = 0.0;
7043   dial->adjustment = NULL;
7044 }
7045
7046 GtkWidget*
7047 gtk_dial_new (GtkAdjustment *adjustment)
7048 {
7049   GtkDial *dial;
7050
7051   dial = gtk_type_new (gtk_dial_get_type ());
7052
7053   if (!adjustment)
7054     adjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
7055
7056   gtk_dial_set_adjustment (dial, adjustment);
7057
7058   return GTK_WIDGET (dial);
7059 }
7060
7061 static void
7062 gtk_dial_destroy (GtkObject *object)
7063 {
7064   GtkDial *dial;
7065
7066   g_return_if_fail (object != NULL);
7067   g_return_if_fail (GTK_IS_DIAL (object));
7068
7069   dial = GTK_DIAL (object);
7070
7071   if (dial->adjustment)
7072     gtk_object_unref (GTK_OBJECT (dial->adjustment));
7073
7074   if (GTK_OBJECT_CLASS (parent_class)->destroy)
7075     (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
7076 }
7077 </verb></tscreen>
7078
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.
7085
7086 <p>
7087 Il y a aussi quelques fonctions pour manipuler les options du widget&nbsp;:
7088
7089 <tscreen><verb>
7090 GtkAdjustment*
7091 gtk_dial_get_adjustment (GtkDial *dial)
7092 {
7093   g_return_val_if_fail (dial != NULL, NULL);
7094   g_return_val_if_fail (GTK_IS_DIAL (dial), NULL);
7095
7096   return dial->adjustment;
7097 }
7098
7099 void
7100 gtk_dial_set_update_policy (GtkDial      *dial,
7101                              GtkUpdateType  policy)
7102 {
7103   g_return_if_fail (dial != NULL);
7104   g_return_if_fail (GTK_IS_DIAL (dial));
7105
7106   dial->policy = policy;
7107 }
7108
7109 void
7110 gtk_dial_set_adjustment (GtkDial      *dial,
7111                           GtkAdjustment *adjustment)
7112 {
7113   g_return_if_fail (dial != NULL);
7114   g_return_if_fail (GTK_IS_DIAL (dial));
7115
7116   if (dial->adjustment)
7117     {
7118       gtk_signal_disconnect_by_data (GTK_OBJECT (dial->adjustment), (gpointer) dial);
7119       gtk_object_unref (GTK_OBJECT (dial->adjustment));
7120     }
7121
7122   dial->adjustment = adjustment;
7123   gtk_object_ref (GTK_OBJECT (dial->adjustment));
7124
7125   gtk_signal_connect (GTK_OBJECT (adjustment), "changed",
7126                       (GtkSignalFunc) gtk_dial_adjustment_changed,
7127                       (gpointer) dial);
7128   gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed",
7129                       (GtkSignalFunc) gtk_dial_adjustment_value_changed,
7130                       (gpointer) dial);
7131
7132   dial->old_value = adjustment->value;
7133   dial->old_lower = adjustment->lower;
7134   dial->old_upper = adjustment->upper;
7135
7136   gtk_dial_update (dial);
7137 }
7138 </verb></tscreen>
7139
7140 <sect2> <em/gtk_dial_realize()/
7141
7142 <p>
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
7151 intéressent.
7152
7153 <p>
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
7157 correct.
7158
7159 <tscreen><verb>
7160 static void
7161 gtk_dial_realize (GtkWidget *widget)
7162 {
7163   GtkDial *dial;
7164   GdkWindowAttr attributes;
7165   gint attributes_mask;
7166
7167   g_return_if_fail (widget != NULL);
7168   g_return_if_fail (GTK_IS_DIAL (widget));
7169
7170   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
7171   dial = GTK_DIAL (widget);
7172
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);
7185
7186   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
7187   widget->window = gdk_window_new (widget->parent->window, &amp;attributes, attributes_mask);
7188
7189   widget->style = gtk_style_attach (widget->style, widget->window);
7190
7191   gdk_window_set_user_data (widget->window, widget);
7192
7193   gtk_style_set_background (widget->style, widget->window, GTK_STATE_ACTIVE);
7194 }
7195 </verb></tscreen>
7196
7197 <sect2>Négotiation de la taille
7198
7199 <p>
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.
7206
7207 <tscreen><verb>
7208 static void 
7209 gtk_dial_size_request (GtkWidget      *widget,
7210                        GtkRequisition *requisition)
7211 {
7212   requisition->width = DIAL_DEFAULT_SIZE;
7213   requisition->height = DIAL_DEFAULT_SIZE;
7214 }
7215 </verb></tscreen>
7216
7217 <p>
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.
7227
7228 <tscreen><verb>
7229 static void
7230 gtk_dial_size_allocate (GtkWidget     *widget,
7231                         GtkAllocation *allocation)
7232 {
7233   GtkDial *dial;
7234
7235   g_return_if_fail (widget != NULL);
7236   g_return_if_fail (GTK_IS_DIAL (widget));
7237   g_return_if_fail (allocation != NULL);
7238
7239   widget->allocation = *allocation;
7240   if (GTK_WIDGET_REALIZED (widget))
7241     {
7242       dial = GTK_DIAL (widget);
7243
7244       gdk_window_move_resize (widget->window,
7245                               allocation->x, allocation->y,
7246                               allocation->width, allocation->height);
7247
7248       dial->radius = MAX(allocation->width,allocation->height) * 0.45;
7249       dial->pointer_width = dial->radius / 5;
7250     }
7251 }
7252 </verb></tscreen>.
7253
7254 <sect2> <em/gtk_dial_expose()/
7255
7256 <p>
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. 
7262 style.
7263
7264 <tscreen><verb>
7265 static gint
7266 gtk_dial_expose (GtkWidget      *widget,
7267                  GdkEventExpose *event)
7268 {
7269   GtkDial *dial;
7270   GdkPoint points[3];
7271   gdouble s,c;
7272   gdouble theta;
7273   gint xc, yc;
7274   gint tick_length;
7275   gint i;
7276
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);
7280
7281   if (event->count > 0)
7282     return FALSE;
7283   
7284   dial = GTK_DIAL (widget);
7285
7286   gdk_window_clear_area (widget->window,
7287                          0, 0,
7288                          widget->allocation.width,
7289                          widget->allocation.height);
7290
7291   xc = widget->allocation.width/2;
7292   yc = widget->allocation.height/2;
7293
7294   /* Draw ticks */
7295
7296   for (i=0; i<25; i++)
7297     {
7298       theta = (i*M_PI/18. - M_PI/6.);
7299       s = sin(theta);
7300       c = cos(theta);
7301
7302       tick_length = (i%6 == 0) ? dial->pointer_width : dial->pointer_width/2;
7303       
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);
7310     }
7311
7312   /* Draw pointer */
7313
7314   s = sin(dial->angle);
7315   c = cos(dial->angle);
7316
7317
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;
7324
7325   gtk_draw_polygon (widget->style,
7326                     widget->window,
7327                     GTK_STATE_NORMAL,
7328                     GTK_SHADOW_OUT,
7329                     points, 3,
7330                     TRUE);
7331   
7332   return FALSE;
7333 }
7334 </verb></tscreen>
7335
7336 <sect2>Gestion des événements
7337
7338 <p>
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.
7344
7345 <p>
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/).
7356
7357 <tscreen><verb>
7358 static gint
7359 gtk_dial_button_press (GtkWidget      *widget,
7360                        GdkEventButton *event)
7361 {
7362   GtkDial *dial;
7363   gint dx, dy;
7364   double s, c;
7365   double d_parallel;
7366   double d_perpendicular;
7367
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);
7371
7372   dial = GTK_DIAL (widget);
7373
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
7377      par le pointeur */
7378   
7379   dx = event->x - widget->allocation.width / 2;
7380   dy = widget->allocation.height / 2 - event->y;
7381   
7382   s = sin(dial->angle);
7383   c = cos(dial->angle);
7384   
7385   d_parallel = s*dy + c*dx;
7386   d_perpendicular = fabs(s*dx - c*dy);
7387   
7388   if (!dial->button &&
7389       (d_perpendicular < dial->pointer_width/2) &&
7390       (d_parallel > - dial->pointer_width))
7391     {
7392       gtk_grab_add (widget);
7393
7394       dial->button = event->button;
7395
7396       gtk_dial_update_mouse (dial, event->x, event->y);
7397     }
7398
7399   return FALSE;
7400 }
7401
7402 static gint
7403 gtk_dial_button_release (GtkWidget      *widget,
7404                           GdkEventButton *event)
7405 {
7406   GtkDial *dial;
7407
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);
7411
7412   dial = GTK_DIAL (widget);
7413
7414   if (dial->button == event->button)
7415     {
7416       gtk_grab_remove (widget);
7417
7418       dial->button = 0;
7419
7420       if (dial->policy == GTK_UPDATE_DELAYED)
7421         gtk_timeout_remove (dial->timer);
7422       
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");
7426     }
7427
7428   return FALSE;
7429 }
7430
7431 static gint
7432 gtk_dial_motion_notify (GtkWidget      *widget,
7433                          GdkEventMotion *event)
7434 {
7435   GtkDial *dial;
7436   GdkModifierType mods;
7437   gint x, y, mask;
7438
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);
7442
7443   dial = GTK_DIAL (widget);
7444
7445   if (dial->button != 0)
7446     {
7447       x = event->x;
7448       y = event->y;
7449
7450       if (event->is_hint || (event->window != widget->window))
7451         gdk_window_get_pointer (widget->window, &amp;x, &amp;y, &amp;mods);
7452
7453       switch (dial->button)
7454         {
7455         case 1:
7456           mask = GDK_BUTTON1_MASK;
7457           break;
7458         case 2:
7459           mask = GDK_BUTTON2_MASK;
7460           break;
7461         case 3:
7462           mask = GDK_BUTTON3_MASK;
7463           break;
7464         default:
7465           mask = 0;
7466           break;
7467         }
7468
7469       if (mods & mask)
7470         gtk_dial_update_mouse (dial, x,y);
7471     }
7472
7473   return FALSE;
7474 }
7475
7476 static gint
7477 gtk_dial_timer (GtkDial *dial)
7478 {
7479   g_return_val_if_fail (dial != NULL, FALSE);
7480   g_return_val_if_fail (GTK_IS_DIAL (dial), FALSE);
7481
7482   if (dial->policy == GTK_UPDATE_DELAYED)
7483     gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
7484
7485   return FALSE;
7486 }
7487
7488 static void
7489 gtk_dial_update_mouse (GtkDial *dial, gint x, gint y)
7490 {
7491   gint xc, yc;
7492   gfloat old_value;
7493
7494   g_return_if_fail (dial != NULL);
7495   g_return_if_fail (GTK_IS_DIAL (dial));
7496
7497   xc = GTK_WIDGET(dial)->allocation.width / 2;
7498   yc = GTK_WIDGET(dial)->allocation.height / 2;
7499
7500   old_value = dial->adjustment->value;
7501   dial->angle = atan2(yc-y, x-xc);
7502
7503   if (dial->angle < -M_PI/2.)
7504     dial->angle += 2*M_PI;
7505
7506   if (dial->angle < -M_PI/6)
7507     dial->angle = -M_PI/6;
7508
7509   if (dial->angle > 7.*M_PI/6.)
7510     dial->angle = 7.*M_PI/6.;
7511
7512   dial->adjustment->value = dial->adjustment->lower + (7.*M_PI/6 - dial->angle) *
7513     (dial->adjustment->upper - dial->adjustment->lower) / (4.*M_PI/3.);
7514
7515   if (dial->adjustment->value != old_value)
7516     {
7517       if (dial->policy == GTK_UPDATE_CONTINUOUS)
7518         {
7519           gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
7520         }
7521       else
7522         {
7523           gtk_widget_draw (GTK_WIDGET(dial), NULL);
7524
7525           if (dial->policy == GTK_UPDATE_DELAYED)
7526             {
7527               if (dial->timer)
7528                 gtk_timeout_remove (dial->timer);
7529
7530               dial->timer = gtk_timeout_add (SCROLL_DELAY_LENGTH,
7531                                              (GtkFunction) gtk_dial_timer,
7532                                              (gpointer) dial);
7533             }
7534         }
7535     }
7536 }
7537 </verb></tscreen>
7538
7539 <p>
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()/).
7545
7546 <tscreen><verb>
7547 static void
7548 gtk_dial_update (GtkDial *dial)
7549 {
7550   gfloat new_value;
7551   
7552   g_return_if_fail (dial != NULL);
7553   g_return_if_fail (GTK_IS_DIAL (dial));
7554
7555   new_value = dial->adjustment->value;
7556   
7557   if (new_value < dial->adjustment->lower)
7558     new_value = dial->adjustment->lower;
7559
7560   if (new_value > dial->adjustment->upper)
7561     new_value = dial->adjustment->upper;
7562
7563   if (new_value != dial->adjustment->value)
7564     {
7565       dial->adjustment->value = new_value;
7566       gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
7567     }
7568
7569   dial->angle = 7.*M_PI/6. - (new_value - dial->adjustment->lower) * 4.*M_PI/3. /
7570     (dial->adjustment->upper - dial->adjustment->lower);
7571
7572   gtk_widget_draw (GTK_WIDGET(dial), NULL);
7573 }
7574
7575 static void
7576 gtk_dial_adjustment_changed (GtkAdjustment *adjustment,
7577                               gpointer       data)
7578 {
7579   GtkDial *dial;
7580
7581   g_return_if_fail (adjustment != NULL);
7582   g_return_if_fail (data != NULL);
7583
7584   dial = GTK_DIAL (data);
7585
7586   if ((dial->old_value != adjustment->value) ||
7587       (dial->old_lower != adjustment->lower) ||
7588       (dial->old_upper != adjustment->upper))
7589     {
7590       gtk_dial_update (dial);
7591
7592       dial->old_value = adjustment->value;
7593       dial->old_lower = adjustment->lower;
7594       dial->old_upper = adjustment->upper;
7595     }
7596 }
7597
7598 static void
7599 gtk_dial_adjustment_value_changed (GtkAdjustment *adjustment,
7600                                     gpointer       data)
7601 {
7602   GtkDial *dial;
7603
7604   g_return_if_fail (adjustment != NULL);
7605   g_return_if_fail (data != NULL);
7606
7607   dial = GTK_DIAL (data);
7608
7609   if (dial->old_value != adjustment->value)
7610     {
7611       gtk_dial_update (dial);
7612
7613       dial->old_value = adjustment->value;
7614     }
7615 }
7616 </verb></tscreen>
7617
7618 <sect2>Améliorations possibles
7619
7620 <p>
7621
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&nbsp;:
7627
7628 <itemize>
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).
7635
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.
7638
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/.
7645
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.
7650
7651 </itemize>
7652
7653 <sect1>En savoir plus
7654
7655 <p>
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&nbsp;: 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
7662 chance !
7663
7664 <sect>Scribble, un programme simple de dessin
7665
7666 <sect1>Présentation
7667
7668 <p>
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
7676 périphériques.
7677
7678 <sect1>Gestion d'événement
7679
7680 <p>
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&nbsp;:
7690
7691 <tscreen><verb>
7692 struct _GdkEventMotion
7693 {
7694   GdkEventType type;
7695   GdkWindow *window;
7696   guint32 time;
7697   gdouble x;
7698   gdouble y;
7699   ...
7700   guint state;
7701   ...
7702 };
7703 </verb></tscreen>
7704
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
7711 suivantes&nbsp;: 
7712
7713 <tscreen><verb>
7714 GDK_SHIFT_MASK  
7715 GDK_LOCK_MASK   
7716 GDK_CONTROL_MASK
7717 GDK_MOD1_MASK   
7718 GDK_MOD2_MASK   
7719 GDK_MOD3_MASK   
7720 GDK_MOD4_MASK   
7721 GDK_MOD5_MASK   
7722 GDK_BUTTON1_MASK
7723 GDK_BUTTON2_MASK
7724 GDK_BUTTON3_MASK
7725 GDK_BUTTON4_MASK
7726 GDK_BUTTON5_MASK
7727 </verb></tscreen>
7728
7729 <p>
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&nbsp;:
7734
7735 <tscreen><verb>
7736 void       gtk_widget_set_events          (GtkWidget           *widget,
7737                                            gint                 events);
7738 </verb></tscreen>
7739
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&nbsp;:
7743
7744 <tscreen><verb>
7745 GDK_EXPOSURE_MASK
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    
7754 GDK_KEY_PRESS_MASK         
7755 GDK_KEY_RELEASE_MASK       
7756 GDK_ENTER_NOTIFY_MASK      
7757 GDK_LEAVE_NOTIFY_MASK      
7758 GDK_FOCUS_CHANGE_MASK      
7759 GDK_STRUCTURE_MASK         
7760 GDK_PROPERTY_CHANGE_MASK   
7761 GDK_PROXIMITY_IN_MASK      
7762 GDK_PROXIMITY_OUT_MASK     
7763 </verb></tscreen>
7764
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&nbsp;:
7772
7773 <tscreen><verb>
7774 GtkAlignment
7775 GtkArrow
7776 GtkBin
7777 GtkBox
7778 GtkImage
7779 GtkItem
7780 GtkLabel
7781 GtkPaned
7782 GtkPixmap
7783 GtkScrolledWindow
7784 GtkSeparator
7785 GtkTable
7786 GtkViewport
7787 GtkAspectFrame
7788 GtkFrame
7789 GtkVPaned
7790 GtkHPaned
7791 GtkVBox
7792 GtkHBox
7793 GtkVSeparator
7794 GtkHSeparator
7795 </verb></tscreen>
7796
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.
7800
7801 <p>
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.
7810
7811 <p>
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/.
7823
7824 <p>
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&nbsp;:
7830
7831 <tscreen><verb>
7832 GdkWindow*    gdk_window_get_pointer     (GdkWindow       *window,
7833                                           gint            *x,
7834                                           gint            *y,
7835                                           GdkModifierType *mask);
7836 </verb></tscreen>
7837
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
7841 pressés).
7842
7843 <p>
7844 Le code pour configurer les événements pour notre fenêtre ressemble alors
7845 à&nbsp;:
7846
7847 <tscreen><verb>
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);
7856
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);
7862 </verb></tscreen>
7863
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&nbsp;:
7867
7868 <tscreen><verb>
7869 static gint
7870 button_press_event (GtkWidget *widget, GdkEventButton *event)
7871 {
7872   if (event->button == 1 &amp;&amp; pixmap != NULL)
7873       draw_brush (widget, event->x, event->y);
7874
7875   return TRUE;
7876 }
7877
7878 static gint
7879 motion_notify_event (GtkWidget *widget, GdkEventMotion *event)
7880 {
7881   int x, y;
7882   GdkModifierType state;
7883
7884   if (event->is_hint)
7885     gdk_window_get_pointer (event->window, &amp;x, &amp;y, &amp;state);
7886   else
7887     {
7888       x = event->x;
7889       y = event->y;
7890       state = event->state;
7891     }
7892     
7893   if (state &amp; GDK_BUTTON1_MASK &amp;&amp; pixmap != NULL)
7894     draw_brush (widget, x, y);
7895   
7896   return TRUE;
7897 }
7898 </verb></tscreen>
7899
7900
7901 <sect1>Le widget DrawingArea et le dessin
7902
7903 <p>
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. 
7908
7909 Une zone de dessin est créée avec l'appel&nbsp;:
7910
7911 <tscreen><verb>
7912 GtkWidget* gtk_drawing_area_new        (void);
7913 </verb></tscreen>
7914
7915 Une taille par défaut peut être donnée au widget par l'appel&nbsp;:
7916
7917 <tscreen><verb>
7918 void       gtk_drawing_area_size       (GtkDrawingArea      *darea,
7919                                         gint                 width,
7920                                         gint                 height);
7921 </verb></tscreen>
7922
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
7926 dessin.
7927
7928 <p>
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.
7933
7934 <p>
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.
7944
7945 <p>
7946 Pour créer un pixmap mémoire, on appelle la fonction&nbsp;:
7947
7948 <tscreen><verb>
7949 GdkPixmap* gdk_pixmap_new               (GdkWindow  *window,
7950                                          gint        width,
7951                                          gint        height,
7952                                          gint        depth);
7953 </verb></tscreen>
7954
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/.
7960
7961 <p>
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
7964 initialement créée.
7965
7966 <tscreen><verb>
7967 /* Pixmap d'arrière-plan pour la zone de dessin */
7968 static GdkPixmap *pixmap = NULL;
7969
7970 /* Création d'un nouveau pixmap d'arrière-plan de la taille voulue */
7971 static gint
7972 configure_event (GtkWidget *widget, GdkEventConfigure *event)
7973 {
7974   if (pixmap)
7975     {
7976       gdk_pixmap_destroy(pixmap);
7977     }
7978   pixmap = gdk_pixmap_new(widget->window,
7979                           widget->allocation.width,
7980                           widget->allocation.height,
7981                           -1);
7982   gdk_draw_rectangle (pixmap,
7983                       widget->style->white_gc,
7984                       TRUE,
7985                       0, 0,
7986                       widget->allocation.width,
7987                       widget->allocation.height);
7988
7989   return TRUE;
7990 }
7991 </verb></tscreen>
7992
7993 L'appel à <em/gdk_draw_rectangle()/ remet le pixmap à blanc. Nous en dirons un
7994 peu plus dans un moment.
7995
7996 <p>
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)&nbsp;:
8000
8001 <tscreen><verb>
8002 /* Remplit l'écran à partir du pixmap d'arrière-plan */
8003 static gint
8004 expose_event (GtkWidget *widget, GdkEventExpose *event)
8005 {
8006   gdk_draw_pixmap(widget->window,
8007                   widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
8008                   pixmap,
8009                   event->area.x, event->area.y,
8010                   event->area.x, event->area.y,
8011                   event->area.width, event->area.height);
8012
8013   return FALSE;
8014 }
8015 </verb></tscreen>
8016
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
8024 est&nbsp;:
8025
8026 <tscreen><verb>
8027 gdk_draw_line ()
8028 gdk_draw_rectangle ()
8029 gdk_draw_arc ()
8030 gdk_draw_polygon ()
8031 gdk_draw_string ()
8032 gdk_draw_text ()
8033 gdk_draw_pixmap ()
8034 gdk_draw_bitmap ()
8035 gdk_draw_image ()
8036 gdk_draw_points ()
8037 gdk_draw_segments ()
8038 </verb></tscreen>
8039
8040 Consultez la documentation de référence ou le fichier en-tête
8041 <em/&lt;gdk/gdk.h&gt;/ 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).
8044
8045 <p>
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&nbsp;:
8054
8055 <tscreen><verb>
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)]
8060 </verb></tscreen>
8061
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&nbsp;:
8064
8065 <tscreen><verb>
8066 GTK_STATE_NORMAL,
8067 GTK_STATE_ACTIVE,
8068 GTK_STATE_PRELIGHT,
8069 GTK_STATE_SELECTED,
8070 GTK_STATE_INSENSITIVE
8071 </verb></tscreen>
8072
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é.
8075
8076 <p>
8077 Notre fonction <em/draw_brush()/, qui réalise le dessin à l'écran est alors&nbsp;:
8078
8079 <tscreen><verb>
8080 /* Dessine un rectangle à l'écran */
8081 static void
8082 draw_brush (GtkWidget *widget, gdouble x, gdouble y)
8083 {
8084   GdkRectangle update_rect;
8085
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,
8092                       TRUE,
8093                       update_rect.x, update_rect.y,
8094                       update_rect.width, update_rect.height);
8095   gtk_widget_draw (widget, &amp;update_rect);
8096 }
8097 </verb></tscreen>
8098
8099 Après avoir dessiné le rectangle représentant la brosse sur le pixmap, nous
8100 appelons la fonction&nbsp;:
8101
8102 <tscreen><verb>
8103 void       gtk_widget_draw                (GtkWidget           *widget,
8104                                            GdkRectangle        *area);
8105 </verb></tscreen>
8106
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.
8112
8113 <p>
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.
8117
8118 <sect1>Ajouter le support XInput
8119
8120 <p>
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,
8126 comme&nbsp;:
8127
8128 <itemize>
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).
8133 </itemize>
8134
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">.
8138
8139 <p>
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.
8143
8144 <tscreen><verb>
8145 struct _GdkEventMotion
8146 {
8147   GdkEventType type;
8148   GdkWindow *window;
8149   guint32 time;
8150   gdouble x;
8151   gdouble y;
8152   gdouble pressure;
8153   gdouble xtilt;
8154   gdouble ytilt;
8155   guint state;
8156   gint16 is_hint;
8157   GdkInputSource source;
8158   guint32 deviceid;
8159 };
8160 </verb></tscreen>
8161
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&nbsp;:
8168
8169 <tscreen><verb>
8170 GDK_SOURCE_MOUSE
8171 GDK_SOURCE_PEN
8172 GDK_SOURCE_ERASER
8173 GDK_SOURCE_CURSOR
8174 </verb></tscreen>
8175
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).
8181
8182 <sect2>Valider l'information supplémentaire sur un périphérique
8183
8184 <p>
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
8187 programmes. 
8188
8189 <tscreen><verb>
8190 gtk_widget_set_extension_events (drawing_area, GDK_EXTENSION_EVENTS_CURSOR);
8191 </verb></tscreen>
8192
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.
8201
8202 <p>
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.
8209
8210 <tscreen><verb>
8211 void
8212 input_dialog_destroy (GtkWidget *w, gpointer data)
8213 {
8214   *((GtkWidget **)data) = NULL;
8215 }
8216
8217 void
8218 create_input_dialog ()
8219 {
8220   static GtkWidget *inputd = NULL;
8221
8222   if (!inputd)
8223     {
8224       inputd = gtk_input_dialog_new();
8225
8226       gtk_signal_connect (GTK_OBJECT(inputd), "destroy",
8227                           (GtkSignalFunc)input_dialog_destroy, &amp;inputd);
8228       gtk_signal_connect_object (GTK_OBJECT(GTK_INPUT_DIALOG(inputd)->close_button),
8229                                  "clicked",
8230                                  (GtkSignalFunc)gtk_widget_hide,
8231                                  GTK_OBJECT(inputd));
8232       gtk_widget_hide ( GTK_INPUT_DIALOG(inputd)->save_button);
8233
8234       gtk_widget_show (inputd);
8235     }
8236   else
8237     {
8238       if (!GTK_WIDGET_MAPPED(inputd))
8239         gtk_widget_show(inputd);
8240       else
8241         gdk_window_raise(inputd->window);
8242     }
8243 }
8244 </verb></tscreen>
8245
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
8249 segmentation).
8250
8251
8252 <p>
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.
8257
8258 <sect2>Utiliser l'information supplémentaire d'un périphérique
8259
8260 <p>
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
8265 sont pas autorisés.
8266
8267 <p>
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.
8271
8272 <tscreen><verb>
8273 void gdk_input_window_get_pointer     (GdkWindow       *window,
8274                                        guint32         deviceid,
8275                                        gdouble         *x,
8276                                        gdouble         *y,
8277                                        gdouble         *pressure,
8278                                        gdouble         *xtilt,
8279                                        gdouble         *ytilt,
8280                                        GdkModifierType *mask);
8281 </verb></tscreen>
8282
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/).
8288
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.
8292
8293 <tscreen><verb>
8294 static gint
8295 button_press_event (GtkWidget *widget, GdkEventButton *event)
8296 {
8297   print_button_press (event->deviceid);
8298   
8299   if (event->button == 1 &amp;&amp; pixmap != NULL)
8300     draw_brush (widget, event->source, event->x, event->y, event->pressure);
8301
8302   return TRUE;
8303 }
8304
8305 static gint
8306 motion_notify_event (GtkWidget *widget, GdkEventMotion *event)
8307 {
8308   gdouble x, y;
8309   gdouble pressure;
8310   GdkModifierType state;
8311
8312   if (event->is_hint)
8313     gdk_input_window_get_pointer (event->window, event->deviceid,
8314                                   &amp;x, &amp;y, &amp;pressure, NULL, NULL, &amp;state);
8315   else
8316     {
8317       x = event->x;
8318       y = event->y;
8319       pressure = event->pressure;
8320       state = event->state;
8321     }
8322     
8323   if (state &amp; GDK_BUTTON1_MASK &amp;&amp; pixmap != NULL)
8324     draw_brush (widget, event->source, x, y, pressure);
8325   
8326   return TRUE;
8327 }
8328 </verb></tscreen>
8329
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.
8333
8334 <tscreen><verb>
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 */
8337 static void
8338 draw_brush (GtkWidget *widget, GdkInputSource source,
8339             gdouble x, gdouble y, gdouble pressure)
8340 {
8341   GdkGC *gc;
8342   GdkRectangle update_rect;
8343
8344   switch (source)
8345     {
8346     case GDK_SOURCE_MOUSE:
8347       gc = widget->style->dark_gc[GTK_WIDGET_STATE (widget)];
8348       break;
8349     case GDK_SOURCE_PEN:
8350       gc = widget->style->black_gc;
8351       break;
8352     case GDK_SOURCE_ERASER:
8353       gc = widget->style->white_gc;
8354       break;
8355     default:
8356       gc = widget->style->light_gc[GTK_WIDGET_STATE (widget)];
8357     }
8358
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, &amp;update_rect);
8367 }
8368 </verb></tscreen>
8369
8370 <sect2>En savoir plus sur un périphérique
8371
8372 <p>
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&nbsp;:
8375
8376 <tscreen><verb>
8377 GList *gdk_input_list_devices               (void);
8378 </verb></tscreen>
8379
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
8382 suivante&nbsp;:
8383
8384 <tscreen><verb>
8385 struct _GdkDeviceInfo
8386 {
8387   guint32 deviceid;
8388   gchar *name;
8389   GdkInputSource source;
8390   GdkInputMode mode;
8391   gint has_cursor;
8392   gint num_axes;
8393   GdkAxisUse *axes;
8394   gint num_keys;
8395   GdkDeviceKey *keys;
8396 };
8397 </verb></tscreen>
8398
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.
8406
8407 <p>
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.
8410
8411 <tscreen><verb>
8412 static void
8413 print_button_press (guint32 deviceid)
8414 {
8415   GList *tmp_list;
8416
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();
8420
8421   while (tmp_list)
8422     {
8423       GdkDeviceInfo *info = (GdkDeviceInfo *)tmp_list->data;
8424
8425       if (info->deviceid == deviceid)
8426         {
8427           printf("Bouton pressé sur le périphérique '%s'\n", info->name);
8428           return;
8429         }
8430
8431       tmp_list = tmp_list->next;
8432     }
8433 }
8434 </verb></tscreen>
8435
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.
8439
8440 <sect2>Sophistications supplémentaires<label id="sec_Further_Sophistications">
8441
8442 <p>
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.
8450
8451 <p>
8452 Pour restaurer un état au prochain démarrage du programme, GDK dispose de
8453 fonctions pour changer la configuration des périphériques&nbsp;:
8454
8455 <tscreen><verb>
8456 gdk_input_set_extension_events()
8457 gdk_input_set_source()
8458 gdk_input_set_mode()
8459 gdk_input_set_axes()
8460 gdk_input_set_key()
8461 </verb></tscreen>
8462
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.
8471
8472 <p>
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.
8481
8482 <p>
8483 Une application qui dessine son propre curseur doit faire deux choses&nbsp;:
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.
8493
8494
8495 <sect>Conseils pour l'écriture d'applications GTK
8496 <p>
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 :)
8500
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.
8503
8504 <sect>Contributions
8505
8506 <p>
8507 Ce document, comme beaucoup d'autres beaux logiciels, a été créé (NdT&nbsp;: et 
8508 traduit) par des
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.
8511
8512 <p>
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.
8519 <p>
8520 Merci.
8521
8522
8523 <sect>Remerciements
8524 <p>
8525 Je voudrai remercier les personnes suivantes pour leurs contributions à ce texte&nbsp;:
8526
8527 <itemize>
8528 <item>Bawer Dagdeviren, <tt><htmlurl url="mailto:chamele0n@geocities.com"
8529 name="chamele0n@geocities.com"></tt> pour le didacticiel sur les menus.
8530
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.
8535
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 :)
8539
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.
8543
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.
8547
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 !
8553
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.
8558
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.
8561 Merci Tim :)
8562
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
8565 sur les Pixmaps.
8566
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.
8569
8570 </itemize>
8571 <p>
8572 Et à tous ceux d'entre vous qui ont commenté et aidé à améliorer ce document.
8573 <p>
8574 Merci.
8575
8576 <sect>Copyright
8577 <p>
8578 Ce didacticiel est  Copyright (C) 1997 Ian Main 
8579 <p>
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.
8584 <p>
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.
8588 <p>
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.
8592
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.
8596 <p>
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.
8600 </article>