  Didacticiel
  Ian Main, slow@intergate.bc.ca
  January 24, 1998.

  11..  IInnttrroodduuccttiioonn

  GTK (GIMP Toolkit) a t d'abord dvelopp pour tre une bote outils pour
  GIMP (General Image Manipulation Program).  GTK est construit sur GDK
  (GIMP Drawing Kit) qui est, avant tout, une encapsulation des
  fonctions Xlib. On l'appelle  GIMP toolkit  car il fut cr pour
  dvelopper GIMP, mais il est dsormais utilis dans plusieurs projets de
  logiciels libres. Les auteurs sont :

  +o  Peter Mattis   petm@xcf.berkeley.edu

  +o  Spencer Kimball spencer@xcf.berkeley.edu

  +o  Josh MacDonald jmacd@xcf.berkeley.edu


  GTK est essentiellement une interface de programmation (API) oriente
  objet.  Bien qu'il soit entirement crit en C, il est implant en
  utilisant la notion de classes et de fonctions de rappel (pointeurs de
  fonctions).

  Un troisime composant, appel glib, remplace certains appels standard
  et comporte quelques fonctions supplmentaires pour grer les listes
  chanes, etc. Les fonctions de remplacement sont utilises pour accrotre
  la portabilit de GTK car certaines de ces fonctions, comme
  g_strerror(), ne sont pas disponibles ou ne sont pas standard sur
  d'autres Unix. D'autres comportent des amliorations par rapport aux
  versions de la libc : g_malloc(), par exemple, facilite le dbuggage.


  Ce didacticiel tente de dcrire du mieux possible GTK, mais il n'est
  pas exhaustif. Il suppose une bonne connaissance du langage C, et de
  la faon de crer des programmes C. Il serait trs prcieux au lecteur
  d'avoir dj une exprience de la programmation X, mais cela n'est pas
  ncessaire. Si l'apprentissage de GTK marque vos dbuts dans l'approche
  des widgets, n'hsitez pas  faire des commentaires sur ce didacticiel
  et sur les problmes qu'il vous a pos.  Il y a aussi une API C++ pour
  GTK (GTK--), si vous prfrez utiliser ce langage, consultez plutt la
  documentation qui la concerne. Une encapsulation en Objective C et des
  liaisons Guile sont galement disponibles, mais ne seront pas abordes
  ici.

  J'apprcierais beaucoup avoir un cho des problmes que vous avez
  rencontr pour apprendre GTK  partir de ce document. De plus, toute
  suggestion sur son amlioration est la bienvenue.


  22..  BBiieenn ddbbuutteerr

  La premire chose  faire est, bien sr, de rcuprer les sources de GTK et
  de les installer. Vous pouvez en obtenir la dernire version sur
  ftp.gimp.org dans le rpertoire /pub/gtk. D'autres sources
  d'informations se trouvent sur http://www.gimp.org/gtk. GTK utilise
  _a_u_t_o_c_o_n_f de GNU pour se configurer. Lorsque vous l'aurez dtarr, tapez
  _._/_c_o_n_f_i_g_u_r_e _-_-_h_e_l_p pour consulter la liste des options.

  Pour commencer notre introduction  GTK, nous dbuterons avec le
  programme le plus simple qui soit. Celui-ci crera une fentre de
  200x200 pixels et ne pourra se terminer qu'en le tuant  partir du
  shell.


       #include <gtk/gtk.h>

       int main (int argc, char *argv[])
       {
           GtkWidget *window;

           gtk_init (&argc, &argv);

           window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
           gtk_widget_show (window);

           gtk_main ();

           return 0;
       }



  Tous les programmes inclueront videmment le fichier gtk/gtk.h qui
  dclare les variables, fonctions, structures, etc. qui seront utilises
  par votre application GTK.

  La ligne  :



       gtk_init (&argc, &argv);



  appelle la fonction _g_t_k___i_n_i_t_(_g_i_n_t _*_a_r_g_c_, _g_c_h_a_r _*_*_*_a_r_g_v_) qui sera
  appele dans toutes les applications GTK. Cette fonction configure
  certaines choses pour nous, comme l'aspect visuel et les couleurs par
  dfaut, puis appelle _g_d_k___i_n_i_t_(_g_i_n_t _*_a_r_g_c_, _g_c_h_a_r _*_*_*_a_r_g_v_).  Cette
  dernire initialise la bibliothque pour qu'elle puisse tre utilise,
  configure les gestionnaires de signaux par dfaut et vrifie les
  paramtres passs  notre application via la ligne de commande en
  recherchant l'un des suivants :


  +o  --display

  +o  --debug-level

  +o  --no-xshm

  +o  --sync

  +o  --show-events

  +o  --no-show-events

  Elle les supprime alors de la liste des paramtres, en laissant tout ce
  qu'elle ne reconnat pas pour que notre application l'analyse ou
  l'ignore. Ceci cre un ensemble de paramtres standards accepts par
  toutes les applications GTK.

  Les deux lignes de code suivantes crent et affichent une fentre.



         window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
         gtk_widget_show (window);

  Le paramtre GTK_WINDOW_TOPLEVEL prcise que l'on veut que la fentre cre
  suive l'aspect et le placement dfinis par le gestionnaire de fentres.
  Plutt que de crer une fentre de 0x0, une fentre sans fentre fille est
  de 200x200 par dfaut : on peut ainsi la manipuler facilement.

  La fonction _g_t_k___w_i_d_g_e_t___s_h_o_w_(_) informe GTK que l'on a configur le
  widget et qu'il peut l'afficher.

  La ligne suivante lance la boucle principale de traitement de GTK.



       gtk_main ();



  _g_t_k___m_a_i_n_(_) est un autre appel que vous verrez dans toute application
  GTK. Lorsque le contrle atteind ce point, GTK se met en attente
  d'vnements X (click sur un bouton, ou appui d'une touche, par
  exemple), de timeouts ou d'entres-sorties fichier. Dans notre exemple
  simple, cependant, les vnements sont ignors.



  22..11..   BBoonnjjoouurr ttoouutt llee mmoonnddee  eenn GGTTKK

  OK, crivons un programme avec un widget (bouton). C'est le classique
  Bonjour tout le monde   la sauce GTK.



  #include <gtk/gtk.h>

      /* fonction de rappel. Dans cet exemple, les paramtres sont ignors...
       *  Les fonctions de rappel sont dtailles plus loin. */

  void hello (GtkWidget *widget, gpointer data)
  {
      g_print ("Bonjour tout le monde.\n");
  }

  gint delete_event(GtkWidget *widget, GdkEvent *event, gpointer data)
  {
      g_print ("le signal delete_event est survenu.\n");

      /* Si l'on renvoit TRUE dans le gestionnaire du signal "delete_event",
       * GTK mettra le signal "destroy". Retourner FALSE signifie que l'on
       * ne veut pas que la fentre soit dtruite.
       * Utilis pour faire apparatre des botes de dialogue du type
       *  tes-vous sr de vouloir quitter ?  */

      /* Remplacez FALSE par TRUE et la fentre principale sera dtruite par
       * un signal  delete_event . */

      return (FALSE);
  }

  /* Autre fonction de rappel */

  void destroy (GtkWidget *widget, gpointer data)
  {
      gtk_main_quit ();
  }

  int main (int argc, char *argv[])
  {
      /* GtkWidget est le type pour dclarer les widgets. */

      GtkWidget *window;
      GtkWidget *button;

      /* Cette fonction est appele dans toutes les applications GTK.
       * Les paramtres passs en ligne de commande sont analyss et
       * retourns  l'application. */

      gtk_init (&argc, &argv);

      /* Cration d'une nouvelle fentre. */

      window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

      /* Lorsque la fentre reoit le signal "delete_event"
       * (envoy par le gestionnaire de fentres en utilisant l'option
       *  close  ou la barre de titre), on lui demande d'appeler la
       * fonction delete_event() dfinie plus haut. La donne passe en
       * paramtre  la fonction de rappel est NULL et est ignor dans le
       * rappel. */

      gtk_signal_connect (GTK_OBJECT (window), "delete_event",
                          GTK_SIGNAL_FUNC (delete_event), NULL);

      /* Ici, on connecte l'venement "destroy"  un gestionnaire de signal.
       * Cet vnement arrive lorsqu'on appelle gtk_widget_destroy() sur la
       * fentre, ou si l'on retourne TRUE dans le rappel "delete_event". */

      gtk_signal_connect (GTK_OBJECT (window), "destroy",
                          GTK_SIGNAL_FUNC (destroy), NULL);
      /* Configuration de la largeur du contour de la fentre. */

      gtk_container_border_width (GTK_CONTAINER (window), 10);

      /* Cration d'un nouveau bouton portant le label
       * "Bonjour tout le monde". */

      button = gtk_button_new_with_label ("Bonjour tout le monde");

      /* Quand le bouton recevra le signal "clicked", il appellera la
       * fonction hello() dfinie plus haut en lui passant NULL en paramtre. */

      gtk_signal_connect (GTK_OBJECT (button), "clicked",
                          GTK_SIGNAL_FUNC (hello), NULL);

      /* Ceci provoquera la destruction de la fentre par appel de la
       * fonction gtk_widget_destroy(window) lors du signal "clicked".
       * Le signal de destruction pourrait venir de l, ou du
       * gestionnaire de fentres. */

      gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
                                 GTK_SIGNAL_FUNC (gtk_widget_destroy),
                                 GTK_OBJECT (window));

      /* Insertion du bouton dans la fentre (container gtk). */

      gtk_container_add (GTK_CONTAINER (window), button);

      /* L'tape finale consiste  afficher ce nouveau widget... */

      gtk_widget_show (button);

      /* ... et la fentre. */

      gtk_widget_show (window);

      /* Toutes les applications GTK doivent avoir un gtk_main().
       * Le droulement du programme se termine l et attend qu'un
       * vnement survienne (touche presse ou vnement souris). */

      gtk_main ();

      return 0;
  }



  22..22..  CCoommppiillaattiioonn ddee  BBoonnjjoouurr ttoouutt llee mmoonnddee

  Supposons que vous avez sauvegard le code prcdent dans un fichier nomm
  _b_o_n_j_o_u_r_._c, pour le compiler tapez la commande suivante :



       gcc -Wall -g bonjour.c -o bonjour_monde -L/usr/X11R6/lib \
           -lgtk -lgdk -lglib -lXext -lX11 -lm



  Les bibliothques invoques ci-dessus doivent toutes tre dans vos
  chemins de recherche par dfaut, sinon, ajoutez -L<library directory>
  pour que _g_c_c recherche dans ces rpertoires les bibliothques
  ncessaires. Sur mon systme Debian GNU/Linux, par exemple, je dois
  ajouter -L/usr/X11R6/lib pour qu'il trouve les bibliothques X11 (NdT :
  et c'est pareil sur mon systme Red Hat Linux...).

  L'ordre des bibliothques est important. L'diteur de liens doit
  connatre les fonctions d'une bibliothque dont il a besoin avant de les
  traiter.

  Si vous compilez en utilisant des bibliothques statiques, l'ordre dans
  lequel vous listez les bibliothques devient trs important. L'exemple
  donn ci-dessus devrait fonctionner dans tous les cas.

  Les bibliothques que l'on utilise sont :

  +o  La bibliothque glib (-lglib), qui contient diverses fonctions.
     Seule _g___p_r_i_n_t_(_) est utilise dans cet exemple. GTK est construit au
     dessus de _g_l_i_b et vous aurez donc toujours besoin de celle-ci. Voir
     la section concernant ``glib'' pour plus de dtails.


  +o  La bibliothque GDK (-lgdk), l'enveloppe de Xlib.

  +o  La bibliothque GTK (-lgtk), la bibliothque des widgets, construite
     au dessus de GDK.

  +o  La bibliothque Xlib (-lX11 utilise par GDK.

  +o  La bibliothque Xext (-lXext). Cette dernire contient le code pour
     les pixmaps en mmoire partage et les autres extensions X.

  +o  La bibliothque mathmatique (-lm).  Elle est utilise pour diffrentes
     raisons par GTK.


  22..33..  TThhoorriiee ddeess ssiiggnnaauuxx eett ddeess rraappppeellss

  Avant de voir en dtail le programme  Bonjour tout le monde , nous
  parlerons d'abord des vnements et des fonctions de rappel.  GTK est
  dirig par les vnements, ce qui signifie qu'il restera inactif dans
  _g_t_k___m_a_i_n jusqu' ce qu'un vnement survienne et que le contrle soit pass
  la fonction approprie.

  Ce passage du contrle est ralis en utilisant le concept de  signal .
  Lorsqu'un vnement survient, comme l'appui sur un bouton, le signal
  appropri sera  mis  par le widget qui a t press. C'est de cette faon
  que GTK ralise la plupart de son travail. Pour qu'un bouton ralise une
  action, on configure un gestionnaire de signal pour capturer ces
  signaux et appeler la fonction adquate.  Ceci est fait en utilisant
  une fonction comme :



       gint gtk_signal_connect (GtkObject *object,
                                gchar *name,
                                GtkSignalFunc func,
                                gpointer func_data);



  O le premier paramtre est le widget qui mettra le signal, et le
  deuxime est le nom du signal que l'on souhaite intercepter. Le
  troisime paramtre est la fonction que l'on veut appeler quand le
  signal est captur, et le quatrime sont les donnes que l'on souhaite
  passer  cette fonction.


  La fonction spcifie par le troisime paramtre s'appelle une fonction de
  rappel  et doit tre de la forme :



       void callback_func(GtkWidget *widget, gpointer *callback_data);



  O le premier paramtre sera un pointeur vers le widget qui a mis le
  signal, et le second un pointeur vers les donnes passes par le dernier
  paramtre de la fonction _g_t_k___s_i_g_n_a_l___c_o_n_n_e_c_t_(_) dcrite plus haut.

  Un autre appel utilis dans l'exemple  Bonjour tout le monde  est :



       gint gtk_signal_connect_object (GtkObject *object,
                                       gchar  *name,
                                       GtkSignalFunc func,
                                       GtkObject *slot_object);



  _g_t_k___s_i_g_n_a_l___c_o_n_n_e_c_t___o_b_j_e_c_t_(_) est la mme chose que _g_t_k___s_i_g_n_a_l___c_o_n_n_e_c_t_(_)
  sauf que la fonction de rappel utilise un seul paramtre : un pointeur
  vers un objet GTK. Lorsqu'on utilise cette fonction pour connecter des
  signaux, le rappel doit tre de cette forme :



       void callback_func (GtkObject *object);



  O l'objet est d'ordinaire un widget. En gnral, on ne configure pas de
  rappels pour _g_t_k___s_i_g_n_a_l___c_o_n_n_e_c_t___o_b_j_e_c_t. D'habitude, ceux-ci sont
  utiliss pour appeler une fonction GTK acceptant un simple widget ou
  objet comme paramtre, comme dans notre exemple.

  La raison pour laquelle il y a deux fonctions pour connecter les
  signaux est simplement de permettre aux fonctions de rappel d'avoir un
  nombre diffrent de paramtres. De nombreuses fonctions de la
  bibliothque GTK n'acceptent qu'un simple pointeur vers un _G_t_k_W_i_d_g_e_t
  comme paramtre et vous pouvez donc  utiliser
  _g_t_k___s_i_g_n_a_l___c_o_n_n_e_c_t___o_b_j_e_c_t_(_) pour celles-ci, tandis que pour vos
  fonctions vous pouvez avoir besoin d'avoir de fournir plus de donnes
  aux fonctions de rappel.


  22..44..   BBoonnjjoouurr ttoouutt llee mmoonnddee  ppaass  ppaass

  Maintenant que nous connaissons la thorie, clarifions un peu en
  progressant  travers le programme  Bonjour tout le monde .

  Voici la fonction de rappel appele lorsque le bouton est  clicked .
  Dans notre exemple, on ignore le widget et les donnes mais il n'est
  pas difficile de faire quelque chose avec. Le prochain exemple
  utilisera le paramtre des donnes pour nous dire quel bouton a t press.



  void hello (GtkWidget *widget, gpointer *data)
  {
      g_print ("Bonjour tout le monde\n");
  }



  Cette fonction de rappel est un peu spciale. L'vnement "delete_event"
  survient lorsque le gestionnaire de fentres l'envoie l'application. On
  doit choisir ce qu'il faut faire de ces vnements. On peut les ignorer,
  leur donner une rponse, ou simplement quitter l'application.

  La valeur que l'on retourne dans cette fonction de rappel permet  GTK
  de savoir ce qu'il a  faire. En retournant FALSE, on l'informe que
  l'on ne veut pas que le signal "destroy" soit mis, afin de laisser
  notre application tourner. En retournant TRUE, on lui demande d'mettre
  "destroy" qui appellera  son tour notre gestionnaire du signal
  "destroy".



       gint delete_event(GtkWidget *widget, GdkEvent *event, gpointer data)
       {
           g_print ("le signal delete_event est survenu.\n");

           return (FALSE);
       }



  Voici une autre fonction de rappel qui ne fait que quitter
  l'application en appelant _g_t_k___m_a_i_n___q_u_i_t_(_).  Il n'y a pas grand chose
  de plus  dire car elle est plutt triviale :



       void destroy (GtkWidget *widget, gpointer *data)
       {
           gtk_main_quit ();
       }



  Je suppose que vous connaissez la fonction _m_a_i_n_(_)... oui, comme les
  autres programmes C, toutes les applications GTK en ont une.



       int main (int argc, char *argv[])
       {



  La partie qui suit dclare deux pointeurs sur des structures de type
  _G_t_k_W_i_d_g_e_t.  Ceux-ci sont utiliss plus loin pour crer une fentre et un
  bouton.



      GtkWidget *window;
      GtkWidget *button;



  Et revoici notre _g_t_k___i_n_i_t.  Comme prcdemment, il initialise le toolkit
  et analyse les paramtres de la ligne de commande. Il supprime chaque
  paramtre reconnu de la liste et modifie _a_r_g_c et _a_r_g_v pour faire comme
  si ces paramtres n'avaient jamais exist, laissant notre application
  analyser les paramtres restants.



           gtk_init (&argc, &argv);



  Cration d'une nouvelle fentre. C'est plutt classique. La mmoire est
  alloue pour une structure _G_t_k_W_i_d_g_e_t et _w_i_n_d_o_w pointe donc sur celle-
  ci. Cela configure une nouvelle fentre, mais celle-ci ne sera pas
  affiche tant que l'on n'a pas appel _g_t_k___w_i_d_g_e_t___s_h_o_w_(_w_i_n_d_o_w_) vers la
  fin de notre programme.



           window = gtk_window_new (GTK_WINDOW_TOPLEVEL);



  Voici maintenant un exemple de connexion d'un gestionnaire de signal
  un objet : la fentre. Le signal "destroy" est captur. Il est mis
  lorsqu'on utilise le gestionnaire de fentres pour tuer la fentre (et
  que l'on retourne TRUE dans le gestionnaire "delete_event"), ou
  lorsqu'on utilise l'appel _g_t_k___w_i_d_g_e_t___d_e_s_t_r_o_y_(_) en lui passant le
  widget _w_i_n_d_o_w comme objet  dtruire.  Ici, on appelle juste la fonction
  _d_e_s_t_r_o_y_(_) dfinie ci-dessus avec le paramtre NULL, ce qui quitte GTK
  pour nous.

  GTK_OBJECT et GTK_SIGNAL_FUNC sont des macros qui ralisent les
  conversions et les vrifications de types pour nous. Elles rendent
  aussi le code plus lisible.



           gtk_signal_connect (GTK_OBJECT (window), "destroy",
                               GTK_SIGNAL_FUNC (destroy), NULL);



  La fonction suivante sert  configurer un attribut d'un objet
  container. Elle configure simplement la fentre pour qu'elle ait une
  zone vide autour d'elle de 10 pixels de large o aucun widget ne pourra
  se trouver. Il existe d'autres fonctions similaires que nous verrons
  dans la section sur la ``Configuration des attributs des widgets''

   nouveau, GTK_CONTAINER est une macro ralisant la conversion de type.


           gtk_container_border_width (GTK_CONTAINER (window), 10);



  Cet appel cre un nouveau bouton. Il alloue l'espace mmoire pour une
  nouvelle structure GtkWidget, l'initialise et fait pointer _b_u_t_t_o_n vers
  elle. Ce bouton portera le label  Bonjour tout le monde lorsqu'il sera
  affich.



           button = gtk_button_new_with_label ("Bonjour tout le monde");



  Maintenant, prenons ce bouton et faisons lui faire quelque chose
  d'utile. On lui attache un gestionnaire de signal pour que, lorsqu'il
  mettra le signal "clicked", notre fonction _h_e_l_l_o_(_) soit appele. On
  ignore les paramtres et on ne passe donc que la valeur NULL  la
  fonction de rappel _h_e_l_l_o_(_).  videmment, le signal "clicked" est mis
  lorsqu'on clique sur le bouton avec la souris.



           gtk_signal_connect (GTK_OBJECT (button), "clicked",
                               GTK_SIGNAL_FUNC (hello), NULL);



  On utilisera aussi ce bouton pour quitter notre programme, ce qui
  permettra d'illustrer la faon dont le signal "destroy" peut venir soit
  du gestionnaire de fentres, soit de notre programme. Quand le bouton
  est "clicked" comme cela est dcrit plus haut, il appelle d'abord la
  fonction de rappel _h_e_l_l_o_(_) puis celle-ci dans l'ordre dans lequel
  elles sont configures. On peut avoir autant de fonctions de rappel que
  l'on dsire, elles seront excutes selon leur ordre de connexion.
  Puisque la fonction _g_t_k___w_i_d_g_e_t___d_e_s_t_r_o_y_(_) n'accepte que _G_t_k_W_i_d_g_e_t
  _*_w_i_d_g_e_t comme paramtre, on utilise ici la fonction
  _g_t_k___s_i_g_n_a_l___c_o_n_n_e_c_t___o_b_j_e_c_t_(_)  la place de _g_t_k___s_i_g_n_a_l___c_o_n_n_e_c_t_(_).



           gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
                                      GTK_SIGNAL_FUNC (gtk_widget_destroy),
                                      GTK_OBJECT (window));



  Voici un appel de placement, qui sera expliqu en dtail plus tard, mais
  qui est plutt facile  comprendre. Il indique simplement  GTK que le
  bouton doit tre plac dans la fentre o il s'affichera.



        gtk_container_add (GTK_CONTAINER (window), button);



  Maintenant, nous avons tout configur comme on le souhaitait : les
  gestionnaires de signaux sont en place et le bouton est mis dans la
  fentre o il doit se trouver. On demande alors  GTK de  montrer  les
  widgets  l'cran. Le widget _w_i_n_d_o_w est affich en dernier afin que la
  fentre entire surgisse d'un coup plutt que voir d'abord la fentre
  s'afficher puis ensuite le bouton apparatre l'intrieur. Il faut dire
  qu'avec des exemples simples comme celui-ci, vous ne ferez pas la
  diffrence.
           gtk_widget_show(button);

           gtk_widget_show (window);



  Bien sr, on appelle _g_t_k___m_a_i_n_(_) qui attendra les vnements venant du
  serveur X et demandera aux widgets d'mettre les signaux lorsque ces
  vnements surviendront.


           gtk_main ();



  Enfin, le _r_e_t_u_r_n final. Il est excut lorsque _g_t_k___q_u_i_t_(_) est appel.


           return 0;



  Lorsque l'on clique sur un bouton GTK, le widget met un signal
  "clicked". Afin de pouvoir utiliser cette information, notre programme
  configure un gestionnaire pour capturer ce signal. Ce gestionnaire
  appelle la fonction de notre choix. Dans notre exemple, lorsque le
  bouton que l'on a cr est "clicked", la fonction _h_e_l_l_o_(_) est appele
  avec le paramtre NULL, puis le gestionnaire suivant de ce signal est
  son tour appel. Il appelle la fonction _g_t_k___w_i_d_g_e_t___d_e_s_t_r_o_y_(_) en lui
  passant le widget _w_i_n_d_o_w comme paramtre, ce qui provoque la
  destruction de celui-ci. Ceci force la fentre  envoyer un signal
  "destroy", qui est captur  son tour et appelle notre fonction de
  rappel _d_e_s_t_r_o_y_(_) qui ferme simplement GTK.

  Une autre faon de procder consiste  utiliser le gestionnaire de
  fentres pour dtruire la fentre. Cela provoquera l'mission du signal
  "delete_event" qui sera pris en charge par notre gestionnaire
  _d_e_l_e_t_e___e_v_e_n_t_(_). S'il retourne FALSE, la fentre restera telle quelle et
  rien ne se passera. Retourner TRUE forcera GTK  mettre le signal
  "destroy" qui, bien sr, appelera la fonction de rappel _d_e_s_t_r_o_y_(_)
  provoquant la sortie du GTK.


  On remarquera que ces signaux ne sont pas les mmes que les signaux
  systmes Unix et ne sont pas implants en utilisant ceux-ci, bien que la
  terminologie employe soit presque identique.



  33..  CCoonnttiinnuuoonnss


  33..11..  TTyyppeess ddee ddoonnnneess

  Vous avez probablement not certaines choses qui ncessitent des
  explications dans les exemples prcdents. les _g_i_n_t, _g_c_h_a_r, etc. que
  vous avez pu voir sont des redfinitions de _i_n_t et _c_h_a_r,
  respectivement. Leur raison d'tre est de s'affranchir des dpendances
  ennuyeuses concernant la taille des types de donnes simples lorsqu'on
  ralise des calculs. Un bon exemple est _g_i_n_t_3_2 qui dsignera un entier
  cod sur 32 bits pour toutes les plateformes, que ce soit une station
  Alpha 64 bits ou un PC i386 32 bits. Les redfinitions de type sont trs
  simples et intuitives. Elles sont toutes dcrites dans le fichier
  _g_l_i_b_/_g_l_i_b_._h (qui est inclus par _g_t_k_._h).

  On notera aussi la possibilit d'utiliser un _G_t_k_W_i_d_g_e_t lorsque la
  fonction attend un _G_t_k_O_b_j_e_c_t. GTK possde une architecture oriente
  objet, et un widget est un objet.


  33..22..  CCoommppllmmeennttss ssuurr lleess ggeessttiioonnnnaaiirreess ddee ssiiggnnaauuxx

  Regardons  nouveau la dclaration de _g_t_k___s_i_g_n_a_l___c_o_n_n_e_c_t.



       gint gtk_signal_connect (GtkObject *object, gchar *name,
                                GtkSignalFunc func, gpointer func_data);



  Vous avez remarqu que le valeur de retour est de type _g_i_n_t ? Il s'agit
  d'un marqueur qui identifie votre fonction de rappel. Comme on le
  disait plus haut, on peut avoir autant de fonctions de rappel que l'on
  a besoin, par signal et par objet, et chacune sera excute  son tour,
  dans l'ordre dans lequel elle a t attache. Ce marqueur vous permet
  d'ter ce rappel de la liste en faisant &;:



             void gtk_signal_disconnect (GtkObject *object, gint id);



  Ainsi, en passant le widget dont on veut supprimer le gestionnaire et
  le marqueur ou identificateur retourn par l'une des fonctions
  _s_i_g_n_a_l___c_o_n_n_e_c_t, on peut dconnecter un gestionnaire de signal.

  Une autre fonction permettant de supprimer tous les gestionnaires de
  signaux pour un objet est :



             gtk_signal_handlers_destroy (GtkObject *object);



  Cet appel n'a pas trop besoin d'explications. Il te simplement tous
  les gestionnaires de signaux de l'objet pass en paramtre.



  33..33..  UUnn  BBoonnjjoouurr ttoouutt llee mmoonnddee  aammlliioorr

  tudions une version lgrement amliore avec de meilleurs exemples de
  fonctions de rappel. Ceci permettra aussi d'introduire le sujet
  suivant : le placement des wigdets.



  #include <gtk/gtk.h>

  /* Notre nouveau rappel amlior. La donne passe  cette fonction est
   * imprime sur stdout. */

  void rappel (GtkWidget *widget, gpointer *data)
  {
      g_print ("Re-Bonjour - %s a t press\n", (char *) data);
  }

  /* Un autre rappel */

  void delete_event (GtkWidget *widget, GdkEvent *event, gpointer *data)
  {
      gtk_main_quit ();
  }

  int main (int argc, char *argv[])
  {
      /* GtkWidget est le type pour dclarer les widgets */

      GtkWidget *window;
      GtkWidget *button;
      GtkWidget *box1;

      /* Cette fonction est appele dans toutes les applications GTK.
       * Les paramtre passs en ligne de commande sont analyss et
       * retourns  l'application. */

      gtk_init (&argc, &argv);

      /* Cration d'une nouvelle fentre. */

      window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

      /* Nouvel appel qui intitule notre nouvelle fentre
       * "Salut les boutons !" */

      gtk_window_set_title (GTK_WINDOW (window), "Salut les boutons !");

      /* Configuration d'un gestionnaire pour "delete_event" afin de
       * quitter immdiatement GTK. */

      gtk_signal_connect (GTK_OBJECT (window), "delete_event",
                          GTK_SIGNAL_FUNC (delete_event), NULL);


      /* Configuration de la largeur du contour de la fentre. */

      gtk_container_border_width (GTK_CONTAINER (window), 10);

      /* Cration d'une bote pour y placer les widgets.
       * Ceci est dcrit en dtails plus loin dans la section
       *  placement . La bote n'est pas matrialise, elle est juste
       * utilise comme moyen d'arranger les widgets. */

      box1 = gtk_hbox_new(FALSE, 0);

      /* On met la bote dans la fentre principale. */

      gtk_container_add (GTK_CONTAINER (window), box1);

      /* On cre un nouveau bouton portant le label  Bouton 1 . */

      button = gtk_button_new_with_label ("Bouton 1");

      /* Lorsque le bouton est cliqu, on appelle la fonction  rappel
       * avec un pointeur sur la chane  Bouton 1  comme paramtre. */

      gtk_signal_connect (GTK_OBJECT (button), "clicked",
                          GTK_SIGNAL_FUNC (rappel), (gpointer) "Bouton 1");

      /* Au lieu d'utiliser gtk_container_add, on place ce bouton dans
       * la bote invisible qui a t place dans la fentre. */

      gtk_box_pack_start(GTK_BOX(box1), button, TRUE, TRUE, 0);

      /* N'oubliez jamais cette tape qui indique  GTK que la configuration
       * de ce bouton est termine et qu'il peut tre affich. */

      gtk_widget_show(button);

      /* On fait la mme chose pour crer un deuxime bouton. */

      button = gtk_button_new_with_label ("Bouton 2");

      /* On appelle la mme fonction de rappel avec un paramtre diffrent,
       * un pointeur sur la chane  Bouton 2 . */

      gtk_signal_connect (GTK_OBJECT (button), "clicked",
                          GTK_SIGNAL_FUNC (rappel), (gpointer) "Bouton 2");

      gtk_box_pack_start(GTK_BOX(box1), button, TRUE, TRUE, 0);

      /* L'ordre dans lequel on affiche les boutons n'est pas vraiment
       * important, mais il est prfrable d'afficher la fentre en dernier
       * pour qu'elle surgisse d'un coup. */

      gtk_widget_show(button);

      gtk_widget_show(box1);

      gtk_widget_show (window);

      /* Le reste est dans gtk_main et on attend que la fte commence ! */

      gtk_main ();

      return 0;
  }



  Compilez ce programme en utilisant les mmes paramtres que pour
  l'exemple prcdent. Vous remarquerez que, maintenant, il est plus
  difficile de quitter le programme : vous devez utiliser le
  gestionnaire de fentres ou une commande shell pour le dtruire. Un bon
  exercice pour le lecteur serait d'insrer un troisime bouton Quitter
  qui permettrait de sortir du programme. Vous pouvez aussi jouer avec
  les options de _g_t_k___b_o_x___p_a_c_k___s_t_a_r_t_(_) en lisant la section suivante.
  Essayez de redimensionner la fentre, et observez son comportement.

  Juste une remarque : il existe une autre constante utilisable avec
  _g_t_k___w_i_n_d_o_w___n_e_w_(_) - GTK_WINDOW_DIALOG.  Ceci permet d'interagir de faon
  un peu diffrente avec le gestionnaire de fentres et doit tre utilis
  pour les fentres temporaires comme les botes de dialogue, par exemple.



  44..  PPllaacceemmeenntt ddeess wwiiddggeettss

  Lorsqu'on cre une application, on veut mettre plus qu'un simple bouton
  dans une fentre. Notre premier exemple  Bonjour le monde n'utilisait
  qu'un seul widget et on pouvait donc simplement faire un appel
  _g_t_k___c_o_n_t_a_i_n_e_r___a_d_d pour  placer  le widget dans la fentre. Mais si l'on
  dsire en mettre plus, comment peut-on contrler l'endroit o le widget
  sera positionn ? C'est ici que le placement entre en jeu.


  44..11..  TThhoorriiee ddeess bbootteess ddee ppllaacceemmeenntt

  La majeure partie du placement est faites en crant des botes comme
  dans l'exemple ci-dessus. Ce sont des widgets containers invisibles o
  l'on peut placer nos widgets. Elles existent sous deux formes : botes
  horizontales et botes verticales. Lorsque l'on place des widgets dans
  une bote horizontale, les objets sont insrs horizontalement de gauche
  droite ou de droite  gauche selon l'appel utilis. Dans une bote
  verticale, les widgets sont placs de haut en bas ou vice versa. On
  peut utiliser n'importe quelle combinaison de botes  l'intrieur ou  ct
  d'autres botes pour crer l'effet dsir.

  Pour crer une nouvelle bote horizontale, on appelle _g_t_k___h_b_o_x___n_e_w_(_), et
  pour les botes verticales, _g_t_k___v_b_o_x___n_e_w_(_). Les fonctions
  _g_t_k___b_o_x___p_a_c_k___s_t_a_r_t_(_) et _g_t_k___b_o_x___p_a_c_k___e_n_d_(_) servent  placer les objets
  l'intrieur de ces containers.  La fonction _g_t_k___b_o_x___p_a_c_k___s_t_a_r_t_(_)
  placera de haut en bas dans une bote verticale et de gauche  droite
  dans une bote horizontale.  _g_t_k___b_o_x___p_a_c_k___e_n_d_(_) fera le contraire en
  plaant de bas en haut et de droite  gauche. En utilisant ces
  fonctions, on peut aligner  droite ou  gauche nos widgets et mme les
  mlanger de n'importe quelle faon pour obtenir l'effet dsir. Dans la
  plupart de nos exemples, on utilisera _g_t_k___b_o_x___p_a_c_k___s_t_a_r_t_(_). Un objet
  peut tre un autre container ou un widget. En fait, de nombreux widgets
  (dont les boutons) sont eux-mmes des containers, mais on utilise
  gnralement seulement un label dans un bouton.

  En utilisant ces appels, GTK sait o vous voulez placer vos widgets et
  il peut donc les dimensionner automatiquement et faire d'autres choses
  bien pratiques. Il existe aussi plusieurs options permettant de
  prciser comment les widgets doivent tre placs. Comme vous pouvez
  l'imaginer, cette mthode nous donne pas mal de libert pour placer et
  crer les widgets.


  44..22..  DDttaaiillss ssuurr lleess bbootteess

   cause de cette libert, le placement des botes avec GTK peut paratre
  droutant au premier abord. Il existe beaucoup d'options et il n'est
  pas tout de suite vident de comprendre comment elles s'accordent
  toutes ensemble. En fait, il y a 5 styles de base diffrents.



  <IMG ALIGN="center" SRC="packbox1.gif" VSPACE="15" HSPACE="10"
  ALT="Box Packing Example Image" WIDTH="528" HEIGHT="235">

  Chaque ligne contient une bote horizontale (_h_b_o_x) contenant plusieurs
  boutons. L'appel  _g_t_k___b_o_x___p_a_c_k indique la faon dont sont placs tous
  les boutons dans la hbox.  Chaque bouton est plac dans la hbox de la
  mme faon (mmes paramtres que la fonction _g_t_k___b_o_x___p_a_c_k___s_t_a_r_t_(_)).

  Voici la dclaration de la fonction _g_t_k___b_o_x___p_a_c_k___s_t_a_r_t.



  void gtk_box_pack_start (GtkBox    *box,
                           GtkWidget *child,
                           gint       expand,
                           gint       fill,
                           gint       padding);



  Le premier paramtre est la bote dans laquelle on place l'objet, le
  second est cet objet. Tous les objets sont tous des boutons jusqu'
  maintenant, on place donc des boutons dans des botes.

  Le paramtre _e_x_p_a_n_d de _g_t_k___b_o_x___p_a_c_k___s_t_a_r_t_(_) ou _g_t_k___b_o_x___p_a_c_k___e_n_d_(_)
  contrle la faon dont le widget est plac dans la bote. S'il vaut TRUE,
  les widgets sont disposs dans la bote de faon  en occuper tout
  l'espace. S'il vaut FALSE, la bote est rtrcie pour correspondre  la
  taille du widget. Mettre _e_x_p_a_n_d FALSE vous permettra d'aligner  droite
  et  gauche vos widgets. Sinon, ils s'largiront pour occuper toute la
  bote. Le mme effet pourrait tre obtenu en utilisant uniquement une des
  deux fonctions _g_t_k___b_o_x___p_a_c_k___s_t_a_r_t ou _p_a_c_k___e_n_d.

  Le paramtre _f_i_l_l des fonctions _g_t_k___b_o_x___p_a_c_k contrle si de l'espace
  supplmentaire doit tre allou aux objets eux-mmes (TRUE), ou si on doit
  rajouter de l'espace (_p_a_d_d_i_n_g) dans la bote autour des objets (FALSE).
  Il n'a de sens que si le paramtre _e_x_p_a_n_d vaut TRUE.

  Lorsque l'on cre une nouvelle bote, on utilise une fonction comme :



       GtkWidget * gtk_hbox_new (gint homogeneous,
                                 gint spacing);



  Le paramtre _h_o_m_o_g_e_n_e_o_u_s de _g_t_k___h_b_o_x___n_e_w (et c'est la mme chose pour
  _g_t_k___v_b_o_x___n_e_w) vrifie que chaque objet de la bote ait la mme taille
  (i.e. la mme largeur dans une hbox, la mme hauteur dans une vbox).
  S'il vaut TRUE, le paramtre _e_x_p_a_n_d des fonctions _g_t_k___b_o_x___p_a_c_k sera
  toujours mis  TRUE.

  Quelle est alors la diffrence entre les paramtres _s_p_a_c_i_n_g (configur
  lorsque la bote est cre) et _p_a_d_d_i_n_g (configur lorque les lments sont
  placs) ? _s_p_a_c_i_n_g ajoute de l'espace entre les objets, et _p_a_d_d_i_n_g en
  ajoute de chaque ct d'un objet. La figure suivante devrait clairer
  tout cela :


  <IMG ALIGN="center" SRC="packbox2.gif" VSPACE="15" HSPACE="10"
  ALT="Box Packing Example Image" WIDTH="509" HEIGHT="213">

  Voici le code utilis pour crer les images ci-dessus. J'y ai mis
  beaucoup de commentaires en esprant que vous n'aurez pas de problme
  pour le relire. Compilez-le et jouez avec les diffrents paramtres.


  44..33..  PPrrooggrraammmmee ddee ddmmoonnssttrraattiioonn ddeess ppllaacceemmeennttss



  #include "gtk/gtk.h"

  void
  delete_event (GtkWidget *widget, GdkEvent *event, gpointer *data)
  {
      gtk_main_quit ();
  }

  /* Construction d'une nouvelle hbox remplie de boutons. Les paramtres qui
   * nous intressent sont passs  cette fonction.
   * On n'affiche pas la bote, mais tout ce qu'elle contient. */

  GtkWidget *make_box (gint homogeneous, gint spacing,
                       gint expand, gint fill, gint padding)
  {
      GtkWidget *box;
      GtkWidget *button;
      char padstr[80];

      /* Cration d'une hbox avec les paramtres homogeneous et spacing
       * voulus. */

      box = gtk_hbox_new (homogeneous, spacing);

      /* Cration d'une srie de boutons configurs de faon approprie */

      button = gtk_button_new_with_label ("gtk_box_pack");
      gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
      gtk_widget_show (button);

      button = gtk_button_new_with_label ("(box,");
      gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
      gtk_widget_show (button);

      button = gtk_button_new_with_label ("button,");
      gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
      gtk_widget_show (button);

      /* Cration d'un bouton portant un label dpendant de la valeur
       * du paramtre expand. */

      if (expand == TRUE)
              button = gtk_button_new_with_label ("TRUE,");
      else
              button = gtk_button_new_with_label ("FALSE,");

      gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
      gtk_widget_show (button);

      /* Mme chose que ci-dessus mais sous forme abrge. */

      button = gtk_button_new_with_label (fill ? "TRUE," : "FALSE,");
      gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
      gtk_widget_show (button);

      /* Rcupration du paramtre padding sous forme de chane. */

      sprintf (padstr, "%d);", padding);

      button = gtk_button_new_with_label (padstr);
      gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
      gtk_widget_show (button);

      return box;
  }

  int main (int argc, char *argv[])
  {
      GtkWidget *window;
      GtkWidget *button;
      GtkWidget *box1;
      GtkWidget *box2;
      GtkWidget *separator;
      GtkWidget *label;
      GtkWidget *quitbox;
      int which;

      /* Initialisation,  ne jamais oublier ! :) */

      gtk_init (&argc, &argv);

      if (argc != 2) {
          fprintf (stderr, "usage : %s num, o num vaut 1, 2, ou 3.\n", *argv);

          /* Nettoyage dans GTK et sortie avec un code d'erreur de 1 */
          gtk_exit (1);
      }

      which = atoi (argv[1]);

      /* Cration de notre fentre. */

      window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

      /* Il ne faut jamais oublier de connecter le signal "destroy"  la
       * fentre principale. C'est trs important pour disposer d'un
       * comportement intuitif adquat. */

      gtk_signal_connect (GTK_OBJECT (window), "delete_event",
                          GTK_SIGNAL_FUNC (delete_event), NULL);

      gtk_container_border_width (GTK_CONTAINER (window), 10);


      /* Cration d'une bote verticale (vbox) pour y placer les botes
       * horizontales.
       * Ceci permet de placer les botes horizontales contenant les boutons
       * les unes au dessus des autres dans cette vbox. */

      box1 = gtk_vbox_new (FALSE, 0);

      /* L'exemple  afficher. Ils correspondent aux images ci-dessus. */

      switch (which) {
      case 1:
          /* Cration d'un label. */

          label = gtk_label_new ("gtk_hbox_new (FALSE, 0);");

          /* Alignement du label  gauche.  On prcisera cette fonction ainsi
           * que les autres dans la section sur les attributs des widgets. */

          gtk_misc_set_alignment (GTK_MISC (label), 0, 0);

          /* Placement du label dans la bote verticale (vbox box1). Il ne
           * faut pas oublier que les widgets qui s'ajoutent  une vbox sont
           * placs les uns au dessus des autres. */

          gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0);

          /* Affichage du label */

          gtk_widget_show (label);

          /* On appelle notre fonction de construction de bote :
           * homogeneous = FALSE, spacing = 0,
           * expand = FALSE, fill = FALSE, padding = 0 */

          box2 = make_box (FALSE, 0, FALSE, FALSE, 0);
          gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
          gtk_widget_show (box2);

          /* On appelle notre fonction de construction de bote :
           * homogeneous = FALSE, spacing = 0,
           * expand = FALSE, fill = FALSE, padding = 0 */

          box2 = make_box (FALSE, 0, TRUE, FALSE, 0);
          gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
          gtk_widget_show (box2);

          /* Paramtres : homogeneous = FALSE, spacing = 0,
           * expand = TRUE, fill = TRUE, padding = 0 */

          box2 = make_box (FALSE, 0, TRUE, TRUE, 0);
          gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
          gtk_widget_show (box2);

          /* Cration d'un sparateur, on verra cela plus tard, mais ils sont
           * simples  utiliser. */

          separator = gtk_hseparator_new ();

          /* Placement du sparateur dans la vbox. Ne pas oublier que tous les
           * widgets sont placs dans une vbox et qu'il seront placs
           * verticalement. */

          gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
          gtk_widget_show (separator);

          /* Cration d'un nouveau label et affichage de celui-ci. */

          label = gtk_label_new ("gtk_hbox_new (TRUE, 0);");
          gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
          gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0);
          gtk_widget_show (label);

          /* Paramtres : homogeneous = TRUE, spacing = 0,
           * expand = TRUE, fill = FALSE, padding = 0 */

          box2 = make_box (TRUE, 0, TRUE, FALSE, 0);
          gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
          gtk_widget_show (box2);

          /* Paramtres : homogeneous = TRUE, spacing = 0,
           * expand = TRUE, fill = TRUE, padding = 0 */

          box2 = make_box (TRUE, 0, TRUE, TRUE, 0);
          gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
          gtk_widget_show (box2);

          /* Un autre sparateur */

          separator = gtk_hseparator_new ();

          /* Les 3 derniers paramtres de gtk_box_pack_start sont :
           * expand = FALSE, fill = TRUE, padding = 5. */

          gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
          gtk_widget_show (separator);

          break;

      case 2:

          /* Cration d'un label, box1 est une vbox identique
           * celle cre au dbut de main() */

          label = gtk_label_new ("gtk_hbox_new (FALSE, 10);");
          gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
          gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0);
          gtk_widget_show (label);

          /* Paramtres : homogeneous = FALSE, spacing = 10,
           * expand = TRUE, fill = FALSE, padding = 0 */

          box2 = make_box (FALSE, 10, TRUE, FALSE, 0);
          gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
          gtk_widget_show (box2);

          /* Paramtres : homogeneous = FALSE, spacing = 10,
           * expand = TRUE, fill = TRUE, padding = 0 */

          box2 = make_box (FALSE, 10, TRUE, TRUE, 0);
          gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
          gtk_widget_show (box2);

          separator = gtk_hseparator_new ();

          /* Les 3 derniers paramtres de gtk_box_pack_start sont :
           * expand = FALSE, fill = TRUE, padding = 5. */

          gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
          gtk_widget_show (separator);

          label = gtk_label_new ("gtk_hbox_new (FALSE, 0);");
          gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
          gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0);
          gtk_widget_show (label);

          /* Paramtres : homogeneous = FALSE, spacing = 0,
           * expand = TRUE, fill = FALSE, padding = 10 */

          box2 = make_box (FALSE, 0, TRUE, FALSE, 10);
          gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
          gtk_widget_show (box2);

          /* Paramtres : homogeneous = FALSE, spacing = 0,
           * expand = TRUE, fill = TRUE, padding = 10 */

          box2 = make_box (FALSE, 0, TRUE, TRUE, 10);
          gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
          gtk_widget_show (box2);

          separator = gtk_hseparator_new ();

          /* Les 3 derniers paramtres de gtk_box_pack_start sont :
           * expand = FALSE, fill = TRUE, padding = 5. */

          gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
          gtk_widget_show (separator);
          break;

      case 3:

          /* Ceci est une dmonstration de la possibilit d'utiliser
           * gtk_box_pack_end() pour aligner les widgets  droite.
           * On cre d'abord une nouvelle bote comme d'habitude. */

          box2 = make_box (FALSE, 0, FALSE, FALSE, 0);

          /* On cre le label qui sera mis  la fin. */

          label = gtk_label_new ("end");

          /* On le place en utilisant gtk_box_pack_end(), il est ainsi
           * mis  droite de la hbox cre par l'appel  make_box(). */

          gtk_box_pack_end (GTK_BOX (box2), label, FALSE, FALSE, 0);

          /* Affichage du label. */

          gtk_widget_show (label);

          /* Placement de box2 dans box1 (la vbox, vous vous rappelez ? :) */

          gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
          gtk_widget_show (box2);

          /* Sparateur pour le bas. */

          separator = gtk_hseparator_new ();

          /* Configuration du sparateur en 400x5 pixels.
           * La hbox que l'on a cre aura donc 400 pixels de large,
           * et le label "end" sera spar des autres de la hbox.
           * Sinon, tous les widgets de la hbox seraient placs les plus
           * prs possible les uns des autres. */

          gtk_widget_set_usize (separator, 400, 5);

          /* Placement du sparateur dans la vbox (box1)
           * cre au debut de main(). */

          gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
          gtk_widget_show (separator);
      }

      /* Cration d'une nouvelle hbox.. vous pouvez en utiliser autant que
       * que vous en avez besoin ! */

      quitbox = gtk_hbox_new (FALSE, 0);

      /* Notre bouton pour quitter. */

      button = gtk_button_new_with_label ("Quit");

      /* Configuration du signal pour dtruire la fentre. Ceci enverra le
       * signal "destroy"  la fentre. Ce signal sera  son tour captur
       * par notre gestionnaire de signal dfini plus haut. */

      gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
                                 GTK_SIGNAL_FUNC (gtk_widget_destroy),
                                 GTK_OBJECT (window));

      /* Placement du bouton dans la  quitbox .
       * Les 3 derniers paramtres de gtk_box_pack_start sont :
       * expand = TRUE, fill = FALSE, padding = 0. */

      gtk_box_pack_start (GTK_BOX (quitbox), button, TRUE, FALSE, 0);

      /* Placement de la quitbox dans la vbox (box1) */

      gtk_box_pack_start (GTK_BOX (box1), quitbox, FALSE, FALSE, 0);

      /* Placement de la vbox (box1), qui contient maintenant tous nos
       * widgets, dans la fentre principale. */

      gtk_container_add (GTK_CONTAINER (window), box1);

      /* Affichage */

      gtk_widget_show (button);
      gtk_widget_show (quitbox);

      gtk_widget_show (box1);

      /* Affichage de la fentre en dernier */

      gtk_widget_show (window);

      /* Ne pas oublier notre fonction principale. */

      gtk_main ();

      /* Le contrle revient ici lorsque gtk_main_quit() est appele,
       * jusqu' ce que  gtk_exit() soitutilise. */

      return 0;
  }



  44..44..  PPllaacceemmeenntt aavveecc lleess ttaabblleess

  tudions une autre mthode de placement : les tables. Elles peuvent
  s'avrer trs utiles dans certaines situations.

  En utilisant des tables, on cre une grille dans laquelle on peut
  placer les widgets. Ceux-ci peuvent occuper tous les endroits que l'on
  dsire.

  La premire chose  faire est, bien sr, d'tudier la fonction
  _g_t_k___t_a_b_l_e___n_e_w :



       GtkWidget* gtk_table_new (gint rows,
                                 gint columns,
                                 gint homogeneous);



  Le premier paramtre est le nombre de lignes de la table et le deuxime,
  le nombre de colonnes.

  Le paramtre _h_o_m_o_g_e_n_e_o_u_s s'occupe de la faon dont les cases de la table
  seront dimensionnes. Si homogeneous vaut TRUE, les cases prennent la
  taille du plus grand widget de la table. S'il vaut FALSE, la taille
  des cases dpend du widget le plus haut de la ligne et du plus large de
  cette colonne.


  Le nombre de lignes et colonnes va de 0  n, o n est le nombre spcifi
  dans l'appel  _g_t_k___t_a_b_l_e___n_e_w. Ainsi, avec _r_o_w_s = 2 et _c_o_l_u_m_n_s = 2, la
  table ressemblera  ceci :



        0          1          2
       0+----------+----------+
        |          |          |
       1+----------+----------+
        |          |          |
       2+----------+----------+



  On notera que le systme de coordonnes part du coin en haut gauche.
  Pour placer un widget dans une case, ou utilise la fonction suivante :



       void gtk_table_attach (GtkTable      *table,
                              GtkWidget     *child,
                              gint           left_attach,
                              gint           right_attach,
                              gint           top_attach,
                              gint           bottom_attach,
                              gint           xoptions,
                              gint           yoptions,
                              gint           xpadding,
                              gint           ypadding);



  O le premier paramtre (_t_a_b_l_e) est la table que l'on a cre et le second
  (_c_h_i_l_d) est le widget que l'on veut placer dans la table.

  Les paramtres _l_e_f_t___a_t_t_a_c_h et _r_i_g_h_t___a_t_t_a_c_h spcifient l'emplacement du
  widget et le nombre de cases  utiliser. Par exemple, si on veut placer
  un bouton dans le coin infrieur droit de la table dcrite plus haut et
  que l'on dsire ne remplir QUE cette case, _l_e_f_t___a_t_t_a_c_h vaudra 1,
  _r_i_g_h_t___a_t_t_a_c_h vaudra 2; _t_o_p___a_t_t_a_c_h vaudra 1 et _b_o_t_t_o_m___a_t_t_a_c_h vaudra 2.

  Si on veut un widget occupant toute la ligne suprieure de notre table,
  on utilisera les valeurs 0, 2, 0, 1.

  Les paramtres _x_o_p_t_i_o_n_s et _y_o_p_t_i_o_n_s servent  prciser les options de
  placement et peuvent tre combines par un OU logique pour permettre des
  options multiples.

  Ces options sont :

  +o  GTK_FILL - Si la case de la table est plus large que le widget, et
     que GTK_FILL est spcifi, le widget s'largira pour occuper toute la
     place disponible.

  +o  GTK_SHRINK - Si la table a moins de place qu'il ne lui en faut
     (gnralement,  cause d'un redimensionnement de la fentre par
     l'utilisateur), les widgets sont alors simplement pousss vers le
     bas de la fentre et disparaissent. Si GTK_SHRINK est spcifi, les
     widgets se rduiront en mme temps que la table.

  +o  GTK_EXPAND - Cette option provoque l'extension de la table pour
     qu'elle utilise tout l'espace restant dans la fentre.
  Le paramtres de _p_a_d_d_i_n_g jouent le mme rle que pour les botes, il crent
  une zone libre, spcifie en pixels, autour du widget.

  gtk_table_attach() a BEAUCOUP d'options.  Voici donc une
  fonction-raccourci :



       void gtk_table_attach_defaults (GtkTable   *table,
                                       GtkWidget  *widget,
                                       gint        left_attach,
                                       gint        right_attach,
                                       gint        top_attach,
                                       gint        bottom_attach);



  _x_o_p_t_i_o_n_s et _o_p_t_i_o_n_s valent par dfaut GTK_FILL | GTK_EXPAND, et
  _x_p_a_d_d_i_n_g et _y_p_a_d_d_i_n_g valent 0. Les autres paramtres sont les mmes que
  ceux de la fonction prcdente.

  Il existe aussi les fonctions _g_t_k___t_a_b_l_e___s_e_t___r_o_w___s_p_a_c_i_n_g_(_) et
  _g_t_k___t_a_b_l_e___s_e_t___c_o_l___s_p_a_c_i_n_g_(_). Elles permettent de placer des espaces
  aprs une ligne ou une colonne.



       void gtk_table_set_row_spacing (GtkTable      *table,
                                       gint           row,
                                       gint           spacing);



  et


       void gtk_table_set_col_spacing  (GtkTable      *table,
                                        gint           column,
                                        gint           spacing);



  Pour les colonnes, l'espace est ajout  droite de la colonne et pour
  les lignes, il est ajout en dessous.

  On peut aussi configurer un espacement pour toutes les lignes et/ou
  colonnes avec :



       void gtk_table_set_row_spacings (GtkTable *table,
                                        gint      spacing);



  Et,


       void gtk_table_set_col_spacings (GtkTable  *table,
                                        gint       spacing);


  Avec ces appels, la dernire ligne et la dernire colonne n'ont pas
  d'espace supplmentaire.


  44..55..  EExxeemmppllee ddee ppllaacceemmeenntt aavveecc ttaabbllee

  Pour le moment, tudiez l'exemple sur les tables (testgtk.c) distribu
  avec les sources de GTK.


  55..  VVuuee dd''eennsseemmbbllee ddeess wwiiddggeettss


  Les tapes pour crer un widget en GTK sont :

  1. _g_t_k___*___n_e_w_(_) - une des fonctions disponibles pour crer un nouveau
     widget. Ces fonctions sont dcrites dans cette section.

  2. Connexion de tous les signaux que l'on souhaite utiliser avec les
     gestionnaires adquats.

  3. Configuration des attributs du widget.

  4. Placement du widget dans un container en utilisant un appel
     appropri comme _g_t_k___c_o_n_t_a_i_n_e_r___a_d_d_(_) ou _g_t_k___b_o_x___p_a_c_k___s_t_a_r_t_(_).

  5. Affichage du widget grce  _g_t_k___w_i_d_g_e_t___s_h_o_w_(_).

  _g_t_k___w_i_d_g_e_t___s_h_o_w_(_) permet  GTK de savoir que l'on a fini de configurer
  les attributs du widget et qu'il est prt  tre affich. On peut aussi
  utiliser _g_t_k___w_i_d_g_e_t___h_i_d_e_(_) pour le faire disparatre. L'ordre dans
  lequel on affiche les widgets n'est pas important, mais il est
  prfrable d'afficher la fentre en dernier pour qu'elle surgisse d'un
  seul coup plutt que de voir les diffrents widgets apparatre  l'cran au
  fur et  mesure. Les fils d'un widget (une fentre est aussi un widget)
  ne seront pas affichs tant que la fentre elle-mme n'est pas affiche
  par la fonction _g_t_k___w_i_d_g_e_t___s_h_o_w_(_).


  55..11..  CCoonnvveerrssiioonnss ddee ttyyppeess

  Vous remarquerez, au fur et  mesure que vous progressez, que GTK
  utilise un systme de coercition de type. Celle-ci est toujours ralise
  en utilisant des macros qui vrifient si l'objet donn peut tre converti
  et qui ralisent cette coercition. Les macros que vous rencontrerez le
  plus sont :


  +o  GTK_WIDGET(widget)

  +o  GTK_OBJECT(object)

  +o  GTK_SIGNAL_FUNC(function)

  +o  GTK_CONTAINER(container)

  +o  GTK_WINDOW(window)

  +o  GTK_BOX(box)

  Elles sont toutes utilises pour convertir les paramtres des fonctions.
  Vous les verrez dans les exemples et, en rgle gnrale, vous saurez les
  utiliser simplement en regardant la dclaration d'une fonction.

  Comme vous pouvez le voir dans la hirarchie de classes ci-dessous,
  tous les _G_t_k_W_i_d_g_e_t_s drivent d'une classe de base _G_t_k_O_b_j_e_c_t. Ceci
  signifie que vous pouvez utiliser un widget chaque fois qu'une
  fonction requiert un objet - il suffit d'utiliser la macro
  GTK_OBJECT().

  Par exemple :



       gtk_signal_connect(GTK_OBJECT(button), "clicked",
                          GTK_SIGNAL_FUNC(fonction_rappel), donnee_de_rappel);



  Cet appel convertit le bouton en objet et fournit une conversion pour
  le pointeur de fonction vers la fonction de rappel.

  De nombreux widgets sont aussi des containers. Si vous regardez la
  hirarchie de classe ci-dessous, vous remarquerez que beaucoup de
  widgets viennent de la classe _G_t_k_C_o_n_t_a_i_n_e_r. N'importe lequel de ces
  widgets peut tre utilis avec la macro GTK_CONTAINER pour tre pass en
  paramtre  une fonction qui attend un container.

  Malheureusement, ces macros ne peuvent tre couvertes en dtail dans ce
  didacticiel, Je vous recommande donc de jeter un coup d'oeil sur les
  fichier en-ttes GTK : ils peuvent s'avrer trs instructifs. En fait, il
  n'est pas difficile de comprendre comment fonctionne un widget, il
  suffit d'tudier les dclarations des fonctions.



  55..22..  LLaa hhiirraarrcchhiiee ddeess wwiiddggeettss

  Voici l'arbre de la hirarchie de classes utilises pour implanter les
  widgets.



      GtkObject
      +-- GtkData
      |   \-- GtkAdjustment
      |
      \-- GtkWidget
          +-- GtkContainer
          |   +-- GtkBin
          |   |   +-- GtkAlignment
          |   |   +-- GtkFrame
          |   |   |   *-- GtkAspectFrame
          |   |   |
          |   |   +-- GtkItem
          |   |   |   +-- GtkListItem
          |   |   |   +-- GtkMenuItem
          |   |   |   |   +-- GtkCheckMenuItem
          |   |   |   |       *-- GtkRadioMenuItem
          |   |   |   |
          |   |   |   *-- GtkTreeItem
          |   |   |
          |   |   +-- GtkViewport
          |   |   \-- GtkWindow
          |   |       +-- GtkDialog
          |   |       \-- GtkFileSelection
          |   |
          |   +-- GtkBox
          |   |   +-- GtkHBox
          |   |   \-- GtkVBox
          |   |       +-- GtkColorSelection
          |   |       \-- GtkCurve
          |   |
          |   +-- GtkButton
          |   |   +-- GtkOptionMenu
          |   |   \-- GtkToggleButton
          |   |       \-- GtkCheckButton
          |   |           \-- GtkRadioButton
          |   |
          |   +-- GtkList
          |   +-- GtkMenuShell
          |   |   +-- GtkMenu
          |   |   \-- GtkMenuBar
          |   |
          |   +-- GtkNotebook
          |   +-- GtkScrolledWindow
          |   +-- GtkTable
          |   \-- GtkTree
          |
          +-- GtkDrawingArea
          +-- GtkEntry
          +-- GtkMisc
          |   +-- GtkArrow
          |   +-- GtkImage
          |   +-- GtkLabel
          |   \-- GtkPixmap
          |
          +-- GtkPreview
          +-- GtkProgressBar
          +-- GtkRange
          |   +-- GtkScale
          |   |   +-- GtkHScale
          |   |   \-- GtkVScale
          |   |
          |   \-- GtkScrollbar
          |       +-- GtkHScrollbar
          |       \-- GtkVScrollbar
          |
          +-- GtkRuler
          |   +-- GtkHRuler
          |   \-- GtkVRuler
          |
          \-- GtkSeparator
              +-- GtkHSeparator
              \-- GtkVSeparator



  55..33..  WWiiddggeettss ssaannss ffeennttrree

  Les widgets suivants n'ont pas de fentre associe. Si vous voulez
  capturer des vnements, vous devez utiliser _G_t_k_E_v_e_n_t_B_o_x.  Reportez-vous
  la section sur ``Le widget EventBox''



       GtkAlignment
       GtkArrow
       GtkBin
       GtkBox
       GtkImage
       GtkItem
       GtkLabel
       GtkPaned
       GtkPixmap
       GtkScrolledWindow
       GtkSeparator
       GtkTable
       GtkViewport
       GtkAspectFrame
       GtkFrame
       GtkVPaned
       GtkHPaned
       GtkVBox
       GtkHBox
       GtkVSeparator
       GtkHSeparator



  Nous continuerons notre exploration de GTK en examinant chaque widget
  tour  tour, crant quelques fonctions simples pour les afficher. Une
  autre source intressante est le programme _t_e_s_t_g_t_k_._c livr avec GTK. Il
  se trouve dans le rpertoire _g_t_k_/


  66..  WWiiddggeettss bboouuttoonnss


  66..11..  BBoouuttoonnss nnoorrmmaauuxx

  On a dj presque vu tout ce qu'il y avait  voir sur le widget bouton.
  Il est trs simple. Cependant, il y a deux faons de crer un bouton. On
  peut utiliser _g_t_k___b_u_t_t_o_n___n_e_w___w_i_t_h___l_a_b_e_l_(_) pour crer un bouton avec un
  label, ou _g_t_k___b_u_t_t_o_n___n_e_w_(_) pour crer un bouton vide. Dans ce dernier
  cas, c'est  vous de placer un label ou un pixmap sur celui-ci. Pour ce
  faire, crez une bote, puis placez vos objets dans celle-ci en
  utilisant la fonction habituelle _g_t_k___b_o_x___p_a_c_k___s_t_a_r_t, utilisez alors
  _g_t_k___c_o_n_t_a_i_n_e_r___a_d_d pour placer la bote dans le bouton.


  Voici un exemple d'utilisation de _g_t_k___b_u_t_t_o_n___n_e_w_(_) pour crer un bouton
  contenant une image et un label. J'ai spar du reste le code qui cre
  une bote pour que vous puissiez l'utiliser dans vos programmes.



  #include <gtk/gtk.h>


  /* Cration d'une hbox avec une image et un label. Cette fonction
   * retourne la bote... */

  GtkWidget *xpm_label_box (GtkWidget *parent, gchar *xpm_filename,
                            gchar *label_text)
  {
      GtkWidget *box1;
      GtkWidget *label;
      GtkWidget *pixmapwid;
      GdkPixmap *pixmap;
      GdkBitmap *mask;
      GtkStyle *style;

      /* Cration de la boite pour un xpm et un label */

      box1 = gtk_hbox_new (FALSE, 0);
      gtk_container_border_width (GTK_CONTAINER (box1), 2);

      /* Choix d'un style de bouton... Je suppose que c'est pour obtenir
       * la couleur du fond. Si quelqu'un connat la vraie raison, qu'il
       * m'claire sur ce point. */

      style = gtk_widget_get_style(parent);

      /* Chargement de xpm pour crer une image */

      pixmap = gdk_pixmap_create_from_xpm (parent->window, &mask,
                                           &style->bg[GTK_STATE_NORMAL],
                                           xpm_filename);
      pixmapwid = gtk_pixmap_new (pixmap, mask);

      /* Cration d'un label  */

      label = gtk_label_new (label_text);

      /* placement de l'image et du label dans la bote */

      gtk_box_pack_start (GTK_BOX (box1),
                          pixmapwid, FALSE, FALSE, 3);

      gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 3);

      gtk_widget_show(pixmapwid);
      gtk_widget_show(label);

      return (box1);
  }

  /* Notre fonction de rappel habituelle */

  void callback (GtkWidget *widget, gpointer *data)
  {
      g_print ("Bonjour - %s a t press\n", (char *) data);
  }


  int main (int argc, char *argv[])
  {
      /* GtkWidget est le type utilis pour dclarer les widgets */

      GtkWidget *window;
      GtkWidget *button;
      GtkWidget *box1;
      gtk_init (&argc, &argv);

      /* Cration d'une fentre */

      window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

      gtk_window_set_title (GTK_WINDOW (window), "Pixmap'd Buttons!");

      /* Il est prfrable de faire cela pour toutes les fentres */

      gtk_signal_connect (GTK_OBJECT (window), "destroy",
                          GTK_SIGNAL_FUNC (gtk_exit), NULL);


      /* Configuration du bord de la fentre */

      gtk_container_border_width (GTK_CONTAINER (window), 10);

      /* Cration d'un bouton */

      button = gtk_button_new ();

      /* Vous devriez tre habitu  voir ces fonctions maintenant */

      gtk_signal_connect (GTK_OBJECT (button), "clicked",
                          GTK_SIGNAL_FUNC (callback), (gpointer) "cool button");

      /* Appel de notre fonction de cration de bote */

      box1 = xpm_label_box(window, "info.xpm", "cool button");

      /* Placement et affichage de tous nos widgets */

      gtk_widget_show(box1);

      gtk_container_add (GTK_CONTAINER (button), box1);

      gtk_widget_show(button);

      gtk_container_add (GTK_CONTAINER (window), button);

      gtk_widget_show (window);

      /* Le reste est dans gtk_main */
      gtk_main ();

      return 0;
  }



  La fonction _x_p_m___l_a_b_e_l___b_o_x_(_) peut tre utilise pour placer des xpms et
  des labels sur tout widget qui peut tre container.


  66..22..  BBoouuttoonnss ccoommmmuuttaatteeuurrss

  Les boutons commutateurs ressemblent beaucoup aux boutons normaux,
  sauf qu'ils seront toujours alternativement dans un tat ou dans un
  autre. Le changement d'tat s'effectue par un click. Ils peuvent tre
  enfoncs et, lorsqu'on clique dessus, ils se relvent.  Re-cliquez, et
  ils se renfoncent.

  Les boutons commutateurs sont la base des cases  cocher ou des boutons
  radio, donc la plupart des appels utiliss pour les boutons
  commutateurs sont hrits par les cases  cocher et les boutons radio.
  J'insisterai l dessus quand nous les aborderons.

  Cration d'un bouton commutateur :



       GtkWidget* gtk_toggle_button_new (void);

       GtkWidget* gtk_toggle_button_new_with_label (gchar *label);



  Comme vous pouvez l'imaginer, elles fonctionnent comme celles des
  boutons normaux. La premire cre un bouton commutateur vide et la
  deuxime un bouton commutateur contenant dj un label.

  Pour rcuprer l'tat d'un commutateur et cela comprend aussi les cases
  cocher et les boutons radio, on utilise une macro comme nous le
  montrons dans l'exemple qui suit et qui teste l'tat du commutateur
  dans une fonction de rappel. Le signal qui nous intresse et qui est
  mis par les boutons commutateurs (ce qui comprend aussi les cases
  cocher et les boutons radio), est le signal "toggled". Pour vrifier
  l'tat de ces boutons, on configure un gestionnaire de signal qui
  capture "toggled" et utilise la macro pour dterminer l'tat. La
  fonction de rappel ressemblera  ceci :



       void rappel_bouton_commutateur (GtkWidget *widget, gpointer data)
       {
           if (GTK_TOGGLE_BUTTON(widget)->active)
           {
               /* Si l'on est ici, c'est que le bouton est relch. */

           } else {

               /* le bouton est enfonc */
           }
       }



  L'appel qui suit peut tre utilis pour configurer l'tat d'un bouton
  commutateur et de ses descendants, les cases  cocher et les boutons
  radio. On lui passe notre bouton en premier paramtre et TRUE ou FALSE
  pour spcifier s'il doit tre relch ou enfonc. Par dfaut, il est relch
  (FALSE).



       void gtk_toggle_button_set_state (GtkToggleButton *toggle_button,
                                         gint state);



  On notera que lorsqu'on utilise cette fonction, et que l'tat est
  modifi, cela force le bouton  mettre un signal "clicked".



  void gtk_toggle_button_toggled (GtkToggleButton *toggle_button);



  Cet appel ne fait que commuter le bouton et mettre le signal
  "toggled".


  66..33..  CCaasseess  ccoocchheerr

  Les cases  cocher hritent de nombreuses proprits et fonctions des
  boutons commutateurs, mais ont un aspect diffrent. Au lieu d'tre des
  boutons contenant du texte, ce sont de petits carrs avec un texte sur
  leur droite. Il sont souvent utiliss pour valider ou non des options
  dans les applications.

  Les deux fonctions de cration sont identiques  celles des boutons
  normaux.



       GtkWidget* gtk_check_button_new (void);

       GtkWidget* gtk_check_button_new_with_label (gchar *label);



  La fonction _n_e_w___w_i_t_h___l_a_b_e_l cre une case  cocher avec un texte cot
  d'elle.

  La vrification de l'tat d'une case  cocher est identique  celle des
  boutons commutateurs.


  66..44..  BBoouuttoonnss rraaddiioo

  Les boutons radio ressemblent aux cases  cocher sauf qu'ils sont
  groups de faon  ce qu'un seul d'entre-eux puisse tre slectionn un
  moment donn. Ils sont utiliss par les applications lorsqu'il s'agit
  d'effectuer un choix dans une liste d'options.

  La cration d'un bouton radio s'effectue grce  l'un des appels
  suivants :



       GtkWidget* gtk_radio_button_new (GSList *group);

       GtkWidget* gtk_radio_button_new_with_label (GSList *group,
                                                   gchar *label);



  On notera le paramtre supplmentaire de ces fonctions. Elles ncessitent
  un groupe pour raliser correctement leur tche. Le premier appel doit
  passer NULL au premier paramtre puis on peut crer un groupe en
  utilisant :



       GSList* gtk_radio_button_group (GtkRadioButton *radio_button);


  On passe alors ce groupe en premier paramtre des appels suivants aux
  fonctions de cration.  Il est prfrable, aussi, de prciser quel bouton
  doit tre choisi par dfaut avec la fonction :



       void gtk_toggle_button_set_state (GtkToggleButton *toggle_button,
                                         gint state);



  Celle-ci est dcrite dans la section sur les boutons commutateurs et
  fonctionne exactement de la mme faon.

  [Mettre ici un exemple d'utilisation de tout cela car je crois que
  cela ferait beaucoup de bien...]



  77..  WWiiddggeettss ddiivveerrss


  77..11..  LLaabbeellss

  Les labels sont trs utiliss dans GTK et sont relativement simples. Ils
  n'mettent pas de signaux car ils n'ont pas de fentre X qui leur est
  associe. Si vous avez besoin de capturer des signaux ou de faire des
  coupures ( clippings ), utilisez un widget EventBox.

  Pour crer un label, on utilise :



       GtkWidget* gtk_label_new (char *str);



  O l'unique paramtre est la chane de caractres que l'on veut que le
  label affiche.

  Pour changer le texte d'un label aprs sa cration, on utilise la
  fonction :



       void gtk_label_set (GtkLabel  *label,
                           char      *str);



  o le premier paramtre est le label que l'on veut modifier, que l'on
  convertit en utilisant la macro GTK_LABEL(), et le second est la
  nouvelle chane.

  L'espace ncessaire  la nouvelle chane sera automatiquement ajust si
  ncessaire.

  Pour rcuprer la chane courante, on utilise la fonction :



  void gtk_label_get (GtkLabel  *label,
                      char     **str);



  o le premier paramtre est le label dont on veut rcuprer la chane et le
  second sert  retourner cette chane.


  77..22..  LLee wwiiddggeett bbuullllee dd''aaiiddee

  Ce sont les petits textes qui surgissent lorsque vous laissez votre
  pointeur sur un bouton ou un autre widget pendant quelques secondes.
  Ils sont faciles  utiliser, on ne donnera donc pas d'exemple. Si vous
  voulez voir du code, consultez le programme _t_e_s_t_g_t_k_._c distribu avec
  GTK.

  Certains widgets (comme les labels) ne fonctionnent pas avec les
  bulles d'aide.

  Le premier appel que vous utiliserez sera pour crer une nouvelle bulle
  d'aide. Vous n'avez besoin que de le faire une fois dans une fonction
  donne. Le _G_t_k_T_o_o_l_t_i_p que cette fonction retourne peut tre utilis pour
  crer plusieurs bulles d'aide.



       GtkTooltips *gtk_tooltips_new (void);



  Lorsque vous avez cr une nouvelle bulle d'aide et le widget sur lequel
  vous voulez l'utiliser, vous n'avez qu' faire cet appel pour la
  configurer :



       void gtk_tooltips_set_tips   (GtkTooltips *tooltips,
                                     GtkWidget   *widget,
                                     gchar       *tips_text);



  Les paramtres sont la bulle d'aide dj cre, suivi du widget pour lequel
  vous voulez voir apparatre cette bulle et le texte que vous voulez
  qu'elle contienne.

  Voici un petit exemple :



       GtkTooltips *tooltips;
       GtkWidget *button;
       ...
       tooltips = gtk_tooltips_new ();
       button = gtk_button_new_with_label ("bouton 1");
       ...
       gtk_tooltips_set_tips (tooltips, button, "C'est le bouton 1");



  D'autres fonctions peuvent tre utilises avec les bulles d'aide. Je ne
  ferais que les numrer et les dcrire brivement.



       void gtk_tooltips_destroy    (GtkTooltips *tooltips);



  Destruction de bulles d'aide.



       void gtk_tooltips_enable     (GtkTooltips *tooltips);



  Activation d'un ensemble de bulles d'aide dsactives.



       void gtk_tooltips_disable    (GtkTooltips *tooltips);



  Dsactivation d'un ensemble de bulles d'aide actives.



       void gtk_tooltips_set_delay  (GtkTooltips *tooltips,
                                     gint         delay);



  Configure le nombre de millisecondes pendant lequel le pointeur soit
  se trouver sur le widget avant que la bulle d'aide n'apparaisse. Par
  dfaut, ce dlai est de 1000 millisecondes, soit 1 seconde.



       void      gtk_tooltips_set_tips (GtkTooltips *tooltips,
                                        GtkWidget   *widget,
                                        gchar    *tips_text);



  Change le texte d'une bulle d'aide dj cre.



       void gtk_tooltips_set_colors (GtkTooltips *tooltips,
                                     GdkColor    *background,
                                     GdkColor    *foreground);



  Configure les couleurs de fond et de premier plan des bulles d'aides.
  Je ne sais toujours pas comment spcifier les couleurs...

  Et c'est tout concernant les fonctions associes aux bulles d'aide.
  C'est plus que vous ne vouliez srement en savoir :)
  77..33..  BBaarrrreess ddee pprrooggrreessssiioonn

  Les barres de progression sont utilises pour afficher la progression
  d'une opration. Elles sont trs simple  utiliser comme vous pourrez le
  constater en tudiant le code ci-dessous. Commenons d'abord par l'appel
  permettant de crer une nouvelle barre.



       GtkWidget *gtk_progress_bar_new (void);



  Maintenant que la barre est cre, nous pouvons l'utiliser.



       void gtk_progress_bar_update (GtkProgressBar *pbar, gfloat percentage);



  Le premier paramtre est la barre de progression sur laquelle on veut
  agir, et le second est le pourcentage  effectu , signifiant le
  remplissage de la barres de 0  100 % (rel compris entre 0 et 1).

  Les barres de progression sont gnralement utilises avec les dlais
  d'expiration ou autres fonctions identiques (voir la section sur
  ``Expirations, fonctions d'E/S et d'attente'') pour donner l'illusion
  du multi-tches. Toutes emploient la fonction _g_t_k___p_r_o_g_r_e_s_s___b_a_r___u_p_d_a_t_e
  de la mme faon.

  Voici un exemple de barre de progression mise  jour par des
  expirations. Ce code montre aussi comment rinitialiser une barre.



  #include <gtk/gtk.h>

  static int ptimer = 0;
  int pstat = TRUE;

  /* Cette fonction incrmente et met  jour la barre de progression,
   * elle la rinitialise si pstat vaut FALSE */

  gint progress (gpointer data)
  {
      gfloat pvalue;

      /* rcupration de la valeur courante de la barre */

      pvalue = GTK_PROGRESS_BAR (data)->percentage;

      if ((pvalue >= 1.0) || (pstat == FALSE)) {
          pvalue = 0.0;
          pstat = TRUE;
      }
      pvalue += 0.01;

      gtk_progress_bar_update (GTK_PROGRESS_BAR (data), pvalue);

      return TRUE;
  }

  /* Cette fonction signale une rinitialisation de la barre */

  void progress_r (void)
  {
      pstat = FALSE;
  }

  void destroy (GtkWidget *widget, gpointer *data)
  {
      gtk_main_quit ();
  }

  int main (int argc, char *argv[])
  {
      GtkWidget *window;
      GtkWidget *button;
      GtkWidget *label;
      GtkWidget *table;
      GtkWidget *pbar;

      gtk_init (&argc, &argv);

      window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

      gtk_signal_connect (GTK_OBJECT (window), "delete_event",
                          GTK_SIGNAL_FUNC (destroy), NULL);

      gtk_container_border_width (GTK_CONTAINER (window), 10);

      table = gtk_table_new(3,2,TRUE);
      gtk_container_add (GTK_CONTAINER (window), table);

      label = gtk_label_new ("Exemple de barre de progression");
      gtk_table_attach_defaults(GTK_TABLE(table), label, 0,2,0,1);
      gtk_widget_show(label);

      /* Cre une barre, la place dans la table et l'affiche */

      pbar = gtk_progress_bar_new ();
      gtk_table_attach_defaults(GTK_TABLE(table), pbar, 0,2,1,2);
      gtk_widget_show (pbar);

      /* Configure le dlai d'expiration pour grer automatiquement la
       *  mise  jour de la barre */

      ptimer = gtk_timeout_add (100, progress, pbar);

      /* Ce bouton indique  la barre qu'elle doit se rinitialiser */

      button = gtk_button_new_with_label ("Reset");
      gtk_signal_connect (GTK_OBJECT (button), "clicked",
                          GTK_SIGNAL_FUNC (progress_r), NULL);
      gtk_table_attach_defaults(GTK_TABLE(table), button, 0,1,2,3);
      gtk_widget_show(button);

      button = gtk_button_new_with_label ("Annuler");
      gtk_signal_connect (GTK_OBJECT (button), "clicked",
                          GTK_SIGNAL_FUNC (destroy), NULL);

      gtk_table_attach_defaults(GTK_TABLE(table), button, 1,2,2,3);
      gtk_widget_show (button);

      gtk_widget_show(table);
      gtk_widget_show(window);

      gtk_main ();

      return 0;
  }



  Dans ce petit programme, il y a quatre parties concernant le
  fonctionnement gnral des barres de progression, nous les tudierons
  dans l'ordre de leurs appels.



       pbar = gtk_progress_bar_new ();



  Cet appel cre une nouvelle barre, nomme _p_b_a_r.



       ptimer = gtk_timeout_add (100, progress, pbar);



  Cet appel utilise des dlais d'expiration pour permettre un intervalle
  de temps constant. ces dlais ne sont pas ncessaires  l'utilisation des
  barres de progression.



       pvalue = GTK_PROGRESS_BAR (data)->percentage;



  Ce code assigne  _p_v_a_l_u_e la valeur du pourcentage de la barre.



       gtk_progress_bar_update (GTK_PROGRESS_BAR (data), pvalue);



  Finalement, ce code met  jour la barre avec la valeur de _p_v_a_l_u_e.

  Et c'est tout ce qu'il y a  savoir sur les barres de progression.
  Amusez-vous bien.


  77..44..  BBootteess ddee ddiiaalloogguuee


  Les widgets botes de dialogue sont trs simples : ce sont simplement
  des fentres avec plusieurs choses dj places dedans. La structure d'une
  bote de dialogue est :



       struct GtkDialog
       {
             GtkWindow window;

             GtkWidget *vbox;
             GtkWidget *action_area;
       };



  Comme vous le voyez, cela cre simplement une fentre et la place dans
  une vbox suivie d'un sparateur et d'une hbox pour la  zone d'action .

  Le widget bote de dialogue peut servir  produire des messages pour
  l'utilisateur ainsi qu' d'autres tches. Il est vraiment rudimentaire
  et il n'y a qu'une seule fonction pour les botes de dialogue :



       GtkWidget* gtk_dialog_new (void);



  Ainsi, pour crer un nouveau dialogue, on utilise :



       GtkWidget window;
       window = gtk_dialog_new ();



  Ceci crera la bote de dialogue et c'est maintenant  vous de
  l'utiliser. Vous pouvez, par exemple, placer un bouton dans la zone
  d'action en faisant quelque chose comme :



  button = ...
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->action_area), button,
                      TRUE, TRUE, 0);
  gtk_widget_show (button);



  Et vous pouvez aussi ajouter un label  la zone de la vboxb :



       label = gtk_label_new ("Les botes de dialogues sont pratiques");
       gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->vbox), label, TRUE,
                           TRUE, 0);
       gtk_widget_show (label);



  Comme exemple d'utilisation d'une bote de dialogue, vous pourriez
  mettre deux boutons dans la zone d'action (un bouton  Annuler  et un
  bouton  Ok ) et un label dans la zone de la vbox posant une question
  l'utilisateur ou signalant une erreur, etc. Vous pouvez alors attacher
  un signal diffrent  chacun des boutons et raliser l'opration que
  l'utilisateur a choisie.



  77..55..  PPiixxmmaappss

  Les pixmaps sont des structures de donnes contenant des images.
  Celles-ci peuvent tre utilises  diffrents endroits, mais le plus
  souvent comme icnes dans le bureau X Window. Un bitmap est un pixmap
  de 2 couleurs.

  Pour utiliser des pixmaps avec GTK, on doit d'abord construire une
  structure _G_d_k_P_i_x_m_a_p en utilisant les fonctions de la couche GDK. Les
  pixmaps peuvent soit tre crs  partir de donnes en memoire, ou  partir
  de donnes lues dans un fichier. Nous utiliserons chacun des appels
  pour crer un pixmap.



       GdkPixmap *gdk_bitmap_create_from_data( GdkWindow *window,
                                               gchar     *data,
                                               gint      width,
                                               gint      height );



  Cette fonction sert  crer un pixmap mono-plan (2 couleurs)  partir de
  donnes en mmoire. Chaque bit de la donne _d_a_t_a. _w_i_d_t_h et _h_e_i_g_h_t sont
  exprims en pixels. Le pointeur vers un _G_d_k_W_i_n_d_o_w pointe sur la fentre
  courante car les ressources d'un pixmap n'ont de signification que
  dans le contexte de l'cran o il doit s'afficher.



  GdkPixmap* gdk_pixmap_create_from_data( GdkWindow  *window,
                                          gchar      *data,
                                          gint        width,
                                          gint        height,
                                          gint        depth,
                                          GdkColor   *fg,
                                          GdkColor   *bg );



  Cette fonction est utilise pour crer un pixmap d'une profondeur donne
  (nombre de couleurs)  partir de la donne spcifie pas _d_a_t_a. _f_g et _b_g
  sont les couleurs  utiliser pour l'avant et l'arrire-plan.



       GdkPixmap* gdk_pixmap_create_from_xpm( GdkWindow  *window,
                                              GdkBitmap **mask,
                                              GdkColor   *transparent_color,
                                              const gchar *filename );



  Le format XPM est une reprsentation des pixmaps reconnue par le systme
  X Window. Il est largement utilis et de nombreux utilitaires pour crer
  des fichiers d'images  ce format sont disponibles. Le fichier _f_i_l_e_n_a_m_e
  doit contenir une image dans ce format qui sera charge dans la
  structure pixmap. Le masque _m_a_s_k indique quels sont les bits opaques
  du pixmap. Tous les autres bits sont coloriss en utilisant la couleur
  spcifie par _t_r_a_n_s_p_a_r_e_n_t___c_o_l_o_r. Un exemple d'utilisation est prsent ci-
  dessous.



       GdkPixmap* gdk_pixmap_create_from_xpm_d (GdkWindow  *window,
                                                GdkBitmap **mask,
                                                GdkColor   *transparent_color,
                                                gchar     **data);



  De petites images peuvent tre intgres dans un programme sous la forme
  de donnes _d_a_t_a au format XPM. Un pixmap est cr en utilisant ces donnes
  au lieu de les lire dans un fichier. Un exemple de telles donnes est :



  /* XPM */
  static const char * xpm_data[] = {
  "16 16 3 1",
  "       c None",
  ".      c #000000000000",
  "X      c #FFFFFFFFFFFF",
  "                ",
  "   ......       ",
  "   .XXX.X.      ",
  "   .XXX.XX.     ",
  "   .XXX.XXX.    ",
  "   .XXX.....    ",
  "   .XXXXXXX.    ",
  "   .XXXXXXX.    ",
  "   .XXXXXXX.    ",
  "   .XXXXXXX.    ",
  "   .XXXXXXX.    ",
  "   .XXXXXXX.    ",
  "   .XXXXXXX.    ",
  "   .........    ",
  "                ",
  "                "};



       void gdk_pixmap_destroy( GdkPixmap  *pixmap );



  Lorsqu'on a utilis un pixmap et que l'on en a plus besoin tout de
  suite, il est prfrable de librer la ressource en utilisant un appel
  _g_d_k___p_i_x_m_a_p___d_e_s_t_r_o_y.  Les pixmaps doivent tre considres comme une
  ressource prcieuse.

  Quand le pixmap est cr, on peut l'afficher comme un widget GTK. On
  doit crer un widget pixmap qui contiendra le pixmap GDK. Ceci est
  ralis de la faon suivante :



       GtkWidget* gtk_pixmap_new( GdkPixmap  *pixmap,
                                  GdkBitmap  *mask );



  Les autres fonctions pour les widgets pixmap sont :



       guint gtk_pixmap_get_type( void );
       void  gtk_pixmap_set( GtkPixmap  *pixmap,
                             GdkPixmap  *val,
                             GdkBitmap  *mask);
       void  gtk_pixmap_get( GtkPixmap  *pixmap,
                             GdkPixmap **val,
                             GdkBitmap **mask);



  _g_t_k___p_i_x_m_a_p___s_e_t sert  changer le pixmap pris en charge par le widget.
  _v_a_l est le pixmap cr par le GDK.

  Voici un exemple illustrant l'utilisation d'un pixmap dans un bouton :



  #include <gtk/gtk.h>


  /* donnes XPM d'une icne "Ouvrir fichier" */
  static const char * xpm_data[] = {
  "16 16 3 1",
  "       c None",
  ".      c #000000000000",
  "X      c #FFFFFFFFFFFF",
  "                ",
  "   ......       ",
  "   .XXX.X.      ",
  "   .XXX.XX.     ",
  "   .XXX.XXX.    ",
  "   .XXX.....    ",
  "   .XXXXXXX.    ",
  "   .XXXXXXX.    ",
  "   .XXXXXXX.    ",
  "   .XXXXXXX.    ",
  "   .XXXXXXX.    ",
  "   .XXXXXXX.    ",
  "   .XXXXXXX.    ",
  "   .........    ",
  "                ",
  "                "};


  /* Termine l'application lorsqu'elle est appele
   * via le signal "delete_event" */

  void close_application( GtkWidget *widget, GdkEvent *event, gpointer *data )
  {
      gtk_main_quit();
  }


  /* Invoque lorsque le bouton est cliqu. Affiche simplement
   * un message. */

  void button_clicked( GtkWidget *widget, gpointer *data )
  {
      printf( "bouton cliqu\n" );
  }


  int main( int argc, char *argv[] )
  {
      /* GtkWidget est le type pour dclarer les widgets */

      GtkWidget *window, *pixmapwid, *button;
      GdkPixmap *pixmap;
      GdkBitmap *mask;
      GtkStyle *style;

      /* Cre la fentre principale et attache le signal "delete_event" pour
       * terminer l'application */

      gtk_init( &argc, &argv );
      window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
      gtk_signal_connect( GTK_OBJECT (window), "delete_event",
                          GTK_SIGNAL_FUNC (close_application), NULL );
      gtk_container_border_width( GTK_CONTAINER (window), 10 );
      gtk_widget_show( window );

      /* Utilisation de GDK pour crer le pixmap */

      style = gtk_widget_get_style( window );
      pixmap = gdk_pixmap_create_from_xpm_d( window->window,  &mask,
                                             &style->bg[GTK_STATE_NORMAL],
                                             (gchar **)xpm_data );

      /* Cration d'un widget pixmap GTK pour contenir le pixmap GDK */

      pixmapwid = gtk_pixmap_new( pixmap, mask );
      gtk_widget_show( pixmapwid );

      /* Cration d'un bouton pour contenir le widget pixmap */

      button = gtk_button_new();
      gtk_container_add( GTK_CONTAINER(button), pixmapwid );
      gtk_container_add( GTK_CONTAINER(window), button );
      gtk_widget_show( button );

      gtk_signal_connect( GTK_OBJECT(button), "clicked",
                          GTK_SIGNAL_FUNC(button_clicked), NULL );

      /* Affichage de la fentre */
      gtk_main ();

      return 0;
  }



  Pour charger un fichier  partir d'un fichier XPM appel _i_c_o_n_0_._x_p_m se
  trouvant dans le rpertoire courant, on aurait cr le pixmap ainsi :



           /* Charge un pixmap  partir d'un fichier */

           pixmap = gdk_pixmap_create_from_xpm( window->window, &mask,
                                                &style->bg[GTK_STATE_NORMAL],
                                                "./icon0.xpm" );
           pixmapwid = gtk_pixmap_new( pixmap, mask );
           gtk_widget_show( pixmapwid );
           gtk_container_add( GTK_CONTAINER(window), pixmapwid );



  Utilisation des formes

  Un dsavantage de l'utilisation des pixmaps est que l'objet affich est
  toujours rectangulaire, quelle que soit l'image. On voudrait pouvoir
  crer des bureaux et des applications avec des icnes ayant des formes
  plus naturelles. Par exemple, pour une interface de jeu, on aimerait
  avoir des boutons ronds  pousser. Pour faire cela, on doit utiliser
  des fentres avec des formes.

  Une fentre avec forme est simplement un pixmap dont les pixels du fond
  sont transparents. Ainsi, lorsque l'image d'arrire-plan est
  multicolore, on ne la cache pas avec le bord de notre icne. L'exemple
  suivant affiche une image de brouette sur le bureau.



  #include <gtk/gtk.h>

  /* XPM */
  static char * WheelbarrowFull_xpm[] = {
  "48 48 64 1",
  "       c None",
  ".      c #DF7DCF3CC71B",
  "X      c #965875D669A6",
  "o      c #71C671C671C6",
  "O      c #A699A289A699",
  "+      c #965892489658",
  "@      c #8E38410330C2",
  "#      c #D75C7DF769A6",
  "$      c #F7DECF3CC71B",
  "%      c #96588A288E38",
  "&      c #A69992489E79",
  "*      c #8E3886178E38",
  "=      c #104008200820",
  "-      c #596510401040",
  ";      c #C71B30C230C2",
  ":      c #C71B9A699658",
  ">      c #618561856185",
  ",      c #20811C712081",
  "<      c #104000000000",
  "1      c #861720812081",
  "2      c #DF7D4D344103",
  "3      c #79E769A671C6",
  "4      c #861782078617",
  "5      c #41033CF34103",
  "6      c #000000000000",
  "7      c #49241C711040",
  "8      c #492445144924",
  "9      c #082008200820",
  "0      c #69A618611861",
  "q      c #B6DA71C65144",
  "w      c #410330C238E3",
  "e      c #CF3CBAEAB6DA",
  "r      c #71C6451430C2",
  "t      c #EFBEDB6CD75C",
  "y      c #28A208200820",
  "u      c #186110401040",
  "i      c #596528A21861",
  "p      c #71C661855965",
  "a      c #A69996589658",
  "s      c #30C228A230C2",
  "d      c #BEFBA289AEBA",
  "f      c #596545145144",
  "g      c #30C230C230C2",
  "h      c #8E3882078617",
  "j      c #208118612081",
  "k      c #38E30C300820",
  "l      c #30C2208128A2",
  "z      c #38E328A238E3",
  "x      c #514438E34924",
  "c      c #618555555965",
  "v      c #30C2208130C2",
  "b      c #38E328A230C2",
  "n      c #28A228A228A2",
  "m      c #41032CB228A2",
  "M      c #104010401040",
  "N      c #492438E34103",
  "B      c #28A2208128A2",
  "V      c #A699596538E3",
  "C      c #30C21C711040",
  "Z      c #30C218611040",
  "A      c #965865955965",
  "S      c #618534D32081",
  "D      c #38E31C711040",
  "F      c #082000000820",
  "                                                ",
  "          .XoO                                  ",
  "         +@#$%o&                                ",
  "         *=-;#::o+                              ",
  "           >,<12#:34                            ",
  "             45671#:X3                          ",
  "               +89<02qwo                        ",
  "e*                >,67;ro                       ",
  "ty>                 459@>+&&                    ",
  "$2u+                  ><ipas8*                  ",
  "%$;=*                *3:.Xa.dfg>                ",
  "Oh$;ya             *3d.a8j,Xe.d3g8+             ",
  " Oh$;ka          *3d$a8lz,,xxc:.e3g54           ",
  "  Oh$;kO       *pd$%svbzz,sxxxxfX..&wn>         ",
  "   Oh$@mO    *3dthwlsslszjzxxxxxxx3:td8M4       ",
  "    Oh$@g& *3d$XNlvvvlllm,mNwxxxxxxxfa.:,B*     ",
  "     Oh$@,Od.czlllllzlmmqV@V#V@fxxxxxxxf:%j5&   ",
  "      Oh$1hd5lllslllCCZrV#r#:#2AxxxxxxxxxcdwM*  ",
  "       OXq6c.%8vvvllZZiqqApA:mq:Xxcpcxxxxxfdc9* ",
  "        2r<6gde3bllZZrVi7S@SV77A::qApxxxxxxfdcM ",
  "        :,q-6MN.dfmZZrrSS:#riirDSAX@Af5xxxxxfevo",
  "         +A26jguXtAZZZC7iDiCCrVVii7Cmmmxxxxxx%3g",
  "          *#16jszN..3DZZZZrCVSA2rZrV7Dmmwxxxx&en",
  "           p2yFvzssXe:fCZZCiiD7iiZDiDSSZwwxx8e*>",
  "           OA1<jzxwwc:$d%NDZZZZCCCZCCZZCmxxfd.B ",
  "            3206Bwxxszx%et.eaAp77m77mmmf3&eeeg* ",
  "             @26MvzxNzvlbwfpdettttttttttt.c,n&  ",
  "             *;16=lsNwwNwgsvslbwwvccc3pcfu<o    ",
  "              p;<69BvwwsszslllbBlllllllu<5+     ",
  "              OS0y6FBlvvvzvzss,u=Blllj=54       ",
  "               c1-699Blvlllllu7k96MMMg4         ",
  "               *10y8n6FjvllllB<166668           ",
  "                S-kg+>666<M<996-y6n<8*          ",
  "                p71=4 m69996kD8Z-66698&&        ",
  "                &i0ycm6n4 ogk17,0<6666g         ",
  "                 N-k-<>     >=01-kuu666>        ",
  "                 ,6ky&      &46-10ul,66,        ",
  "                 Ou0<>       o66y<ulw<66&       ",
  "                  *kk5       >66By7=xu664       ",
  "                   <<M4      466lj<Mxu66o       ",
  "                   *>>       +66uv,zN666*       ",
  "                              566,xxj669        ",
  "                              4666FF666>        ",
  "                               >966666M         ",
  "                                oM6668+         ",
  "                                  *4            ",
  "                                                ",
  "                                                "};


  /* Termine l'application lorsqu'elle est appele
   * (via le signal "delete_event"). */

  void close_application( GtkWidget *widget, GdkEvent *event, gpointer *data )
  {
      gtk_main_quit();
  }


  int main (int argc, char *argv[])
  {
      GtkWidget *window, *pixmap, *fixed;
      GdkPixmap *gdk_pixmap;
      GdkBitmap *mask;
      GtkStyle *style;
      GdkGC *gc;

      /* cre la fentre principale et attache le signal "delete_event"
       * pour terminer l'application. On notera que la fentre principale
       * n'a pas de barre de titre car nous la faisons surgir. */

      gtk_init (&argc, &argv);
      window = gtk_window_new( GTK_WINDOW_POPUP );
      gtk_signal_connect (GTK_OBJECT (window), "delete_event",
                          GTK_SIGNAL_FUNC (close_application), NULL);
      gtk_widget_show (window);

      /* Cration du pixmap et du widget pixmap */

      style = gtk_widget_get_default_style();
      gc = style->black_gc;
      gdk_pixmap = gdk_pixmap_create_from_xpm_d( window->window, &mask,
                                               &style->bg[GTK_STATE_NORMAL],
                                               WheelbarrowFull_xpm );
      pixmap = gtk_pixmap_new( gdk_pixmap, mask );
      gtk_widget_show( pixmap );

      /* Pour afficher le pixmap, on utilise un widget fixe pour placer
       * le pixmap */

      fixed = gtk_fixed_new();
      gtk_widget_set_usize( fixed, 200, 200 );
      gtk_fixed_put( GTK_FIXED(fixed), pixmap, 0, 0 );
      gtk_container_add( GTK_CONTAINER(window), fixed );
      gtk_widget_show( fixed );

      /* On masque tout sauf l'image elle-mme */

      gtk_widget_shape_combine_mask( window, mask, 0, 0 );

      /* Affichage de la fentre */

      gtk_widget_set_uposition( window, 20, 400 );
      gtk_widget_show( window );
      gtk_main ();

      return 0;
  }



  Pour rendre l'image de la brouette sensible aux clics, on peut lui
  attacher le signal "button_press_event" pour lui faire faire quelque
  chose. Les quelques lignes suivantes font que l'image sera sensible un
  clic souris qui provoquera l'arrt de l'application.



       gtk_widget_set_events( window,
                              gtk_widget_get_events( window ) |
                              GDK_BUTTON_PRESS_MASK );

       gtk_signal_connect( GTK_OBJECT(window), "button_press_event",
                           GTK_SIGNAL_FUNC(close_application), NULL );



  88..  WWiiddggeettss ccoonnttaaiinneerrss

  88..11..  BBlloocc--nnootteess

  Le widget bloc-notes est un ensemble de  pages  qui se chevauchent.
  Chaque page contient des informations diffrentes. Rcemment, ce widget
  est devenu plus commun dans la programmation des interfaces graphiques
  et c'est un bon moyen de montrer des blocs d'information similaires
  qui justifient une sparation de leur affichage.

  Le premier appel de fonction que l'on doit connatre est, vous l'aviez
  devin, celui qui cre un widget bloc-notes.



       GtkWidget* gtk_notebook_new (void);



  Lorsque le bloc-notes a t cr, il y a 12 fonctions permettant de
  travailler sur les blocs-notes. tudions-les sparment.

  La premire permet de positionner les indicateurs de pages. Ceux-ci
  (dsigns par le mot  tab  (signet)), peuvent se trouver en haut, en
  bas,  gauche ou  droite des pages.



       void gtk_notebook_set_tab_pos (GtkNotebook *notebook, GtkPositionType pos);



  _G_t_k_P_o_s_i_t_i_o_n_T_y_p_e peut prendre les valeurs suivantes qu'il n'est pas
  ncessaire d'expliquer :

  +o  GTK_POS_LEFT

  +o  GTK_POS_RIGHT

  +o  GTK_POS_TOP

  +o  GTK_POS_BOTTOM

  GTK_POS_TOP est la valeur par dfaut.

  La fonction suivante permet d'ajouter des pages  un bloc-notes. Il y a
  trois faons d'ajouter des pages. Regardons les deux premires qui sont
  trs semblables.



       void gtk_notebook_append_page (GtkNotebook *notebook, GtkWidget *child,
                                      GtkWidget *tab_label);

       void gtk_notebook_prepend_page (GtkNotebook *notebook, GtkWidget *child,
                                       GtkWidget *tab_label);



  Ces fonctions ajoutent des pages au bloc-notes_*_n_o_t_e_b_o_o_k en les insrant
  la fin (_a_p_p_e_n_d) ou au dbut (_p_r_e_p_e_n_d). _*_c_h_i_l_d est le widget qui est
  plac dans la page du bloc-notes, et _*_t_a_b___l_a_b_e_l est le label de la page
  qui est ajoute.
  La troisime fonction ajoutant une page  un bloc-notes conserve toutes
  les proprits des deux prcdentes, mais elle nous permet en plus de
  spcifier la position o l'on dsire insrer cette page.



       void gtk_notebook_insert_page (GtkNotebook *notebook, GtkWidget *child,
                                      GtkWidget *tab_label, gint position);



  Les paramtres sont les mmes que ___a_p_p_e_n_d__ et ___p_r_e_p_e_n_d__ sauf qu'il y en
  a un de plus : _p_o_s_i_t_i_o_n. Celui-ci sert spcifier l'endroit o cette page
  sera insre.

  Maintenant que nous savons insrer une page, voyons comment en
  supprimer une.



       void gtk_notebook_remove_page (GtkNotebook *notebook, gint page_num);



  Cette fonction te la page spcifie par _p_a_g_e___n_u_m du widget _*_n_o_t_e_b_o_o_k.

  Pour connatre la page courante d'un bloc-notes, on dispose de la
  fonction :



       gint gtk_notebook_current_page (GtkNotebook *notebook);



  Les deux fonctions suivantes permettent de passer  la page suivante ou
  prcdente d'un bloc-notes. Il suffit de faire l'appel de la fonction
  adquate avec le widget sur lequel on veut oprer. Remarque : lorsqu'on
  est sur la dernire page du bloc-notes et que l'on appelle
  _g_t_k___n_o_t_e_b_o_o_k___n_e_x_t___p_a_g_e, on revient  la premire page. De mme, si l'on
  est sur la premire page et que l'onappelle _g_t_k___n_o_t_e_b_o_o_k___p_r_e_v___p_a_g_e, on
  se retrouve sur sa dernire page.



       void gtk_notebook_next_page (GtkNoteBook *notebook);
       void gtk_notebook_prev_page (GtkNoteBook *notebook);



  La fonction qui suit permet de choisir la page  active . Si vous
  voulez ouvrir le bloc-notes  la page 5, par exemple, vous utiliserez
  cette fonction. Sans elle, le bloc-notes s'ouvre sur sa premire page
  par dfaut.



       void gtk_notebook_set_page (GtkNotebook *notebook, gint page_num);



  Les deux fonctions suivantes ajoutent ou tent les indicateurs de page
  et le contour du bloc-notes, respectivement.



       void gtk_notebook_set_show_tabs (GtkNotebook *notebook, gint show_tabs);
       void gtk_notebook_set_show_border (GtkNotebook *notebook, gint show_border);



  _s_h_o_w___t_a_b_s et _s_h_o_w___b_o_r_d_e_r peuvent valoir TRUE ou FALSE (0 ou 1).

  Voyons maintenant un exemple, il est tir du code de _t_e_s_t_g_t_k_._c de la
  distribution GTK et montre l'utilisation des 13 fonctions. Ce petit
  programme cre une fentre contenant un bloc-notes et six boutons. Le
  bloc-notes contient 11 pages, ajoutes par trois moyens diffrents :  la
  fin, au milieu et au dbut. Les boutons permettent de faire tourner les
  indicateurs de page, ajouter/ter les indicateurs et le contour, ter
  une page, passer  la page suivante et prcdente, et sortir du
  programme.



  #include <gtk/gtk.h>

  /* Rotation des indicateurs de page  */

  void rotate_book (GtkButton *button, GtkNotebook *notebook)
  {
      gtk_notebook_set_tab_pos (notebook, (notebook->tab_pos +1) %4);
  }

  /* Ajout/Suppression des indicateurs de pages et des contours */

  void tabsborder_book (GtkButton *button, GtkNotebook *notebook)
  {
      gint tval = FALSE;
      gint bval = FALSE;
      if (notebook->show_tabs == 0)
              tval = TRUE;
      if (notebook->show_border == 0)
              bval = TRUE;

      gtk_notebook_set_show_tabs (notebook, tval);
      gtk_notebook_set_show_border (notebook, bval);
  }

  /* Suppression d'une page */

  void remove_book (GtkButton *button, GtkNotebook *notebook)
  {
      gint page;

      page = gtk_notebook_current_page(notebook);
      gtk_notebook_remove_page (notebook, page);

      /* Il faut rafrachir  le widget --
       * ce qui force le widget  se redessiner. */

        gtk_widget_draw(GTK_WIDGET(notebook), NULL);
  }

  void delete (GtkWidget *widget, GdkEvent *event, gpointer *data)
  {
      gtk_main_quit ();
  }

  int main (int argc, char *argv[])
  {
      GtkWidget *window;
      GtkWidget *button;
      GtkWidget *table;
      GtkWidget *notebook;
      GtkWidget *frame;
      GtkWidget *label;
      GtkWidget *checkbutton;
      int i;
      char bufferf[32];
      char bufferl[32];

      gtk_init (&argc, &argv);

      window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

      gtk_signal_connect (GTK_OBJECT (window), "delete_event",
                          GTK_SIGNAL_FUNC (delete), NULL);

      gtk_container_border_width (GTK_CONTAINER (window), 10);

      table = gtk_table_new(2,6,TRUE);
      gtk_container_add (GTK_CONTAINER (window), table);

      /* Cration d'un bloc-notes, placement des indicateurs de page. */

      notebook = gtk_notebook_new ();
      gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_TOP);
      gtk_table_attach_defaults(GTK_TABLE(table), notebook, 0,6,0,1);
      gtk_widget_show(notebook);

      /* Ajoute un groupe de pages  la fin du bloc-notes. */

      for (i=0; i < 5; i++) {
          sprintf(bufferf, "Append Frame %d", i+1);
          sprintf(bufferl, "Page %d", i+1);

          frame = gtk_frame_new (bufferf);
          gtk_container_border_width (GTK_CONTAINER (frame), 10);
          gtk_widget_set_usize (frame, 100, 75);
          gtk_widget_show (frame);

          label = gtk_label_new (bufferf);
          gtk_container_add (GTK_CONTAINER (frame), label);
          gtk_widget_show (label);

          label = gtk_label_new (bufferl);
          gtk_notebook_append_page (GTK_NOTEBOOK (notebook), frame, label);
      }


      /* Ajoute une page  un endroit prcis. */

      checkbutton = gtk_check_button_new_with_label ("Cochez moi !");
      gtk_widget_set_usize(checkbutton, 100, 75);
      gtk_widget_show (checkbutton);

      label = gtk_label_new ("Emplacement de la nouvelle page");
      gtk_container_add (GTK_CONTAINER (checkbutton), label);
      gtk_widget_show (label);
      label = gtk_label_new ("Ajout de page");
      gtk_notebook_insert_page (GTK_NOTEBOOK (notebook), checkbutton, label, 2);

      /* Ajout de pages au dbut du bloc-notes */

      for (i=0; i < 5; i++) {
          sprintf(bufferf, "Prepend Frame %d", i+1);
          sprintf(bufferl, "Page %d", i+1);

          frame = gtk_frame_new (bufferf);
          gtk_container_border_width (GTK_CONTAINER (frame), 10);
          gtk_widget_set_usize (frame, 100, 75);
          gtk_widget_show (frame);

          label = gtk_label_new (bufferf);
          gtk_container_add (GTK_CONTAINER (frame), label);
          gtk_widget_show (label);

          label = gtk_label_new (bufferl);
          gtk_notebook_prepend_page (GTK_NOTEBOOK(notebook), frame, label);
      }

      /* Configuration de la page de dpart (page 4) */

      gtk_notebook_set_page (GTK_NOTEBOOK(notebook), 3);


      /* Cration des boutons */

      button = gtk_button_new_with_label ("Fermer");
      gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
                                 GTK_SIGNAL_FUNC (delete), NULL);
      gtk_table_attach_defaults(GTK_TABLE(table), button, 0,1,1,2);
      gtk_widget_show(button);

      button = gtk_button_new_with_label ("Page suivante");
      gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
                                 (GtkSignalFunc) gtk_notebook_next_page,
                                 GTK_OBJECT (notebook));
      gtk_table_attach_defaults(GTK_TABLE(table), button, 1,2,1,2);
      gtk_widget_show(button);

      button = gtk_button_new_with_label ("Page prcdente");
      gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
                                 (GtkSignalFunc) gtk_notebook_prev_page,
                                 GTK_OBJECT (notebook));
      gtk_table_attach_defaults(GTK_TABLE(table), button, 2,3,1,2);
      gtk_widget_show(button);

      button = gtk_button_new_with_label ("Position des indicateurs");
      gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
                                 (GtkSignalFunc) rotate_book, GTK_OBJECT(notebook));
      gtk_table_attach_defaults(GTK_TABLE(table), button, 3,4,1,2);
      gtk_widget_show(button);

      button = gtk_button_new_with_label ("Indicateurs/Contours oui/non");
      gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
                                 (GtkSignalFunc) tabsborder_book,
                                 GTK_OBJECT (notebook));
      gtk_table_attach_defaults(GTK_TABLE(table), button, 4,5,1,2);
      gtk_widget_show(button);

      button = gtk_button_new_with_label ("Oter page");
      gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
                                 (GtkSignalFunc) remove_book,
                                 GTK_OBJECT(notebook));
      gtk_table_attach_defaults(GTK_TABLE(table), button, 5,6,1,2);
      gtk_widget_show(button);

      gtk_widget_show(table);
      gtk_widget_show(window);

      gtk_main ();

      return 0;
  }



  En esprant que ceci vous aide  crer des blocs-notes pour vos
  applications GTK.



  88..22..  FFeennttrreess aavveecc bbaarrrreess ddee ddffiilleemmeenntt

  Les fentres avec barres de dfilement servent  crer des zones dfilantes
  l'intrieur d'une vraie fentre. On peut insrer n'importe quel widget
  dans ces fentres, ils seront accessibles quelle que soit leur taille
  en utilisant les barres de dfilement.


  La fonction suivante sert  crer une fentre avec barre de dfilement :



       GtkWidget* gtk_scrolled_window_new (GtkAdjustment *hadjustment,
                                           GtkAdjustment *vadjustment);



  Le premier paramtre est l'ajustement horizontal, et le second
  l'ajustement vertical. Ils sont presque toujours positionns  NULL.



       void gtk_scrolled_window_set_policy      (GtkScrolledWindow *scrolled_window,
                                                 GtkPolicyType      hscrollbar_policy,
                                                 GtkPolicyType      vscrollbar_policy);



  Cela permet de configurer le fonctionnement des barres de dfilement.
  Le premier paramtre est la fentre  dfilement que l'on veut modifier,
  le second configure le fonctionnement de la barre horizontale et le
  troisime celui de la barre verticale.

  Ce fonctionnement peut tre GTK_POLICY AUTOMATIC ou GTK_POLICY_ALWAYS.
  GTK_POLICY_AUTOMATIC dcidera automatiquement de votre besoin en barres
  de dfilement, alors que GTK_POLICY_ALWAYS mettra toujours celles-ci.

  Voici un exemple simple qui place 100 boutons commutateurs dans une
  fentre  dfilement. Je n'ai comment que les parties qui sont nouvelles
  pour vous.



  #include <gtk/gtk.h>

  void destroy(GtkWidget *widget, gpointer *data)
  {
      gtk_main_quit();
  }

  int main (int argc, char *argv[])
  {
      static GtkWidget *window;
      GtkWidget *scrolled_window;
      GtkWidget *table;
      GtkWidget *button;
      char buffer[32];
      int i, j;

      gtk_init (&argc, &argv);

      /* Cration d'une bote de dialogue pour y placer la fentre  dfilement.
       * Une bote de dialogue est une fentre comme les autres sauf qu'elle contient
       * une vbox et un sparateur horizontal. Ce n'est qu'un raccourci pour crer des
       * zones de dialogue. */

      window = gtk_dialog_new ();
      gtk_signal_connect (GTK_OBJECT (window), "destroy",
                          (GtkSignalFunc) destroy, NULL);
      gtk_window_set_title (GTK_WINDOW (window), "dialog");
      gtk_container_border_width (GTK_CONTAINER (window), 0);

      /* Cration d'une fentre  dfilement. */

      scrolled_window = gtk_scrolled_window_new (NULL, NULL);

      gtk_container_border_width (GTK_CONTAINER (scrolled_window), 10);

      /* La gestion des barres est soit GTK_POLICY AUTOMATIC, soit  GTK_POLICY_ALWAYS.
       * GTK_POLICY_AUTOMATIC dcide automatiquement s'il faut ou non des barres,
       * GTK_POLICY_ALWAYS met toujours des barres
       * Le premier paramtre correspond  la barre horizontale,
       * le second  la barre verticale. */

      gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
                                      GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);

      /* Cration d'une bote de dialogue */

      gtk_box_pack_start (GTK_BOX (GTK_DIALOG(window)->vbox), scrolled_window,
                          TRUE, TRUE, 0);
      gtk_widget_show (scrolled_window);

      /* Cration d'une table de 10x10 cases. */

      table = gtk_table_new (10, 10, FALSE);

      /* Configure l'espace des lignes et des colonnes de 10 pixels */

      gtk_table_set_row_spacings (GTK_TABLE (table), 10);
      gtk_table_set_col_spacings (GTK_TABLE (table), 10);

      /* Place la table fans la fentre  dfilement */

      gtk_container_add (GTK_CONTAINER (scrolled_window), table);
      gtk_widget_show (table);

      /* Cre une grille de boutons commutateurs dans la table */

      for (i = 0; i < 10; i++)
              for (j = 0; j < 10; j++) {
                  sprintf (buffer, "bouton (%d,%d)\n", i, j);
                  button = gtk_toggle_button_new_with_label (buffer);
                  gtk_table_attach_defaults (GTK_TABLE (table), button,
                                             i, i+1, j, j+1);
                  gtk_widget_show (button);
              }

      /* Ajoute un bouton  Fermer  en bas de la bote de dialogue */

      button = gtk_button_new_with_label ("Fermer");
      gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
                                 (GtkSignalFunc) gtk_widget_destroy,
                                 GTK_OBJECT (window));

      /* On met ce bouton en  bouton par dfaut . */

      GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
      gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->action_area), button, TRUE, TRUE, 0);

      /* Rcupre le bouton par dfaut. Le fait de presser la touche  Entre
       * activera le bouton. */

      gtk_widget_grab_default (button);
      gtk_widget_show (button);

      gtk_widget_show (window);

      gtk_main();

      return(0);
  }



  Essayez de changer la taille de la fentre et faites attention aux
  ractions des barres de dfilement. On peut aussi utiliser la fonction
  _g_t_k___w_i_d_g_e_t___s_e_t___u_s_i_z_e_(_) pour configurer la taille par dfaut de la
  fentre et des autres widgets.



  99..  WWiiddggeettss lliisstteess

  Le widget _G_t_k_L_i_s_t sert de container vertical pour des widgets
  _G_t_k_L_i_s_t_I_t_e_m.

  Un widget _G_t_k_L_i_s_t possde sa propre fentre pour recevoir les vnements
  et sa propre couleur de fond qui est habituellement blanche. Comme il
  est directement driv de _G_t_k_C_o_n_t_a_i_n_e_r, il peut tre trait comme tel en
  utilisant la macro GTK_CONTAINER(List) : voir le widget _G_t_k_C_o_n_t_a_i_n_e_r
  pour en savoir plus.

  On doit d'abord connatre l'utilisation des _G_L_i_s_t et des fonctions
  _g___l_i_s_t___*_(_) qui leur sont lies pour pouvoir utiliser pleinement le
  widget _G_t_k_L_i_s_t.

  Un champ de la structure d'un widget _G_t_k_L_i_s_t nous intresse
  particulirement :



  struct _GtkList
  {
    ...
    GList *selection;
    guint selection_mode;
    ...
  };



  Le champ _s_e_l_e_c_t_i_o_n d'un _G_t_k_L_i_s_t pointe sur une liste chane de tous les
  items qui sont slectionns, ou vaut NULL si aucune slection n'est
  faite. Ainsi, pour connatre la slection courante, on consulte le champ
  _G_T_K___L_I_S_T_(_)_-_>_s_e_l_e_c_t_i_o_n mais on ne doit pas le modifier car ses champs
  internes sont grs par les fonctions _g_t_k___l_i_s_t___*_(_).

  Le champ _s_e_l_e_c_t_i_o_n___m_o_d_e dtermine les options de slection d'un _G_t_k_L_i_s_t
  et donc le contenu du champ du _G_T_K___L_I_S_T_(_)_-_>_s_e_l_e_c_t_i_o_n :

  _s_e_l_e_c_t_i_o_n___m_o_d_e peut avoir l'une des valeurs suivantes :

  +o  GTK_SELECTION_SINGLE - _s_e_l_e_c_t_i_o_n vaut NULL ou contient un pointeur
     vers un seul item slectionn.

  +o  GTK_SELECTION_BROWSE - _s_e_l_e_c_t_i_o_n vaut NULL si la liste ne contient
     aucun widget ou seulement des widgets non sensitifs. Sinon, ce
     champ contient un pointeur vers une seule structure Glist, et donc
     vers exactement un item.

  +o  GTK_SELECTION_MULTIPLE - _s_e_l_e_c_t_i_o_n vaut NULL si aucun item n'est
     slectionn ou pointe vers le premier item slectionn. Ce dernier
     point  son tour vers le second item, etc.

  +o  GTK_SELECTION_EXTENDED - _s_e_l_e_c_t_i_o_n vaut toujours NULL.

  La valeur par dfaut est GTK_SELECTION_MULTIPLE.


  99..11..  SSiiggnnaauuxx



       void GtkList::selection_changed (GtkList *LIST)



  Ce signal sera invoqu  chaque fois que le champ slection d'un GtkList
  a chang. Cela arrive lorsqu'un fils d'un GtkList a t slectionn ou
  dslectionn.



       void GtkList::select_child (GtkList *LIST, GtkWidget *CHILD)



  Ce signal est invoqu lorsqu'un fils du GtkList va tre slectionn. Ceci
  arrive principalement lors d'appels _g_t_k___l_i_s_t___s_e_l_e_c_t___i_t_e_m_(_)_,
  _g_t_k___l_i_s_t___s_e_l_e_c_t___c_h_i_l_d_(_) et lors d'appuis de boutons. Quelques fois, il
  est indirectement dclench lorsque des fils sont ajouts ou supprims du
  GtkList.


       void GtkList::unselect_child (GtkList *LIST, GtkWidget *CHILD)



  Ce signal est invoqu lorsqu'un fils du GtkList va tre dslectionn. Cela
  arrive principalement lors d'appels _g_t_k___l_i_s_t___u_n_s_e_l_e_c_t___i_t_e_m_(_)_,
  _g_t_k___l_i_s_t___u_n_s_e_l_e_c_t___c_h_i_l_d_(_), et lors d'appuis de boutons. Quelques fois,
  il est indirectement dclench lorsque des fils sont ajouts ou supprims
  du GtkList.



  99..22..  FFoonnccttiioonnss



       guint gtk_list_get_type (void)



  Retourne l'identificateur de type  GtkList .



       GtkWidget* gtk_list_new (void)



  Cre un nouvel objet  GtkList . Le nouveau widget est retourn sous la
  forme d'un pointeur vers un objet  GtkWidget . NULL est retourn en cas
  d'erreur.



       void gtk_list_insert_items (GtkList *LIST, GList *ITEMS, gint POSITION)



  Insre des items dans _L_I_S_T,  partir de _P_O_S_I_T_I_O_N.  _I_T_E_M_S est une liste
  doublement chane o chaque noeud doit pointer vers un nouveau
  _G_t_k_L_i_s_t_I_t_e_m. Les noeuds _G_L_i_s_t de _I_T_E_M_S sont pris en charge par _L_I_S_T.



       void gtk_list_append_items (GtkList *LIST, GList *ITEMS)



  Insre des items  la fin de _L_I_S_T selon le mme principe que
  _g_t_k___l_i_s_t___i_n_s_e_r_t___i_t_e_m_s. Les noeuds _G_L_i_s_t de _I_T_E_M_S sont pris en charge
  par _L_I_S_T.



       void gtk_list_prepend_items (GtkList *LIST, GList *ITEMS)



  Insre des items au dbut de _L_I_S_T selon le mme principe que
  _g_t_k___l_i_s_t___i_n_s_e_r_t___i_t_e_m_s. Les noeuds _G_L_i_s_t de _I_T_E_M_S sont pris en charge
  par _L_I_S_T.



       void gtk_list_remove_items (GtkList *LIST, GList *ITEMS)



  te des items de _L_I_S_T. _I_T_E_M_S est une liste doublement chane dont chaque
  noeud pointe vers un fils direct de _L_I_S_T. Il est de la responsabilit
  de l'appelant de faire un appel _g___l_i_s_t___f_r_e_e_(_I_T_E_M_S_) aprs cela.
  L'appelant doit aussi dtruire lui-mme les items.



       void gtk_list_clear_items (GtkList *LIST, gint START, gint END)



  te et dtruit des items de _L_I_S_T. Un widget est concern si sa position
  courante dans _L_I_S_T est dans l'intervalle spcifi par _S_T_A_R_T et _E_N_D.



       void gtk_list_select_item (GtkList *LIST, gint ITEM)



  Invoque le signal _G_t_k_L_i_s_t_:_:_s_e_l_e_c_t___c_h_i_l_d pour un item spcifi par sa
  position courante dans _L_I_S_T.



       void gtk_list_unselect_item (GtkList *LIST, gint ITEM)



  Invoque le signal _G_t_k_L_i_s_t_:_:_u_n_s_e_l_e_c_t___c_h_i_l_d pour un item spcifi par sa
  position courante dans _L_I_S_T.



       void gtk_list_select_child (GtkList *LIST, GtkWidget *CHILD)



  Invoque le signal _G_t_k_L_i_s_t_:_:_s_e_l_e_c_t___c_h_i_l_d pour le fils _C_H_I_L_D spcifi.



       void gtk_list_unselect_child (GtkList *LIST, GtkWidget *CHILD)



  Invoque le signal _G_t_k_L_i_s_t_:_:_u_n_s_e_l_e_c_t___c_h_i_l_d pour le fils _C_H_I_L_D spcifi.



  gint gtk_list_child_position (GtkList *LIST, GtkWidget *CHILD)



  Retourne la position de _C_H_I_L_D dans _L_I_S_T. Retourne -1 en cas d'erreur.



       void gtk_list_set_selection_mode (GtkList *LIST, GtkSelectionMode MODE)



  Configure _L_I_S_T dans le mode de slection _M_O_D_E qui peut tre
  GTK_SELECTION_SINGLE, GTK_SELECTION_BROWSE, GTK_SELECTION_MULTIPLE ou
  GTK_SELECTION_EXTENDED.



       GtkList* GTK_LIST (gpointer OBJ)



  Convertit un pointeur gnrique en  <\em GtkList*\ . Voir _S_t_a_n_d_a_r_d
  _M_a_c_r_o_s_:_:, pour plus d'informations.



       GtkListClass* GTK_LIST_CLASS (gpointer CLASS)



  Convertit un pointeur gnrique en  GtkListClass* .  Voir _S_t_a_n_d_a_r_d
  _M_a_c_r_o_s_:_:, pour plus d'informations.



       gint GTK_IS_LIST (gpointer OBJ)



  Dtermine si un pointeur gnrique rfrence un objet  GtkList . Voir
  _S_t_a_n_d_a_r_d _M_a_c_r_o_s_:_:, pour plus d'informations.


  99..33..  EExxeemmppllee

  Voici un programme affichant les changements de slection dans une
  _G_t_k_L_i_s_t et permettant d' emprisonner  des items en les slectionnant
  avec le bouton droit de la souris.



  /* Compilez ce programme avec :
   * $ gcc -L/usr/X11R6/lib/ -I/usr/local/include/ -lgtk -lgdk -lglib -lX11 -lm -Wall main.c
   */
  #include        <gtk/gtk.h>
  #include        <stdio.h>

  /* Chane pour stocker les donnes dans les items de la liste. */

  const   gchar   *list_item_data_key="list_item_data";


  /* prototypes des gestionnaires de signaux que l'on connectera au widget GtkList. */

  static  void    sigh_print_selection    (GtkWidget      *gtklist,
                                           gpointer       func_data);
  static  void    sigh_button_event       (GtkWidget      *gtklist,
                                           GdkEventButton *event,
                                           GtkWidget      *frame);


  /* fonction principale pour configurer l'interface utilisateur */

  gint main (int argc, gchar *argv[])
  {
      GtkWidget       *separator;
      GtkWidget       *window;
      GtkWidget       *vbox;
      GtkWidget       *scrolled_window;
      GtkWidget       *frame;
      GtkWidget       *gtklist;
      GtkWidget       *button;
      GtkWidget       *list_item;
      GList           *dlist;
      guint           i;
      gchar           buffer[64];


      /* initialise gtk (et donc gdk) */

      gtk_init(&argc, &argv);


      /* Cration d'une fentre pour placer tous les widgets.
       * Connexion de  gtk_main_quit()  l'vnement "destroy" de
       * la fentre afin de prendre en charge les vnements  fermeture d'une
       * fentre  du gestionnaire de fentre. */

      window=gtk_window_new(GTK_WINDOW_TOPLEVEL);
      gtk_window_set_title(GTK_WINDOW(window), "Exemple de widget GtkList");
      gtk_signal_connect(GTK_OBJECT(window),
                         "destroy",
                         GTK_SIGNAL_FUNC(gtk_main_quit),
                         NULL);


      /*  l'intrieur de la fentre, on a besoin d'une bote pour placer
       * verticalement les widgets. */

      vbox=gtk_vbox_new(FALSE, 5);
      gtk_container_border_width(GTK_CONTAINER(vbox), 5);
      gtk_container_add(GTK_CONTAINER(window), vbox);
      gtk_widget_show(vbox);

      /* Fentre  dfilement pour placer le widget GtkList  l'intrieur. */

      scrolled_window=gtk_scrolled_window_new(NULL, NULL);
      gtk_widget_set_usize(scrolled_window, 250, 150);
      gtk_container_add(GTK_CONTAINER(vbox), scrolled_window);
      gtk_widget_show(scrolled_window);

      /* Cration du widget GtkList
       * Connexion du gestionnaire de signal sigh_print_selection() au signal
       * "selection_changed" du GtkList pour afficher les items slectionns
       *  chaque fois que la slection change. */

      gtklist=gtk_list_new();
      gtk_container_add(GTK_CONTAINER(scrolled_window), gtklist);
      gtk_widget_show(gtklist);
      gtk_signal_connect(GTK_OBJECT(gtklist),
                         "selection_changed",
                         GTK_SIGNAL_FUNC(sigh_print_selection),
                         NULL);

      /* Cration d'une  Prison  pour y mettre un item. */

      frame=gtk_frame_new("Prison");
      gtk_widget_set_usize(frame, 200, 50);
      gtk_container_border_width(GTK_CONTAINER(frame), 5);
      gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
      gtk_container_add(GTK_CONTAINER(vbox), frame);
      gtk_widget_show(frame);

      /* Connexion du gestionnaire de signal sigh_button_event() au signal
       *  mise au arrts  des items du GtkList. */

      gtk_signal_connect(GTK_OBJECT(gtklist),
                         "button_release_event",
                         GTK_SIGNAL_FUNC(sigh_button_event),
                         frame);

      /* Cration d'un sparateur. */

      separator=gtk_hseparator_new();
      gtk_container_add(GTK_CONTAINER(vbox), separator);
      gtk_widget_show(separator);

      /* Cration d'un bouton et connexion de son signal "clicked"  la
       * destruction de la fentre. */

      button=gtk_button_new_with_label("Fermeture");
      gtk_container_add(GTK_CONTAINER(vbox), button);
      gtk_widget_show(button);
      gtk_signal_connect_object(GTK_OBJECT(button),
                                "clicked",
                                GTK_SIGNAL_FUNC(gtk_widget_destroy),
                                GTK_OBJECT(window));


      /* Cration de 5 items, chacun ayant son propre label.
       * Ajout de ceux-ci au GtkList en utilisant gtk_container_add().
       * On interroge le texte du label et on l'associe avec
       * list_item_data_key  chaque item. */

      for (i=0; i<5; i++) {
          GtkWidget       *label;
          gchar           *string;

          sprintf(buffer, "ListItemContainer avec Label #%d", i);
          label=gtk_label_new(buffer);
          list_item=gtk_list_item_new();
          gtk_container_add(GTK_CONTAINER(list_item), label);
          gtk_widget_show(label);
          gtk_container_add(GTK_CONTAINER(gtklist), list_item);
          gtk_widget_show(list_item);
          gtk_label_get(GTK_LABEL(label), &string);
          gtk_object_set_data(GTK_OBJECT(list_item),
                              list_item_data_key,
                              string);
      }
      /* Cration de 5 autres labels. Cette fois-ci, on utilise
       * gtk_list_item_new_with_label(). On ne peut interroger la chane
       * des labels car on n'a pas les pointeurs de labels et on associe
       * donc simplement le list_item_data_key de chaque item ayant la mme
       * chane de texte pour l'ajouter au items que l'on place dans une liste
       * doublement chane (GList). On les ajoute alors par un simple appel
       * gtk_list_append_items().
       * Comme on utilise g_list_prepend() pour mettre les items dans la liste
       * doublement chane, leur ordre sera dcroissant (au lieu d'tre croissant si
       * on utilisait g_list_append()). */

      dlist=NULL;
      for (; i<10; i++) {
          sprintf(buffer, "Item avec le label %d", i);
          list_item=gtk_list_item_new_with_label(buffer);
          dlist=g_list_prepend(dlist, list_item);
          gtk_widget_show(list_item);
          gtk_object_set_data(GTK_OBJECT(list_item),
                              list_item_data_key,
                              "Item avec label intgr");
      }
      gtk_list_append_items(GTK_LIST(gtklist), dlist);

      /* Enfin, on veut voir la fentre... */

      gtk_widget_show(window);

      /* Lancement de la boucle principale de gtk */

      gtk_main();

      /* On arrive ici aprs que gtk_main_quit() ait t appele lorsque
       * la fentre principale a t dtruite. */

  }

  /* Gestionnaire de signal connect aux vnements boutons presser/relcher
   * du GtkList. */

  void
  sigh_button_event       (GtkWidget      *gtklist,
                           GdkEventButton *event,
                           GtkWidget      *frame)
  {
      /* On ne fait quelque chose que si le troisime bouton (celui de droite) a t
       * relch. */

      if (event->type==GDK_BUTTON_RELEASE &&
          event->button==3) {
          GList           *dlist, *free_list;
          GtkWidget       *new_prisoner;

          /* On recherche l'item slectionn  ce moment prcis.
           * Ce sera notre prisonnier ! */

          dlist=GTK_LIST(gtklist)->selection;
          if (dlist)
                  new_prisoner=GTK_WIDGET(dlist->data);
          else
                  new_prisoner=NULL;

          /* On recherche les items dj prisonniers et on les
           * remet dans la liste.
           * Il ne faut pas oublier de librer la liste doublement
           * chane que gtk_container_children() retourne. */

          dlist=gtk_container_children(GTK_CONTAINER(frame));
          free_list=dlist;
          while (dlist) {
              GtkWidget       *list_item;

              list_item=dlist->data;

              gtk_widget_reparent(list_item, gtklist);

              dlist=dlist->next;
          }
          g_list_free(free_list);

          /* Si l'on a un nouveau prisonnier, on l'te du GtkList et on le place
           * dans le cadre  Prison . On doit dslectionner l'item avant.

          if (new_prisoner) {
              GList   static_dlist;

              static_dlist.data=new_prisoner;
              static_dlist.next=NULL;
              static_dlist.prev=NULL;

              gtk_list_unselect_child(GTK_LIST(gtklist),
                                      new_prisoner);
              gtk_widget_reparent(new_prisoner, frame);
          }
      }
  }

  /* Gestionnaire de signal appel lorsque le GtkList
   * met le signal "selection_changed". */

  void
  sigh_print_selection    (GtkWidget      *gtklist,
                           gpointer       func_data)
  {
      GList   *dlist;

      /* Recherche dans la liste doublement chane des items slectionns
       * du GtkList,  faire en lecture seulement ! */

      dlist=GTK_LIST(gtklist)->selection;

      /* S'il n'y a pas d'items slectionn, il n'y a rien d'autre  faire que
       * de le dire  l'utilisateur. */

      if (!dlist) {
          g_print("Slection nettoye\n");
          return;
      }
      /* Ok, on a une slection et on l'affiche. */

      g_print("La slection est ");

      /* On rcupre l'item dans la liste doublement chane
       * puis on interroge la donne associe par list_item_data_key
       * et on l'affiche. */

      while (dlist) {
          GtkObject       *list_item;
          gchar           *item_data_string;

          list_item=GTK_OBJECT(dlist->data);
          item_data_string=gtk_object_get_data(list_item,
                                               list_item_data_key);
          g_print("%s ", item_data_string);

          dlist=dlist->next;
      }
      g_print("\n");
  }



  99..44..  WWiiddggeett iitteemm ddee lliissttee

  Le widget _G_t_k_L_i_s_t_I_t_e_m sert de container pour contenir un fils, lui
  fournissant des fonctions de slection/dsselection exactement comme le
  widget GtkList les demande pour ses fils.

  Un _G_t_k_L_i_s_t_I_t_e_m a sa propre fentre pour recevoir les vnements et a sa
  propre couleur de fond, habituellement blanche.

  Comme il est directement driv d'un _G_t_k_I_t_e_m, il peut tre trait comme
  tel en utilisant la macro GTK_ITEM(ListItem), reportez-vous  la
  section sur le widget GtkItem pour plus de dtail sur celui-ci.
  Habituellement, un _G_t_k_L_i_s_t_I_t_e_m contient juste un label pour
  identifier, par exemple, un nom de fichier dans un _G_t_k_L_i_s_t -- la
  fonction approprie _g_t_k___l_i_s_t___i_t_e_m___n_e_w___w_i_t_h___l_a_b_e_l_(_) est donc fournie. Le
  mme effet peut tre obtenu en crant un _G_t_k_L_a_b_e_l part, en configurant
  son alignement avec _x_a_l_i_g_n=0 et _y_a_l_i_g_n=0.5 suivi d'un ajout ultrieur
  au _G_t_k_L_i_s_t_I_t_e_m.

  Tout comme on n'est pas forc d'ajouter un _G_t_k_L_a_b_e_l  un _G_t_k_L_i_s_t_I_t_e_m, on
  peut aussi ajouter un _G_t_k_V_B_o_x ou un _G_t_k_A_r_r_o_w etc.  un _G_t_k_L_i_s_t_I_t_e_m.


  99..55..  SSiiggnnaauuxx

  Un _G_t_k_L_i_s_t_I_t_e_m ne cre pas de nouveaux signaux par lui-mme, mais hrite
  de ceux d'un _G_t_k_I_t_e_m. Voir _G_t_k_I_t_e_m_:_:, pour plus d'informations.



  99..66..  FFoonnccttiioonnss



       guint gtk_list_item_get_type (void)



  Retourne l'identificateur du type  GtkListItem .



       GtkWidget* gtk_list_item_new (void)



  Cration d'un objet _G_t_k_L_i_s_t_I_t_e_m. Le nouveau widget est retourn sous la
  forme d'un pointeur vers un objet _G_t_k_W_i_d_g_e_t. NULL est retourn en cas
  d'erreur.



       GtkWidget* gtk_list_item_new_with_label (gchar *LABEL)



  Cration d'un objet _G_t_k_L_i_s_t_I_t_e_m ayant un simple _G_t_k_L_a_b_e_l comme seul
  fils. Le nouveau widget est retourn sous la forme d'un pointeur vers
  un objet _G_t_k_W_i_d_g_e_t. NULL est retourn en cas d'erreur.



       void gtk_list_item_select (GtkListItem *LIST_ITEM)



  Cette fonction est surtout un emballage de _g_t_k___i_t_e_m___s_e_l_e_c_t _(_G_T_K___I_T_E_M
  _(_l_i_s_t___i_t_e_m_)_) qui mettra le signal _G_t_k_I_t_e_m_:_:_s_e_l_e_c_t.  Voir _G_t_k_I_t_e_m_:_:,
  pour plus d'informations.



       void gtk_list_item_deselect (GtkListItem *LIST_ITEM)



  Cette fonction est surtout un emballage de _g_t_k___i_t_e_m___d_e_s_e_l_e_c_t _(_G_T_K___I_T_E_M
  _(_l_i_s_t___i_t_e_m_)_) qui mettra le signal _G_t_k_I_t_e_m_:_:_d_e_s_e_l_e_c_t. Voir _G_t_k_I_t_e_m_:_:,
  pour plus d'informations.



       GtkListItem* GTK_LIST_ITEM (gpointer OBJ)



  Convertit un pointeur gnrique en _G_t_k_L_i_s_t_I_t_e_m_*. Voir _S_t_a_n_d_a_r_d _M_a_c_r_o_s_:_:
  pour plus d'informations.



       GtkListItemClass* GTK_LIST_ITEM_CLASS (gpointer CLASS)



  Convertit un pointeur gnrique en _G_t_k_L_i_s_t_I_t_e_m_C_l_a_s_s_*. Voir _S_t_a_n_d_a_r_d
  _M_a_c_r_o_s_:_: pour plus d'informations.



       gint GTK_IS_LIST_ITEM (gpointer OBJ)



  Dtermine si un pointeur gnrique se rfre  un objet _G_t_k_L_i_s_t_I_t_e_m.  Voir
  _S_t_a_n_d_a_r_d _M_a_c_r_o_s_:_: pour plus d'informations.
  99..77..  EExxeemmppllee

  L'exemple des GtkList couvre aussi l'utilisation des GtkListItem.
  tudiez-le attentivement.



  1100..  WWiiddggeettss sslleeccttiioonnss ddee ffiicchhiieerrss

  Le widget slection de fichier est un moyen simple et rapide pour
  afficher un fichier dans une bote de dialogue. Il est complet, avec
  des boutons Ok, Annuler et Aide. C'est un bon moyen de raccourcir les
  temps de programmation.

  Pour crer une bote de slection de fichier, on utilise :



       GtkWidget* gtk_file_selection_new (gchar *title);



  Pour configurer le nom de fichier, par exemple pour aller dans un
  rpertoire prcis ou donner un nom de fichier par dfaut, on utilise la
  fonction :



       void gtk_file_selection_set_filename (GtkFileSelection *filesel, gchar *filename);



  Pour rcuprer le texte que l'utilisateur a entr, ou sur lequel il a
  cliqu, on utilisera la fonction :



       gchar* gtk_file_selection_get_filename (GtkFileSelection *filesel);



  Des pointeurs permettent d'accder aux widgets contenus dans la widget
  de slection de fichiers. Ce sont :


  +o  dir_list

  +o  file_list

  +o  selection_entry

  +o  selection_text

  +o  main_vbox

  +o  ok_button

  +o  cancel_button

  +o  help_button

  Le plus souvent, on utilise les pointeurs _o_k___b_u_t_t_o_n_, _c_a_n_c_e_l___b_u_t_t_o_n, et
  _h_e_l_p___b_u_t_t_o_n pour prciser leurs utilisations.
  Voici un exemple emprunt  _t_e_s_t_g_t_k_._c et modifi pour fonctionner tout
  seul. Comme vous le verrez, il n'y a pas grand chose  faire pour crer
  un wigdget de slection de fichier. Cependant, dans cet exemple, si le
  bouton Aide apparat  l'cran, il ne fait rien car aucun signal ne lui
  est attach.



       #include <gtk/gtk.h>

       /* Rcupre le nom de fichier slectionn et l'affiche sur la console. */

       void file_ok_sel (GtkWidget *w, GtkFileSelection *fs)
       {
           g_print ("%s\n", gtk_file_selection_get_filename (GTK_FILE_SELECTION (fs)));
       }

       void destroy (GtkWidget *widget, gpointer *data)
       {
           gtk_main_quit ();
       }

       int main (int argc, char *argv[])
       {
           GtkWidget *filew;

           gtk_init (&argc, &argv);

           /* Cration d'un widget de slection de fichier. */

           filew = gtk_file_selection_new ("File selection");

           gtk_signal_connect (GTK_OBJECT (filew), "destroy",
                               (GtkSignalFunc) destroy, &filew);

           /* Connexion de ok_button  la fonction file_ok_sel() */

           gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (filew)->ok_button),
                               "clicked", (GtkSignalFunc) file_ok_sel, filew );

           /* Connexion de cancel_button pour dtruire le widget */

           gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION (filew)->cancel_button),
                                      "clicked", (GtkSignalFunc) gtk_widget_destroy,
                                      GTK_OBJECT (filew));

           /* Configuration du nom de fichier, comme s'il s'agissait d'un dialogue de
            * sauvegarde et que nous donnions un nom de fichier par dfaut. */

           gtk_file_selection_set_filename (GTK_FILE_SELECTION(filew),
                                            "penguin.png");

           gtk_widget_show(filew);
           gtk_main ();
           return 0;
       }



  1111..  WWiiddggeettss MMeennuu

  Il y a deux faons de crer des menus, la facile et la complique. Les
  deux ont leur utilit, mais on peut gnralement utiliser l'_u_s_i_n_e  _m_e_n_u_s
  (c'est la mthode facile...). La mthode  complique consiste  crer tous
  les menus en utilisant directement les appels. La mthode facile
  consiste  utiliser les appels _g_t_k___m_e_n_u___f_a_c_t_o_r_y. C'est beaucoup plus
  simple, mais chaque approche a ses avantages et inconvnients.

  L'usine  menus est beaucoup plus facile  utiliser, elle facilite aussi
  l'ajout d'autres menus. Par contre, crire quelques fonctions
  permettant de crer des menus en utilisant la mthode manuelle peut tre
  le dbut d'un long chemin avant une quelconque utilisation. Avec
  l'usine  menus, il n'est pas possible d'ajouter des images ou des  /
  aux menus.


  1111..11..  CCrraattiioonn mmaannuueellllee ddee mmeennuuss

  Selon la tradition pdagogique, nous commencerons par le plus compliqu
  :)

  Regardons les fonctions utilises pour crer les menus. La premire sert
  crer un nouveau menu.



       GtkWidget *gtk_menu_bar_new()



  Cette fonction cre une nouvelle barre de menu. On utilise la fonction
  _g_t_k___c_o_n_t_a_i_n_e_r___a_d_d pour la placer dans une fentre, ou les fonctions
  _b_o_x___p_a_c_k pour la placer dans une bote - la mme que pour les boutons.



       GtkWidget *gtk_menu_new();



  Cette fonction retourne un pointeur vers un nouveau menu, il n'est
  jamais montr (avec _g_t_k___w_i_d_g_e_t___s_h_o_w), il ne fait que contenir les items
  du menu. Ceci deviendra plus clair lorsque nous tudierons l'exemple
  ci-dessous.

  Les deux appels suivants servent  crer des items de menu qui seront
  placs dans le menu.



       GtkWidget *gtk_menu_item_new()



  et



       GtkWidget *gtk_menu_item_new_with_label(const char *label)



  Ces appels servent  crer les menus qui doivent tre affichs. On doit
  bien faire la diffrence entre un  menu  qui est cr avec _g_t_k___m_e_n_u___n_e_w_(_)
  et un  item de menu  cr avec les fonctions _g_t_k___m_e_n_u___i_t_e_m___n_e_w_(_). L'item
  de menu sera un vritable bouton avec une action associe alors qu'un
  menu sera un container contenant les items.



       gtk_menu_item_append()

       gtk_menu_item_set_submenu()



  Les fonctions _g_t_k___m_e_n_u___n_e_w___w_i_t_h___l_a_b_e_l_(_) et _g_t_k___m_e_n_u___n_e_w_(_) sont telles
  que vous les attendiez aprs avoir tudi les boutons. L'une cre un
  nouvel item de menu contenant dj un label, et l'autre ne fait que crer
  un item de menu vide.

  Voici les tapes pour crer une barre de menu avec des menus attachs :

  +o  Crer un nouveau menu avec _g_t_k___m_e_n_u___n_e_w_(_)

  +o  Crer un item de menu avec _g_t_k___m_e_n_u___i_t_e_m___n_e_w_(_).  Ce sera la racine
     du menu, le texte apparaissant ici sera aussi sur la barre de menu.

  +o  Utiliser plusieurs appels  _g_t_k___m_e_n_u___i_t_e_m___n_e_w_(_) pour chaque item que
     l'on dsire dans le menu. Utiliser _g_t_k___m_e_n_u___i_t_e_m___a_p_p_e_n_d_(_) pour
     placer chacun de ces items les uns aprs les autres. Cela cre une
     liste d'items de menu.

  +o  Utiliser _g_t_k___m_e_n_u___i_t_e_m___s_e_t___s_u_b_m_e_n_u_(_) pour attacher les items de
     menus nouvellement crs  l'item de menu racine (celui cr  la seconde
     tape).

  +o  Crer une nouvelle barre de menu avec _g_t_k___m_e_n_u___b_a_r___n_e_w_(_). Cette tape
     ne doit tre faite qu'une fois lorsque l'on cre une srie de menu sur
     une seule barre de menus.

  +o  Utiliser _g_t_k___m_e_n_u___b_a_r___a_p_p_e_n_d_(_) pour placer le menu racine dans la
     barre de menu.

  La cration d'un menu surgissant est presque identique. La diffrence
  est que le menu n'est pas post  automatiquement  par une barre de
  menu, mais explicitement en appelant la fonction _g_t_k___m_e_n_u___p_o_p_u_p_(_) par
  un vnement  bouton press .

  Suivez ces tapes

  +o  Crer une fonction de gestion d'vnement. Elle doit avoir le
     prototype

       static gint handler(GtkWidget *widget, GdkEvent *event);


  et elle utilisera l'vnement _e_v_e_n_t pour savoir o faire surgir le menu.

  +o  Ce gestionnaire, si l'vnement est un appui sur un bouton souris,
     traite _e_v_e_n_t comme un vnement bouton (ce qu'il est) et l'utilise,
     de la faon indique dans le code d'exemple, pour passer
     l'information  _g_t_k___m_e_n_u___p_o_p_u_p_(_).

  +o  Lier ce gestionnaire  un widget avec :

       gtk_signal_connect_object(GTK_OBJECT(widget), "event", GTK_SIGNAL_FUNC
       (handler), GTK_OBJECT(menu));


  o _w_i_d_g_e_t est le widget auquel vous le liez, _h_a_n_d_l_e_r est le gestion-
  naire, et _m_e_n_u est un menu cr avec _g_t_k___m_e_n_u___n_e_w_(_). Cela peut tre un
  menu qui est aussi post par une barre de menu, comme le montre l'exem-
  ple.


  1111..22..  EExxeemmppllee ddee mmeennuu mmaannuueell



  #include <gtk/gtk.h>

  static gint button_press (GtkWidget *, GdkEvent *);
  static void menuitem_response (GtkWidget *, gchar *);


  int main (int argc, char *argv[])
  {

      GtkWidget *window;
      GtkWidget *menu;
      GtkWidget *menu_bar;
      GtkWidget *root_menu;
      GtkWidget *menu_items;
      GtkWidget *vbox;
      GtkWidget *button;
      char buf[128];
      int i;

      gtk_init (&argc, &argv);

      /* Cration d'un fentre */

      window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
      gtk_window_set_title(GTK_WINDOW (window), "Test de Menu GTK");
      gtk_signal_connect(GTK_OBJECT (window), "delete_event",
                         (GtkSignalFunc) gtk_exit, NULL);

      /* Initialise le widget menu -- Attention : n'appelez jamais
       * gtk_show_widget() pour le widget menu !!!
       * C'est le menu qui contient les items de menu, celui qui surgira
       * lorsque vous cliquez sur le  menu racine  de l'application. */

      menu = gtk_menu_new();

      /* Voici le menu racine dont le label sera le nom du menu affich sur la barre
       * de menu. Il n'a pas de gestionnaire de signal attach car il ne fait
       * qu'afficher le reste du menu lorsqu'il est press. */

      root_menu = gtk_menu_item_new_with_label("Menu racine");

      gtk_widget_show(root_menu);

      /* Puis, on cre une petite boucle crant trois entres pour  menu test
       * Notez l'appel  gtk_menu_append(). Ici, on ajoute une liste d'items
       * notre menu. Normalement, on devrait aussi capturer le signal "clicked"
       * pour chacun des items et configurer une fonction de rappel pour lui,
       * mais on l'a omis ici pour gagner de la place. */

      for(i = 0; i < 3; i++)
          {
              /* Copie des noms dans buf. */

              sprintf(buf, "Sous-menu Test - %d", i);

              /* Cration d'un nouveau item de menu avec un nom... */

              menu_items = gtk_menu_item_new_with_label(buf);

              /* ...et ajout de celui-ci dans le menu. */

              gtk_menu_append(GTK_MENU (menu), menu_items);

              /* On fait quelque chose d'intressant lorsque l'item est
               * slectionn. */

              gtk_signal_connect (GTK_OBJECT(menu_items), "activate",
                  GTK_SIGNAL_FUNC(menuitem_response), (gpointer)
                  g_strdup(buf));

              /* Affichage du widget. */

              gtk_widget_show(menu_items);
          }

      /* Maintenant, on spcifi que nous voulons que notre nouveau  menu
       * soit le menu du  menu racine . */

      gtk_menu_item_set_submenu(GTK_MENU_ITEM (root_menu), menu);

      /* Cration d'une vbox pour y mettre un menu et un bouton. */

      vbox = gtk_vbox_new(FALSE, 0);
      gtk_container_add(GTK_CONTAINER(window), vbox);
      gtk_widget_show(vbox);

      /* Cration d'une barre de menus pour contenir les menus. Puis, on
       * l'ajoute  notre fentre principale. */

      menu_bar = gtk_menu_bar_new();
      gtk_box_pack_start(GTK_BOX(vbox), menu_bar, FALSE, FALSE, 2);
      gtk_widget_show(menu_bar);

      /* Cration d'un bouton pour y attacher le menu. */

      button = gtk_button_new_with_label("Pressez moi");
      gtk_signal_connect_object(GTK_OBJECT(button), "event",
          GTK_SIGNAL_FUNC (button_press), GTK_OBJECT(menu));
      gtk_box_pack_end(GTK_BOX(vbox), button, TRUE, TRUE, 2);
      gtk_widget_show(button);

      /* Finalement, on ajoute l'item de menu  la barre de menu --
       * c'est l'item de menu racine sur lequel je me suis dchan ;-) */

      gtk_menu_bar_append(GTK_MENU_BAR (menu_bar), root_menu);

      /* Affichage de la fentre. */

      gtk_widget_show(window);

      gtk_main ();

      return 0;
  }



  /* On rpond  un appui sur le bouton en postant un nouveau menu pass comme
   * un widget.
   *
   * On remarque que le paramtre "widget" est le menu  poster, PAS le bouton
   * qui a t press. */


  static gint button_press (GtkWidget *widget, GdkEvent *event)
  {

      if (event->type == GDK_BUTTON_PRESS) {
          GdkEventButton *bevent = (GdkEventButton *) event;
          gtk_menu_popup (GTK_MENU(widget), NULL, NULL, NULL, NULL,
                          bevent->button, bevent->time);

          /* On indique  l'appelant que l'on a gr cet vnement. */

          return TRUE;
      }

      /* On indique  l'appelant que l'on n'a pas gr cet vnement. */

      return FALSE;
  }


  /* Affiche une chane lorsqu'un item de menu est choisi. */

  static void menuitem_response (GtkWidget *widget, gchar *string)
  {
      printf("%s\n", string);
  }



  Vous pouvez aussi configurer un item de menu pour qu'il ne soit pas
  slectionnable et, en utilisant une table de raccourcis clavier, lier
  des touches aux fonctions du menu.


  1111..33..  UUttiilliissaattiioonn ddee GGttkkMMeennuuFFaaccttoorryy

  Maintenant que nous avons explor la voie difficile, nous allons voir
  l'utilisation des appels _g_t_k___m_e_n_u___f_a_c_t_o_r_y_.


  1111..44..  EExxeemmppllee dd''uussiinnee  mmeennuu

  Voici un exemple utilisant l'usine  menu de GTK. Le premier fichier
  est _m_e_n_u_s_._h.  Nous sparerons _m_e_n_u_s_._c et _m_a_i_n_._c cause des variables
  globales utilises dans le fichier _m_e_n_u_s_._c.



       #ifndef __MENUS_H__
       #define __MENUS_H__

       #ifdef __cplusplus
       extern "C" {
       #endif /* __cplusplus */

       void get_main_menu (GtkWidget **menubar, GtkAcceleratorTable **table);
       void menus_create(GtkMenuEntry *entries, int nmenu_entries);

       #ifdef __cplusplus
       }
       #endif /* __cplusplus */

       #endif /* __MENUS_H__ */



  Voici le fichier _m_e_n_u_s_._c :



  #include <gtk/gtk.h>
  #include <strings.h>

  #include "main.h"


  static void menus_remove_accel(GtkWidget * widget, gchar * signal_name, gchar * path);
  static gint menus_install_accel(GtkWidget * widget, gchar * signal_name, gchar key, gchar modifiers, gchar * path);
  void menus_init(void);
  void menus_create(GtkMenuEntry * entries, int nmenu_entries);


  /* Structure GtkMenuEntry utilise pour crer les menus. Le premier champ
   * est la chane de dfinition du menu. Le second, la touche de raccourci
   * utilise pour accder  cette fonction du menu avec le clavier.
   * Le troisime est la fonction de rappel  utiliser lorsque l'item de menu
   * est choisi (par la touche de raccourci ou avec la souris). Le dernier
   * lment est la donne  passer  la fonction de rappel. */


  static GtkMenuEntry menu_items[] =
  {
          {"<Main>/Fichier/Nouveau", "<control>N", NULL, NULL},
          {"<Main>/Fichier/Ouvrir", "<control>O", NULL, NULL},
          {"<Main>/Fichier/Sauver", "<control>S", NULL, NULL},
          {"<Main>/Fichier/Sauver sous", NULL, NULL, NULL},
          {"<Main>/Fichier/<separator>", NULL, NULL, NULL},
          {"<Main>/Fichier/Quitter", "<control>Q", file_quit_cmd_callback, "OK, c'est fini"},
          {"<Main>/Options/Test", NULL, NULL, NULL}
  };

  /* Calcul du nombre d'lments de menu_item */

  static int nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);

  static int initialize = TRUE;
  static GtkMenuFactory *factory = NULL;
  static GtkMenuFactory *subfactory[1];
  static GHashTable *entry_ht = NULL;

  void get_main_menu(GtkWidget ** menubar, GtkAcceleratorTable ** table)
  {
      if (initialize)
              menus_init();

      if (menubar)
              *menubar = subfactory[0]->widget;
      if (table)
              *table = subfactory[0]->table;
  }

  void menus_init(void)
  {
      if (initialize) {
          initialize = FALSE;

          factory = gtk_menu_factory_new(GTK_MENU_FACTORY_MENU_BAR);
          subfactory[0] = gtk_menu_factory_new(GTK_MENU_FACTORY_MENU_BAR);

          gtk_menu_factory_add_subfactory(factory, subfactory[0], "<Main>");
          menus_create(menu_items, nmenu_items);
      }
  }

  void menus_create(GtkMenuEntry * entries, int nmenu_entries)
  {
      char *accelerator;
      int i;

      if (initialize)
              menus_init();

      if (entry_ht)
              for (i = 0; i < nmenu_entries; i++) {
                  accelerator = g_hash_table_lookup(entry_ht, entries[i].path);
                  if (accelerator) {
                      if (accelerator[0] == '\0')
                              entries[i].accelerator = NULL;
                      else
                              entries[i].accelerator = accelerator;
                  }
              }
      gtk_menu_factory_add_entries(factory, entries, nmenu_entries);

      for (i = 0; i < nmenu_entries; i++)
              if (entries[i].widget) {
                  gtk_signal_connect(GTK_OBJECT(entries[i].widget), "install_accelerator",
                                     (GtkSignalFunc) menus_install_accel,
                                     entries[i].path);
                  gtk_signal_connect(GTK_OBJECT(entries[i].widget), "remove_accelerator",
                                     (GtkSignalFunc) menus_remove_accel,
                                     entries[i].path);
              }
  }

  static gint menus_install_accel(GtkWidget * widget, gchar * signal_name, gchar key, gchar modifiers, gchar * path)
  {
      char accel[64];
      char *t1, t2[2];

      accel[0] = '\0';
      if (modifiers & GDK_CONTROL_MASK)
              strcat(accel, "<control>");
      if (modifiers & GDK_SHIFT_MASK)
              strcat(accel, "<shift>");
      if (modifiers & GDK_MOD1_MASK)
              strcat(accel, "<alt>");

      t2[0] = key;
      t2[1] = '\0';
      strcat(accel, t2);

      if (entry_ht) {
          t1 = g_hash_table_lookup(entry_ht, path);
          g_free(t1);
      } else
              entry_ht = g_hash_table_new(g_string_hash, g_string_equal);

      g_hash_table_insert(entry_ht, path, g_strdup(accel));

      return TRUE;
  }

  static void menus_remove_accel(GtkWidget * widget, gchar * signal_name, gchar * path)
  {
      char *t;

      if (entry_ht) {
          t = g_hash_table_lookup(entry_ht, path);
          g_free(t);

          g_hash_table_insert(entry_ht, path, g_strdup(""));
      }
  }

  void menus_set_sensitive(char *path, int sensitive)
  {
      GtkMenuPath *menu_path;

      if (initialize)
              menus_init();

      menu_path = gtk_menu_factory_find(factory, path);
      if (menu_path)
              gtk_widget_set_sensitive(menu_path->widget, sensitive);
      else
              g_warning("Impossible de configurer la sensitivit d'un menu qui n'existe pas : %s", path);
  }



  Voici _m_a_i_n_._h :



       #ifndef __MAIN_H__
       #define __MAIN_H__


       #ifdef __cplusplus
       extern "C" {
       #endif /* __cplusplus */

       void file_quit_cmd_callback(GtkWidget *widget, gpointer data);

       #ifdef __cplusplus
       }
       #endif /* __cplusplus */

       #endif /* __MAIN_H__ */



  Et, enfin, _m_a_i_n_._c :



  #include <gtk/gtk.h>

  #include "main.h"
  #include "menus.h"


  int main(int argc, char *argv[])
  {
      GtkWidget *window;
      GtkWidget *main_vbox;
      GtkWidget *menubar;

      GtkAcceleratorTable *accel;

      gtk_init(&argc, &argv);

      window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
      gtk_signal_connect(GTK_OBJECT(window), "destroy",
                         GTK_SIGNAL_FUNC(file_quit_cmd_callback),
                         "WM destroy");
      gtk_window_set_title(GTK_WINDOW(window), "Usine  menu");
      gtk_widget_set_usize(GTK_WIDGET(window), 300, 200);

      main_vbox = gtk_vbox_new(FALSE, 1);
      gtk_container_border_width(GTK_CONTAINER(main_vbox), 1);
      gtk_container_add(GTK_CONTAINER(window), main_vbox);
      gtk_widget_show(main_vbox);

      get_main_menu(&menubar, &accel);
      gtk_window_add_accelerator_table(GTK_WINDOW(window), accel);
      gtk_box_pack_start(GTK_BOX(main_vbox), menubar, FALSE, TRUE, 0);
      gtk_widget_show(menubar);

      gtk_widget_show(window);
      gtk_main();

      return(0);
  }

  /* Juste une dmonstration du fonctionnement des fonctions de rappel
   * lorsqu'on utilise l'usine  menus. Souvent, on met tous les rappels
   * des menus dans un fichier spar, ce qui assure une meilleure
   * organisation. */

  void file_quit_cmd_callback (GtkWidget *widget, gpointer data)
  {
      g_print ("%s\n", (char *) data);
      gtk_exit(0);
  }



  Un _m_a_k_e_f_i_l_e pour que cela soit plus facile  compiler :



  CC      = gcc
  PROF    = -g
  C_FLAGS =  -Wall $(PROF) -L/usr/local/include -DDEBUG
  L_FLAGS =  $(PROF) -L/usr/X11R6/lib -L/usr/local/lib
  L_POSTFLAGS = -lgtk -lgdk -lglib -lXext -lX11 -lm
  PROGNAME = at

  O_FILES = menus.o main.o

  $(PROGNAME): $(O_FILES)
          rm -f $(PROGNAME)
          $(CC) $(L_FLAGS) -o $(PROGNAME) $(O_FILES) $(L_POSTFLAGS)

  .c.o:
          $(CC) -c $(C_FLAGS) $<

  clean:
          rm -f core *.o $(PROGNAME) nohup.out
  distclean: clean
          rm -f *~



  Pour l'instant, il n'y a que cet exemple. Une explication et de
  nombreux commentaires seront intgrs plus tard.



  1122..  WWiiddggeettss nnoonn ddooccuummeennttss

  On a besoin de leurs auteurs! :). Participez  notre didacticiel.

  Si vous devez utiliser un de ces widgets non documents, je vous
  recommande fortement de consulter leurs fichiers en-ttes respectifs
  dans la distribution GTK. Les noms de fonctions du GTK sont trs
  parlantes. Lorsque vous avez compris comment les choses fonctionnent,
  il n'est pas difficile de savoir comment utiliser un widget  partir
  des dclarations de ses fonctions.  Cela, avec quelques exemples de
  codes pris ailleurs, devrait ne pas poser de problme.

  Lorsque vous avez compris toutes les fonctions d'un nouveau widget non
  document, pensez  crire un didacticiel pour que les autres puissent
  bnficier du temps que vous y avez pass.


  1122..11..  EEnnttrreess ddee tteexxttee



  1122..22..  SSlleeccttiioonnss ddee ccoouulleeuurrss



  1122..33..  CCoonnttrrllee dd''iinntteerrvvaallllee



  1122..44..  RRgglleess



  1122..55..  BBootteess ddee tteexxttee



  1122..66..  PPrrvviissuuaalliissaattiioonn

  (Ceci peut devoir tre rcrit pour suivre le style du reste de ce
  didacticiel).



  Les prvisualisateurs servent  plusieurs choses dans GIMP/GTK. La
  plus importante est celle-ci : les images de haute qualit peuvent
  occuper des dizaines de mega-octets en mmoire - facilement ! Toute
  opration sur une image aussi grosse implique un temps de traitement
  lev. Si cela vous prend 5  10 essais (i.e. 10  20 tapes puisque
  vous devez recommencer lorsque vous avez fait une erreur) pour choisir
  la bonne modification, cela prendra littralement des heures pour
  produire la bonne image - pour peu que vous ne manquiez pas de mmoire
  avant. Ceux qui on pass des heures dans les chambres noires de
  dveloppement couleur connaissent cette sensation. Les
  prvisualisations sont notre planche de salut !

  L'aspect pnible de l'attente n'est pas le seul problme. souvent, il
  est utile de comparer les versions  Avant  et  Aprs  cte  cte
  ou, au pire l'une aprs l'autre. Si vous travaillez avec de grosses
  images et des attentes de 10 secondes, l'obtention des versions
  Avant  et  Aprs  est, pour le moins, difficile. Pour des images de
  30Mo (4"x6", 600dpi, 24 bits), la comparaison cte  cte est
  impossible pour la plupart des gens, et la comparaison squentielle
  n'est gure mieux. Les prvisualisations sont notre planche de salut !

  Mais il y a plus. Les prvisualisations permettent les
  pr-prvisualisations cte  cte. En d'autres termes, vous crivez un
  plug-in (par exemple la simulation filterpack) qui aurait plusieurs
  prvisualisations de ce-que-ce-serait-si-vous-faisiez-ceci. Une approche
  comme celle ci agit comme une sorte de palette de prvisualisation et
  est trs pratique pour les petites modifications. Utilisons les
  prvisualisations !

  Encore plus : pour certains plug-ins une intervention humaine en
  temps rel, spcifique aux images, peut s'avrer ncessaire. Dans le
  plug-in SuperNova, par exemple, on demande  l'utilisateur d'entrer
  les coordonnes du centre de la future supernova. La faon la plus
  simple de faire cela, vraiment, est de prsenter une prvisualisation
   l'utilisateur et de lui demander de choisir interactivement le
  point. Utilisons les prvisualisations !

  Enfin, quelques utilisations diverses : on peut utiliser les
  prvisualisations, mme lorsqu'on ne travaille pas avec de grosses
  images. Elles sont utiles, par exemple, lorsqu'on veut avoir un rendu
  de motifs complexes. (Testez le vnrable plug-in Diffraction et
  d'autres !). Comme autre exemple, regardez le plug-in de rotation de
  couleur (travail en cours). Vous pouvez aussi utiliser les
  prvisualisations pour des petits logos dans vos plug-ins et mme pour
  une photo de vous, l'Auteur. Utilisons les prvisualisations !

  Quand ne pas utiliser les prvisualisations

  N'utilisez pas les prvisualisations pour les graphes, les tracs,
  etc. GDK est bien plus rapide pour a. N'utilisez les que pour les
  images !

  Utilisons les prvisualisations !

  Vous pouvez mettre une prvisualisation dans  peu prs n'importe
  quoi. Dans une vbox, une hbox, un bouton, etc. Mais elles donnent leur
  meilleur d'elles-mmes dans des cadres resserrs autour d'elles. Les
  prvisualisations n'ont, par elles-mmes, aucun contour et semblent
  plates sans eux. (Bien sr, si c'est cet aspect que vous
  voulez...). Les cadres serrs fournissent les bordures ncessaires.

                                 [Image][Image]

  Les prvisualisations sont,  bien des gards, comme tous les autres
  widgets de GTK (avec tout ce que cela implique) sauf qu'il disposent
  d'une fonctionnalit supplmentaire : ils doivent tre remplis
  avec une image ! Nous traiterons d'abord exclusivement de l'aspect GTK
  des prvisualisations, puis nous verrons comment les remplir.


                                /* Cration d'un widget prvisualisation,
                                 * configuration de sa taille et affichage */
  GtkWidget *preview;
  preview=gtk_preview_new(GTK_PREVIEW_COLOR)
                                /* Autre option :
                                GTK_PREVIEW_GRAYSCALE);*/

  gtk_preview_size (GTK_PREVIEW (preview), WIDTH, HEIGHT);
  gtk_widget_show(preview);
  my_preview_rendering_function(preview);

  Ah oui, comme je le disais, les prvisualisations rendent mieux dans
  des cadres :

  GtkWidget *create_a_preview(int        Width,
                              int        Height,
                              int        Colorfulness)
  {
    GtkWidget *preview;
    GtkWidget *frame;

    frame = gtk_frame_new(NULL);
    gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
    gtk_container_border_width (GTK_CONTAINER(frame),0);
    gtk_widget_show(frame);

    preview=gtk_preview_new (Colorfulness?GTK_PREVIEW_COLOR
                                         :GTK_PREVIEW_GRAYSCALE);
    gtk_preview_size (GTK_PREVIEW (preview), Width, Height);
    gtk_container_add(GTK_CONTAINER(frame),preview);
    gtk_widget_show(preview);

    my_preview_rendering_function(preview);
    return frame;
  }

  Ceci est ma prvisualisation de base. Cette fonction retourne le cadre
   pre , on peut ainsi le placer ailleurs dans notre interface. Bien
  sr, on peut passer le cadre  pre  en paramtre  cette
  fonction. Dans de nombreuses situations, toutefois, le contenu de la
  prvisualisation est change continuellement par notre application. En
  ce cas, on peut passer un pointeur vers une prvisualisation  la
  fonction <em/create_a_preview()/ et avoir ainsi un contrle sur elle
  plus tard.

  Un point plus important qui pourra un jour vous faire conomiser
  beaucoup de temps. Quelques fois, il est souhaitable de mettre un
  label  votre prvisualisation. Par exemple, on peut nommer la
  prvisualisation contenant l'image originale  Original  et celle
  contenant l'image modifie  Moins Originale . Il peut vous arriver
  de placer la prvisualisation avec le label appropri dans une
  vbox. L'effet inattendu est que si le label est plus large que la
  prvisualisation (taille de cette dernire, taille de la fonte du
  label, etc), le cadre s'largit et ne convient plus  la
  prvisualisation. Le mme problme se passera probablement dans
  d'autres situations aussi.

                                     [Image]

  La solution consiste  placer la prvisualisation et le label dans une
  table de 2x2 en les attachant avec les paramtres suivants (c'est
  l'une des possibilits, bien sr. La cl consiste  ne pas mettre
  GTK_FILL dans le second attachement)nbsp;:

  gtk_table_attach(GTK_TABLE(table),label,0,1,0,1,
                   0,
                   GTK_EXPAND|GTK_FILL,
                   0,0);
  gtk_table_attach(GTK_TABLE(table),frame,0,1,1,2,
                   GTK_EXPAND,
                   GTK_EXPAND,
                   0,0);


  Et voici le rsultat :

                                     [Image]

  Divers

  Rendre une prvisualisation cliquable se fait trs facilement en la plaant dans un bouton. Cela ajoute aussi une bordure agrable autour de la prvisualisation et vous n'avez mme pas besoin de la mettre dans un cadre.  Voir le plug-in Filter Pack Simulation comme exemple.

  Remplir une prvisualisation

  Afin de nous familiariser avec les bases de ce remplissage, crons le motif suivant :

                                     [Image]

  void
  my_preview_rendering_function(GtkWidget     *preview)
  {
  #define SIZE 100
  #define HALF (SIZE/2)

    guchar *row=(guchar *) malloc(3*SIZE); /* 3 bits par point */
    gint i, j;                             /* Coordonnes      */
    double r, alpha, x, y;

    if (preview==NULL) return; /* J'ajoute gnralement ceci quand je */
                               /* veux viter des plantages stupides  */
                               /* Vous devez vous assurer que tout a  */
                               /* t correctement initialis !       */

    for (j=0; j < ABS(cos(2*alpha)) ) { /* Sommes-nous dans la forme ? */
                                           /* glib.h contient ABS(x).   */
          row[i*3+0] = sqrt(1-r)*255;      /* Definit rouge             */
          row[i*3+1] = 128;                /* Definit vert              */
          row[i*3+2] = 224;                /* Definit bleu              */
        }                                  /* "+0" est pour l'alignement ! */
        else {
          row[i*3+0] = r*255;
          row[i*3+1] = ABS(sin((float)i/SIZE*2*PI))*255;
          row[i*3+2] = ABS(sin((float)j/SIZE*2*PI))*255;
        }
      }
      gtk_preview_draw_row( GTK_PREVIEW(preview),row,0,j,SIZE);

      /* Insre "row" dans "preview" en partant du point de */
      /* coordonnes (0,j) premire colonne, j_ime ligne allant de SIZE */
      /* pixels vers la droite */
    }

    free(row); /* on rcupre un peu d'espace */
    gtk_widget_draw(preview,NULL); /* qu'est-ce que a fait ? */
    gdk_flush(); /* et a ?  */
  }

  Ceux qui n'utilisent pas GIMP en ont suffisamment vu pour faire
  dj beaucoup de choses. Pour ceux qui l'utilisent, j'ai quelques
  prcisions  ajouter.

  Prvisualisation d'image

  Il est pratique de conserver une version rduite de l'image ayant
  juste assez de pixels pour remplir la prvisualisation. Ceci est
  possible en choisissant chaque nime pixel o n est le ratio de la
  taille de l'image par rapport  la taille de la visualisation. Toutes
  les oprations suivantes (y compris le remplissage des
  prvisualisations) sont alors ralises seulement sur le nombre rduit
  de pixels. Ce qui suit est mon implantation de la rduction d'image
  (Gardez  l'esprit que je n'ai que quelques notions de base en C !).

  (ATTENTION : CODE NON TEST !!!)

  typedef struct {
    gint      width;
    gint      height;
    gint      bbp;
    guchar    *rgb;
    guchar    *mask;
  } ReducedImage;

  enum {
    SELECTION_ONLY,
    SELCTION_IN_CONTEXT,
    ENTIRE_IMAGE
  };

  ReducedImage *Reduce_The_Image(GDrawable *drawable,
                                 GDrawable *mask,
                                 gint LongerSize,
                                 gint Selection)
  {
    /* Cette fonction rduit l'image  la taille de prvisualisation choisie */
    /* La taille de la prvisualisation est dtermine par LongerSize, i.e.  */
    /* la plus grande des deux dimensions. Ne fonctionne qu'avec des images  */
    /* RGB !                                                                 */

    gint RH, RW;          /* Hauteur et Largeur rduites                     */
    gint width, height;   /* Largeur et Hauteur de la surface  rduire      */
    gint bytes=drawable->bpp;
    ReducedImage *temp=(ReducedImage *)malloc(sizeof(ReducedImage));

    guchar *tempRGB, *src_row, *tempmask, *src_mask_row,R,G,B;
    gint i, j, whichcol, whichrow, x1, x2, y1, y2;
    GPixelRgn srcPR, srcMask;
    gint NoSelectionMade=TRUE; /* Suppose que l'on traite l'image entire    */

    gimp_drawable_mask_bounds (drawable->id, &x1, &y1, &x2, &y2);
    width  = x2-x1;
    height = y2-y1;
    /* S'il y a une SELECTION, on rcupre ses frontires ! */

    if (width != drawable->width && height != drawable->height)
      NoSelectionMade=FALSE;
    /* On vrifie si l'utilisateur a rendu une slection active         */
    /* Ceci sera important plus tard, lorsqu'on crera un masque rduit */

    /* Si on veut prvisualiser l'image entire, supprimer ce qui suit ! */
    /* Bien sr, s'il n'y a pas de slection, cela n'a aucun effet !     */
    if (Selection==ENTIRE_IMAGE) {
      x1=0;
      x2=drawable->width;
      y1=0;
      y2=drawable->height;
    }

    /* Si on veut prvisualiser une slection avec une surface qui l'entoure, */
    /* on doit l'agrandir un petit peu. Considrez a comme une devinette.   */

    if (Selection==SELECTION_IN_CONTEXT) {
      x1=MAX(0,                x1-width/2.0);
      x2=MIN(drawable->width,  x2+width/2.0);
      y1=MAX(0,                y1-height/2.0);
      y2=MIN(drawable->height, y2+height/2.0);
    }

    /* Calcul de la largeur et de la hauteur de la surface  rduire. */

    width  = x2-x1;
    height = y2-y1;

    /* Les lignes ci-dessous dterminent la dimension qui sera le cot */
    /* le plus long. Cette ide est emprunte au plug-in Supernova.    */
    /* Je souponne que j'aurais pu y penser moi-mme, mais la vrit  */
    /* doit tre dite. Le plagiat pue !                                */

    if (width>height) {
      RW=LongerSize;
      RH=(float) height * (float) LongerSize/ (float) width;
    }
    else {
      RH=LongerSize;
      RW=(float)width * (float) LongerSize/ (float) height;
    }

    /* L'image entire est rduite dans une chane ! */

    tempRGB   = (guchar *) malloc(RW*RH*bytes);
    tempmask  = (guchar *) malloc(RW*RH);

    gimp_pixel_rgn_init (&srcPR, drawable, x1, y1, width, height, FALSE, FALSE);
    gimp_pixel_rgn_init (&srcMask, mask, x1, y1, width, height, FALSE, FALSE);

    /* Rservation pour sauver une ligne d'image et une ligne du masque */
    src_row       = (guchar *) malloc (width*bytes);
    src_mask_row  = (guchar *) malloc (width);

    for (i=0; i < RH; i++) {
      whichrow=(float)i*(float)height/(float)RH;
      gimp_pixel_rgn_get_row (&srcPR, src_row, x1, y1+whichrow, width);
      gimp_pixel_rgn_get_row (&srcMask, src_mask_row, x1, y1+whichrow, width);

      for (j=0; j < RW; j++) {
        whichcol=(float)j*(float)width/(float)RW;

        /* Pas de slection = chaque point est compltement slectionn ! */

        if (NoSelectionMade)
          tempmask[i*RW+j]=255;
        else
          tempmask[i*RW+j]=src_mask_row[whichcol];

        /* Ajout de la ligne  la longue chane qui contient maintenant */
        /* l'image !                                                    */

        tempRGB[i*RW*bytes+j*bytes+0]=src_row[whichcol*bytes+0];
        tempRGB[i*RW*bytes+j*bytes+1]=src_row[whichcol*bytes+1];
        tempRGB[i*RW*bytes+j*bytes+2]=src_row[whichcol*bytes+2];

        /* On s'accroche aussi  l'alpha */
        if (bytes==4)
          tempRGB[i*RW*bytes+j*bytes+3]=src_row[whichcol*bytes+3];
      }
    }
    temp->bpp=bytes;
    temp->width=RW;
    temp->height=RH;
    temp->rgb=tempRGB;
    temp->mask=tempmask;
    return temp;
  }

  La suite est une fonction de prvisualisation qui utilise le mme type
  <em/ReducedImage/ !  On remarque qu'elle utilise une fausse
  transparence (au moyen de <em/fake_transparancy/ qui est dfini comme
  suit :

  gint fake_transparency(gint i, gint j)
  {
    if ( ((i%20)- 10) * ((j%20)- 10)>0   )
      return 64;
    else
      return 196;
  }

  Voici maintenant la fonction de prvisualisationnbsp;:

  void
  my_preview_render_function(GtkWidget     *preview,
                             gint          changewhat,
                             gint          changewhich)
  {
    gint Inten, bytes=drawable->bpp;
    gint i, j, k;
    float partial;
    gint RW=reduced->width;
    gint RH=reduced->height;
    guchar *row=malloc(bytes*RW);;


    for (i=0; i < RH; i++) {
      for (j=0; j < RW; j++) {

        row[j*3+0] = reduced->rgb[i*RW*bytes + j*bytes + 0];
        row[j*3+1] = reduced->rgb[i*RW*bytes + j*bytes + 1];
        row[j*3+2] = reduced->rgb[i*RW*bytes + j*bytes + 2];

        if (bytes==4)
          for (k=0; k<3; k++) {
            float transp=reduced->rgb[i*RW*bytes+j*bytes+3]/255.0;
            row[3*j+k]=transp*a[3*j+k]+(1-transp)*fake_transparency(i,j);
          }
      }
      gtk_preview_draw_row( GTK_PREVIEW(preview),row,0,i,RW);
    }

    free(a);
    gtk_widget_draw(preview,NULL);
    gdk_flush();
  }

  Fonctions applicables

  guint           gtk_preview_get_type           (void);
  /* Aucune ide */
  void            gtk_preview_uninit             (void);
  /* Aucune ide */
  GtkWidget*      gtk_preview_new                (GtkPreviewType   type);
  /* Dcrite ci-dessous */
  void            gtk_preview_size               (GtkPreview      *preview,
                                                  gint             width,
                                                  gint             height);
  /* Permet de changer la taille d'une prvisualisation existante */
  /* Apparamment, il y a un bug dans GTK qui rend ce traitement   */
  /* hasardeux. Une mthode pour corriger ce problme consiste   */
  /* changer manuellement la taille de la fentre contenant la    */
  /* prvisualisation aprs avoir chang la taille de la          */
  /* prvisualisation.                                            */

  void            gtk_preview_put                (GtkPreview      *preview,
                                                  GdkWindow       *window,
                                                  GdkGC           *gc,
                                                  gint             srcx,
                                                  gint             srcy,
                                                  gint             destx,
                                                  gint             desty,
                                                  gint             width,
                                                  gint             height);
  /* Aucune ide */

  void            gtk_preview_put_row            (GtkPreview      *preview,
                                                  guchar          *src,
                                                  guchar          *dest,
                                                  gint             x,
                                                  gint             y,
                                                  gint             w);
  /* Aucune ide */

  void            gtk_preview_draw_row           (GtkPreview      *preview,
                                                  guchar          *data,
                                                  gint             x,
                                                  gint             y,
                                                  gint             w);
  /* Dcrite dans le texte */

  void            gtk_preview_set_expand         (GtkPreview      *preview,
                                                  gint             expand);
  /* Aucune ide */

  /* Aucune piste pour celles qui suivent mais devrait tre */
  /* un standard pour la plupart des widgets.               */

  void            gtk_preview_set_gamma          (double           gamma);
  void            gtk_preview_set_color_cube     (guint            nred_shades,
                                                  guint            ngreen_shades,
                                                  guint            nblue_shades,
                                                  guint            ngray_shades);
  void            gtk_preview_set_install_cmap   (gint             install_cmap);
  void            gtk_preview_set_reserved       (gint             nreserved);
  GdkVisual*      gtk_preview_get_visual         (void);
  GdkColormap*    gtk_preview_get_cmap           (void);
  GtkPreviewInfo* gtk_preview_get_info           (void);

  That's all, folks!



  1122..77..  CCoouurrbbeess



  1133..  WWiiddggeett EEvveennttBBooxx

  Il n'est disponible que dans _g_t_k_+_9_7_0_9_1_6_._t_a_r_._g_z et les distributions
  ultrieures.

  Certains widgets GTK n'ont pas de fentre X associe, il se dessinent
  donc sur leurs parents.  cause de cela, ils ne peuvent recevoir
  d'vnements et, s'ils ont une taille incorrecte, ils ne peuvent pas se
  mettre en place correctement : on peut alors avoir des surimpressions
  douteuses, etc. Si vous avez besoin de ces widgets, _E_v_e_n_t_B_o_x est fait
  pour vous.

  Au premier abord, le widget _E_v_e_n_t_B_o_x peut apparatre comme totalement
  dnu d'intrt. Il ne dessine rien  l'cran et ne rpond  aucun venement.
  Cependant, il joue un rle - il fournit une fentre X pour son widget
  fils. Ceci est important car de nombreux widgets GTK n'ont pas de
  fentre X associe. Ne pas avoir de fentre permet d'conomiser de la
  mmoire mais a aussi quelques inconvnients. Un widget sans fentre ne
  peut recevoir d'vnement, et ne ralise aucune mise en place de ce qu'il
  contient. Bien que le nom  EventBox insiste sur la fonction de gestion
  d'vnement, le widget peut aussi tre utilis pour la mise en place (et
  plus... voir l'exemple ci-dessous).


  Pour crer un widget EventBox, on utilise :



       GtkWidget* gtk_event_box_new (void);



  Un widget fils peut alors tre ajout  cet _E_v_e_n_t_B_o_x :



       gtk_container_add (GTK_CONTAINER(event_box), widget);



  L'exemple suivant montre l'utilisation d'un _E_v_e_n_t_B_o_x - un label est cr
  et mis en place sur une petite bote, et configur pour qu'un clic
  souris sur le label provoque la fin du programme.



  #include <gtk/gtk.h>

  int
  main (int argc, char *argv[])
  {
      GtkWidget *window;
      GtkWidget *event_box;
      GtkWidget *label;

      gtk_init (&argc, &argv);

      window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

      gtk_window_set_title (GTK_WINDOW (window), "Event Box");

      gtk_signal_connect (GTK_OBJECT (window), "destroy",
                          GTK_SIGNAL_FUNC (gtk_exit), NULL);

      gtk_container_border_width (GTK_CONTAINER (window), 10);

      /* Cration d'un EventBox et ajout de celui-ci dans la fentre. */

      event_box = gtk_event_box_new ();
      gtk_container_add (GTK_CONTAINER(window), event_box);
      gtk_widget_show (event_box);

      /* Cration d'un long label */

      label = gtk_label_new ("Cliquez ici pour quitter, quitter, quitter, quitter, quitter");
      gtk_container_add (GTK_CONTAINER (event_box), label);
      gtk_widget_show (label);

      /* Placement serr. */

      gtk_widget_set_usize (label, 110, 20);

      /* Attachement d'une action  celui-ci. */

      gtk_widget_set_events (event_box, GDK_BUTTON_PRESS_MASK);
      gtk_signal_connect (GTK_OBJECT(event_box), "button_press_event",
                          GTK_SIGNAL_FUNC (gtk_exit), NULL);

      /* Encore une fois, vous avez besoin d'une fentre X pour... */

      gtk_widget_realize (event_box);
      gdk_window_set_cursor (event_box->window, gdk_cursor_new (GDK_HAND1));

      gtk_widget_show (window);

      gtk_main ();

      return 0;
  }



  1144..  CCoonnffiigguurraattiioonn ddeess aattttrriibbuuttss ddee wwiiddggeett

  Cette section dcrit les fonctions oprant sur les widgets. Elles
  peuvent tre utilises pour configurer le style, l'espacement, la
  taille, etc.

  (Je devrais peut tre faire une section uniquement consacre aux
  raccourcis clavier.)
       void       gtk_widget_install_accelerator (GtkWidget           *widget,
                                                  GtkAcceleratorTable *table,
                                                  gchar               *signal_name,
                                                  gchar                key,
                                                  guint8               modifiers);

       void       gtk_widget_remove_accelerator  (GtkWidget           *widget,
                                                  GtkAcceleratorTable *table,
                                                  gchar               *signal_name);

       void       gtk_widget_activate            (GtkWidget           *widget);

       void       gtk_widget_set_name            (GtkWidget           *widget,
                                                  gchar               *name);
       gchar*     gtk_widget_get_name            (GtkWidget           *widget);

       void       gtk_widget_set_sensitive       (GtkWidget           *widget,
                                                  gint                 sensitive);

       void       gtk_widget_set_style           (GtkWidget           *widget,
                                                  GtkStyle            *style);

       GtkStyle*    gtk_widget_get_style     (GtkWidget *widget);

       GtkStyle*    gtk_widget_get_default_style    (void);

       void       gtk_widget_set_uposition       (GtkWidget           *widget,
                                                  gint                 x,
                                                  gint                 y);
       void       gtk_widget_set_usize           (GtkWidget           *widget,
                                                  gint                 width,
                                                  gint                 height);

       void       gtk_widget_grab_focus          (GtkWidget           *widget);

       void       gtk_widget_show                (GtkWidget           *widget);

       void       gtk_widget_hide                (GtkWidget           *widget);



  1155..  TTeemmppoorriissaattiioonnss,, ffoonnccttiioonnss dd''EE//SS eett dd''aatttteennttee


  1155..11..  TTeemmppoorriissaattiioonnss

  Vous pouvez vous demander comment faire pour que GTK fasse quelque
  chose d'utile lorsqu'il est dans _g_t_k___m_a_i_n. En fait, on a plusieurs
  options. L'utilisation des fonctions suivantes permet de crer une
  temporisation qui sera appele tous les _i_n_t_e_r_v_a_l millisecondes.



       gint gtk_timeout_add (guint32 interval,
                             GtkFunction function,
                             gpointer data);



  Le premier paramtre est le nombre de millisecondes entre les appels
  notre fonction. Le deuxime est la fonction  appeler et le troisime est
  la donne passe  cette fonction de rappel. La valeur retourne est un
  marqueur  de type entier qui pourra tre utilis pour arrter la
  temporisation en appelant :



       void gtk_timeout_remove (gint tag);



  On peut aussi stopper la fonction de temporisation en faisant
  retourner zro ou FALSE  notre fonction de rappel. videmment, cela veut
  dire que si vous voulez que votre fonction continue  tre appele, elle
  doit retourner une valeur non nulle, ou TRUE.

  La dclaration de votre fonction de rappel doit ressembler  a :



       gint timeout_callback (gpointer data);



  1155..22..  SSuurrvveeiillllaannccee ddeess EE//SS

  Une autre caractristique intressante du GTK est la possibilit de
  vrifier les donnes d'un descripteur de fichier (celles retournes par
  _o_p_e_n(2) ou _s_o_c_k_e_t(2)). C'est particulirement pratique pour les
  applications rseau. La fonction suivante permet cette vrification :



       gint gdk_input_add (gint source,
                           GdkInputCondition condition,
                           GdkInputFunction  function,
                           gpointer data);



  Le premier paramtre est le descripteur de fichier que l'on veut
  tudier, le second spcifie ce qu'on veut que le GDK recherche. Cela
  peut tre :

  GDK_INPUT_READ - Appel _f_u_n_c_t_i_o_n lorsqu'il y a une donne prte tre lue
  dans le descripteur de fichier.

  GDK_INPUT_WRITE - Appel de _f_u_n_c_t_i_o_n lorsque le descripteur de fichier
  est prt pour une criture.

  Je suis sr que vous vous doutez, maintenant, que le troisime paramtre
  est la fonction que l'on veut appeler lorsque les conditions ci-dessus
  sont satisfaites. Le dernier paramtre est la donne passer  cette
  fonction.

  La valeur retourne est un marqueur qui pourra tre utilis pour dire au
  GDK de cesser de surveiller ce descripteur  l'aide de la fonction :



       void gdk_input_remove (gint tag);


  La fonction de rappel doit tre dclare de la faon suivante :



       void input_callback (gpointer data, gint source,
                            GdkInputCondition condition);



  1155..33..  FFoonnccttiioonnss dd''aatttteennttee

  Que se passe-t'il si vous avez une fonction qui doit tre appele
  lorsque rien d'autre ne se passe ? On utilise la fonction suivante qui
  force GTK  appeler _f_u_n_c_t_i_o_n lorsqu'on est en phase d'inaction ;



       gint gtk_idle_add (GtkFunction function,
                          gpointer data);



       void gtk_idle_remove (gint tag);



  Je n'expliquerai pas la signification des paramtres car ils
  ressemblent beaucoup  ceux dj vus ci-dessus. La fonction pointe par le
  premier paramtre de _g_t_k___i_d_l_e___a_d_d_(_) sera appele  chaque occasion. Comme
  pour les autres, retourner FALSE empchera la fonction d'attente d'tre
  appele.


  1166..  GGeessttiioonn ddeess sslleeccttiioonnss

  1166..11..  IInnttrroodduuccttiioonn

  Un type de communication inter-processus gre par GTK est les
  _s_l_e_c_t_i_o_n_s. Une slection identifie un morceau de donnes, par exemple
  une portion de texte slectionne par l'utilisateur avec la souris.
  Seule une application sur un cran (le _p_r_o_p_r_i_t_a_i_r_e) peut possder une
  slection particulire  un moment donn, ainsi lorsqu'une slection est
  rclame par une application, le propritaire prcdent doit indiquer
  l'utilisateur que la slection a t abandonne. Les autres applications
  peuvent demander le contenu d'une slection sous diffrentes formes
  appeles _c_i_b_l_e_s. Il peut y avoir un nombre quelconque de slections,
  mais la plupart des applications X n'en grent qu'une, la _s_l_e_c_t_i_o_n
  _p_r_i_m_a_i_r_e.


  Dans la plupart des cas, une application GTK n'a pas besoin de grer
  elle-mme les slections. Les widgets standards, comme le widget Entre
  de texte, possdent dj la capacit de rclamer la slection lorsqu'il le
  faut (par exemple, lorsque l'utilisateur glisse au dessus d'un texte)
  et de rcuprer le contenu de la slection dtenue par un autre widget ou
  une autre application (par exemple, lorsque l'utilisateur clique avec
  le deuxime bouton de la souris). Cependant, il peut il y avoir des cas
  dans lesquels vous voulez donner aux autres widgets la possibilit de
  fournir la slection, ou vous dsirez rcuprer des cibles non supportes
  par dfaut.


  Un concept fondamental dans la comprhension du fonctionnement des
  slections est celui d'_a_t_o_m_e. Un atome est un entier qui dfinit de faon
  unique une chane (sur un affichage particulier). Certains atomes sont
  prdfinis par le serveur X et, dans certains cas, des constantes
  dfinies dans _g_t_k_._h correspondent  ces atomes. Par exemple, la
  constante GDK_PRIMARY_SELECTION correspond  la chane "PRIMARY".  Dans
  d'autres cas, on doit utiliser les fonctions _g_d_k___a_t_o_m___i_n_t_e_r_n_(_), pour
  obtenir l'atome correspondant  une chane, et _g_d_k___a_t_o_m___n_a_m_e_(_), pour
  obtenir le nom d'un atome. Les slections et les cibles sont identifis
  par des atomes.


  1166..22..  RRccuupprraattiioonn ddee llaa sslleeccttiioonn

  La rcupration de la slection est un processus asynchrone. Pour dmarrer
  le processus, on appelle :



       gint gtk_selection_convert   (GtkWidget           *widget,
                                     GdkAtom              selection,
                                     GdkAtom              target,
                                     guint32              time)



  Cela _c_o_n_v_e_r_t_i_t la slection dans la forme spcifie par _t_a_r_g_e_t. Si tout
  est possible, le paramtre _t_i_m_e sera le moment de l'vnement qui a
  dclench la slection. Ceci aide s'assurer que les vnements arrivent
  dans l'ordre o l'utilisateur les a demand. Cependant, si cela n'est
  pas possible (par exemple, lorsque la conversion a t dclenche par un
  signal "clicked"), alors on peut utiliser la macro GDK_CURRENT_TIME.


  Quand le propritaire de la slection rpond  la requte, un signal
  "selection_received" est envoy  notre application. Le gestionnaire de
  ce signal reoit un pointeur vers une structure GtkSelectionData dfinie
  ainsi :



       struct _GtkSelectionData
       {
         GdkAtom selection;
         GdkAtom target;
         GdkAtom type;
         gint    format;
         guchar *data;
         gint    length;
       };



  _s_e_l_e_c_t_i_o_n et _t_a_r_g_e_t sont les valeurs que l'on a donn dans notre appel
  _g_t_k___s_e_l_e_c_t_i_o_n___c_o_n_v_e_r_t_(_). _t_y_p_e est un atome qui identifie le type de
  donnes retourn par le propritaire de la slection. Quelques valeurs
  possibles sont : "STRING", une chane de caractres latin-1, "ATOM", une
  srie d'atomes, "INTEGER", un entier, etc. La plupart des cibles ne
  peuvent retourner qu'un type. _f_o_r_m_a_t donne la longueur des units (les
  caractres, par exemple) en bits. Habituellement, on ne se proccupe pas
  de cela lorsqu'on reoit des donnes. _d_a_t_a est un pointeur vers la donne
  retourne et _l_e_n_g_t_h donne la longueur en octets de la donne retourne.
  Si _l_e_n_g_t_h est ngative, cela indique qu'une erreur est survenue et que
  la slection ne peut tre rcupre. Ceci peut arriver si aucune
  application n'est propritaire de la slection, ou si vous avez demand
  une cible que l'application ne sait pas grer. Le tampon est garanti
  d'tre un octet plus long que _l_e_n_g_t_h ; l'octet supplmentaire sera
  toujours zro, et il n'est donc pas ncessaire de faire une copie de
  chane simplement pour qu'elle soit termine par zro (comme doivent
  l'tre toutes les chanes C).


  Dans l'exemple qui suit, on rcupre la cible spciale "TARGETS", qui est
  une liste de toutes les cibles en lesquelles la slection peut tre
  convertie.



  #include <gtk/gtk.h>

  void selection_received (GtkWidget *widget,
                           GtkSelectionData *selection_data,
                           gpointer data);

  /* Gestionnaire de signal invoqu lorsque l'utilisateur clique sur
   * le bouton  Obtenir les cibles . */

  void get_targets (GtkWidget *widget, gpointer data)
  {
    static GdkAtom targets_atom = GDK_NONE;

    /* Obtention de l'atome correspondant  la chane "TARGETS" */

    if (targets_atom == GDK_NONE)
      targets_atom = gdk_atom_intern ("TARGETS", FALSE);

    /* Demande de la cible "TARGETS" pour la slection primaire */

    gtk_selection_convert (widget, GDK_SELECTION_PRIMARY, targets_atom,
                           GDK_CURRENT_TIME);
  }

  /* Gestionnaire de signal appel quand le propritaire des slections
   * retourne la donne. */

  void selection_received (GtkWidget *widget, GtkSelectionData *selection_data,
                      gpointer data)
  {
    GdkAtom *atoms;
    GList *item_list;
    int i;

    /* **** IMPORTANT **** On vrifie si la rcupration s'est bien passe. */

    if (selection_data->length < 0)
      {
        g_print ("Selection retrieval failed\n");
        return;
      }

    /* On s'assure que l'on a obtenu la donne sous la forme attendue. */

    if (selection_data->type != GDK_SELECTION_TYPE_ATOM)
      {
        g_print ("La slection \"TARGETS\" n'a pas t retourne sous la forme d'atomes !\n");
        return;
      }

    /* Affichage des atomes reus. */

    atoms = (GdkAtom *)selection_data->data;

    item_list = NULL;
    for (i=0; i<selection_data->length/sizeof(GdkAtom); i++)
      {
        char *name;
        name = gdk_atom_name (atoms[i]);
        if (name != NULL)
          g_print ("%s\n",name);
        else
          g_print ("(atome incorrect)\n");
      }

    return;
  }

  int
  main (int argc, char *argv[])
  {
    GtkWidget *window;
    GtkWidget *button;

    gtk_init (&argc, &argv);

    /* Cration de la fentre de l'application. */

    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title (GTK_WINDOW (window), "Slections");
    gtk_container_border_width (GTK_CONTAINER (window), 10);

    gtk_signal_connect (GTK_OBJECT (window), "destroy",
                        GTK_SIGNAL_FUNC (gtk_exit), NULL);

    /* Cration d'un bouton pour obtenir les cibles */

    button = gtk_button_new_with_label ("Obtenir les cibles");
    gtk_container_add (GTK_CONTAINER (window), button);

    gtk_signal_connect (GTK_OBJECT(button), "clicked",
                        GTK_SIGNAL_FUNC (get_targets), NULL);
    gtk_signal_connect (GTK_OBJECT(button), "selection_received",
                        GTK_SIGNAL_FUNC (selection_received), NULL);

    gtk_widget_show (button);
    gtk_widget_show (window);

    gtk_main ();

    return 0;
  }



  1166..33..  FFoouurrnniirr llaa sslleeccttiioonn


  Fournir la slection est un peu plus compliqu. On doit enregistrer les
  gestionnaires qui seront appels lorsque notre slection est demande.
  Pour chaque paire slection/cible que l'on grera, on fera un appel  :



       void gtk_selection_add_handler (GtkWidget           *widget,
                                       GdkAtom              selection,
                                       GdkAtom              target,
                                       GtkSelectionFunction function,
                                       GtkRemoveFunction    remove_func,
                                       gpointer             data);



  _w_i_d_g_e_t, _s_e_l_e_c_t_i_o_n et _t_a_r_g_e_t identifient les requtes que ce
  gestionnaire grera. S'il ne vaut pas NULL, _r_e_m_o_v_e___f_u_n_c sera appel
  lorsque le gestionnaire de signal est supprim. Ceci est utile, par
  exemple, pour des langages interprts qui doivent garder une trace du
  nombre de rfrences  _d_a_t_a.

  La fonction de rappel _f_u_n_c_t_i_o_n doit avoir la signature suivante :



       typedef void (*GtkSelectionFunction) (GtkWidget *widget,
                                             GtkSelectionData *selection_data,
                                             gpointer data);



  Le _G_t_k_S_e_l_e_c_t_i_o_n_D_a_t_a est le mme qu'au dessus, mais, cette fois, nous
  sommes responsables de l'initialisation de ses champs _t_y_p_e, _f_o_r_m_a_t,
  _d_a_t_a, et _l_e_n_g_t_h. (Le champ _f_o_r_m_a_t est important ici - le serveur X
  l'utilise pour savoir si la donne doit tre change par octet ou non.
  Habituellement, ce sera 8 (un caractre), ou 32 (un entier)). Cette
  initialisation est faite en utilisant l'appel :



       void gtk_selection_data_set (GtkSelectionData *selection_data,
                                    GdkAtom           type,
                                    gint              format,
                                    guchar           *data,
                                    gint              length);



  Cette fonction s'occupe de faire une copie correcte des donnes afin
  que l'on n'ait pas  se soucier du reste. (On ne doit pas remplir ces
  champs  la main).


  Lorsque cela est demand par l'utilisateur, on rclame la possession de
  la slection en appelant :



       gint gtk_selection_owner_set (GtkWidget           *widget,
                                     GdkAtom              selection,
                                     guint32              time);



  Si une autre application rclame la possession de la slection, on
  recevra un "selection_clear_event".

  Comme exemple de fourniture de slection, l'exemple suivant ajoute une
  fonctionnalit de slection  un bouton commutateur. Lorsque ce bouton
  est appuy, le programme rclame la slection primaire. La seule cible
  supporte ( part certaines cibles fournies par GTK lui-mme, comme
  TARGETS ) est  STRING . Lorsque celle-ci est demande, on retourne une
  reprsentation de l'heure sous forme de chane.



  #include <gtk/gtk.h>
  #include <time.h>

  /* Fonction de rappel appele lorsque l'utilisateur commute la slection. */

  void selection_toggled (GtkWidget *widget, gint *have_selection)
  {
    if (GTK_TOGGLE_BUTTON(widget)->active)
      {
        *have_selection = gtk_selection_owner_set (widget,
                                                   GDK_SELECTION_PRIMARY,
                                                   GDK_CURRENT_TIME);
        /* Si la demande de slection choue, on remet le bouton en position sortie. */

        if (!*have_selection)
          gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON(widget), FALSE);
      }
    else
      {
        if (*have_selection)
          {
            /* Avant de nettoyer la selection en mettant son propritaire  NULL,
             * on vrifie que nous sommes bien son propritaire actuel. */

            if (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == widget->window)
              gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY,
                                       GDK_CURRENT_TIME);
            *have_selection = FALSE;
          }
      }
  }

  /* Appele lorsqu'une autre application demande la slection. */

  gint selection_clear (GtkWidget *widget, GdkEventSelection *event,
                   gint *have_selection)
  {
    *have_selection = FALSE;
    gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON(widget), FALSE);

    return TRUE;
  }

  /* Fournit l'heure comme slection. */

  void selection_handle (GtkWidget *widget,
                    GtkSelectionData *selection_data,
                    gpointer data)
  {
    gchar *timestr;
    time_t current_time;

    current_time = time (NULL);
    timestr = asctime (localtime(&current_time));

    /* Lorsqu'on retourne une chane, elle ne doit pas se terminer par
     * 0, ce sera fait pour nous. */

    gtk_selection_data_set (selection_data, GDK_SELECTION_TYPE_STRING,
                            8, timestr, strlen(timestr));
  }

  int main (int argc, char *argv[])
  {
    GtkWidget *window;

    GtkWidget *selection_button;

    static int have_selection = FALSE;

    gtk_init (&argc, &argv);

    /* Cration de la fentre principale. */

    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title (GTK_WINDOW (window), "Event Box");
    gtk_container_border_width (GTK_CONTAINER (window), 10);

    gtk_signal_connect (GTK_OBJECT (window), "destroy",
                        GTK_SIGNAL_FUNC (gtk_exit), NULL);

    /* Cration d'un bouton commutateur pour qu'il agisse comme une slection. */

    selection_button = gtk_toggle_button_new_with_label ("Demande de slection");
    gtk_container_add (GTK_CONTAINER (window), selection_button);
    gtk_widget_show (selection_button);

    gtk_signal_connect (GTK_OBJECT(selection_button), "toggled",
                        GTK_SIGNAL_FUNC (selection_toggled), &have_selection);
    gtk_signal_connect (GTK_OBJECT(selection_button), "selection_clear_event",
                        GTK_SIGNAL_FUNC (selection_clear), &have_selection);

    gtk_selection_add_handler (selection_button, GDK_SELECTION_PRIMARY,
                               GDK_SELECTION_TYPE_STRING,
                               selection_handle, NULL, NULL);

    gtk_widget_show (selection_button);
    gtk_widget_show (window);

    gtk_main ();

    return 0;
  }



  1177..  gglliibb

  La _g_l_i_b fournit de nombreuses fonctions et dfinitions utiles, prtes
  tre utilises lorsqu'on cre des applications GDK et GTK. Je les
  numrerais toutes avec une brve explication. Beaucoup sont des rpliques
  des fonctions standards de la _l_i_b_c, et je ne les dtaillerais donc pas
  trop. Ceci doit surtout servir de rfrence afin de savoir ce qui est
  disponible pour tre utilis.


  1177..11..  DDffiinniittiioonnss

  Les dfinitions pour les bornes de la plupart des types standards
  sont :



  G_MINFLOAT
  G_MAXFLOAT
  G_MINDOUBLE
  G_MAXDOUBLE
  G_MINSHORT
  G_MAXSHORT
  G_MININT
  G_MAXINT
  G_MINLONG
  G_MAXLONG



  Voici aussi les redfinitions de types. Celles qui ne sont pas spcifies
  sont configures dynamiquement selon l'architecture. vitez surtout de
  compter sur la taille d'un pointeur si vous voulez un programme
  portable ! Un pointeur sur un Alpha fait 8 octets, mais il en fait 4
  sur un Intel.



       char   gchar;
       short  gshort;
       long   glong;
       int    gint;
       char   gboolean;

       unsigned char   guchar;
       unsigned short  gushort;
       unsigned long   gulong;
       unsigned int    guint;

       float   gfloat;
       double  gdouble;
       long double gldouble;

       void* gpointer;

       gint8
       guint8
       gint16
       guint16
       gint32
       guint32



  1177..22..  LLiisstteess ddoouubblleemmeenntt cchhaanneess

  Les fonctions suivantes servent  crer, grer et dtruire des listes
  doublement chanes. Je suppose que vous savez ce qu'est une liste chane
  car leur explication n'entre pas dans le cadre de ce document. Bien
  sr, il n'y a pas besoin de les connatre pour une utilisation gnrale de
  GTK, mais c'est bien de savoir comment elles fonctionnent.



  GList* g_list_alloc       (void);

  void   g_list_free        (GList     *list);

  void   g_list_free_1      (GList     *list);

  GList* g_list_append      (GList     *list,
                             gpointer   data);

  GList* g_list_prepend     (GList     *list,
                             gpointer   data);

  GList* g_list_insert      (GList     *list,
                             gpointer   data,
                             gint       position);

  GList* g_list_remove      (GList     *list,
                             gpointer   data);

  GList* g_list_remove_link (GList     *list,
                             GList     *link);

  GList* g_list_reverse     (GList     *list);

  GList* g_list_nth         (GList     *list,
                             gint       n);

  GList* g_list_find        (GList     *list,
                             gpointer   data);

  GList* g_list_last        (GList     *list);

  GList* g_list_first       (GList     *list);

  gint   g_list_length      (GList     *list);

  void   g_list_foreach     (GList     *list,
                             GFunc      func,
                             gpointer   user_data);



  1177..33..  LLiisstteess ssiimmpplleemmeenntt cchhaanneess

  La plupart des fonctions pour les listes simplement chanes ci-dessous
  sont identiques  celles vues plus haut. Voici une liste complte :



  GSList* g_slist_alloc       (void);

  void    g_slist_free        (GSList   *list);

  void    g_slist_free_1      (GSList   *list);

  GSList* g_slist_append      (GSList   *list,
                               gpointer  data);

  GSList* g_slist_prepend     (GSList   *list,
                               gpointer  data);

  GSList* g_slist_insert      (GSList   *list,
                               gpointer  data,
                               gint      position);

  GSList* g_slist_remove      (GSList   *list,
                               gpointer  data);

  GSList* g_slist_remove_link (GSList   *list,
                               GSList   *link);

  GSList* g_slist_reverse     (GSList   *list);

  GSList* g_slist_nth         (GSList   *list,
                               gint      n);

  GSList* g_slist_find        (GSList   *list,
                               gpointer  data);

  GSList* g_slist_last        (GSList   *list);

  gint    g_slist_length      (GSList   *list);

  void    g_slist_foreach     (GSList   *list,
                               GFunc     func,
                               gpointer  user_data);



  1177..44..  GGeessttiioonn ddee llaa mmmmooiirree



       gpointer g_malloc      (gulong    size);



  Remplace _m_a_l_l_o_c_(_).  On n'a pas besoin de vrifier la valeur de retour
  car cela est fait pour nous dans cette fonction.



       gpointer g_malloc0     (gulong    size);



  Identique  la prcdente, mais initialise la mmoire  zro avant de
  retourner un pointeur vers la zone rserve.


       gpointer g_realloc     (gpointer  mem,
                               gulong    size);



  Ralloue _s_i_z_e octets de mmoire  partir de _m_e_m. videmment, la mmoire
  doit avoir t alloue auparavant.



       void     g_free        (gpointer  mem);



  Libre la mmoire. Facile.



       void     g_mem_profile (void);



  Produit un profil de la mmoire utilise, mais requiert l'ajout de
  _#_d_e_f_i_n_e _M_E_M___P_R_O_F_I_L_E au dbut de _g_l_i_b_/_g_m_e_m_._c, de refaire un _m_a_k_e et un
  _m_a_k_e _i_n_s_t_a_l_l.



       void     g_mem_check   (gpointer  mem);



  Vrifie qu'un emplacement mmoire est valide. Ncessite que l'on ajoute
  _#_d_e_f_i_n_e _M_E_M___C_H_E_C_K au dbut de _g_m_e_m_._c que l'on refasse un _m_a_k_e et un
  _m_a_k_e _i_n_s_t_a_l_l.


  1177..55..  TTiimmeerrss

  Fonctions des timers...



       GTimer* g_timer_new     (void);

       void    g_timer_destroy (GTimer  *timer);

       void    g_timer_start   (GTimer  *timer);

       void    g_timer_stop    (GTimer  *timer);

       void    g_timer_reset   (GTimer  *timer);

       gdouble g_timer_elapsed (GTimer  *timer,
                                gulong  *microseconds);



  1177..66..  GGeessttiioonn ddeess cchhaanneess

  Un ensemble complet de fonction de gestion des chanes. Elles semblent
  toutes trs intressantes et sont srement meilleures,  bien des gards,
  que les fonctions C standards, mais elle ncessitent de la
  documentation.



       GString* g_string_new       (gchar   *init);
       void     g_string_free      (GString *string,
                                    gint     free_segment);

       GString* g_string_assign    (GString *lval,
                                    gchar   *rval);

       GString* g_string_truncate  (GString *string,
                                    gint     len);

       GString* g_string_append    (GString *string,
                                    gchar   *val);

       GString* g_string_append_c  (GString *string,
                                    gchar    c);

       GString* g_string_prepend   (GString *string,
                                    gchar   *val);

       GString* g_string_prepend_c (GString *string,
                                    gchar    c);

       void     g_string_sprintf   (GString *string,
                                    gchar   *fmt,
                                    ...);

       void     g_string_sprintfa  (GString *string,
                                    gchar   *fmt,
                                    ...);



  1177..77..  UUttiilliittaaiirreess eett ffoonnccttiioonnss dd''eerrrreeuurrss



       gchar* g_strdup    (const gchar *str);



  Remplace la fonction _s_t_r_d_u_p. Elle copie le contenu de la chane
  d'origine dans la mmoire venant d'tre alloue et retourne un pointeur
  sur cette zone.



       gchar* g_strerror  (gint errnum);



  Je recommande de l'utiliser pour tous les messages d'erreur. Elle est
  beaucoup plus propre et plus portable que _p_e_r_r_o_r_(_) ou les autres. La
  sortie est habituellement de la forme :



       nom du programme:fonction qui a chou:fichier ou autre descripteur:strerror



  Voici un exemple d'appel utilis dans le programme  Bonjour tout le
  monde !  :



       g_print("bonjour_monde:open:%s:%s\n", filename, g_strerror(errno));



       void g_error   (gchar *format, ...);



  Affiche un message d'erreur. Le format est comme _p_r_i_n_t_f, mais il
  ajoute  ** ERROR **:  au dbut du message et sort du programme.
  n'utiliser que pour les erreurs fatales.



       void g_warning (gchar *format, ...);



  Comme au dessus, mais ajoute  ** WARNING **: , et ne termine pas le
  programme.



       void g_message (gchar *format, ...);



  Affiche   message:  avant la chane passe en paramtre.



       void g_print   (gchar *format, ...);



  Remplace _p_r_i_n_t_f_(_).

  Enfin la dernire fonction :



       gchar* g_strsignal (gint signum);



  Affiche le nom du signal systme Unix correspondant au numro de signal.
  Utile pour les fonctions gnriques de gestion de signaux.

  Tout ce qui est ci-dessus est plus ou moins vol  _g_l_i_b_._h. Si quelqu'un
  s'occupe de documenter une fonction, qu'il m'envoit un courrier !



  1188..  FFiicchhiieerrss rrcc ddee GGTTKK

  GTK a sa propre mthode pour grer les configurations par dfaut des
  applications, en utilisant des fichiers rc. Ceux-ci peuvent tre
  utiliss pour configurer les couleurs de presque tous les widgets, et
  pour mettre des pixmaps sur le fond de certains widgets.


  1188..11..  FFoonnccttiioonnss ppoouurr lleess ffiicchhiieerrss rrcc

  Au dmarrage de votre application, ajoutez un appel  :


       void gtk_rc_parse (char *filename);



  en lui passant le nom de votre fichier rc. Ceci forcera GTK  analyser
  ce fichier et  utiliser les configurations de styles pour les types de
  widgets qui y sont dfinis.

  Si vous voulez avoir un ensemble particulier de widgets qui prenne le
  pas sur le style des autres, ou une autre division logique de widgets,
  utilisez un appel  :


       void gtk_widget_set_name (GtkWidget *widget,
                                 gchar *name);



  En lui passant comme premier paramtre le widget que vous avez cr, et
  le nom que vous voulez lui donner comme second paramtre. Ceci vous
  permettra de changer les attributs de ce widget par son nom dans le
  fichier rc.

  Si vous utilisez un appel comme celui-ci :



       button = gtk_button_new_with_label ("Bouton Spcial");
       gtk_widget_set_name (button, "bouton special");



  Ce bouton s'appelle  bouton special  et peut tre accd par son nom dans
  le fichier rc en tant que  bouton special.GtkButton .  [<--- Vrifiez
  !]

  Le fichier rc ci-dessous configure les proprits de la fentre
  principale et fait hriter tous les fils de celle-ci du style dcrit par
  bouton_principal . Le code utilis dans l'application est :



  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_widget_set_name (window, "fenetre principale");



  Et le style est dfini dans le fichier rc avec :



       widget "fenetre principale.*GtkButton*" style "bouton_principal"



  Ce qui configure tous les widgets _G_t_k_B_u_t_t_o_n de  fentre principale
  avec le style  bouton_principal  dfini dans le fichier rc.

  Ainsi que vous pouvez le voir, il s'agit d'un systme puissant et
  flexible. Utilisez votre imagination pour en tirer le meilleur.


  1188..22..  FFoorrmmaatt ddeess ffiicchhiieerrss rrcc ddee GGTTKK

  Le format du fichier GTK est illustr dans l'exemple suivant. Il s'agit
  du fichier _t_e_s_t_g_t_k_r_c de la distribution GTK mais j'ai ajout quelques
  commentaires et autres choses. Vous pouvez inclure cette explication
  votre application pour permettre  l'utilisateur de rgler finement son
  application.

  Il y a plusieurs directives pour changer les attributs d'un widget.

  +o  fg - configure la couleur de premier plan d'un widget.

  +o  bg - configure la couleur d'arrire plan d'un widget.

  +o  bg_pixmap - configure l'arrire plan d'un widget avec un pixmap.

  +o  font - configure la fonte  utiliser pour un widget.

  De plus, un widget peut se trouver dans diffrents tats et l'on peut
  configurer des couleurs, pixmaps et fontes diffrentes pour chacun
  d'eux. Ces tats sont :

  +o  NORMAL - L'tat normal d'un widget, sans la souris au dessus de lui,
     non press, etc.

  +o  PRELIGHT - Lorsque la souris se trouve au dessus du widget, les
     couleurs dfinies pour cet tat sont actives.

  +o  ACTIVE - Lorsque le widget est press ou cliqu, il devient actif et
     les attributs associs  cet tat sont appliqus.

  +o  INSENSITIVE - Quand un widget est configur pour tre insensible et
     qu'il ne peut tre activ, il prend ces attributs.

  +o  SELECTED - Lorsqu'un objet est choisi, il prend ces attributs.

  Lorsqu'on utilise les mots-cls  _f_g  et  _b_g  pour configurer les
  couleurs des widgets, le format est :



        fg[<STATE>] = { Red, Green, Blue }


  O STATE est l'un des tats vus plus haut (PRELIGHT, ACTIVE etc), et o
  _R_e_d, _G_r_e_e_n et _B_l_u_e sont des valeurs comprises entre 0 et 1.0. { 1.0,
  1.0, 1.0 } reprsente la couleur blanche.  Ces valeurs doivent tre de
  type rel ou elles seront considres comme valant 0, ainsi un simple  1
  ne marchera pas, il faut mettre  1.0 . Un  0  simple convient car ce
  n'est pas un problme s'il n'est pas reconnu puisque toutes les valeurs
  non reconnues sont mises  0.

  _b_g___p_i_x_m_a_p est trs similaire, sauf que les couleurs sont remplaces par
  un nom de fichier.

  _p_i_x_m_a_p___p_a_t_h est une liste de chemins spars par des  : . Ces chemins
  seront parcourus pour chaque pixmap que l'on spcifie.


  La directive _f_o_n_t est simplement :


       font = "<font name>"



  O la seule partie difficile est d'arriver  comprendre la chane
  contenant le nom de la fonte. L'utilisation de _x_f_o_n_t_s_e_l ou d'un autre
  utilitaire semblable peut aider.

   _w_i_d_g_e_t___c_l_a_s_s  configure le style d'une classe de widgets. Ces classes
  sont listes dans la section sur la hirarchie des widgets.

  La directive  _w_i_d_g_e_t  configure un ensemble spcifique de widgets selon
  un style donn, annulant tout style de configuration pour la classe de
  widget donne. Ces widgets sont enregistrs dans l'application en
  utilisant l'appel _g_t_k___w_i_d_g_e_t___s_e_t___n_a_m_e_(_). Ceci vous permet de spcifier
  les attributs d'un widget, widget par widget, au lieu de configurer
  les attributs d'une classe entire de widgets. Je vous demande
  instamment de documenter tous ces widgets spciaux pour que les
  utilisateurs puisse les adapter  leurs besoins.

  Lorsque le mot-cl  _p_a_r_e_n_t  est utilis comme attribut, le widget
  prendra les attributs de son parent dans l'application.

  Lorsqu'on dfinit un style, on peut assigner les attributs d'un style
  dj dfini  ce nouveau style.



       style "bouton_principal" = "button"
       {
         font = "-adobe-helvetica-medium-r-normal--*-100-*-*-*-*-*-*"
         bg[PRELIGHT] = { 0.75, 0, 0 }
       }



  Cet exemple prend le style "button" et cre un nouveau style
  "bouton_principal"en changeant simplement la fonte et la couleur de
  fond pour l'tat PRELIGHT.

  Bien sr, un bon nombre de ces attributs ne s'applique pas  tous les
  widgets. C'est une question de bon sens. Tout ce qui peut s'appliquer
  s'applique.



  1188..33..  EExxeemmppllee ddee ffiicchhiieerr rrcc



  # pixmap_path "<dir 1>:<dir 2>:<dir 3>:..."
  #
  pixmap_path "/usr/include/X11R6/pixmaps:/home/imain/pixmaps"
  #
  # style <name> [= <name>]
  # {
  #   <option>
  # }
  #
  # widget <widget_set> style <style_name>
  # widget_class <widget_class_set> style <style_name>


  # Voici une liste des tats possibles. Remarquez que certains ne s'appliquent
  # pas  certains widgets.
  #
  # NORMAL - L'tat normal d'un widget, sans la souris au dessus de lui,
  # non press, etc.
  #
  # PRELIGHT - Lorsque la souris se trouve au dessus du widget, les couleurs
  # dfinies pour cet tat sont actives.
  #
  # ACTIVE - Lorsque le widget est press ou cliqu, il devient actif et les
  # attributs associs  cet tat sont appliqus.
  #
  # INSENSITIVE - Quand un widget est configur pour tre insensible, et qu'il
  # ne peut tre activ, il prend ces attributs.
  #
  # SELECTED - Lorsqu'un objet est choisi, il prend ces attributs.
  #
  # Avec ces tats, on peut configurer les attributs des widgets dans chacun
  # de ces tats en utilisant les directives suivantes.
  #
  # fg - configure la couleur de premier plan d'un widget.
  # bg - configure la couleur d'arrire plan d'un widget.
  # bg_pixmap - configure l'arrire plan d'un widget avec un pixmap.
  # font - configure la fonte  utiliser pour un widget.

  # Configuration d'un style appel "button". Le nom n'est pas important
  # car il est assign aux widgets rels  la fin du fichier.

  style "window"
  {
    #Configure l'espace autour de la fentre avec le pixmap spcifi.
    #bg_pixmap[<STATE>] = "<pixmap filename>"
    bg_pixmap[NORMAL] = "warning.xpm"
  }

  style "scale"
  {
    #Configure la couleur de premier plan (celle de la fonte)  rouge
    #lorsqu'on est dans l'tat "NORMAL".

    fg[NORMAL] = { 1.0, 0, 0 }

    #Configure le pixmap d'arrire plan de ce widget  celui de son parent.
    bg_pixmap[NORMAL] = "<parent>"
  }

  style "button"
  {
    # Voici tous les tats possibles pour un bouton. Le seul qui ne peut
    # s'appliquer est l'tat SELECTED.

    fg[PRELIGHT] = { 0, 1.0, 1.0 }
    bg[PRELIGHT] = { 0, 0, 1.0 }
    bg[ACTIVE] = { 1.0, 0, 0 }
    fg[ACTIVE] = { 0, 1.0, 0 }
    bg[NORMAL] = { 1.0, 1.0, 0 }
    fg[NORMAL] = { .99, 0, .99 }
    bg[INSENSITIVE] = { 1.0, 1.0, 1.0 }
    fg[INSENSITIVE] = { 1.0, 0, 1.0 }
  }

  # Dans cet exemple, on hrite des attributs du style "button" puis on
  # crase la fonte et la couleur de fond pour crer un nouveau style
  # "main_button".

  style "main_button" = "button"
  {
    font = "-adobe-helvetica-medium-r-normal--*-100-*-*-*-*-*-*"
    bg[PRELIGHT] = { 0.75, 0, 0 }
  }

  style "toggle_button" = "button"
  {
    fg[NORMAL] = { 1.0, 0, 0 }
    fg[ACTIVE] = { 1.0, 0, 0 }

    # Configure le pixmap de fond du toggle_button  celui de son widget
    # parent (comme dfini dans l'application).
    bg_pixmap[NORMAL] = "<parent>"
  }

  style "text"
  {
    bg_pixmap[NORMAL] = "marble.xpm"
    fg[NORMAL] = { 1.0, 1.0, 1.0 }
  }

  style "ruler"
  {
    font = "-adobe-helvetica-medium-r-normal--*-80-*-*-*-*-*-*"
  }

  # pixmap_path "~/.pixmaps"

  # Configuration des types de widget pour utiliser les styles dfinis
  # plus haut.
  # Les types de widget sont lists dans la hirarchie des classes, mais
  # peut probablement tre liste dans ce document pour que l'utilisateur
  # puisse s'y rfrer.

  widget_class "GtkWindow" style "window"
  widget_class "GtkDialog" style "window"
  widget_class "GtkFileSelection" style "window"
  widget_class "*Gtk*Scale" style "scale"
  widget_class "*GtkCheckButton*" style "toggle_button"
  widget_class "*GtkRadioButton*" style "toggle_button"
  widget_class "*GtkButton*" style "button"
  widget_class "*Ruler" style "ruler"
  widget_class "*GtkText" style "text"

  # Configure tous les boutons fils de la "main window" avec le style
  # main_button. Ceci doit tre document pour en tirer profit.
  widget "main window.*GtkButton*" style "main_button"



  1199..  ccrriittuurree ddee vvooss pprroopprreess wwiiddggeettss


  1199..11..  VVuuee dd''eennsseemmbbllee

  Bien que la distribution GTK fournisse de nombreux types de widgets
  qui devraient couvrir la plupart des besoins de base, il peut arriver
  un moment o vous aurez besoin de crer votre propre type de widget.
  Comme GTK utilise l'hritage de widget de faon intensive et qu'il y a
  dj un widget ressemblant  celui que vous voulez, il est souvent
  possible de crer un nouveau type de widget en seulement quelques
  lignes de code. Mais, avant de travailler sur un nouveau widget, il
  faut vrifier d'abord que quelqu'un ne l'a pas dj crit. Ceci viter la
  duplication des efforts et maintient au minimum le nombre de widgets,
  ce qui permet de garder la cohrence du code et de l'interface des
  diffrentes applications. Un effet de bord est que, lorsque l'on a cr
  un nouveau widget, il faut l'annoncer afin que les autres puissent en
  bnficier. Le meilleur endroit pour faire cela est, sans doute, la gtk-
  list.


  1199..22..  AAnnaattoommiiee dd''uunn wwiiddggeett

  Afin de crer un nouveau widget, il importe de comprendre comment
  fonctionnent les objets GTK. Cette section ne se veut tre qu'un rapide
  survol. Consultez la documentation de rfrence pour plus de dtails.


  Les widgets sont implants selon une mthode oriente objet. Cependant,
  ils sont crits en C standard. Ceci amliore beaucoup la portabilit et
  la stabilit, par contre cela signifie que celui qui crit des widgets
  doit faire attention  certains dtails d'implantation. Les informations
  communes  toutes les instances d'une classe de widget (tous les
  widgets boutons, par exemple) sont stockes dans la _s_t_r_u_c_t_u_r_e _d_e _l_a
  _c_l_a_s_s_e.  Il n'y en a qu'une copie dans laquelle sont stockes les
  informations sur les signaux de la classe (fonctionnement identique
  aux fonctions virtuelles en C). Pour permettre l'hritage, le premier
  champ de la structure de classe doit tre une copie de la structure de
  classe du pre. La dclaration de la structure de classe de _G_t_k_B_u_t_t_o_n
  ressemble  ceci :



       struct _GtkButtonClass
       {
         GtkContainerClass parent_class;

         void (* pressed)  (GtkButton *button);
         void (* released) (GtkButton *button);
         void (* clicked)  (GtkButton *button);
         void (* enter)    (GtkButton *button);
         void (* leave)    (GtkButton *button);
       };



  Lorsqu'un bouton est trait comme un container (par exemple, lorsqu'il
  change de taille), sa structure de classe peut tre convertie en
  _G_t_k_C_o_n_t_a_i_n_e_r_C_l_a_s_s et les champs adquats utiliss pour grer les signaux.


  Il y a aussi une structure pour chaque widget cr sur une base
  d'instance. Cette structure a des champs pour stocker les informations
  qui sont diffrentes pour chaque instance du widget. Nous l'appelerons
  _s_t_r_u_c_t_u_r_e _d_'_o_b_j_e_t. Pour la classe _B_u_t_t_o_n, elle ressemble  :



       struct _GtkButton
       {
         GtkContainer container;

         GtkWidget *child;

         guint in_button : 1;
         guint button_down : 1;
       };



  Notez que, comme pour la structure de classe, le premier champ est la
  structure d'objet de la classe parente, cette structure peut donc tre
  convertie dans la structure d'objet de la classe parente si besoin
  est.


  1199..33..  CCrraattiioonn dd''uunn wwiiddggeett ccoommppooss

  1199..33..11..  IInnttrroodduuccttiioonn

  Un type de widget qui peut tre intressant  crer est un widget qui est
  simplement un agrgat d'autres widgets GTK. Ce type de widget ne fait
  rien qui ne pourrait tre fait sans crer de nouveaux widgets, mais
  offre une mthode pratique pour empaqueter les lments d'une interface
  utilisateur afin de la rutiliser facilement. Les widgets _F_i_l_e_S_e_l_e_c_t_i_o_n
  et _C_o_l_o_r_S_e_l_e_c_t_i_o_n de la distribution standard sont des exemples de ce
  type de widget.


  L'exemple de widget que nous allons crer dans cette section crera un
  widget _T_i_c_t_a_c_t_o_e, un tableau de 3x3 boutons commutateurs qui dclenche
  un signal lorsque tous les boutons d'une ligne, d'une colonne, ou
  d'une diagonale sont presss.


  1199..33..22..  CChhooiixx dd''uunnee ccllaassssee ppaarreenntt

  La classe parent d'un widget compos est, typiquement, la classe
  container contenant tous les lments du widget compos. Par exemple, la
  classe parent du widget _F_i_l_e_S_e_l_e_c_t_i_o_n est la classe _D_i_a_l_o_g. Comme nos
  boutons seront mis sous la forme d'un tableau, il semble naturel
  d'utiliser la classe _G_t_k_T_a_b_l_e comme parent. Malheureusement, cela ne
  peut marcher. La cration d'un widget est divise en deux fonctions --
  _W_I_D_G_E_T_N_A_M_E___n_e_w_(_) que l'utilisateur appelle, et _W_I_D_G_E_T_N_A_M_E___i_n_i_t_(_) qui
  ralise le travail d'initialisation du widget indpendamment des
  paramtre passs  la fonction _new(). Les widgets fils n'appellent que
  la fonction ___i_n_i_t de leur widget parent. Mais cette division du
  travail ne fonctionne pas bien avec les tableaux qui, lorsqu'ils sont
  crs, ont besoin de connatre leue nombre de lignes et de colonnes. Sauf
  dupliquer la plupart des fonctionnalits de _g_t_k___t_a_b_l_e___n_e_w_(_) dans notre
  widget _T_i_c_t_a_c_t_o_e, nous ferions mieux d'viter de le driver de _G_t_k_T_a_b_l_e.
  Pour cette raison, nous la driverons plutt de _G_t_k_V_B_o_x et nous
  placerons notre table dans la VBox.



  1199..33..33..  TThhee hheeaaddeerr ffiillee

  Chaque classe de widget possde un fichier en-tte qui dclare les
  structures d'objet et de classe pour ce widget, en plus de fonctions
  publiques. Quelques caractristiques mritent d'tre indiques. Afin
  d'viter des dfinitions multiples, on enveloppe le fichier en-tte
  avec :



       #ifndef __TICTACTOE_H__
       #define __TICTACTOE_H__
       .
       .
       .
       #endif /* __TICTACTOE_H__ */



  Et, pour faire plaisir aux programmes C++ qui inclueront ce fichier,
  on l'enveloppe aussi dans :



       #ifdef __cplusplus
       extern "C" {
       #endif /* __cplusplus */
       .
       .
       .
       #ifdef __cplusplus
       }
       #endif /* __cplusplus */



  En plus des fonctions et structures, nous dclarons trois macros
  standard, TICTACTOE(obj), TICTACTOE_CLASS(class), et
  IS_TICTACTOE(obj), qui, respectivement, convertissent un pointeur en
  un pointeur vers une structure d'objet ou de classe, et vrifient si un
  objet est un widget Tictactoe.


  Voici le fichier en-tte complet :



  #ifndef __TICTACTOE_H__
  #define __TICTACTOE_H__

  #include <gdk/gdk.h>
  #include <gtk/gtkvbox.h>

  #ifdef __cplusplus
  extern "C" {
  #endif /* __cplusplus */

  #define TICTACTOE(obj)          GTK_CHECK_CAST (obj, tictactoe_get_type (), Tictactoe)
  #define TICTACTOE_CLASS(klass)  GTK_CHECK_CLASS_CAST (klass, tictactoe_get_type (), TictactoeClass)
  #define IS_TICTACTOE(obj)       GTK_CHECK_TYPE (obj, tictactoe_get_type ())


  typedef struct _Tictactoe       Tictactoe;
  typedef struct _TictactoeClass  TictactoeClass;

  struct _Tictactoe
  {
    GtkVBox vbox;

    GtkWidget *buttons[3][3];
  };

  struct _TictactoeClass
  {
    GtkVBoxClass parent_class;

    void (* tictactoe) (Tictactoe *ttt);
  };

  guint          tictactoe_get_type        (void);
  GtkWidget*     tictactoe_new             (void);
  void           tictactoe_clear           (Tictactoe *ttt);

  #ifdef __cplusplus
  }
  #endif /* __cplusplus */

  #endif /* __TICTACTOE_H__ */



  1199..33..44..  LLaa ffoonnccttiioonn  __ggeett__ttyyppee(())

  Continuons maintenant avec l'implantation de notre widget. La fonction
  centrale pour chaque widget est _W_I_D_G_E_T_N_A_M_E___g_e_t___t_y_p_e_(_). Cette fonction,
  lorsqu'elle est appele pour la premire fois, informe le GTK de la
  classe et rcupre un ID permettant d'identifier celle-ci de faon
  unique. Lors des appels suivants, elle ne fait que retourner cet ID.



  guint
  tictactoe_get_type ()
  {
    static guint ttt_type = 0;

    if (!ttt_type)
      {
        GtkTypeInfo ttt_info =
        {
          "Tictactoe",
          sizeof (Tictactoe),
          sizeof (TictactoeClass),
          (GtkClassInitFunc) tictactoe_class_init,
          (GtkObjectInitFunc) tictactoe_init,
          (GtkArgFunc) NULL,
        };

        ttt_type = gtk_type_unique (gtk_vbox_get_type (), &ttt_info);
      }

    return ttt_type;
  }



  La structure _G_t_k_T_y_p_e_I_n_f_o est dfinie de la faon suivante :



       struct _GtkTypeInfo
       {
         gchar *type_name;
         guint object_size;
         guint class_size;
         GtkClassInitFunc class_init_func;
         GtkObjectInitFunc object_init_func;
         GtkArgFunc arg_func;
       };



  Les champs de cette structure s'expliquent d'eux-mmes. Nous ignorerons
  le champ _a_r_g___f_u_n_c ici : il a un rle important permettant aux options
  des widgets d'tre correctement initialises partir des langages
  interprts, mais cette fonctionnalit est encore trs peu implante.
  Lorsque GTK dispose d'une copie correctement remplie de cette
  structure, il sait comment crer des objets d'un type particulier de
  widget.


  1199..33..55..  LLaa ffoonnccttiioonn __ccllaassss__iinniitt(())

  La fonction _W_I_D_G_E_T_N_A_M_E___c_l_a_s_s___i_n_i_t_(_) initialise les champs de la
  structure de classe du widget et configure tous les signaux de cette
  classe. Pour notre widget Tictactoe, cet appel est :



  enum {
    TICTACTOE_SIGNAL,
    LAST_SIGNAL
  };

  static gint tictactoe_signals[LAST_SIGNAL] = { 0 };

  static void
  tictactoe_class_init (TictactoeClass *class)
  {
    GtkObjectClass *object_class;

    object_class = (GtkObjectClass*) class;

    tictactoe_signals[TICTACTOE_SIGNAL] = gtk_signal_new ("tictactoe",
                                           GTK_RUN_FIRST,
                                           object_class->type,
                                           GTK_SIGNAL_OFFSET (TictactoeClass, tictactoe),
                                           gtk_signal_default_marshaller, GTK_ARG_NONE, 0);


    gtk_object_class_add_signals (object_class, tictactoe_signals, LAST_SIGNAL);

    class->tictactoe = NULL;
  }



  Notre widget n'a qu'un signal : "tictactoe", invoqu lorsqu'une ligne,
  une colonne ou une diagonale est compltement remplie. Tous les widgets
  composs n'ont pas besoin de signaux. Si vous lisez ceci pour la
  premire fois, vous pouvez passer directement  la section suivante car
  les choses vont se compliquer un peu

  La fonction :



       gint   gtk_signal_new                     (gchar               *name,
                                                  GtkSignalRunType     run_type,
                                                  gint                 object_type,
                                                  gint                 function_offset,
                                                  GtkSignalMarshaller  marshaller,
                                                  GtkArgType           return_val,
                                                  gint                 nparams,
                                                  ...);



  cre un nouveau signal. Les paramtres sont :


  +o  _n_a_m_e : Le nom du signal signal.

  +o  _r_u_n___t_y_p_e : Indique si le gestionnaire par dfaut doit tre lanc avant
     ou aprs le gestionnaire de l'utilisateur. Le plus souvent, ce sera
     GTK_RUN_FIRST, ou GTK_RUN_LAST, bien qu'il y ait d'autres
     possibilits.

  +o  _o_b_j_e_c_t___t_y_p_e : L'ID de l'objet auquel s'applique ce signal (il
     s'appliquera aussi au descendants de l'objet).


  +o  _f_u_n_c_t_i_o_n___o_f_f_s_e_t : L'offset d'un pointeur vers le gestionnaire par
     dfaut dans la structure de classe.

  +o  _m_a_r_s_h_a_l_l_e_r : Fonction utilise pour invoquer le gestionnaire de
     signal. Pour les gestionnaires de signaux n'ayant pas d'autres
     paramtres que l'objet metteur et les donnes utilisateur, on peut
     utiliser la fonction prdfinie _g_t_k___s_i_g_n_a_l___d_e_f_a_u_l_t___m_a_r_s_h_a_l_l_e_r_(_).

  +o  _r_e_t_u_r_n___v_a_l : Type de la valeur retourne.

  +o  _n_p_a_r_a_m_s : Nombre de paramtres du gestionnaire de signal (autres que
     les deux par dfaut mentionns plus haut).

  +o  _._._. : Types des paramtres.

  Lorsque l'on spcifie les types, on utilise l'numration _G_t_k_A_r_g_T_y_p_e :



       typedef enum
       {
         GTK_ARG_INVALID,
         GTK_ARG_NONE,
         GTK_ARG_CHAR,
         GTK_ARG_SHORT,
         GTK_ARG_INT,
         GTK_ARG_LONG,
         GTK_ARG_POINTER,
         GTK_ARG_OBJECT,
         GTK_ARG_FUNCTION,
         GTK_ARG_SIGNAL
       } GtkArgType;



  _g_t_k___s_i_g_n_a_l___n_e_w_(_) retourne un identificateur entier pour le signal, que
  l'on stocke dans le tableau _t_i_c_t_a_c_t_o_e___s_i_g_n_a_l_s, indic par une numration
  (conventionnellement, les lments de l'numration sont le nom du signal,
  en majuscules, mais, ici, il y aurait un conflit avec la macro
  TICTACTOE(), nous l'appellerons donc TICTACTOE_SIGNAL  la place.

  Aprs avoir cr nos signaux, nous devons demander  GTK d'associer ceux-
  ci  la classe Tictactoe. Ceci est fait en appelant
  _g_t_k___o_b_j_e_c_t___c_l_a_s_s___a_d_d___s_i_g_n_a_l_s_(_). Puis nous configurons le pointeur qui
  pointe sur le gestionnaire par dfaut du signal "tictactoe" NULL, pour
  indiquer qu'il n'y a pas d'action par dfaut.


  1199..33..66..  LLaa ffoonnccttiioonn __iinniitt(())


  Chaque classe de widget a aussi besoin d'une fonction pour initialiser
  la structure d'objet. Habituellement, cette fonction a le rle, plutt
  limit, d'initialiser les champs de la structure avec des valeurs par
  dfaut. Cependant, pour les widgets composs, cette fonction cre aussi
  les widgets composants.



  static void
  tictactoe_init (Tictactoe *ttt)
  {
    GtkWidget *table;
    gint i,j;

    table = gtk_table_new (3, 3, TRUE);
    gtk_container_add (GTK_CONTAINER(ttt), table);
    gtk_widget_show (table);

    for (i=0;i<3; i++)
      for (j=0;j<3; j++)
        {
          ttt->buttons[i][j] = gtk_toggle_button_new ();
          gtk_table_attach_defaults (GTK_TABLE(table), ttt->buttons[i][j],
                                     i, i+1, j, j+1);
          gtk_signal_connect (GTK_OBJECT (ttt->buttons[i][j]), "toggled",
                              GTK_SIGNAL_FUNC (tictactoe_toggle), ttt);
          gtk_widget_set_usize (ttt->buttons[i][j], 20, 20);
          gtk_widget_show (ttt->buttons[i][j]);
        }
  }



  1199..33..77..  EEtt llee rreessttee......


  Il reste une fonction que chaque widget (sauf pour les types widget de
  base, comme _G_t_k_B_i_n, qui ne peuvent tre instancis)  besoin d'avoir --
  celle que l'utilisateur appelle pour crer un objet de ce type. Elle
  est conventionnellement appele _W_I_D_G_E_T_N_A_M_E___n_e_w_(_). Pour certains
  widgets, par pour ceux de Tictactoe, cette fonction prend des
  paramtres et ralise certaines initialisations dpendantes des
  paramtres. Les deux autres fonctions sont spcifiques au widget
  Tictactoe.


  _t_i_c_t_a_c_t_o_e___c_l_e_a_r_(_) est une fonction publique qui remet tous les boutons
  du widget en position relche. Notez l'utilisation de
  _g_t_k___s_i_g_n_a_l___h_a_n_d_l_e_r___b_l_o_c_k___b_y___d_a_t_a_(_) pour empcher notre gestionnaire de
  signaux des boutons commutateurs d'tre dclench sans besoin.


  _t_i_c_t_a_c_t_o_e___t_o_g_g_l_e_(_) est le gestionnaire de signal invoqu lorsqu'on
  clique sur un bouton. Il vrifie s'il y a des combinaisons gagnantes
  concernant le bouton qui vient d'tre commut et, si c'est le cas, met
  le signal "tictactoe".



  GtkWidget*
  tictactoe_new ()
  {
    return GTK_WIDGET ( gtk_type_new (tictactoe_get_type ()));
  }

  void
  tictactoe_clear (Tictactoe *ttt)
  {
    int i,j;

    for (i=0;i<3;i++)
      for (j=0;j<3;j++)
        {
          gtk_signal_handler_block_by_data (GTK_OBJECT(ttt->buttons[i][j]), ttt);
          gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (ttt->buttons[i][j]),
                                       FALSE);
          gtk_signal_handler_unblock_by_data (GTK_OBJECT(ttt->buttons[i][j]), ttt);
        }
  }

  static void
  tictactoe_toggle (GtkWidget *widget, Tictactoe *ttt)
  {
    int i,k;

    static int rwins[8][3] = { { 0, 0, 0 }, { 1, 1, 1 }, { 2, 2, 2 },
                               { 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 },
                               { 0, 1, 2 }, { 0, 1, 2 } };
    static int cwins[8][3] = { { 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 },
                               { 0, 0, 0 }, { 1, 1, 1 }, { 2, 2, 2 },
                               { 0, 1, 2 }, { 2, 1, 0 } };

    int success, found;

    for (k=0; k<8; k++)
      {
        success = TRUE;
        found = FALSE;

        for (i=0;i<3;i++)
          {
            success = success &&
              GTK_TOGGLE_BUTTON(ttt->buttons[rwins[k][i]][cwins[k][i]])->active;
            found = found ||
              ttt->buttons[rwins[k][i]][cwins[k][i]] == widget;
          }

        if (success && found)
          {
            gtk_signal_emit (GTK_OBJECT (ttt),
                             tictactoe_signals[TICTACTOE_SIGNAL]);
            break;
          }
      }
  }



  Enfin, un exemple de programme utilisant notre widget Tictactoe



  #include <gtk/gtk.h>
  #include "tictactoe.h"

  /* Invoqu lorsqu'une ligne, une colonne ou une diagonale est complte */

  void win (GtkWidget *widget, gpointer data)
  {
    g_print ("Ouais !\n");
    tictactoe_clear (TICTACTOE (widget));
  }

  int main (int argc, char *argv[])
  {
    GtkWidget *window;
    GtkWidget *ttt;

    gtk_init (&argc, &argv);

    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

    gtk_window_set_title (GTK_WINDOW (window), "Aspect Frame");

    gtk_signal_connect (GTK_OBJECT (window), "destroy",
                        GTK_SIGNAL_FUNC (gtk_exit), NULL);

    gtk_container_border_width (GTK_CONTAINER (window), 10);

    /* Cration d'un widget Tictactoe */
    ttt = tictactoe_new ();
    gtk_container_add (GTK_CONTAINER (window), ttt);
    gtk_widget_show (ttt);

    /* On lui attache le signal "tictactoe" */
    gtk_signal_connect (GTK_OBJECT (ttt), "tictactoe",
                        GTK_SIGNAL_FUNC (win), NULL);

    gtk_widget_show (window);

    gtk_main ();

    return 0;
  }



  1199..44..  CCrraattiioonn dd''uunn wwiiddggeett  ppaarrttiirr ddee zzrroo

  1199..44..11..  IInnttrroodduuccttiioonn


  Dans cette section, nous en apprendrons plus sur la faon dont les
  widgets s'affichent eux-mmes  l'cran et comment ils interagissent avec
  les vnements. Comme exemple, nous crerons un widget d'appel
  tlephonique interactif avec un pointeur que l'utilisateur pourra
  dplacer pour initialiser la valeur.


  1199..44..22..  AAffffiicchheerr uunn wwiiddggeett  ll''ccrraann

  Il y a plusieurs tapes mises en jeu lors de l'affichage. Lorsque le
  widget est cr par l'appel _W_I_D_G_E_T_N_A_M_E___n_e_w_(_), plusieurs autres fonctions
  supplmentaires sont requises.


  +o  _W_I_D_G_E_T_N_A_M_E___r_e_a_l_i_z_e_(_) s'occupe de crer une fentre X pour le widget,
     s'il en a une.

  +o  _W_I_D_G_E_T_N_A_M_E___m_a_p_(_) est invoque aprs l'appel de _g_t_k___w_i_d_g_e_t___s_h_o_w_(_).
     Elle s'assure que le widget est bien trac  l'cran (_m_a_p_p). Dans le
     cas d'une classe container, elle doit aussi appeler des fonctions
     _m_a_p_(_)> pour chaque widget fils.

  +o  _W_I_D_G_E_T_N_A_M_E___d_r_a_w_(_) est invoque lorsque _g_t_k___w_i_d_g_e_t___d_r_a_w_(_) est appel
     pour le widget ou l'un de ces anctres. Elle ralise les vritables
     appels aux fonctions de dessin pour tracer le widget  l'cran. Pour
     les widgets containers, cette fonction doit appeler
     _g_t_k___w_i_d_g_e_t___d_r_a_w_(_) pour ses widgets fils.

  +o  _W_I_D_G_E_T_N_A_M_E___e_x_p_o_s_e_(_) est un gestionnaire pour les vnements
     d'exposition du widget. Il ralise les appels ncessaires aux
     fonctions de dessin pour tracer la partie expose  l'cran. Pour les
     widgets containers, cette fonction doit gnrer les vnements
     d'exposition pour ses widgets enfants n'ayant pas leurs propres
     fentres (s'ils ont leurs propres fentres, X gnrera les vnements
     d'exposition ncessaires).


  Vous avez pu noter que les deux dernires fonctions sont assez
  similaires -- chacune se charge de tracer le widget  l'cran. En fait,
  de nombreux types de widgets ne se proccupent pas vraiment de la
  diffrence entre les deux. La fonction _d_r_a_w_(_) par dfaut de la classe
  widget gnre simplement un vnement d'exposition synthtique pour la zone
  redessiner. Cependant, certains types de widgets peuvent conomiser du
  travail en faisant la diffrence entre les deux fonctions. Par exemple,
  si un widget a plusieurs fentres X et puisque les vnements
  d'exposition identifient la fentre expose, il peut redessiner
  seulement la fentre concerne, ce qui n'est pas possible avec des
  appels  _d_r_a_w_(_).


  Les widgets container, mme s'ils ne se soucient pas eux-mmes de la
  diffrence, ne peuvent pas utiliser simplement la fonction _d_r_a_w_(_) car
  leurs widgets enfants tiennent compte de cette diffrence. Cependant,
  ce serait du gaspillage de dupliquer le code de trac pour les deux
  fonctions. Conventionnellement, de tels widgets possdent une fonction
  nomme _W_I_D_G_E_T_N_A_M_E___p_a_i_n_t_(_) qui ralise le vritable travail de trac du
  widget et qui est appele par les fonctions draw() et expose().


  Dans notre exemple, comme le widget d'appel n'est pas un widget
  container et n'a qu'une fentre, nous pouvons utiliser l'approche la
  plus simple : utiliser la fonction _d_r_a_w_(_) par dfaut et n'implanter que
  la fonction _e_x_p_o_s_e_(_).


  1199..44..33..  OOrriiggiinneess dduu wwiiddggeett DDiiaall

  Exactement comme les animaux terrestres ne sont que des variantes des
  premiers amphibiens qui ramprent hors de la boue, les widgets GTK sont
  des variantes d'autres widgets, dj crits. Ainsi, bien que cette
  section s'appelle  crer un widget  partir de zro , le widget Dial
  commence rellement avec le code source du widget Range. Celui-ci a t
  pris comme point de dpart car ce serait bien que notre Dial ait la mme
  interface que les widgets Scale qui ne sont que des descendants
  spcialiss du widget Range. Par consquent, bien que le code source soit
  prsent ci-dessous sous une forme acheve, cela n'implique pas qu'il a t
  crit _d_e_u_s _e_x _m_a_c_h_i_n_a. De plus, si vous ne savez pas comment
  fonctionnent les widgets Scale du point de vue du programmeur de
  l'application, il est prfrable de les tudier avant de continuer.

  1199..44..44..  LLeess bbaasseess

  Un petite partie de notre widget devrait ressembler au widget
  Tictactoe. Nous avons d'abord le fichier en-tte :



  /* GTK - The GIMP Toolkit
   * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
   *
   * This library is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Library General Public
   * License as published by the Free Software Foundation; either
   * version 2 of the License, or (at your option) any later version.
   *
   * This library is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   * Library General Public License for more details.
   *
   * You should have received a copy of the GNU Library General Public
   * License along with this library; if not, write to the Free
   * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
   */

  #ifndef __GTK_DIAL_H__
  #define __GTK_DIAL_H__

  #include <gdk/gdk.h>
  #include <gtk/gtkadjustment.h>
  #include <gtk/gtkwidget.h>


  #ifdef __cplusplus
  extern "C" {
  #endif /* __cplusplus */


  #define GTK_DIAL(obj)          GTK_CHECK_CAST (obj, gtk_dial_get_type (), GtkDial)
  #define GTK_DIAL_CLASS(klass)  GTK_CHECK_CLASS_CAST (klass, gtk_dial_get_type (), GtkDialClass)
  #define GTK_IS_DIAL(obj)       GTK_CHECK_TYPE (obj, gtk_dial_get_type ())


  typedef struct _GtkDial        GtkDial;
  typedef struct _GtkDialClass   GtkDialClass;

  struct _GtkDial
  {
    GtkWidget widget;

    /* politique de mise  jour
       (GTK_UPDATE_[CONTINUOUS/DELAYED/DISCONTINUOUS]) */

    guint policy : 2;

    /* Le bouton qui est press, 0 si aucun */
    guint8 button;

    /* Dimensions des composants de dial */
    gint radius;
    gint pointer_width;

    /* ID du timer de mise  jour, 0 si aucun */
    guint32 timer;

    /* Angle courant*/
    gfloat angle;

    /* Anciennes valeurs d'ajustement stockes. On sait donc quand quelque
       chose change */
    gfloat old_value;
    gfloat old_lower;
    gfloat old_upper;
    /* L'objet ajustment qui stocke les donnes de cet appel */
    GtkAdjustment *adjustment;
  };

  struct _GtkDialClass
  {
    GtkWidgetClass parent_class;
  };


  GtkWidget*     gtk_dial_new                    (GtkAdjustment *adjustment);
  guint          gtk_dial_get_type               (void);
  GtkAdjustment* gtk_dial_get_adjustment         (GtkDial      *dial);
  void           gtk_dial_set_update_policy      (GtkDial      *dial,
                                                  GtkUpdateType  policy);

  void           gtk_dial_set_adjustment         (GtkDial      *dial,
                                                  GtkAdjustment *adjustment);
  #ifdef __cplusplus
  }
  #endif /* __cplusplus */


  #endif /* __GTK_DIAL_H__ */



  Comme il y a plus de choses  faire avec ce widget par rapport
  l'autre, nous avons plus de champs dans la structure de donnes, mais
  part a, les choses sont plutt similaires.



  Puis, aprs avoir inclus les fichiers en-tte et dclar quelques
  constantes, nous devons fournir quelques fonctions pour donner des
  informations sur le widget et pour l'initialiser :



  #include <math.h>
  #include <stdio.h>
  #include <gtk/gtkmain.h>
  #include <gtk/gtksignal.h>

  #include "gtkdial.h"

  #define SCROLL_DELAY_LENGTH  300
  #define DIAL_DEFAULT_SIZE 100

  /* Dclararations des prototypes */

  [ omis pour gagner de la place ]

  /* Donnes locales */

  static GtkWidgetClass *parent_class = NULL;

  guint
  gtk_dial_get_type ()
  {
    static guint dial_type = 0;

    if (!dial_type)
      {
        GtkTypeInfo dial_info =
        {
          "GtkDial",
          sizeof (GtkDial),
          sizeof (GtkDialClass),
          (GtkClassInitFunc) gtk_dial_class_init,
          (GtkObjectInitFunc) gtk_dial_init,
          (GtkArgFunc) NULL,
        };

        dial_type = gtk_type_unique (gtk_widget_get_type (), &dial_info);
      }

    return dial_type;
  }

  static void
  gtk_dial_class_init (GtkDialClass *class)
  {
    GtkObjectClass *object_class;
    GtkWidgetClass *widget_class;

    object_class = (GtkObjectClass*) class;
    widget_class = (GtkWidgetClass*) class;

    parent_class = gtk_type_class (gtk_widget_get_type ());

    object_class->destroy = gtk_dial_destroy;

    widget_class->realize = gtk_dial_realize;
    widget_class->expose_event = gtk_dial_expose;
    widget_class->size_request = gtk_dial_size_request;
    widget_class->size_allocate = gtk_dial_size_allocate;
    widget_class->button_press_event = gtk_dial_button_press;
    widget_class->button_release_event = gtk_dial_button_release;
    widget_class->motion_notify_event = gtk_dial_motion_notify;
  }

  static void
  gtk_dial_init (GtkDial *dial)
  {
    dial->button = 0;
    dial->policy = GTK_UPDATE_CONTINUOUS;
    dial->timer = 0;
    dial->radius = 0;
    dial->pointer_width = 0;
    dial->angle = 0.0;
    dial->old_value = 0.0;
    dial->old_lower = 0.0;
    dial->old_upper = 0.0;
    dial->adjustment = NULL;
  }

  GtkWidget*
  gtk_dial_new (GtkAdjustment *adjustment)
  {
    GtkDial *dial;

    dial = gtk_type_new (gtk_dial_get_type ());

    if (!adjustment)
      adjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);

    gtk_dial_set_adjustment (dial, adjustment);

    return GTK_WIDGET (dial);
  }

  static void
  gtk_dial_destroy (GtkObject *object)
  {
    GtkDial *dial;

    g_return_if_fail (object != NULL);
    g_return_if_fail (GTK_IS_DIAL (object));

    dial = GTK_DIAL (object);

    if (dial->adjustment)
      gtk_object_unref (GTK_OBJECT (dial->adjustment));

    if (GTK_OBJECT_CLASS (parent_class)->destroy)
      (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
  }



  Notez que cette fonction _i_n_i_t_(_) fait moins de choses que pour le
  widget Tictactoe car ce n'est pas un widget compos et que la fonction
  _n_e_w_(_) en fait plus car elle a maintenant un paramtre. Notez aussi que
  lorsque nous stockons un pointeur vers l'objet Adjustement, nous
  incrmentons son nombre de rfrences (et nous le dcrmentons lorsque nous
  ne l'utilisons plus) afin que GTK puisse savoir quand il pourra tre
  dtruit sans danger.


  Il y a aussi quelques fonctions pour manipuler les options du widget :



  GtkAdjustment*
  gtk_dial_get_adjustment (GtkDial *dial)
  {
    g_return_val_if_fail (dial != NULL, NULL);
    g_return_val_if_fail (GTK_IS_DIAL (dial), NULL);

    return dial->adjustment;
  }

  void
  gtk_dial_set_update_policy (GtkDial      *dial,
                               GtkUpdateType  policy)
  {
    g_return_if_fail (dial != NULL);
    g_return_if_fail (GTK_IS_DIAL (dial));

    dial->policy = policy;
  }

  void
  gtk_dial_set_adjustment (GtkDial      *dial,
                            GtkAdjustment *adjustment)
  {
    g_return_if_fail (dial != NULL);
    g_return_if_fail (GTK_IS_DIAL (dial));

    if (dial->adjustment)
      {
        gtk_signal_disconnect_by_data (GTK_OBJECT (dial->adjustment), (gpointer) dial);
        gtk_object_unref (GTK_OBJECT (dial->adjustment));
      }

    dial->adjustment = adjustment;
    gtk_object_ref (GTK_OBJECT (dial->adjustment));

    gtk_signal_connect (GTK_OBJECT (adjustment), "changed",
                        (GtkSignalFunc) gtk_dial_adjustment_changed,
                        (gpointer) dial);
    gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed",
                        (GtkSignalFunc) gtk_dial_adjustment_value_changed,
                        (gpointer) dial);

    dial->old_value = adjustment->value;
    dial->old_lower = adjustment->lower;
    dial->old_upper = adjustment->upper;

    gtk_dial_update (dial);
  }



  1199..44..55..  ggttkk__ddiiaall__rreeaalliizzee(())

  Nous arrivons maintenant  quelques nouveaux types de fonctions.
  D'abord, nous avons une fonction qui ralise la cration de la fentre X.
  Notez que l'on passe un masque  la fonction _g_d_k___w_i_n_d_o_w___n_e_w_(_) pour
  spcifier quels sont les champs de la structure GdkWindowAttr qui
  contiennent des donnes (les autres recevront des valeurs par dfaut).
  Notez aussi la faon dont est cr le masque d'vnement du widget. On
  appelle _g_t_k___w_i_d_g_e_t___g_e_t___e_v_e_n_t_s_(_) pour rcuprer le masque d'vnement que
  l'utilisateur a spcifi pour ce widget (avec _g_t_k___w_i_d_g_e_t___s_e_t___e_v_e_n_t_s_(_))
  et ajouter les vnements qui nous intressent.


  Aprs avoir cr la fentre, nous configurons son style et son fond et
  mettons un pointeur vers le widget dans le champ user de la GdkWindow.
  Cette dernire tape permet  GTK de distribuer les vnements pour cette
  fentre au widget correct.



       static void
       gtk_dial_realize (GtkWidget *widget)
       {
         GtkDial *dial;
         GdkWindowAttr attributes;
         gint attributes_mask;

         g_return_if_fail (widget != NULL);
         g_return_if_fail (GTK_IS_DIAL (widget));

         GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
         dial = GTK_DIAL (widget);

         attributes.x = widget->allocation.x;
         attributes.y = widget->allocation.y;
         attributes.width = widget->allocation.width;
         attributes.height = widget->allocation.height;
         attributes.wclass = GDK_INPUT_OUTPUT;
         attributes.window_type = GDK_WINDOW_CHILD;
         attributes.event_mask = gtk_widget_get_events (widget) |
           GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK |
           GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK |
           GDK_POINTER_MOTION_HINT_MASK;
         attributes.visual = gtk_widget_get_visual (widget);
         attributes.colormap = gtk_widget_get_colormap (widget);

         attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
         widget->window = gdk_window_new (widget->parent->window, &attributes, attributes_mask);

         widget->style = gtk_style_attach (widget->style, widget->window);

         gdk_window_set_user_data (widget->window, widget);

         gtk_style_set_background (widget->style, widget->window, GTK_STATE_ACTIVE);
       }



  1199..44..66..  NNggoottiiaattiioonn ddee llaa ttaaiillllee

  Avant le premier affichage de la fentre contenant un widget et  chaque
  fois que la forme de la fentre change, GTK demande  chaque widget fils
  la taille qu'il dsire avoir. Cette requte est gre par la fonction
  _g_t_k___d_i_a_l___s_i_z_e___r_e_q_u_e_s_t_(_). Comme notre widget n'est pas un widget
  container, et n'a pas de contraintes relles sur sa taille, nous ne
  faisons que retourner une valeur raisonnable par dfaut.



       static void
       gtk_dial_size_request (GtkWidget      *widget,
                              GtkRequisition *requisition)
       {
         requisition->width = DIAL_DEFAULT_SIZE;
         requisition->height = DIAL_DEFAULT_SIZE;
       }

  Lorsque tous les widgets on demand une taille idale, le forme de la
  fentre est calcule et chaque widget fils est averti de sa taille.
  Habituellement, ce sera autant que la taille requise, mais si, par
  exemple, l'utilisateur a redimensionn la fentre, cette taille peut
  occasionnellement tre plus petite que la taille requise. La
  notification de la taille est gre par la fonction
  _g_t_k___d_i_a_l___s_i_z_e___a_l_l_o_c_a_t_e_(_). Notez qu'en mme temps qu'elle calcule les
  tailles de certains composants pour une utilisation future, cette
  routine fait aussi le travail de base consistant  dplacer les widgets
  X Window dans leur nouvelles positions et tailles.



       static void
       gtk_dial_size_allocate (GtkWidget     *widget,
                               GtkAllocation *allocation)
       {
         GtkDial *dial;

         g_return_if_fail (widget != NULL);
         g_return_if_fail (GTK_IS_DIAL (widget));
         g_return_if_fail (allocation != NULL);

         widget->allocation = *allocation;
         if (GTK_WIDGET_REALIZED (widget))
           {
             dial = GTK_DIAL (widget);

             gdk_window_move_resize (widget->window,
                                     allocation->x, allocation->y,
                                     allocation->width, allocation->height);

             dial->radius = MAX(allocation->width,allocation->height) * 0.45;
             dial->pointer_width = dial->radius / 5;
           }
       }



  .


  1199..44..77..  ggttkk__ddiiaall__eexxppoossee(())

  Comme cela est mentionn plus haut, tout le dessin de ce widget est
  ralis dans le gestionnaire pour les vnements d'exposition. Il n'y a
  pas grand chose de plus  dire l dessus, sauf constater l'utilisation
  de la fonction _g_t_k___d_r_a_w___p_o_l_y_g_o_n pour dessiner le pointeur avec une
  forme en trois dimensions selon les couleurs stockes dans le style du
  widget.  style.



  static gint
  gtk_dial_expose (GtkWidget      *widget,
                   GdkEventExpose *event)
  {
    GtkDial *dial;
    GdkPoint points[3];
    gdouble s,c;
    gdouble theta;
    gint xc, yc;
    gint tick_length;
    gint i;

    g_return_val_if_fail (widget != NULL, FALSE);
    g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
    g_return_val_if_fail (event != NULL, FALSE);

    if (event->count > 0)
      return FALSE;

    dial = GTK_DIAL (widget);

    gdk_window_clear_area (widget->window,
                           0, 0,
                           widget->allocation.width,
                           widget->allocation.height);

    xc = widget->allocation.width/2;
    yc = widget->allocation.height/2;

    /* Draw ticks */

    for (i=0; i<25; i++)
      {
        theta = (i*M_PI/18. - M_PI/6.);
        s = sin(theta);
        c = cos(theta);

        tick_length = (i%6 == 0) ? dial->pointer_width : dial->pointer_width/2;

        gdk_draw_line (widget->window,
                       widget->style->fg_gc[widget->state],
                       xc + c*(dial->radius - tick_length),
                       yc - s*(dial->radius - tick_length),
                       xc + c*dial->radius,
                       yc - s*dial->radius);
      }

    /* Draw pointer */

    s = sin(dial->angle);
    c = cos(dial->angle);


    points[0].x = xc + s*dial->pointer_width/2;
    points[0].y = yc + c*dial->pointer_width/2;
    points[1].x = xc + c*dial->radius;
    points[1].y = yc - s*dial->radius;
    points[2].x = xc - s*dial->pointer_width/2;
    points[2].y = yc - c*dial->pointer_width/2;

    gtk_draw_polygon (widget->style,
                      widget->window,
                      GTK_STATE_NORMAL,
                      GTK_SHADOW_OUT,
                      points, 3,
                      TRUE);
    return FALSE;
  }



  1199..44..88..  GGeessttiioonn ddeess vvnneemmeennttss

  Le reste du code du widget gre diffrents types d'vnements et n'est pas
  trop diffrent de ce que l'on trouve dans la plupart des applications
  GTK. Deux types d'vnements peuvent survenir -- l'utilisateur peut
  cliquer sur le widget avec la souris et faire glisser pour dplacer le
  pointeur, ou bien la valeur de l'objet Adjustment peut changer  cause
  d'une circonstance extrieure.


  Lorsque l'utilisateur clique sur le widget, on vrifie si le clic s'est
  bien pass prs du pointeur et si c'est le cas, on stocke alors le
  bouton avec lequel l'utilisateur a cliqu dans le champ _b_u_t_t_o_n de la
  structure du widget et on rcupre tous les vnements souris avec un
  appel _g_t_k___g_r_a_b___a_d_d_(_). Un dplacement ultrieur de la souris provoque le
  recalcul de la valeur de contrle (par la fonction
  _g_t_k___d_i_a_l___u_p_d_a_t_e___m_o_u_s_e). Selon la politique qui a t choisie, les
  vnements "value_changed" sont, soit gnrs instantanment
  (_G_T_K___U_P_D_A_T_E___C_O_N_T_I_N_U_O_U_S), aprs un dlai ajout au timer avec
  _g_t_k___t_i_m_e_o_u_t___a_d_d_(_) (_G_T_K___U_P_D_A_T_E___D_E_L_A_Y_E_D), ou seulement lorsque le bouton
  est relch (_G_T_K___U_P_D_A_T_E___D_I_S_C_O_N_T_I_N_U_O_U_S).



  static gint
  gtk_dial_button_press (GtkWidget      *widget,
                         GdkEventButton *event)
  {
    GtkDial *dial;
    gint dx, dy;
    double s, c;
    double d_parallel;
    double d_perpendicular;

    g_return_val_if_fail (widget != NULL, FALSE);
    g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
    g_return_val_if_fail (event != NULL, FALSE);

    dial = GTK_DIAL (widget);

    /* Dtermine si le bouton press est dans la rgion du pointeur.
       On fait cela en calculant les distances parallle et perpendiculaire
       du point o la souris a t presse par rapport  la ligne passant
       par le pointeur */

    dx = event->x - widget->allocation.width / 2;
    dy = widget->allocation.height / 2 - event->y;

    s = sin(dial->angle);
    c = cos(dial->angle);

    d_parallel = s*dy + c*dx;
    d_perpendicular = fabs(s*dx - c*dy);

    if (!dial->button &&
        (d_perpendicular < dial->pointer_width/2) &&
        (d_parallel > - dial->pointer_width))
      {
        gtk_grab_add (widget);

        dial->button = event->button;

        gtk_dial_update_mouse (dial, event->x, event->y);
      }

    return FALSE;
  }

  static gint
  gtk_dial_button_release (GtkWidget      *widget,
                            GdkEventButton *event)
  {
    GtkDial *dial;

    g_return_val_if_fail (widget != NULL, FALSE);
    g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
    g_return_val_if_fail (event != NULL, FALSE);

    dial = GTK_DIAL (widget);

    if (dial->button == event->button)
      {
        gtk_grab_remove (widget);

        dial->button = 0;

        if (dial->policy == GTK_UPDATE_DELAYED)
          gtk_timeout_remove (dial->timer);

        if ((dial->policy != GTK_UPDATE_CONTINUOUS) &&
            (dial->old_value != dial->adjustment->value))
          gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
      }

    return FALSE;
  }

  static gint
  gtk_dial_motion_notify (GtkWidget      *widget,
                           GdkEventMotion *event)
  {
    GtkDial *dial;
    GdkModifierType mods;
    gint x, y, mask;

    g_return_val_if_fail (widget != NULL, FALSE);
    g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
    g_return_val_if_fail (event != NULL, FALSE);

    dial = GTK_DIAL (widget);

    if (dial->button != 0)
      {
        x = event->x;
        y = event->y;

        if (event->is_hint || (event->window != widget->window))
          gdk_window_get_pointer (widget->window, &x, &y, &mods);

        switch (dial->button)
          {
          case 1:
            mask = GDK_BUTTON1_MASK;
            break;
          case 2:
            mask = GDK_BUTTON2_MASK;
            break;
          case 3:
            mask = GDK_BUTTON3_MASK;
            break;
          default:
            mask = 0;
            break;
          }

        if (mods & mask)
          gtk_dial_update_mouse (dial, x,y);
      }

    return FALSE;
  }

  static gint
  gtk_dial_timer (GtkDial *dial)
  {
    g_return_val_if_fail (dial != NULL, FALSE);
    g_return_val_if_fail (GTK_IS_DIAL (dial), FALSE);

    if (dial->policy == GTK_UPDATE_DELAYED)
      gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");

    return FALSE;
  }

  static void
  gtk_dial_update_mouse (GtkDial *dial, gint x, gint y)
  {
    gint xc, yc;
    gfloat old_value;

    g_return_if_fail (dial != NULL);
    g_return_if_fail (GTK_IS_DIAL (dial));

    xc = GTK_WIDGET(dial)->allocation.width / 2;
    yc = GTK_WIDGET(dial)->allocation.height / 2;

    old_value = dial->adjustment->value;
    dial->angle = atan2(yc-y, x-xc);

    if (dial->angle < -M_PI/2.)
      dial->angle += 2*M_PI;

    if (dial->angle < -M_PI/6)
      dial->angle = -M_PI/6;

    if (dial->angle > 7.*M_PI/6.)
      dial->angle = 7.*M_PI/6.;

    dial->adjustment->value = dial->adjustment->lower + (7.*M_PI/6 - dial->angle) *
      (dial->adjustment->upper - dial->adjustment->lower) / (4.*M_PI/3.);

    if (dial->adjustment->value != old_value)
      {
        if (dial->policy == GTK_UPDATE_CONTINUOUS)
          {
            gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
          }
        else
          {
            gtk_widget_draw (GTK_WIDGET(dial), NULL);

            if (dial->policy == GTK_UPDATE_DELAYED)
              {
                if (dial->timer)
                  gtk_timeout_remove (dial->timer);

                dial->timer = gtk_timeout_add (SCROLL_DELAY_LENGTH,
                                               (GtkFunction) gtk_dial_timer,
                                               (gpointer) dial);
              }
          }
      }
  }



  Les changements de l'Adjustement par des moyens extrieurs sont
  communiqus notre widget par les signaux "changed" et "value_changed".
  Les gestionnaires pour ces fonctions appellent _g_t_k___d_i_a_l___u_p_d_a_t_e_(_) pour
  valider les paramtres, calculer le nouvel angle du pointeur et
  redessiner le widget (en appelant _g_t_k___w_i_d_g_e_t___d_r_a_w_(_)).



  static void
  gtk_dial_update (GtkDial *dial)
  {
    gfloat new_value;

    g_return_if_fail (dial != NULL);
    g_return_if_fail (GTK_IS_DIAL (dial));

    new_value = dial->adjustment->value;

    if (new_value < dial->adjustment->lower)
      new_value = dial->adjustment->lower;

    if (new_value > dial->adjustment->upper)
      new_value = dial->adjustment->upper;

    if (new_value != dial->adjustment->value)
      {
        dial->adjustment->value = new_value;
        gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
      }

    dial->angle = 7.*M_PI/6. - (new_value - dial->adjustment->lower) * 4.*M_PI/3. /
      (dial->adjustment->upper - dial->adjustment->lower);

    gtk_widget_draw (GTK_WIDGET(dial), NULL);
  }

  static void
  gtk_dial_adjustment_changed (GtkAdjustment *adjustment,
                                gpointer       data)
  {
    GtkDial *dial;

    g_return_if_fail (adjustment != NULL);
    g_return_if_fail (data != NULL);

    dial = GTK_DIAL (data);

    if ((dial->old_value != adjustment->value) ||
        (dial->old_lower != adjustment->lower) ||
        (dial->old_upper != adjustment->upper))
      {
        gtk_dial_update (dial);

        dial->old_value = adjustment->value;
        dial->old_lower = adjustment->lower;
        dial->old_upper = adjustment->upper;
      }
  }

  static void
  gtk_dial_adjustment_value_changed (GtkAdjustment *adjustment,
                                      gpointer       data)
  {
    GtkDial *dial;

    g_return_if_fail (adjustment != NULL);
    g_return_if_fail (data != NULL);

    dial = GTK_DIAL (data);

    if (dial->old_value != adjustment->value)
      {
        gtk_dial_update (dial);

        dial->old_value = adjustment->value;
      }
  }



  1199..44..99..  AAmmlliioorraattiioonnss ppoossssiibblleess


  Le widget Dial dcrit jusqu' maintenant excute  peu prs 670 lignes de
  code. Bien que cela puisse sembler beaucoup, nous en avons vraiment
  fait beaucoup avec ce code, notamment parce que la majeure partie de
  cette longueur est due aux en-ttes et  la prparation. Cependant,
  certaines amliorations peuvent tre apportes  ce widget :


  +o  Si vous testez ce widget, vous vous apercevrez qu'il y a un peu de
     scintillement lorsque le pointeur est dplac. Ceci est d au fait que
     le widget entier est effac, puis redessin   chaque mouvement du
     pointeur. Souvent, la meilleure faon de grer ce problme est de
     dessiner sur un pixmap non affich, puis de copier le rsultat final
     sur l'cran en une seule tape (le widget _P_r_o_g_r_e_s_s_B_a_r se dessine de
     cette faon).

  +o  L'utilisateur devrait pouvoir utiliser les flches du curseur vers
     le haut et vers le bas pour incrmenter et dcrmenter la valeur.

  +o  Ce serait bien si le widget avait des boutons pour augmenter et
     diminuer la valeur dans de petites ou de grosses proportions. Bien
     qu'il serait possible d'utiliser les widgets _B_u_t_t_o_n pour cela, nous
     voudrions aussi que les boutons s'auto-rptent lorsqu'ils sont
     maintenus appuys, comme font les flches d'une barre de dfilement.
     La majeure partie du code pour implanter ce type de comportement
     peut se trouver dans le widget _G_t_k_R_a_n_g_e.

  +o  Le widget Dial pourrait tre fait dans un widget container avec un
     seul widget fils positionne en bas, entre les boutons mentionns ci-
     dessus. L'utilisateur pourrait alors ajouter au choix, un widget
     label ou entre pour afficher la valeur courante de l'appel.


  1199..55..  EEnn ssaavvooiirr pplluuss

  Seule une petite partie des nombreux dtails de la cration des widgets
  a pu tre dcrite. Si vous dsirez crire vos propres widgets, la
  meilleure source d'exemples est le source de GTK lui-mme. Posez-vous
  quelques questions sur les widgets que vous voulez crire : est-ce un
  widget container ? possde-t-il sa propre fentre ? est-ce une
  modification d'un widget existant ? Puis, trouvez un widget identique
  et commencez  faire les modifications. Bonne chance !


  2200..  SSccrriibbbbllee,, uunn pprrooggrraammmmee ssiimmppllee ddee ddeessssiinn

  2200..11..  PPrrsseennttaattiioonn

  Dans cette section, nous construirons un programme simple de dessin.
  Ce faisant, nous examinerons comment grer les vnements souris, comment
  dessiner dans une fentre, et comment mieux dessiner en utilisant un
  pixmap en arrire plan. Aprs avoir cr ce programme, nous l'tendrons en
  lui ajoutant le support des priphriques _X_i_n_p_u_t, comme les tables de
  trac. GTK dispose de routines de support qui facilitent beaucoup
  l'obtention des informations tendues (comme la pression et
  l'inclinaison du stylet)  partir de tels priphriques.
  2200..22..  GGeessttiioonn dd''vvnneemmeenntt

  Les signaux GTK que nous avons dj vus concernent les actions de haut
  niveau, comme la slection d'un choix d'un menu. Cependant, il est
  quelques fois utile de connatre les cas de bas niveau, comme le
  dplacement de la souris, ou la pression d'une touche. Il existe aussi
  des signaux GTK correspondant  ces _v_n_e_m_e_n_t_s bas niveau. Les
  gestionnaires de ces signaux ont un paramtre supplmentaire qui est un
  pointeur vers une structure contenant des informations sur l'vnement.
  Par exemple, les gestionnaires des vnements de dplacement recoivent un
  paramtre vers une structure _G_d_k_E_v_e_n_t_M_o_t_i_o_n qui ressemble (en partie)
  ceci :



       struct _GdkEventMotion
       {
         GdkEventType type;
         GdkWindow *window;
         guint32 time;
         gdouble x;
         gdouble y;
         ...
         guint state;
         ...
       };



  _t_y_p_e sera initialis avec le type de l'vnement, ici _G_D_K___M_O_T_I_O_N___N_O_T_I_F_Y,
  _w_i_n_d_o_w est la fentre dans laquelle l'vnement est survenu. _x et _y
  donnent les coordonnes de l'vnement et _s_t_a_t_e spcifie l'tat du
  modificateur lorsque l'vnement s'est produit (c'est--dire quelles sont
  les touches de modification et les boutons souris qui ont t presss).
  Il s'agit d'un OU bit  bit de l'une des valeurs suivantes :



       GDK_SHIFT_MASK
       GDK_LOCK_MASK
       GDK_CONTROL_MASK
       GDK_MOD1_MASK
       GDK_MOD2_MASK
       GDK_MOD3_MASK
       GDK_MOD4_MASK
       GDK_MOD5_MASK
       GDK_BUTTON1_MASK
       GDK_BUTTON2_MASK
       GDK_BUTTON3_MASK
       GDK_BUTTON4_MASK
       GDK_BUTTON5_MASK



  Comme pour les autres signaux, on appelle _g_t_k___s_i_g_n_a_l___c_o_n_n_e_c_t_(_) pour
  dterminer ce qui se passe lorsqu'un vnement survient. Mais nous devons
  aussi faire en sorte que GTK sache de quels vnements nous voulons tre
  avertis. Pour ce faire, on appelle la fonction :



  void       gtk_widget_set_events          (GtkWidget           *widget,
                                             gint                 events);



  Le deuxime champ spcifie les vnements qui nous intressent. Il s'agit
  d'un OU bit  bit de constantes qui indiquent diffrent types
  d'vnements. Pour rfrence ultrieure, les types d'vnements sont :



       GDK_EXPOSURE_MASK
       GDK_POINTER_MOTION_MASK
       GDK_POINTER_MOTION_HINT_MASK
       GDK_BUTTON_MOTION_MASK
       GDK_BUTTON1_MOTION_MASK
       GDK_BUTTON2_MOTION_MASK
       GDK_BUTTON3_MOTION_MASK
       GDK_BUTTON_PRESS_MASK
       GDK_BUTTON_RELEASE_MASK
       GDK_KEY_PRESS_MASK
       GDK_KEY_RELEASE_MASK
       GDK_ENTER_NOTIFY_MASK
       GDK_LEAVE_NOTIFY_MASK
       GDK_FOCUS_CHANGE_MASK
       GDK_STRUCTURE_MASK
       GDK_PROPERTY_CHANGE_MASK
       GDK_PROXIMITY_IN_MASK
       GDK_PROXIMITY_OUT_MASK



  Il y a quelques points subtils qui doivent tre observs lorsqu'on
  appelle _g_t_k___w_i_d_g_e_t___s_e_t___e_v_e_n_t_s_(_). D'abord, elle doit tre appele avant
  que la fentre X d'un widget GTK soit cre. En pratique, cela signifie
  que l'on doit l'appeler immdiatement aprs avoir cr le widget. Ensuite,
  le widget doit avoir une fentre X associe. Pour des raisons
  d'efficacit, de nombreux types de widgets n'ont pas de fentre propre,
  mais se dessinent dans la fentre de leur parent. Ces widgets sont :



       GtkAlignment
       GtkArrow
       GtkBin
       GtkBox
       GtkImage
       GtkItem
       GtkLabel
       GtkPaned
       GtkPixmap
       GtkScrolledWindow
       GtkSeparator
       GtkTable
       GtkViewport
       GtkAspectFrame
       GtkFrame
       GtkVPaned
       GtkHPaned
       GtkVBox
       GtkHBox
       GtkVSeparator
       GtkHSeparator

  Pour capturer les vnements pour ces widgets, on doit utiliser un
  widget _E_v_e_n_t_B_o_x. Voir la section sur ``Le widget EventBox'' pour plus
  de dtails.


  Pour notre programme de dessin, on veut savoir quand le bouton de la
  souris est press et quand la souris est dplace, nous indiquons donc
  _G_D_K___P_O_I_N_T_E_R___M_O_T_I_O_N___M_A_S_K et _G_D_K___B_U_T_T_O_N___P_R_E_S_S___M_A_S_K. On veut aussi savoir
  quand il est ncessaire de redessiner notre fentre, on indique donc
  _G_D_K___E_X_P_O_S_U_R_E___M_A_S_K. Bien que nous voulions tre avertis via un vnement
  _C_o_n_f_i_g_u_r_e lorsque la taille de notre fentre change, on n'a pas besoin
  de prciser le flag _G_D_K___S_T_R_U_C_T_U_R_E___M_A_S_K correspondant car il est
  automatiquement spcifi pour chaque fentre.


  Il arrive cependant qu'il puisse y avoir un problme en indiquant
  seulement _G_D_K___P_O_I_N_T_E_R___M_O_T_I_O_N___M_A_S_K. Cela fera que le serveur ajoutera
  un nouvel vnement de dplacement  la file des vnements  chaque fois que
  l'utilisateur dplace la souris. Si cela nous prend 0,1 seconde pour
  grer un vnement de dplacement, si le serveur X n'ajoute un nouvel
  vnement de dplacement dans la queue que toutes les 0,05 secondes, nous
  serons vite  la trane de l'utilisateur. Si l'utilisateur dessine
  pendant 5 secondes, cela nous prendra 5 secondes de plus pour le
  traiter aprs qu'il ait relch le bouton de la souris ! Ce que l'on
  voudrait, c'est ne rcuprer qu'un vnement de dplacement pour chaque
  vnement que l'on traite. Pour cela, il faut prciser
  _G_D_K___P_O_I_N_T_E_R___M_O_T_I_O_N___H_I_N_T___M_A_S_K.


  Avec _G_D_K___P_O_I_N_T_E_R___M_O_T_I_O_N___H_I_N_T___M_A_S_K, le serveur nous envoit un vnement
  de dplacement la premire fois que la pointeur se dplace aprs tre entr
  dans la fentre, ou aprs un vnement d'appui ou de relchement d'un
  bouton. Les vnements de dplacement suivants seront supprims jusqu' ce
  que l'on demande explicitement la position du pointeur en utilisant la
  fonction :



       GdkWindow*    gdk_window_get_pointer     (GdkWindow       *window,
                                                 gint            *x,
                                                 gint            *y,
                                                 GdkModifierType *mask);



  (Il existe une autre fonction, _g_t_k___w_i_d_g_e_t___g_e_t___p_o_i_n_t_e_r_(_) qui possde une
  interface simple, mais n'est pas trs utile car elle ne fait que
  rcuprer la position de la souris et ne se proccupe pas de savoir si
  les boutons sont presss).


  Le code pour configurer les vnements pour notre fentre ressemble alors
  :



    gtk_signal_connect (GTK_OBJECT (drawing_area), "expose_event",
                        (GtkSignalFunc) expose_event, NULL);
    gtk_signal_connect (GTK_OBJECT(drawing_area),"configure_event",
                        (GtkSignalFunc) configure_event, NULL);
    gtk_signal_connect (GTK_OBJECT (drawing_area), "motion_notify_event",
                        (GtkSignalFunc) motion_notify_event, NULL);
    gtk_signal_connect (GTK_OBJECT (drawing_area), "button_press_event",
                        (GtkSignalFunc) button_press_event, NULL);

    gtk_widget_set_events (drawing_area, GDK_EXPOSURE_MASK
                           | GDK_LEAVE_NOTIFY_MASK
                           | GDK_BUTTON_PRESS_MASK
                           | GDK_POINTER_MOTION_MASK
                           | GDK_POINTER_MOTION_HINT_MASK);



  Nous garderons les gestionnaires de "expose_event" et
  "configure_event" pour plus tard. Les gestionnaires de
  "motion_notify_event" et "button_press_event" sont trs simples :



       static gint
       button_press_event (GtkWidget *widget, GdkEventButton *event)
       {
         if (event->button == 1 && pixmap != NULL)
             draw_brush (widget, event->x, event->y);

         return TRUE;
       }

       static gint
       motion_notify_event (GtkWidget *widget, GdkEventMotion *event)
       {
         int x, y;
         GdkModifierType state;

         if (event->is_hint)
           gdk_window_get_pointer (event->window, &x, &y, &state);
         else
           {
             x = event->x;
             y = event->y;
             state = event->state;
           }

         if (state & GDK_BUTTON1_MASK && pixmap != NULL)
           draw_brush (widget, x, y);

         return TRUE;
       }



  2200..33..  LLee wwiiddggeett DDrraawwiinnggAArreeaa eett llee ddeessssiinn

  Revenons au processus de dessin sur l'cran. Le widget que l'on utilise
  pour ceci est le widget _D_r_a_w_i_n_g_A_r_e_a. Un tel widget est essentiellement
  une fentre X et rien de plus. Il s'agit d'une toile vide sur laquelle
  nous pouvons dessiner ce que nous voulons.

  Une zone de dessin est cre avec l'appel :



       GtkWidget* gtk_drawing_area_new        (void);



  Une taille par dfaut peut tre donne au widget par l'appel :



       void       gtk_drawing_area_size       (GtkDrawingArea      *darea,
                                               gint                 width,
                                               gint                 height);



  Cette taille par dfaut peu tre surcharge en appelant
  _g_t_k___w_i_d_g_e_t___s_e_t___u_s_i_z_e_(_) et celle-ci,  son tour, peut tre surcharge si
  l'utilisateur modifie manuellement la taille de la fentre contenant la
  zone de dessin.


  Il faut noter que lorsque l'on cre un widget _D_r_a_w_i_n_g_A_r_e_a, nous sommes
  _c_o_m_p_l_t_e_m_e_n_t responsable du dessin du contenu. Si notre fentre est
  cache puis redcouverte, nous recevrons un vnement d'exposition et
  devrons redessiner ce qui a t cach auparavant.


  Devoir se rappeler tout ce qui a t dessin  l'cran pour pouvoir
  correctement le redessiner peut s'avrer, c'est le moins que l'on
  puisse dire, pnible. De plus, cela peut tre visible si des portions de
  la fentre sont effaces puis redessines tape par tape. La solution  ce
  problme est d'utiliser un _p_i_x_m_a_p _d_'_a_r_r_i_r_e_-_p_l_a_n qui n'est pas sur
  l'cran. Au lieu de dessiner directement  l'cran, on dessine sur une
  image stocke dans la mmoire du serveur et qui n'est pas affiche, puis,
  lorsque l'image change ou lorsque de nouvelles portions de l'image
  sont affiches, on copie les parties adquates sur l'cran.


  Pour crer un pixmap mmoire, on appelle la fonction :



       GdkPixmap* gdk_pixmap_new               (GdkWindow  *window,
                                                gint        width,
                                                gint        height,
                                                gint        depth);



  Le paramtre _w_i_n_d_o_w_s indique une fentre GTK de laquelle ce pixmap tire
  certaines de ses proprits. _w_i_d_t_h et _h_e_i_g_h_t prcisent la taille du
  pixmap. _d_e_p_t_h prcise la  _p_r_o_f_o_n_d_e_u_r _d_e _c_o_u_l_e_u_r, c'est--dire le nombre
  de bits par pixel, de la nouvelle fentre. Si cette profondeur vaut _-_1,
  elle correspondra  celle de _w_i_n_d_o_w.


  Nous crons le pixmap dans notre gestionnaire "configure_event". Cet
  vnement est gnr  chaque fois que la fentre change de taille, y compris
  lorsqu'elle initialement cre.

       /* Pixmap d'arrire-plan pour la zone de dessin */
       static GdkPixmap *pixmap = NULL;

       /* Cration d'un nouveau pixmap d'arrire-plan de la taille voulue */
       static gint
       configure_event (GtkWidget *widget, GdkEventConfigure *event)
       {
         if (pixmap)
           {
             gdk_pixmap_destroy(pixmap);
           }
         pixmap = gdk_pixmap_new(widget->window,
                                 widget->allocation.width,
                                 widget->allocation.height,
                                 -1);
         gdk_draw_rectangle (pixmap,
                             widget->style->white_gc,
                             TRUE,
                             0, 0,
                             widget->allocation.width,
                             widget->allocation.height);

         return TRUE;
       }



  L'appel  _g_d_k___d_r_a_w___r_e_c_t_a_n_g_l_e_(_) remet le pixmap  blanc. Nous en dirons
  un peu plus dans un moment.


  Notre gestionnaire d'vnement d'exposition copie alors simplement la
  partie concernes du pixmap sur l'cran (on dtermine la zone qu'il faut
  redessiner en utilisant le champ _e_v_e_n_t_-_>_a_r_e_a de l'vnement
  d'exposition) :



       /* Remplit l'cran  partir du pixmap d'arrire-plan */
       static gint
       expose_event (GtkWidget *widget, GdkEventExpose *event)
       {
         gdk_draw_pixmap(widget->window,
                         widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
                         pixmap,
                         event->area.x, event->area.y,
                         event->area.x, event->area.y,
                         event->area.width, event->area.height);

         return FALSE;
       }



  Nous avons vu comment garder l'cran  jour avec notre pixmap, mais
  comment dessine-t-on rellement ce que l'on veut dans le pixmap ? Il
  existe un grand nombre d'appels dans la bibliothque GDK de GTK pour
  dessiner sur des _d_e_s_s_i_n_a_b_l_e_s. Un dessinable est simplement quelque
  chose sur lequel on peut dessiner. Cela peut tre une fentre, un
  pixmap, ou un bitmap (une image en noir et blanc). Nous avons dj vu
  plus haut deux de ces appels, _g_d_k___d_r_a_w___r_e_c_t_a_n_g_l_e_(_) et
  _g_d_k___d_r_a_w___p_i_x_m_a_p_(_). La liste complte est :


       gdk_draw_line ()
       gdk_draw_rectangle ()
       gdk_draw_arc ()
       gdk_draw_polygon ()
       gdk_draw_string ()
       gdk_draw_text ()
       gdk_draw_pixmap ()
       gdk_draw_bitmap ()
       gdk_draw_image ()
       gdk_draw_points ()
       gdk_draw_segments ()



  Consultez la documentation de rfrence ou le fichier en-tte _<_g_d_kgdk.h>/
  pour plus de dtails sur ces fonctions.  Celles-ci partagent toutes les
  mmes deux paramtres. Le premier est le dessinable sur lequel dessiner,
  le second est un _c_o_n_t_e_x_t_e _g_r_a_p_h_i_q_u_e (GC).


  Un contexte graphique encapsule l'information sur des choses comme les
  couleurs de premier et d'arrire plan et la largeur de ligne. GDK
  possde un ensemble complet de fonctions pour crer et manipuler les
  contextes graphiques, mais, pour simplifier, nous n'utiliserons que
  les contextes graphiques prdfinis. Chaque widget a un style associ
  (qui peut tre modifi dans un fichier gtkrc, voir la section sur les
  fichiers rc de GTK). Celui-ci, entre autres choses, stocke plusieurs
  contextes graphiques. Quelques exemples d'accs  ces contextes sont :



       widget->style->white_gc
       widget->style->black_gc
       widget->style->fg_gc[GTK_STATE_NORMAL]
       widget->style->bg_gc[GTK_WIDGET_STATE(widget)]



  Les champs _f_g___g_c_, _b_g___g_c_, _d_a_r_k___g_c et _l_i_g_h_t___g_c sont indexs par un
  paramtre de type _G_t_k_S_t_a_t_e_T_y_p_e qui peut prendre les valeurs :



       GTK_STATE_NORMAL,
       GTK_STATE_ACTIVE,
       GTK_STATE_PRELIGHT,
       GTK_STATE_SELECTED,
       GTK_STATE_INSENSITIVE



  Par exemple, pour _G_T_K___S_T_A_T_E___S_E_L_E_C_T_E_D, la couleur de premier plan par
  dfaut est blanc et la couleur d'arrire plan par dfaut est bleu fonc.


  Notre fonction _d_r_a_w___b_r_u_s_h_(_), qui ralise le dessin  l'cran est alors :



  /* Dessine un rectangle  l'cran */
  static void
  draw_brush (GtkWidget *widget, gdouble x, gdouble y)
  {
    GdkRectangle update_rect;

    update_rect.x = x - 5;
    update_rect.y = y - 5;
    update_rect.width = 10;
    update_rect.height = 10;
    gdk_draw_rectangle (pixmap,
                        widget->style->black_gc,
                        TRUE,
                        update_rect.x, update_rect.y,
                        update_rect.width, update_rect.height);
    gtk_widget_draw (widget, &update_rect);
  }



  Aprs avoir dessin le rectangle reprsentant la brosse sur le pixmap,
  nous appelons la fonction :



       void       gtk_widget_draw                (GtkWidget           *widget,
                                                  GdkRectangle        *area);



  qui indique  X que la zone donne par le paramtre _a_r_e_a a besoin d'tre
  mise  jour. X gnrera ventuellement un vnement d'exposition (en
  combinant peut-tre les zones passs dans plusieurs appels
  _g_t_k___w_i_d_g_e_t___d_r_a_w_(_)) ce qui forcera notre gestionnaire d'vnement
  d'exposition  copier les parties adquates  l'cran.


  Nous avons maintenant couvert entirement le programme de dessin, sauf
  quelques dtails banals comme la cration de la fentre principale. Le
  code source complet est disponible  l'endroit o vous avez obtenu ce
  didacticiel.


  2200..44..  AAjjoouutteerr llee ssuuppppoorrtt XXIInnppuutt

  Il est maintenant possible d'acheter des priphriques bon march, comme
  les tablettes graphiques qui permettent d'exprimer beaucoup plus
  facilement son talent qu'avec une souris. La faon la plus simple pour
  utiliser de tels priphriques est simplement de le faire comme un
  remplacement de la souris, mais cela ne tire pas partie des nombreux
  avantages de ces priphriques, comme :


  +o  Sensibilit  la pression ;

  +o  rapport d'inclinaison ;

  +o  positionnement au dessous du pixel ;

  +o  entres multiples (par exemple, un stylet avec pointe et gomme).

  Pour des informations sur l'extension XInput, voir XInput-HOWTO
  <http://www.msc.cornell.edu/~otaylor/xinput/XInput-HOWTO.html>.

  Si l'on examine la dfinition complte de, par exemple, la structure
  _G_d_k_E_v_e_n_t_M_o_t_i_o_n, on voit qu'elle possde des champs pour supporter des
  informations tendues sur les priphriques.



       struct _GdkEventMotion
       {
         GdkEventType type;
         GdkWindow *window;
         guint32 time;
         gdouble x;
         gdouble y;
         gdouble pressure;
         gdouble xtilt;
         gdouble ytilt;
         guint state;
         gint16 is_hint;
         GdkInputSource source;
         guint32 deviceid;
       };



  _p_r_e_s_s_u_r_e indique la pression comme un nombre rel compris entre 0 et 1.
  _x_t_i_l_t et _y_t_i_l_t peuvent prendre des valeurs entre -1 et 1,
  correspondant au degr d'inclinaison dans chaque direction, _s_o_u_r_c_e et
  _d_e_v_i_c_e_i_d prcisent de deux faons diffrentes le priphrique pour lequel
  l'vnement est survenus. _s_o_u_r_c_e donne une simple information sur le
  type du priphrique. Il peut prendre l'une des valeurs suivantes :



       GDK_SOURCE_MOUSE
       GDK_SOURCE_PEN
       GDK_SOURCE_ERASER
       GDK_SOURCE_CURSOR



  deviceid prcise un ID numrique unique pour le priphrique. Il peut tre
  utilis pour trouver des informations supplmentaires sur le priphrique
  en utilisant l'appel _g_d_k___i_n_p_u_t___l_i_s_t___d_e_v_i_c_e_s_(_) (voir ci-dessous). La
  valeur spciale _G_D_K___C_O_R_E___P_O_I_N_T_E_R sert  dsigner le priphrique de
  pointage principal (habituellement la souris).


  2200..44..11..  VVaalliiddeerr ll''iinnffoorrmmaattiioonn ssuuppppllmmeennttaaiirree ssuurr uunn pprriipphhrriiqquuee

  Pour indiquer  GTK que l'on dsire obtenir des informations
  supplmentaires sur le priphrique, on a simplement besoin d'ajouter une
  ligne  nos programmes.



       gtk_widget_set_extension_events (drawing_area, GDK_EXTENSION_EVENTS_CURSOR);



  En donnant la valeur _G_D_K___E_X_T_E_N_S_I_O_N___E_V_E_N_T_S___C_U_R_S_O_R, on indique que nous
  dsirons les vnements d'extension, mais seulement si l'on ne doit pas
  dessiner notre propre curseur. Voir la section ``Sophistications
  supplmentaires'' ci-dessous pour des plus de dtails sur le dessin du
  curseur. Nous pourrions aussi donner les valeurs
  _G_D_K___E_X_T_E_N_S_I_O_N___E_V_E_N_T_S___A_L_L si nous voulons dessiner notre propre
  curseur, ou GDK_EXTENSION_EVENTS_NONE pour revenir  la situation par
  dfaut.


  Toutefois, nous ne sommes pas compltement  la fin de l'histoire. Par
  dfaut, aucun priphrique d'extension n'est autoris. Nous avons besoin
  d'un mcanisme pour que les utilisateurs puissent autoriser et
  configurer leur extensions. GTK dispose du widget _I_n_p_u_t_D_i_a_l_o_g pour
  automatiser cette tche. La procdure suivante gre un widget
  InputDialog. Elle cre le dialogue s'il n'est pas prsent et le place au
  premier plan sinon.



       void
       input_dialog_destroy (GtkWidget *w, gpointer data)
       {
         *((GtkWidget **)data) = NULL;
       }

       void
       create_input_dialog ()
       {
         static GtkWidget *inputd = NULL;

         if (!inputd)
           {
             inputd = gtk_input_dialog_new();

             gtk_signal_connect (GTK_OBJECT(inputd), "destroy",
                                 (GtkSignalFunc)input_dialog_destroy, &inputd);
             gtk_signal_connect_object (GTK_OBJECT(GTK_INPUT_DIALOG(inputd)->close_button),
                                        "clicked",
                                        (GtkSignalFunc)gtk_widget_hide,
                                        GTK_OBJECT(inputd));
             gtk_widget_hide ( GTK_INPUT_DIALOG(inputd)->save_button);

             gtk_widget_show (inputd);
           }
         else
           {
             if (!GTK_WIDGET_MAPPED(inputd))
               gtk_widget_show(inputd);
             else
               gdk_window_raise(inputd->window);
           }
       }



  (vous pouvez remarquer la faon dont nous grons ce dialogue. En le
  connectant au signal "destroy", nous nous assurons que nous ne
  garderons pas un pointeur sur le dialogue aprs l'avoir dtruit -- cela
  pourrait provoquer une erreur de segmentation).



  InputDialog a deux boutons "Close" et "Save", qui n'ont pas d'actions
  qui leur sont assignes par dfaut. Dans la fonction ci-dessus, nous
  associons  "Close" le masquage du dialogue et nous cachons le bouton
  "Save" car nous n'implantons pas la sauvegarde des options XInput dans
  ce programme.

  2200..44..22..  UUttiilliisseerr ll''iinnffoorrmmaattiioonn ssuuppppllmmeennttaaiirree dd''uunn pprriipphhrriiqquuee

  Lorsque l'on a valid le priphrique, on peut simplement utiliser
  l'information supplmentaire des champs des structures d'vnement. En
  fait, il est toujours prident d'utiliser ces informations car ces
  champs auront des valeurs par dfaut judicieuses mme lorsque les
  vnements supplmentaires ne sont pas autoriss.


  La seule modification consiste  appeler _g_d_k___i_n_p_u_t___w_i_n_d_o_w___g_e_t___p_o_i_n_t_e_r_(_)
  au lieu de _g_d_k___w_i_n_d_o_w___g_e_t___p_o_i_n_t_e_r. Cela est ncessaire car
  _g_d_k___w_i_n_d_o_w___g_e_t___p_o_i_n_t_e_r ne retourne pas l'information supplmentaire.



       void gdk_input_window_get_pointer     (GdkWindow       *window,
                                              guint32         deviceid,
                                              gdouble         *x,
                                              gdouble         *y,
                                              gdouble         *pressure,
                                              gdouble         *xtilt,
                                              gdouble         *ytilt,
                                              GdkModifierType *mask);



  Lorsque l'on appelle cette fonction, on doit prciser l'ID du
  priphrique ainsi que la fentre. Habituellement, on aura obtenu cet ID
  par le champ _d_e_v_i_c_e_i_d d'une structure d'vnement. Cette fonction
  retournera une valeur cohrente lorsque les vnements ne sont pas
  autoriss (dans ce cas, _e_v_e_n_t_-_>_d_e_v_i_c_e_i_d aura la valeur
  _G_D_K___C_O_R_E___P_O_I_N_T_E_R).

  La structure de base des gestionnaires d'vnements de dplacement et de
  bouton press ne change donc pas trop -- il faut juste ajouter le code
  permettant de traiter l'information supplmentaire.



  static gint
  button_press_event (GtkWidget *widget, GdkEventButton *event)
  {
    print_button_press (event->deviceid);

    if (event->button == 1 && pixmap != NULL)
      draw_brush (widget, event->source, event->x, event->y, event->pressure);

    return TRUE;
  }

  static gint
  motion_notify_event (GtkWidget *widget, GdkEventMotion *event)
  {
    gdouble x, y;
    gdouble pressure;
    GdkModifierType state;

    if (event->is_hint)
      gdk_input_window_get_pointer (event->window, event->deviceid,
                                    &x, &y, &pressure, NULL, NULL, &state);
    else
      {
        x = event->x;
        y = event->y;
        pressure = event->pressure;
        state = event->state;
      }

    if (state & GDK_BUTTON1_MASK && pixmap != NULL)
      draw_brush (widget, event->source, x, y, pressure);

    return TRUE;
  }



  On doit aussi faire quelquechose de cette nouvelle information. Notre
  nouvelle fonction _d_r_a_w___b_r_u_s_h_(_) dessine avec une couleur diffrente pour
  chaque _e_v_e_n_t_-_>_s_o_u_r_c_e et change la taille du pinceau selon la pression.



  /* Dessine un rectangle  l'cran, la taille dpend de la pression,
     et la couleur dpend du type de priphrique */
  static void
  draw_brush (GtkWidget *widget, GdkInputSource source,
              gdouble x, gdouble y, gdouble pressure)
  {
    GdkGC *gc;
    GdkRectangle update_rect;

    switch (source)
      {
      case GDK_SOURCE_MOUSE:
        gc = widget->style->dark_gc[GTK_WIDGET_STATE (widget)];
        break;
      case GDK_SOURCE_PEN:
        gc = widget->style->black_gc;
        break;
      case GDK_SOURCE_ERASER:
        gc = widget->style->white_gc;
        break;
      default:
        gc = widget->style->light_gc[GTK_WIDGET_STATE (widget)];
      }

    update_rect.x = x - 10 * pressure;
    update_rect.y = y - 10 * pressure;
    update_rect.width = 20 * pressure;
    update_rect.height = 20 * pressure;
    gdk_draw_rectangle (pixmap, gc, TRUE,
                        update_rect.x, update_rect.y,
                        update_rect.width, update_rect.height);
    gtk_widget_draw (widget, &update_rect);
  }



  2200..44..33..  EEnn ssaavvooiirr pplluuss ssuurr uunn pprriipphhrriiqquuee

  Notre programme affichera le nom du priphrique qui a gnr chaque appui
  de bouton. Pour trouver le nom d'un priphrique, nous appelons la
  fonction :



       GList *gdk_input_list_devices               (void);



  qui retourne une GList (un type de liste chane de la bibliothque glib)
  de structures GdkDeviceInfo. La structure GdkDeviceInfo est dfinie de
  la faon suivante :



  struct _GdkDeviceInfo
  {
    guint32 deviceid;
    gchar *name;
    GdkInputSource source;
    GdkInputMode mode;
    gint has_cursor;
    gint num_axes;
    GdkAxisUse *axes;
    gint num_keys;
    GdkDeviceKey *keys;
  };



  La plupart de ces champs sont des informations de configuration que
  l'on peut ignorer sauf si l'on implante la sauvegarde de la
  configuration XInput. Celui qui nous intresse est _n_a_m_e qui est, tout
  simplement, le nom que X donne au priphrique. L'autre champ, qui n'est
  pas une information de configuration, est _h_a_s___c_u_r_s_o_r. Si _h_a_s___c_u_r_s_o_r
  est faux, on doit dessiner notre propre curseur, mais puisque nous
  avons prcis _G_D_K___E_X_T_E_N_S_I_O_N___E_V_E_N_T_S___C_U_R_S_O_R, nous n'avons pas  nous en
  proccuper.


  Notre fonction _p_r_i_n_t___b_u_t_t_o_n___p_r_e_s_s_(_) ne fait parcourir la liste
  retourne jusqu' trouver une correspondance, puis affiche le nom du
  priphrique.



       static void
       print_button_press (guint32 deviceid)
       {
         GList *tmp_list;

         /* gdk_input_list_devices retourne une liste interne, nous ne devons donc
           pas la librer aprs */
         tmp_list = gdk_input_list_devices();

         while (tmp_list)
           {
             GdkDeviceInfo *info = (GdkDeviceInfo *)tmp_list->data;

             if (info->deviceid == deviceid)
               {
                 printf("Bouton press sur le priphrique '%s'\n", info->name);
                 return;
               }

             tmp_list = tmp_list->next;
           }
       }



  Ceci termine les modifications de notre programme  XInputize . Comme
  pour la premire version, le code complet est disponible  l'endroit o
  vous avez obtenu ce didacticiel.



  2200..44..44..  SSoopphhiissttiiccaattiioonnss ssuuppppllmmeennttaaiirreess

  Bien que notre programme supporte maintenant XInput, il y manque des
  caractristiques que l'on souhaite trouver dans les applications
  compltes. D'abord, l'utilisateur ne veut probablement pas avoir
  configurer ses priphriques  chaque fois qu'il lance le programme et
  nous devons donc lui permettre de sauvegarder la configuration du
  priphrique. Ceci est ralis en parcourant ce que retourne
  _g_d_k___i_n_p_u_t___l_i_s_t___d_e_v_i_c_e_s_(_) et en crivant la configuration dans un
  fichier.


  Pour restaurer un tat au prochain dmarrage du programme, GDK dispose
  de fonctions pour changer la configuration des priphriques :



       gdk_input_set_extension_events()
       gdk_input_set_source()
       gdk_input_set_mode()
       gdk_input_set_axes()
       gdk_input_set_key()



  (La liste retourne par _g_d_k___i_n_p_u_t___l_i_s_t___d_e_v_i_c_e_s_(_) ne doit pas tre
  modifie directement). Un exemple est donn dans le programme de dessin
  _g_s_u_m_i (disponible  l'adresse
  http://www.msc.cornell.edu/~otaylor/gsumi/). De plus, ce serait
  pratique d'avoir une mthode standard pour faire cela pour toutes les
  applications. Ceci appartient probablement  un niveau lgrement
  suprieur GTK, peut-tre dans la bibliothque GNOME.


  Une autre grosse omission que nous avons mentionne plus haut est
  l'absence de dessin du curseur. Les plates-formes autres qu'XFree86
  n'autorisent pas encore l'utilisation simultane d'un priphrique comme
  pointeur de base et comme pointeur d'une application. Lisez le XInput-
  HOWTO <http://www.msc.cornell.edu/~otaylor/xinput/XInput-HOWTO.html>
  pour plus d'informations l-dessus. Ceci signifie que les applications
  qui veulent atteindre le plus de monde possible doivent dessiner leur
  propre curseur.


  Une application qui dessine son propre curseur doit faire deux
  choses : dterminer si le priphrique courant a besoin ou non d'un
  curseur dessin et dterminer si le priphrique courant est  proximit (si
  celui-ci est une tablette de dessin, il est pratique de faire
  disparatre le curseur lorsque le stylet est en dehors de la tablette.
  Lorsque le priphrique voit le stylet, on dit qu'il est   proximit ).
  La premire vrification est faite en recherchant dans la liste des
  priphriques, comme nous l'avons fait pour obtenir le nom du
  priphrique. La deuxime est ralise en choisissant des vnements
  "proximity_out". Une exemple de dessin d'un curseur personnel est donn
  dans le programme _t_e_s_t_i_n_p_u_t de la distribution GTK.



  2211..  CCoonnsseeiillss ppoouurr ll''ccrriittuurree dd''aapppplliiccaattiioonnss GGTTKK

  Cette section est simplement un regroupement de lignes de conduites
  gnrales et sages, ainsi que d'astuces pour crer des applications GTK
  correctes. Elle est totalement inutile pour l'instant car il ne s'agit
  que d'une phrase :)

  Utilisez _a_u_t_o_c_o_n_f et _a_u_t_o_m_a_k_e de GNU !  Ce sont vos amis :) J'ai en
  projet de les prsenter brivement ici.


  2222..  CCoonnttrriibbuuttiioonnss

  Ce document, comme beaucoup d'autres beaux logiciels, a t cr (NdT : et
  traduit) par des volontaires bnvoles. Si vous vous y connaissez sur
  certains aspects de GTK qui ne sont pas encore documents, soyez
  gentils de contribuer  ce document.


  Si vous dcidez de contribuer, envoyez-moi (Ian Main) votre texte
  slow@intergate.bc.ca. La totalit de ce document est libre et tout
  ajout que vous pourriez y faire doit l'tre galement. Ainsi, tout le
  monde peut utiliser n'importe quelle partie de vos exemples dans les
  programmes, les copies de ce document peuvent tre distribues  volont,
  etc.

  Merci.



  2233..  RReemmeerrcciieemmeennttss

  Je voudrai remercier les personnes suivantes pour leurs contributions
  ce texte :


  +o  Bawer Dagdeviren, chamele0n@geocities.com pour le didacticiel sur
     les menus.

  +o  Raph Levien, raph@acm.org pour _b_o_n_j_o_u_r _t_o_u_t _l_e _m_o_n_d_e  la GTK, le
     placement des widgets et pour sa sagesse. Il a aussi gnreusement
     donn un abri  ce didacticiel.

  +o  Peter Mattis, petm@xcf.berkeley.edu pour le programme GTK le plus
     simple et pour sa capacit  le faire :)

  +o  Werner Koch werner.koch@guug.de pour la conversion du texte
     original en SGML, et pour la hirarchie des classes de widget.

  +o  Mark Crichton crichton@expert.cc.purdue.edu pour le code de l'usine
     menus et pour le didacticiel de placement des tables.

  +o  Owen Taylor owt1@cornell.edu pour la section sur le widget EventBox
     (et le patch de la distribution). Il est aussi responsable des
     sections sur l'criture de vos propres widgets GTK et de
     l'application exemple. Merci beaucoup  Owen pour toute son aide !

  +o  Mark VanderBoom mvboom42@calvin.edu pour son merveilleux travail
     sur les widgets Notebook, Progress Bar, Dialog et File selection.
     Merci beaucoup, Mark ! Ton aide a t trs prcieuse.

  +o  Tim Janik timj@psynet.net pour son beau travail sur le widget
     Lists.  Merci Tim :)

  +o  Rajat Datta rajat@ix.netcom.com pour son excellent travail sur le
     didacticiel sur les Pixmaps.

  +o  Michael K. Johnson johnsonm@redhat.com pour ses infos et le code
     pour les menus.

  Et  tous ceux d'entre vous qui ont comment et aid  amliorer ce
  document.

  Merci.


  2244..  CCooppyyrriigghhtt

  Ce didacticiel est  Copyright (C) 1997 Ian Main

  Ce programme est un logiciel libre ; vous pouvez le redistribuer et/ou
  le modifier sous les termes de la licence publique gnrale GNU telle
  qu'elle est publie par la Free Software Foundation ; soit la version 2
  de la licence, ou (comme vous voulez) toute version ultrieure.

  Ce programme est distribu dans l'espoir qu'il sera utile, mais SANS
  AUCUNE GARANTIE ; mme sans la garantie de COMMERCIALIT ou d'ADQUATION
  A UN BUT PARTICULIER. Voir la licence publique gnrale GNU pour plus de
  dtails.

  Vous devriez avoir reu une copie de la licence publique gnrale GNU
  avec ce programme ; si ce n'est pas le cas, crivez  la Free Software
  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.


  2244..11..  NNootteess dduu ttrraadduucctteeuurr CCee ddooccuummeenntt aa tt aaddaapptt eenn FFrraannaaiiss ppaarrrriicc
  JJaaccoobboonnii <<mailto:jaco@dotcom.fr>. Toute remarque sur cette adaptation
  sera la bienvenue.

  Merci  Jol Bernier <mailto:kheops@linux-kheops.com> pour avoir initi
  cette adaptation, et  Vincent Renardias <mailto:vincent@debian.org>
  pour sa relecture.



