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

  1.  Introduction

  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.


  2.  Bien dbuter

  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
  autoconf de GNU pour se configurer. Lorsque vous l'aurez dtarr, tapez
  ./configure --help 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 gtk_init(gint *argc, gchar ***argv) 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 gdk_init(gint *argc, gchar ***argv).  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 gtk_widget_show() 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 ();




  gtk_main() 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.



  2.1.   Bonjour tout le monde  en GTK

  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;
  }





  2.2.  Compilation de  Bonjour tout le monde

  Supposons que vous avez sauvegard le code prcdent dans un fichier nomm
  bonjour.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 gcc 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_print() est utilise dans cet exemple. GTK est construit au
     dessus de glib 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.


  2.3.  Thorie des signaux et des rappels

  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
  gtk_main 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 gtk_signal_connect() 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);




  gtk_signal_connect_object() est la mme chose que gtk_signal_connect()
  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 gtk_signal_connect_object. 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 GtkWidget
  comme paramtre et vous pouvez donc  utiliser
  gtk_signal_connect_object() pour celles-ci, tandis que pour vos
  fonctions vous pouvez avoir besoin d'avoir de fournir plus de donnes
  aux fonctions de rappel.


  2.4.   Bonjour tout le monde  pas  pas

  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 gtk_main_quit().  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 main()... 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
  GtkWidget.  Ceux-ci sont utiliss plus loin pour crer une fentre et un
  bouton.



      GtkWidget *window;
      GtkWidget *button;




  Et revoici notre gtk_init.  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 argc et argv 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 GtkWidget et window 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 gtk_widget_show(window) 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 gtk_widget_destroy() en lui passant le
  widget window comme objet  dtruire.  Ici, on appelle juste la fonction
  destroy() 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 button 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 hello() soit appele. On
  ignore les paramtres et on ne passe donc que la valeur NULL  la
  fonction de rappel hello().  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 hello() 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 gtk_widget_destroy() n'accepte que GtkWidget
  *widget comme paramtre, on utilise ici la fonction
  gtk_signal_connect_object()  la place de gtk_signal_connect().



           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 window 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 gtk_main() qui attendra les vnements venant du
  serveur X et demandera aux widgets d'mettre les signaux lorsque ces
  vnements surviendront.


           gtk_main ();




  Enfin, le return final. Il est excut lorsque gtk_quit() 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 hello() est appele
  avec le paramtre NULL, puis le gestionnaire suivant de ce signal est
  son tour appel. Il appelle la fonction gtk_widget_destroy() en lui
  passant le widget window 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 destroy() 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
  delete_event(). 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 destroy()
  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.



  3.  Continuons


  3.1.  Types de donnes

  Vous avez probablement not certaines choses qui ncessitent des
  explications dans les exemples prcdents. les gint, gchar, etc. que
  vous avez pu voir sont des redfinitions de int et char,
  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 gint32 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
  glib/glib.h (qui est inclus par gtk.h).

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


  3.2.  Complments sur les gestionnaires de signaux

  Regardons  nouveau la dclaration de gtk_signal_connect.



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




  Vous avez remarqu que le valeur de retour est de type gint ? 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
  signal_connect, 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.



  3.3.  Un  Bonjour tout le monde  amlior

  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 gtk_box_pack_start() en lisant la section suivante.
  Essayez de redimensionner la fentre, et observez son comportement.

  Juste une remarque : il existe une autre constante utilisable avec
  gtk_window_new() - 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.





  4.  Placement des widgets

  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
  gtk_container_add 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.


  4.1.  Thorie des botes de placement

  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 gtk_hbox_new(), et
  pour les botes verticales, gtk_vbox_new(). Les fonctions
  gtk_box_pack_start() et gtk_box_pack_end() servent  placer les objets
  l'intrieur de ces containers.  La fonction gtk_box_pack_start()
  placera de haut en bas dans une bote verticale et de gauche  droite
  dans une bote horizontale.  gtk_box_pack_end() 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 gtk_box_pack_start(). 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.


  4.2.  Dtails sur les botes

   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 (hbox) contenant plusieurs
  boutons. L'appel  gtk_box_pack 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 gtk_box_pack_start()).

  Voici la dclaration de la fonction gtk_box_pack_start.




  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 expand de gtk_box_pack_start() ou gtk_box_pack_end()
  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 expand 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 gtk_box_pack_start ou pack_end.

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

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



       GtkWidget * gtk_hbox_new (gint homogeneous,
                                 gint spacing);




  Le paramtre homogeneous de gtk_hbox_new (et c'est la mme chose pour
  gtk_vbox_new) 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 expand des fonctions gtk_box_pack sera
  toujours mis  TRUE.

  Quelle est alors la diffrence entre les paramtres spacing (configur
  lorsque la bote est cre) et padding (configur lorque les lments sont
  placs) ? spacing ajoute de l'espace entre les objets, et padding 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.


  4.3.  Programme de dmonstration des placements







  #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;
  }






  4.4.  Placement avec les tables

  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
  gtk_table_new :



       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 homogeneous 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  gtk_table_new. Ainsi, avec rows = 2 et columns = 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 (table) est la table que l'on a cre et le second
  (child) est le widget que l'on veut placer dans la table.

  Les paramtres left_attach et right_attach 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, left_attach vaudra 1,
  right_attach vaudra 2; top_attach vaudra 1 et bottom_attach 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 xoptions et yoptions 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 padding 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);




  xoptions et options valent par dfaut GTK_FILL | GTK_EXPAND, et
  xpadding et ypadding valent 0. Les autres paramtres sont les mmes que
  ceux de la fonction prcdente.

  Il existe aussi les fonctions gtk_table_set_row_spacing() et
  gtk_table_set_col_spacing(). 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.


  4.5.  Exemple de placement avec table

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


  5.  Vue d'ensemble des widgets


  Les tapes pour crer un widget en GTK sont :

  1. gtk_*_new() - 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 gtk_container_add() ou gtk_box_pack_start().

  5. Affichage du widget grce  gtk_widget_show().

  gtk_widget_show() 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 gtk_widget_hide() 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 gtk_widget_show().


  5.1.  Conversions de types

  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 GtkWidgets drivent d'une classe de base GtkObject. 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 GtkContainer. 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.



  5.2.  La hirarchie des widgets

  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






  5.3.  Widgets sans fentre

  Les widgets suivants n'ont pas de fentre associe. Si vous voulez
  capturer des vnements, vous devez utiliser GtkEventBox.  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 testgtk.c livr avec GTK. Il
  se trouve dans le rpertoire gtk/


  6.  Widgets boutons


  6.1.  Boutons normaux

  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 gtk_button_new_with_label() pour crer un bouton avec un
  label, ou gtk_button_new() 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 gtk_box_pack_start, utilisez alors
  gtk_container_add pour placer la bote dans le bouton.


  Voici un exemple d'utilisation de gtk_button_new() 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 xpm_label_box() peut tre utilise pour placer des xpms et
  des labels sur tout widget qui peut tre container.


  6.2.  Boutons commutateurs

  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".


  6.3.  Cases  cocher

  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 new_with_label 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.


  6.4.  Boutons radio

  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...]



  7.  Widgets divers


  7.1.  Labels

  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.


  7.2.  Le widget bulle d'aide

  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 testgtk.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 GtkTooltip 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 :)
  7.3.  Barres de progression

  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 gtk_progress_bar_update
  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 pbar.



       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  pvalue 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 pvalue.

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


  7.4.  Botes de dialogue


  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.



  7.5.  Pixmaps

  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 GdkPixmap 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 data. width et height sont
  exprims en pixels. Le pointeur vers un GdkWindow 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 data. fg et bg
  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 filename
  doit contenir une image dans ce format qui sera charge dans la
  structure pixmap. Le masque mask indique quels sont les bits opaques
  du pixmap. Tous les autres bits sont coloriss en utilisant la couleur
  spcifie par transparent_color. 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 data 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
  gdk_pixmap_destroy.  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);





  gtk_pixmap_set sert  changer le pixmap pris en charge par le widget.
  val 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 icon0.xpm 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 );




  8.  Widgets containers

  8.1.  Bloc-notes

  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);




  GtkPositionType 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*notebook en les insrant
  la fin (append) ou au dbut (prepend). *child est le widget qui est
  plac dans la page du bloc-notes, et *tab_label 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 _append_ et _prepend_ sauf qu'il y en
  a un de plus : position. 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 page_num du widget *notebook.

  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
  gtk_notebook_next_page, on revient  la premire page. De mme, si l'on
  est sur la premire page et que l'onappelle gtk_notebook_prev_page, 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);




  show_tabs et show_border peuvent valoir TRUE ou FALSE (0 ou 1).

  Voyons maintenant un exemple, il est tir du code de testgtk.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.



  8.2.  Fentres avec barres de dfilement

  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
  gtk_widget_set_usize() pour configurer la taille par dfaut de la
  fentre et des autres widgets.



  9.  Widgets listes

  Le widget GtkList sert de container vertical pour des widgets
  GtkListItem.

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

  On doit d'abord connatre l'utilisation des GList et des fonctions
  g_list_*() qui leur sont lies pour pouvoir utiliser pleinement le
  widget GtkList.

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





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




  Le champ selection d'un GtkList 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
  GTK_LIST()->selection mais on ne doit pas le modifier car ses champs
  internes sont grs par les fonctions gtk_list_*().

  Le champ selection_mode dtermine les options de slection d'un GtkList
  et donc le contenu du champ du GTK_LIST()->selection :

  selection_mode peut avoir l'une des valeurs suivantes :

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

  o  GTK_SELECTION_BROWSE - selection 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 - selection 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 - selection vaut toujours NULL.

  La valeur par dfaut est GTK_SELECTION_MULTIPLE.


  9.1.  Signaux



       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 gtk_list_select_item(),
  gtk_list_select_child() 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 gtk_list_unselect_item(),
  gtk_list_unselect_child(), et lors d'appuis de boutons. Quelques fois,
  il est indirectement dclench lorsque des fils sont ajouts ou supprims
  du GtkList.



  9.2.  Fonctions



       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 LIST,  partir de POSITION.  ITEMS est une liste
  doublement chane o chaque noeud doit pointer vers un nouveau
  GtkListItem. Les noeuds GList de ITEMS sont pris en charge par LIST.




       void gtk_list_append_items (GtkList *LIST, GList *ITEMS)




  Insre des items  la fin de LIST selon le mme principe que
  gtk_list_insert_items. Les noeuds GList de ITEMS sont pris en charge
  par LIST.



       void gtk_list_prepend_items (GtkList *LIST, GList *ITEMS)





  Insre des items au dbut de LIST selon le mme principe que
  gtk_list_insert_items. Les noeuds GList de ITEMS sont pris en charge
  par LIST.



       void gtk_list_remove_items (GtkList *LIST, GList *ITEMS)




  te des items de LIST. ITEMS est une liste doublement chane dont chaque
  noeud pointe vers un fils direct de LIST. Il est de la responsabilit
  de l'appelant de faire un appel g_list_free(ITEMS) 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 LIST. Un widget est concern si sa position
  courante dans LIST est dans l'intervalle spcifi par START et END.



       void gtk_list_select_item (GtkList *LIST, gint ITEM)




  Invoque le signal GtkList::select_child pour un item spcifi par sa
  position courante dans LIST.



       void gtk_list_unselect_item (GtkList *LIST, gint ITEM)




  Invoque le signal GtkList::unselect_child pour un item spcifi par sa
  position courante dans LIST.



       void gtk_list_select_child (GtkList *LIST, GtkWidget *CHILD)




  Invoque le signal GtkList::select_child pour le fils CHILD spcifi.



       void gtk_list_unselect_child (GtkList *LIST, GtkWidget *CHILD)




  Invoque le signal GtkList::unselect_child pour le fils CHILD spcifi.



  gint gtk_list_child_position (GtkList *LIST, GtkWidget *CHILD)




  Retourne la position de CHILD dans LIST. Retourne -1 en cas d'erreur.



       void gtk_list_set_selection_mode (GtkList *LIST, GtkSelectionMode MODE)




  Configure LIST dans le mode de slection MODE 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 Standard
  Macros::, pour plus d'informations.



       GtkListClass* GTK_LIST_CLASS (gpointer CLASS)




  Convertit un pointeur gnrique en  GtkListClass* .  Voir Standard
  Macros::, pour plus d'informations.



       gint GTK_IS_LIST (gpointer OBJ)




  Dtermine si un pointeur gnrique rfrence un objet  GtkList . Voir
  Standard Macros::, pour plus d'informations.


  9.3.  Exemple

  Voici un programme affichant les changements de slection dans une
  GtkList 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");
  }





  9.4.  Widget item de liste

  Le widget GtkListItem 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 GtkListItem a sa propre fentre pour recevoir les vnements et a sa
  propre couleur de fond, habituellement blanche.

  Comme il est directement driv d'un GtkItem, 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 GtkListItem contient juste un label pour
  identifier, par exemple, un nom de fichier dans un GtkList -- la
  fonction approprie gtk_list_item_new_with_label() est donc fournie. Le
  mme effet peut tre obtenu en crant un GtkLabel part, en configurant
  son alignement avec xalign=0 et yalign=0.5 suivi d'un ajout ultrieur
  au GtkListItem.

  Tout comme on n'est pas forc d'ajouter un GtkLabel  un GtkListItem, on
  peut aussi ajouter un GtkVBox ou un GtkArrow etc.  un GtkListItem.


  9.5.  Signaux

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



  9.6.  Fonctions




       guint gtk_list_item_get_type (void)




  Retourne l'identificateur du type  GtkListItem .



       GtkWidget* gtk_list_item_new (void)



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



       GtkWidget* gtk_list_item_new_with_label (gchar *LABEL)




  Cration d'un objet GtkListItem ayant un simple GtkLabel comme seul
  fils. 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_item_select (GtkListItem *LIST_ITEM)




  Cette fonction est surtout un emballage de gtk_item_select (GTK_ITEM
  (list_item)) qui mettra le signal GtkItem::select.  Voir GtkItem::,
  pour plus d'informations.



       void gtk_list_item_deselect (GtkListItem *LIST_ITEM)




  Cette fonction est surtout un emballage de gtk_item_deselect (GTK_ITEM
  (list_item)) qui mettra le signal GtkItem::deselect. Voir GtkItem::,
  pour plus d'informations.



       GtkListItem* GTK_LIST_ITEM (gpointer OBJ)




  Convertit un pointeur gnrique en GtkListItem*. Voir Standard Macros::
  pour plus d'informations.



       GtkListItemClass* GTK_LIST_ITEM_CLASS (gpointer CLASS)




  Convertit un pointeur gnrique en GtkListItemClass*. Voir Standard
  Macros:: pour plus d'informations.



       gint GTK_IS_LIST_ITEM (gpointer OBJ)




  Dtermine si un pointeur gnrique se rfre  un objet GtkListItem.  Voir
  Standard Macros:: pour plus d'informations.
  9.7.  Exemple

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



  10.  Widgets slections de fichiers

  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 ok_button, cancel_button, et
  help_button pour prciser leurs utilisations.
  Voici un exemple emprunt  testgtk.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;
       }





  11.  Widgets Menu

  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'usine  menus
  (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 gtk_menu_factory. 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.


  11.1.  Cration manuelle de menus

  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
  gtk_container_add pour la placer dans une fentre, ou les fonctions
  box_pack 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 gtk_widget_show), 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 gtk_menu_new()
  et un  item de menu  cr avec les fonctions gtk_menu_item_new(). 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 gtk_menu_new_with_label() et gtk_menu_new() 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 gtk_menu_new()

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

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

  o  Utiliser gtk_menu_item_set_submenu() 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 gtk_menu_bar_new(). 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 gtk_menu_bar_append() 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 gtk_menu_popup() 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 event pour savoir o faire surgir le menu.

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

  o  Lier ce gestionnaire  un widget avec :

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


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


  11.2.  Exemple de menu manuel



























































  #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.


  11.3.  Utilisation de GtkMenuFactory

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


  11.4.  Exemple d'usine  menu

  Voici un exemple utilisant l'usine  menu de GTK. Le premier fichier
  est menus.h.  Nous sparerons menus.c et main.c cause des variables
  globales utilises dans le fichier menus.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 menus.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 main.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, main.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 makefile 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.



  12.  Widgets non documents

  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.


  12.1.  Entres de texte



  12.2.  Slections de couleurs



  12.3.  Contrle d'intervalle



  12.4.  Rgles



  12.5.  Botes de texte



  12.6.  Prvisualisation

  (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!







  12.7.  Courbes




  13.  Widget EventBox

  Il n'est disponible que dans gtk+970916.tar.gz 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, EventBox est fait
  pour vous.

  Au premier abord, le widget EventBox 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 EventBox :



       gtk_container_add (GTK_CONTAINER(event_box), widget);





  L'exemple suivant montre l'utilisation d'un EventBox - 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;
  }





  14.  Configuration des attributs de widget

  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);







  15.  Temporisations, fonctions d'E/S et d'attente


  15.1.  Temporisations

  Vous pouvez vous demander comment faire pour que GTK fasse quelque
  chose d'utile lorsqu'il est dans gtk_main. En fait, on a plusieurs
  options. L'utilisation des fonctions suivantes permet de crer une
  temporisation qui sera appele tous les interval 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);





  15.2.  Surveillance des E/S

  Une autre caractristique intressante du GTK est la possibilit de
  vrifier les donnes d'un descripteur de fichier (celles retournes par
  open(2) ou socket(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 function lorsqu'il y a une donne prte tre lue
  dans le descripteur de fichier.

  GDK_INPUT_WRITE - Appel de function 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);






  15.3.  Fonctions d'attente

  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 function 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 gtk_idle_add() sera appele  chaque occasion. Comme
  pour les autres, retourner FALSE empchera la fonction d'attente d'tre
  appele.


  16.  Gestion des slections

  16.1.  Introduction

  Un type de communication inter-processus gre par GTK est les
  slections. 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 propritaire) 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 cibles. Il peut y avoir un nombre quelconque de slections,
  mais la plupart des applications X n'en grent qu'une, la slection
  primaire.


  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'atome. 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 gtk.h correspondent  ces atomes. Par exemple, la
  constante GDK_PRIMARY_SELECTION correspond  la chane "PRIMARY".  Dans
  d'autres cas, on doit utiliser les fonctions gdk_atom_intern(), pour
  obtenir l'atome correspondant  une chane, et gdk_atom_name(), pour
  obtenir le nom d'un atome. Les slections et les cibles sont identifis
  par des atomes.


  16.2.  Rcupration de la slection

  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 convertit la slection dans la forme spcifie par target. Si tout
  est possible, le paramtre time 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;
       };




  selection et target sont les valeurs que l'on a donn dans notre appel
  gtk_selection_convert(). type 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. format donne la longueur des units (les
  caractres, par exemple) en bits. Habituellement, on ne se proccupe pas
  de cela lorsqu'on reoit des donnes. data est un pointeur vers la donne
  retourne et length donne la longueur en octets de la donne retourne.
  Si length 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 length ; 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;
  }





  16.3.  Fournir la slection


  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);




  widget, selection et target identifient les requtes que ce
  gestionnaire grera. S'il ne vaut pas NULL, remove_func 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  data.

  La fonction de rappel function doit avoir la signature suivante :



       typedef void (*GtkSelectionFunction) (GtkWidget *widget,
                                             GtkSelectionData *selection_data,
                                             gpointer data);




  Le GtkSelectionData est le mme qu'au dessus, mais, cette fois, nous
  sommes responsables de l'initialisation de ses champs type, format,
  data, et length. (Le champ format 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;
  }







  17.  glib

  La glib 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 libc, et je ne les dtaillerais donc pas
  trop. Ceci doit surtout servir de rfrence afin de savoir ce qui est
  disponible pour tre utilis.


  17.1.  Dfinitions

  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





  17.2.  Listes doublement chanes

  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);







  17.3.  Listes simplement chanes

  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);






  17.4.  Gestion de la mmoire



       gpointer g_malloc      (gulong    size);




  Remplace malloc().  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 size octets de mmoire  partir de mem. 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
  #define MEM_PROFILE au dbut de glib/gmem.c, de refaire un make et un
  make install.



       void     g_mem_check   (gpointer  mem);




  Vrifie qu'un emplacement mmoire est valide. Ncessite que l'on ajoute
  #define MEM_CHECK au dbut de gmem.c que l'on refasse un make et un
  make install.


  17.5.  Timers

  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);







  17.6.  Gestion des chanes

  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,
                                    ...);






  17.7.  Utilitaires et fonctions d'erreurs



       gchar* g_strdup    (const gchar *str);




  Remplace la fonction strdup. 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 perror() 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 printf, 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 printf().

  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  glib.h. Si quelqu'un
  s'occupe de documenter une fonction, qu'il m'envoit un courrier !



  18.  Fichiers rc de GTK

  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.


  18.1.  Fonctions pour les fichiers rc

  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 GtkButton 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.


  18.2.  Format des fichiers rc de GTK

  Le format du fichier GTK est illustr dans l'exemple suivant. Il s'agit
  du fichier testgtkrc 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  fg  et  bg  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
  Red, Green et Blue 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.

  bg_pixmap est trs similaire, sauf que les couleurs sont remplaces par
  un nom de fichier.

  pixmap_path est une liste de chemins spars par des  : . Ces chemins
  seront parcourus pour chaque pixmap que l'on spcifie.


  La directive font 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 xfontsel ou d'un autre
  utilitaire semblable peut aider.

   widget_class  configure le style d'une classe de widgets. Ces classes
  sont listes dans la section sur la hirarchie des widgets.

  La directive  widget  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 gtk_widget_set_name(). 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  parent  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.



  18.3.  Exemple de fichier rc

































































  # 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"






  19.  criture de vos propres widgets


  19.1.  Vue d'ensemble

  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.


  19.2.  Anatomie d'un widget

  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 structure de la
  classe.  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 GtkButton
  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
  GtkContainerClass 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
  structure d'objet. Pour la classe Button, 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.


  19.3.  Cration d'un widget compos

  19.3.1.  Introduction

  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 FileSelection
  et ColorSelection 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 Tictactoe, 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.


  19.3.2.  Choix d'une classe parent

  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 FileSelection est la classe Dialog. Comme nos
  boutons seront mis sous la forme d'un tableau, il semble naturel
  d'utiliser la classe GtkTable comme parent. Malheureusement, cela ne
  peut marcher. La cration d'un widget est divise en deux fonctions --
  WIDGETNAME_new() que l'utilisateur appelle, et WIDGETNAME_init() qui
  ralise le travail d'initialisation du widget indpendamment des
  paramtre passs  la fonction _new(). Les widgets fils n'appellent que
  la fonction _init 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 gtk_table_new() dans notre
  widget Tictactoe, nous ferions mieux d'viter de le driver de GtkTable.
  Pour cette raison, nous la driverons plutt de GtkVBox et nous
  placerons notre table dans la VBox.





  19.3.3.  The header file

  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__ */





  19.3.4.  La fonction  _get_type()

  Continuons maintenant avec l'implantation de notre widget. La fonction
  centrale pour chaque widget est WIDGETNAME_get_type(). 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 GtkTypeInfo 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 arg_func 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.


  19.3.5.  La fonction _class_init()

  La fonction WIDGETNAME_class_init() 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  name : Le nom du signal signal.

  o  run_type : 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  object_type : L'ID de l'objet auquel s'applique ce signal (il
     s'appliquera aussi au descendants de l'objet).


  o  function_offset : L'offset d'un pointeur vers le gestionnaire par
     dfaut dans la structure de classe.

  o  marshaller : 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 gtk_signal_default_marshaller().

  o  return_val : Type de la valeur retourne.

  o  nparams : 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 GtkArgType :



       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;





  gtk_signal_new() retourne un identificateur entier pour le signal, que
  l'on stocke dans le tableau tictactoe_signals, 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
  gtk_object_class_add_signals(). 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.


  19.3.6.  La fonction _init()


  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]);
        }
  }





  19.3.7.  Et le reste...


  Il reste une fonction que chaque widget (sauf pour les types widget de
  base, comme GtkBin, qui ne peuvent tre instancis)  besoin d'avoir --
  celle que l'utilisateur appelle pour crer un objet de ce type. Elle
  est conventionnellement appele WIDGETNAME_new(). 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.


  tictactoe_clear() est une fonction publique qui remet tous les boutons
  du widget en position relche. Notez l'utilisation de
  gtk_signal_handler_block_by_data() pour empcher notre gestionnaire de
  signaux des boutons commutateurs d'tre dclench sans besoin.


  tictactoe_toggle() 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;
  }





  19.4.  Cration d'un widget  partir de zro

  19.4.1.  Introduction


  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.


  19.4.2.  Afficher un widget  l'cran

  Il y a plusieurs tapes mises en jeu lors de l'affichage. Lorsque le
  widget est cr par l'appel WIDGETNAME_new(), plusieurs autres fonctions
  supplmentaires sont requises.


  o  WIDGETNAME_realize() s'occupe de crer une fentre X pour le widget,
     s'il en a une.

  o  WIDGETNAME_map() est invoque aprs l'appel de gtk_widget_show().
     Elle s'assure que le widget est bien trac  l'cran (mapp). Dans le
     cas d'une classe container, elle doit aussi appeler des fonctions
     map()> pour chaque widget fils.

  o  WIDGETNAME_draw() est invoque lorsque gtk_widget_draw() 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
     gtk_widget_draw() pour ses widgets fils.

  o  WIDGETNAME_expose() 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 draw() 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  draw().


  Les widgets container, mme s'ils ne se soucient pas eux-mmes de la
  diffrence, ne peuvent pas utiliser simplement la fonction draw() 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 WIDGETNAME_paint() 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 draw() par dfaut et n'implanter que
  la fonction expose().


  19.4.3.  Origines du widget Dial

  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 deus ex machina. 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.

  19.4.4.  Les bases

  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 init() fait moins de choses que pour le
  widget Tictactoe car ce n'est pas un widget compos et que la fonction
  new() 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);
  }





  19.4.5.  gtk_dial_realize()

  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 gdk_window_new() 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 gtk_widget_get_events() pour rcuprer le masque d'vnement que
  l'utilisateur a spcifi pour ce widget (avec gtk_widget_set_events())
  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);
       }





  19.4.6.  Ngotiation de la taille

  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
  gtk_dial_size_request(). 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
  gtk_dial_size_allocate(). 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;
           }
       }



  .


  19.4.7.  gtk_dial_expose()

  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 gtk_draw_polygon 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;
  }





  19.4.8.  Gestion des vnements

  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 button de la
  structure du widget et on rcupre tous les vnements souris avec un
  appel gtk_grab_add(). Un dplacement ultrieur de la souris provoque le
  recalcul de la valeur de contrle (par la fonction
  gtk_dial_update_mouse). Selon la politique qui a t choisie, les
  vnements "value_changed" sont, soit gnrs instantanment
  (GTK_UPDATE_CONTINUOUS), aprs un dlai ajout au timer avec
  gtk_timeout_add() (GTK_UPDATE_DELAYED), ou seulement lorsque le bouton
  est relch (GTK_UPDATE_DISCONTINUOUS).






































  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 gtk_dial_update() pour
  valider les paramtres, calculer le nouvel angle du pointeur et
  redessiner le widget (en appelant gtk_widget_draw()).









  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;
      }
  }





  19.4.9.  Amliorations possibles


  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 ProgressBar 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 Button 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 GtkRange.

  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.


  19.5.  En savoir plus

  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 !


  20.  Scribble, un programme simple de dessin

  20.1.  Prsentation

  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 Xinput, 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.
  20.2.  Gestion d'vnement

  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 vnements 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 GdkEventMotion qui ressemble (en partie)
  ceci :



       struct _GdkEventMotion
       {
         GdkEventType type;
         GdkWindow *window;
         guint32 time;
         gdouble x;
         gdouble y;
         ...
         guint state;
         ...
       };




  type sera initialis avec le type de l'vnement, ici GDK_MOTION_NOTIFY,
  window est la fentre dans laquelle l'vnement est survenu. x et y
  donnent les coordonnes de l'vnement et state 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 gtk_signal_connect() 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 gtk_widget_set_events(). 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 EventBox. 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
  GDK_POINTER_MOTION_MASK et GDK_BUTTON_PRESS_MASK. On veut aussi savoir
  quand il est ncessaire de redessiner notre fentre, on indique donc
  GDK_EXPOSURE_MASK. Bien que nous voulions tre avertis via un vnement
  Configure lorsque la taille de notre fentre change, on n'a pas besoin
  de prciser le flag GDK_STRUCTURE_MASK correspondant car il est
  automatiquement spcifi pour chaque fentre.


  Il arrive cependant qu'il puisse y avoir un problme en indiquant
  seulement GDK_POINTER_MOTION_MASK. 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
  GDK_POINTER_MOTION_HINT_MASK.


  Avec GDK_POINTER_MOTION_HINT_MASK, 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, gtk_widget_get_pointer() 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;
       }






  20.3.  Le widget DrawingArea et le dessin

  Revenons au processus de dessin sur l'cran. Le widget que l'on utilise
  pour ceci est le widget DrawingArea. 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
  gtk_widget_set_usize() 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 DrawingArea, nous sommes
  compltement 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 pixmap d'arrire-plan 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 windows indique une fentre GTK de laquelle ce pixmap tire
  certaines de ses proprits. width et height prcisent la taille du
  pixmap. depth prcise la  profondeur de couleur, c'est--dire le nombre
  de bits par pixel, de la nouvelle fentre. Si cette profondeur vaut -1,
  elle correspondra  celle de window.


  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  gdk_draw_rectangle() 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 event->area 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 dessinables. 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, gdk_draw_rectangle() et
  gdk_draw_pixmap(). 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 <gdkgdk.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 contexte graphique (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 fg_gc, bg_gc, dark_gc et light_gc sont indexs par un
  paramtre de type GtkStateType qui peut prendre les valeurs :



       GTK_STATE_NORMAL,
       GTK_STATE_ACTIVE,
       GTK_STATE_PRELIGHT,
       GTK_STATE_SELECTED,
       GTK_STATE_INSENSITIVE




  Par exemple, pour GTK_STATE_SELECTED, la couleur de premier plan par
  dfaut est blanc et la couleur d'arrire plan par dfaut est bleu fonc.


  Notre fonction draw_brush(), 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 area a besoin d'tre
  mise  jour. X gnrera ventuellement un vnement d'exposition (en
  combinant peut-tre les zones passs dans plusieurs appels
  gtk_widget_draw()) 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.


  20.4.  Ajouter le support XInput

  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
  GdkEventMotion, 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;
       };




  pressure indique la pression comme un nombre rel compris entre 0 et 1.
  xtilt et ytilt peuvent prendre des valeurs entre -1 et 1,
  correspondant au degr d'inclinaison dans chaque direction, source et
  deviceid prcisent de deux faons diffrentes le priphrique pour lequel
  l'vnement est survenus. source 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 gdk_input_list_devices() (voir ci-dessous). La
  valeur spciale GDK_CORE_POINTER sert  dsigner le priphrique de
  pointage principal (habituellement la souris).


  20.4.1.  Valider l'information supplmentaire sur un priphrique

  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 GDK_EXTENSION_EVENTS_CURSOR, 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
  GDK_EXTENSION_EVENTS_ALL 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 InputDialog 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.

  20.4.2.  Utiliser l'information supplmentaire d'un priphrique

  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 gdk_input_window_get_pointer()
  au lieu de gdk_window_get_pointer. Cela est ncessaire car
  gdk_window_get_pointer 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 deviceid d'une structure d'vnement. Cette fonction
  retournera une valeur cohrente lorsque les vnements ne sont pas
  autoriss (dans ce cas, event->deviceid aura la valeur
  GDK_CORE_POINTER).

  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 draw_brush() dessine avec une couleur diffrente pour
  chaque event->source 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);
  }





  20.4.3.  En savoir plus sur un priphrique

  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 name qui est, tout
  simplement, le nom que X donne au priphrique. L'autre champ, qui n'est
  pas une information de configuration, est has_cursor. Si has_cursor
  est faux, on doit dessiner notre propre curseur, mais puisque nous
  avons prcis GDK_EXTENSION_EVENTS_CURSOR, nous n'avons pas  nous en
  proccuper.


  Notre fonction print_button_press() 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.





  20.4.4.  Sophistications supplmentaires

  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
  gdk_input_list_devices() 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 gdk_input_list_devices() ne doit pas tre
  modifie directement). Un exemple est donn dans le programme de dessin
  gsumi (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 testinput de la distribution GTK.



  21.  Conseils pour l'criture d'applications GTK

  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 autoconf et automake de GNU !  Ce sont vos amis :) J'ai en
  projet de les prsenter brivement ici.


  22.  Contributions

  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.



  23.  Remerciements

  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 bonjour tout le monde  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@gtk.org 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.


  24.  Copyright

  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.


  24.1.  Ce document a t adapt en Franais parric Jacoboni
  <mailto:jaco@dotcom.fr>. Toute remarque sur cette adaptation sera la
  bienvenue.  Notes du traducteur

  Merci  Jol Bernier <mailto:kheops@linux-kheops.com> pour avoir initi
  cette adaptation, et  Vincent Renardias <mailto:vincent@debian.org>
  pour sa relecture.





































