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

  11..  IInnttrroodduuccttiioonn

  GTK (GIMP Toolkit) a d'abord delopppour re une boe outils pour GIMP
  (General Image Manipulation Program).  GTK est construit sur GDK (GIMP
  Drawing Kit) qui est, avant tout, une encapsulation des fonctions
  Xlib. On l'appelle GIMP toolkit car il fut cr pour delopper GIMP, mais
  il est dormais utilisdans plusieurs projets de logiciels libres. Les
  auteurs sont :

  +o  Peter Mattis   petm@xcf.berkeley.edu

  +o  Spencer Kimball spencer@xcf.berkeley.edu

  +o  Josh MacDonald jmacd@xcf.berkeley.edu


  GTK est essentiellement une interface de programmation (API) orient
  objet.  Bien qu'il soit entiement rit en C, il est implanten utilisant
  la notion de classes et de fonctions de rappel (pointeurs de
  fonctions).

  Un troisie composant, appelglib, remplace certains appels standard et
  comporte quelques fonctions supplentaires pour ger les listes chas,
  etc. Les fonctions de remplacement sont utiliss pour accrore la
  portabilitde GTK car certaines de ces fonctions, comme g_strerror(),
  ne sont pas disponibles ou ne sont pas standard sur d'autres Unix.
  D'autres comportent des amiorations par rapport aux versions de la
  libc : g_malloc(), par exemple, facilite le duggage.


  Ce didacticiel tente de drire du mieux possible GTK, mais il n'est pas
  exhaustif. Il suppose une bonne connaissance du langage C, et de la
  fan de crr des programmes C. Il serait tr prieux au lecteur d'avoir d
  une expience de la programmation X, mais cela n'est pas nessaire. Si
  l'apprentissage de GTK marque vos duts dans l'approche des widgets,
  n'hitez pas faire des commentaires sur ce didacticiel et sur les
  probles qu'il vous a pos  Il y a aussi une API C++ pour GTK (GTK--),
  si vous prez utiliser ce langage, consultez plut la documentation qui
  la concerne. Une encapsulation en Objective C et des liaisons Guile
  sont alement disponibles, mais ne seront pas abords ici.

  J'apprierais beaucoup avoir un ho des probles que vous avez
  rencontrpour apprendre GTK partir de ce document. De plus, toute
  suggestion sur son amioration est la bienvenue.


  22..  BBiieenn dduutteerr

  La premie chose faire est, bien s, de ruper les sources de GTK et de
  les installer. Vous pouvez en obtenir la dernie version sur
  ftp.gimp.org dans le rertoire /pub/gtk. D'autres sources
  d'informations se trouvent sur http://www.gimp.org/gtk. GTK utilise
  _a_u_t_o_c_o_n_f de GNU pour se configurer. Lorsque vous l'aurez darr tapez
  _._/_c_o_n_f_i_g_u_r_e _-_-_h_e_l_p pour consulter la liste des options.

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




  #include <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 idemment le fichier gtk/gtk.h qui dlare
  les variables, fonctions, structures, etc. qui seront utiliss par
  votre application GTK.

  La ligne  :



       gtk_init (&argc, &argv);




  appelle la fonction _g_t_k___i_n_i_t_(_g_i_n_t _*_a_r_g_c_, _g_c_h_a_r _*_*_*_a_r_g_v_) qui sera appel
  dans toutes les applications GTK. Cette fonction configure certaines
  choses pour nous, comme l'aspect visuel et les couleurs par daut, puis
  appelle _g_d_k___i_n_i_t_(_g_i_n_t _*_a_r_g_c_, _g_c_h_a_r _*_*_*_a_r_g_v_).  Cette dernie initialise
  la bibliothue pour qu'elle puisse re utilis, configure les
  gestionnaires de signaux par daut et vifie les paramres pass notre
  application via la ligne de commande en recherchant l'un des
  suivants :


  +o  --display

  +o  --debug-level

  +o  --no-xshm

  +o  --sync

  +o  --show-events

  +o  --no-show-events

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

  Les deux lignes de code suivantes crnt et affichent une fenre.



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

  Le paramre GTK_WINDOW_TOPLEVEL prise que l'on veut que la fenre cre
  suive l'aspect et le placement dinis par le gestionnaire de fenres.
  Plut que de crr une fenre de 0x0, une fenre sans fenre fille est de
  200x200 par daut : on peut ainsi la manipuler facilement.

  La fonction _g_t_k___w_i_d_g_e_t___s_h_o_w_(_) informe GTK que l'on a configurle widget
  et qu'il peut l'afficher.

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



       gtk_main ();




  _g_t_k___m_a_i_n_(_) est un autre appel que vous verrez dans toute application
  GTK. Lorsque le contre atteind ce point, GTK se met en attente d'
  ements X (click sur un bouton, ou appui d'une touche, par exemple), de
  timeouts ou d'entrs-sorties fichier. Dans notre exemple simple,
  cependant, les ements sont ignor.



  22..11..  BBoonnjjoouurr ttoouutt llee mmoonnddee eenn GGTTKK

  OK, rivons 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 paramres sont ignor...
       *  Les fonctions de rappel sont daills plus loin. */

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

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

      /* Si l'on renvoit TRUE dans le gestionnaire du signal "delete_event",
       * GTK ettra le signal "destroy". Retourner FALSE signifie que l'on
       * ne veut pas que la fenre soit druite.
       * Utilispour faire apparare des boes de dialogue du type
       * es-vous s de vouloir quitter ? */

      /* Remplacez FALSE par TRUE et la fenre principale sera druite par
       * un signal delete_event  */

      return (FALSE);
  }

  /* Autre fonction de rappel */

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

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

      GtkWidget *window;
      GtkWidget *button;

      /* Cette fonction est appel dans toutes les applications GTK.
       * Les paramres pass en ligne de commande sont analys et
       * retourn l'application. */

      gtk_init (&argc, &argv);

      /* Crtion d'une nouvelle fenre. */

      window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

      /* Lorsque la fenre reit le signal "delete_event"
       * (envoypar le gestionnaire de fenres en utilisant l'option
       * close ou la barre de titre), on lui demande d'appeler la
       * fonction delete_event() dinie plus haut. La donn pass en
       * paramre la fonction de rappel est NULL et est ignordans le
       * rappel. */

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

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

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

      gtk_container_border_width (GTK_CONTAINER (window), 10);

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

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

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

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

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

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

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

      gtk_container_add (GTK_CONTAINER (window), button);

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

      gtk_widget_show (button);

      /* ... et la fenre. */

      gtk_widget_show (window);

      /* Toutes les applications GTK doivent avoir un gtk_main().
       * Le doulement du programme se termine let attend qu'un
       * ement survienne (touche press ou ement souris). */

      gtk_main ();

      return 0;
  }





  22..22..  CCoommppiillaattiioonn ddee BBoonnjjoouurr ttoouutt llee mmoonnddee ..XXSS 55 22..22.. CCoommppiillaattiioonn ddee
  BBoonnjjoouurr ttoouutt llee mmoonnddee ..XXEE

  Supposons que vous avez sauvegardle code prent dans un fichier
  nomm_b_o_n_j_o_u_r_._c, pour le compiler tapez la commande suivante :



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




  Les bibliothues invoqus ci-dessus doivent toutes re dans vos chemins
  de recherche par daut, sinon, ajoutez -L<library directory> pour que
  _g_c_c recherche dans ces rertoires les bibliothues nessaires. Sur mon
  syste Debian GNU/Linux, par exemple, je dois ajouter -L/usr/X11R6/lib
  pour qu'il trouve les bibliothues X11 (NdT : et c'est pareil sur mon
  syste Red Hat Linux...).

  L'ordre des bibliothues est important. L'iteur de liens doit connare
  les fonctions d'une bibliothue dont il a besoin avant de les traiter.

  Si vous compilez en utilisant des bibliothues statiques, l'ordre dans
  lequel vous listez les bibliothues devient tr important. L'exemple
  donnci-dessus devrait fonctionner dans tous les cas.

  Les bibliothues que l'on utilise sont :

  +o  La bibliothue glib (-lglib), qui contient diverses fonctions. Seule
     _g___p_r_i_n_t_(_) est utilis dans cet exemple. GTK est construit au dessus
     de _g_l_i_b et vous aurez donc toujours besoin de celle-ci. Voir la
     section concernant ``glib'' pour plus de dails.


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

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

  +o  La bibliothue Xlib (-lX11 utilis par GDK.

  +o  La bibliothue Xext (-lXext). Cette dernie contient le code pour les
     pixmaps en moire partag et les autres extensions X.

  +o  La bibliothue mathatique (-lm).  Elle est utilis pour diffentes
     raisons par GTK.


  22..33..  TThhrriiee ddeess ssiiggnnaauuxx eett ddeess rraappppeellss

  Avant de voir en dail le programme Bonjour tout le monde  nous
  parlerons d'abord des ements et des fonctions de rappel.  GTK est
  dirigpar les ements, ce qui signifie qu'il restera inactif dans
  _g_t_k___m_a_i_n jusqu'ce qu'un ement survienne et que le contre soit passla
  fonction appropri.

  Ce passage du contre est rlisen utilisant le concept de signal
  Lorsqu'un ement survient, comme l'appui sur un bouton, le signal
  approprisera is par le widget qui a press C'est de cette fan que GTK
  rlise la plupart de son travail. Pour qu'un bouton rlise une action,
  on configure un gestionnaire de signal pour capturer ces signaux et
  appeler la fonction aduate.  Ceci est fait en utilisant une fonction
  comme :



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




  Ole premier paramre est le widget qui ettra le signal, et le deuxie
  est le nom du signal que l'on souhaite intercepter. Le troisie paramre
  est la fonction que l'on veut appeler quand le signal est captur et le
  quatrie sont les donns que l'on souhaite passer cette fonction.

  La fonction spifi par le troisie paramre s'appelle une fonction de
  rappel et doit re de la forme :
       void callback_func(GtkWidget *widget, gpointer *callback_data);




  Ole premier paramre sera un pointeur vers le widget qui a is le
  signal, et le second un pointeur vers les donns passs par le dernier
  paramre de la fonction _g_t_k___s_i_g_n_a_l___c_o_n_n_e_c_t_(_) drite plus haut.

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



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




  _g_t_k___s_i_g_n_a_l___c_o_n_n_e_c_t___o_b_j_e_c_t_(_) est la me chose que _g_t_k___s_i_g_n_a_l___c_o_n_n_e_c_t_(_)
  sauf que la fonction de rappel utilise un seul paramre : un pointeur
  vers un objet GTK. Lorsqu'on utilise cette fonction pour connecter des
  signaux, le rappel doit re de cette forme :



       void callback_func (GtkObject *object);




  Ol'objet est d'ordinaire un widget. En gal, on ne configure pas de
  rappels pour _g_t_k___s_i_g_n_a_l___c_o_n_n_e_c_t___o_b_j_e_c_t. D'habitude, ceux-ci sont
  utilis pour appeler une fonction GTK acceptant un simple widget ou
  objet comme paramre, comme dans notre exemple.

  La raison pour laquelle il y a deux fonctions pour connecter les
  signaux est simplement de permettre aux fonctions de rappel d'avoir un
  nombre diffent de paramres. De nombreuses fonctions de la bibliothue
  GTK n'acceptent qu'un simple pointeur vers un _G_t_k_W_i_d_g_e_t comme paramre
  et vous pouvez donc  utiliser _g_t_k___s_i_g_n_a_l___c_o_n_n_e_c_t___o_b_j_e_c_t_(_) pour celles-
  ci, tandis que pour vos fonctions vous pouvez avoir besoin d'avoir de
  fournir plus de donns aux fonctions de rappel.


  22..44..  BBoonnjjoouurr ttoouutt llee mmoonnddee ppaass ppaass

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

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



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



  Cette fonction de rappel est un peu spiale. L'ement "delete_event"
  survient lorsque le gestionnaire de fenres l'envoie l'application. On
  doit choisir ce qu'il faut faire de cesements. On peut les ignorer,
  leur donner une ronse, ou simplement quitter l'application.

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



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

           return (FALSE);
       }





  Voici une autre fonction de rappel qui ne fait que quitter
  l'application en appelant _g_t_k___m_a_i_n___q_u_i_t_(_).  Il n'y a pas grand chose
  de plus dire car elle est plut triviale :



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




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




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




  La partie qui suit dlare deux pointeurs sur des structures de type
  _G_t_k_W_i_d_g_e_t.  Ceux-ci sont utilis plus loin pour crr une fenre et un
  bouton.



           GtkWidget *window;
           GtkWidget *button;




  Et revoici notre _g_t_k___i_n_i_t.  Comme premment, il initialise le toolkit
  et analyse les paramres de la ligne de commande. Il supprime chaque
  paramre reconnu de la liste et modifie _a_r_g_c et _a_r_g_v pour faire comme
  si ces paramres n'avaient jamais exist laissant notre application
  analyser les paramres restants.



           gtk_init (&argc, &argv);




  Crtion d'une nouvelle fenre. C'est plut classique. La moire est allou
  pour une structure _G_t_k_W_i_d_g_e_t et _w_i_n_d_o_w pointe donc sur celle-ci. Cela
  configure une nouvelle fenre, mais celle-ci ne sera pas affich tant
  que l'on n'a pas appel_g_t_k___w_i_d_g_e_t___s_h_o_w_(_w_i_n_d_o_w_) vers la fin de notre
  programme.



           window = gtk_window_new (GTK_WINDOW_TOPLEVEL);




  Voici maintenant un exemple de connexion d'un gestionnaire de signal
  un objet : la fenre. Le signal "destroy" est captur Il est is
  lorsqu'on utilise le gestionnaire de fenres pour tuer la fenre (et que
  l'on retourne TRUE dans le gestionnaire "delete_event"), ou lorsqu'on
  utilise l'appel _g_t_k___w_i_d_g_e_t___d_e_s_t_r_o_y_(_) en lui passant le widget _w_i_n_d_o_w
  comme objet druire.  Ici, on appelle juste la fonction _d_e_s_t_r_o_y_(_) dinie
  ci-dessus avec le paramre NULL, ce qui quitte GTK pour nous.

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



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




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

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


           gtk_container_border_width (GTK_CONTAINER (window), 10);




  Cet appel cr un nouveau bouton. Il alloue l'espace moire pour une
  nouvelle structure GtkWidget, l'initialise et fait pointer _b_u_t_t_o_n vers
  elle. Ce bouton portera le label Bonjour tout le monde lorsqu'il sera
  affich



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

  Maintenant, prenons ce bouton et faisons lui faire quelque chose
  d'utile. On lui attache un gestionnaire de signal pour que, lorsqu'il
  ettra le signal "clicked", notre fonction _h_e_l_l_o_(_) soit appel. On
  ignore les paramres et on ne passe donc que la valeur NULL la fonction
  de rappel _h_e_l_l_o_(_).  idemment, le signal "clicked" est is lorsqu'on
  clique sur le bouton avec la souris.



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




  On utilisera aussi ce bouton pour quitter notre programme, ce qui
  permettra d'illustrer la fan dont le signal "destroy" peut venir soit
  du gestionnaire de fenres, soit de notre programme. Quand le bouton
  est "clicked" comme cela est drit plus haut, il appelle d'abord la
  fonction de rappel _h_e_l_l_o_(_) puis celle-ci dans l'ordre dans lequel
  elles sont configurs. On peut avoir autant de fonctions de rappel que
  l'on dire, elles seront exuts selon leur ordre de connexion. Puisque
  la fonction _g_t_k___w_i_d_g_e_t___d_e_s_t_r_o_y_(_) n'accepte que _G_t_k_W_i_d_g_e_t _*_w_i_d_g_e_t comme
  paramre, on utilise ici la fonction _g_t_k___s_i_g_n_a_l___c_o_n_n_e_c_t___o_b_j_e_c_t_(_) la
  place de _g_t_k___s_i_g_n_a_l___c_o_n_n_e_c_t_(_).



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




  Voici un appel de placement, qui sera expliquen dail plus tard, mais
  qui est plut facile comprendre. Il indique simplement GTK que le
  bouton doit re placdans la fenre oil s'affichera.



        gtk_container_add (GTK_CONTAINER (window), button);




  Maintenant, nous avons tout configurcomme on le souhaitait : les
  gestionnaires de signaux sont en place et le bouton est mis dans la
  fenre oil doit se trouver. On demande alors GTK de montrer les widgets
  l'ran. Le widget _w_i_n_d_o_w est affichen dernier afin que la fenre entie
  surgisse d'un coup plut que voir d'abord la fenre s'afficher puis
  ensuite le bouton apparare l'intieur. Il faut dire qu'avec des
  exemples simples comme celui-ci, vous ne ferez pas la diffence.



           gtk_widget_show(button);

           gtk_widget_show (window);




  Bien s, on appelle _g_t_k___m_a_i_n_(_) qui attendra les ements venant du
  serveur X et demandera aux widgets d'ettre les signaux lorsque ces
  ements surviendront.
           gtk_main ();




  Enfin, le _r_e_t_u_r_n final. Il est exutlorsque _g_t_k___q_u_i_t_(_) est appel


           return 0;




  Lorsque l'on clique sur un bouton GTK, le widget et un signal
  "clicked". Afin de pouvoir utiliser cette information, notre programme
  configure un gestionnaire pour capturer ce signal. Ce gestionnaire
  appelle la fonction de notre choix. Dans notre exemple, lorsque le
  bouton que l'on a cr est "clicked", la fonction _h_e_l_l_o_(_) est appel avec
  le paramre NULL, puis le gestionnaire suivant de ce signal est son
  tour appel Il appelle la fonction _g_t_k___w_i_d_g_e_t___d_e_s_t_r_o_y_(_) en lui passant
  le widget _w_i_n_d_o_w comme paramre, ce qui provoque la destruction de
  celui-ci. Ceci force la fenre envoyer un signal "destroy", qui est
  capturson tour et appelle notre fonction de rappel _d_e_s_t_r_o_y_(_) qui ferme
  simplement GTK.

  Une autre fan de procer consiste utiliser le gestionnaire de fenres
  pour druire la fenre. Cela provoquera l'ission du signal
  "delete_event" qui sera pris en charge par notre gestionnaire
  _d_e_l_e_t_e___e_v_e_n_t_(_). S'il retourne FALSE, la fenre restera telle quelle et
  rien ne se passera. Retourner TRUE forcera GTK ettre le signal
  "destroy" qui, bien s, appelera la fonction de rappel _d_e_s_t_r_o_y_(_)
  provoquant la sortie du GTK.


  On remarquera que ces signaux ne sont pas les mes que les signaux
  systes Unix et ne sont pas implant en utilisant ceux-ci, bien que la
  terminologie employ soit presque identique.



  33..  CCoonnttiinnuuoonnss


  33..11..  TTyyppeess ddee ddoonnnnss

  Vous avez probablement notcertaines choses qui nessitent des
  explications dans les exemples prents. les _g_i_n_t, _g_c_h_a_r, etc. que vous
  avez pu voir sont des redinitions de _i_n_t et _c_h_a_r, respectivement. Leur
  raison d're est de s'affranchir des dendances ennuyeuses concernant la
  taille des types de donns simples lorsqu'on rlise des calculs. Un bon
  exemple est _g_i_n_t_3_2 qui dignera un entier codsur 32 bits pour toutes
  les plateformes, que ce soit une station Alpha 64 bits ou un PC i386
  32 bits. Les redinitions de type sont tr simples et intuitives. Elles
  sont toutes drites dans le fichier _g_l_i_b_/_g_l_i_b_._h (qui est inclus par
  _g_t_k_._h).

  On notera aussi la possibilitd'utiliser un _G_t_k_W_i_d_g_e_t lorsque la
  fonction attend un _G_t_k_O_b_j_e_c_t. GTK posse une architecture orient objet,
  et un widget est un objet.


  33..22..  CCoommpplleennttss ssuurr lleess ggeessttiioonnnnaaiirreess ddee ssiiggnnaauuxx

  Regardons nouveau la dlaration de _g_t_k___s_i_g_n_a_l___c_o_n_n_e_c_t.


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




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



             void gtk_signal_disconnect (GtkObject *object, gint id);





  Ainsi, en passant le widget dont on veut supprimer le gestionnaire et
  le marqueur ou identificateur retournpar l'une des fonctions
  _s_i_g_n_a_l___c_o_n_n_e_c_t, on peut donnecter un gestionnaire de signal.

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



             gtk_signal_handlers_destroy (GtkObject *object);




  Cet appel n'a pas trop besoin d'explications. Il e simplement tous les
  gestionnaires de signaux de l'objet passen paramre.



  33..33..  UUnn BBoonnjjoouurr ttoouutt llee mmoonnddee aammiioorr..XXSS 1122 33..33.. UUnn BBoonnjjoouurr ttoouutt llee
  mmoonnddee aammiioorr..XXEE

  udions une version lement amior avec de meilleurs exemples de
  fonctions de rappel. Ceci permettra aussi d'introduire le sujet
  suivant : le placement des wigdets.




















  #include <gtk/gtk.h>

  /* Notre nouveau rappel amior La donn pass cette fonction est
   * imprim sur stdout. */

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

  /* Un autre rappel */

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

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

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

      /* Cette fonction est appel dans toutes les applications GTK.
       * Les paramre pass en ligne de commande sont analys et
       * retourn l'application. */

      gtk_init (&argc, &argv);

      /* Crtion d'une nouvelle fenre. */

      window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

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

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

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

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


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

      gtk_container_border_width (GTK_CONTAINER (window), 10);

      /* Crtion d'une boe pour y placer les widgets.
       * Ceci est drit en dails plus loin dans la section
       * placement  La boe n'est pas matialis, elle est juste
       * utilis comme moyen d'arranger les widgets. */

      box1 = gtk_hbox_new(FALSE, 0);

      /* On met la boe dans la fenre principale. */

      gtk_container_add (GTK_CONTAINER (window), box1);

      /* On cr un nouveau bouton portant le label Bouton 1  */

      button = gtk_button_new_with_label ("Bouton 1");

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

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

      /* Au lieu d'utiliser gtk_container_add, on place ce bouton dans
       * la boe invisible qui a plac dans la fenre. */

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

      /* N'oubliez jamais cette ape qui indique GTK que la configuration
       * de ce bouton est termin et qu'il peut re affich */

      gtk_widget_show(button);

      /* On fait la me chose pour crr un deuxie bouton. */

      button = gtk_button_new_with_label ("Bouton 2");

      /* On appelle la me fonction de rappel avec un paramre diffent,
       * un pointeur sur la chae Bouton 2  */

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

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

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

      gtk_widget_show(button);

      gtk_widget_show(box1);

      gtk_widget_show (window);

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

      gtk_main ();

      return 0;
  }




  Compilez ce programme en utilisant les mes paramres que pour l'exemple
  prent. Vous remarquerez que, maintenant, il est plus difficile de
  quitter le programme : vous devez utiliser le gestionnaire de fenres
  ou une commande shell pour le druire. Un bon exercice pour le lecteur
  serait d'inser un troisie bouton Quitter qui permettrait de sortir du
  programme. Vous pouvez aussi jouer avec les options de
  _g_t_k___b_o_x___p_a_c_k___s_t_a_r_t_(_) en lisant la section suivante. Essayez de
  redimensionner la fenre, et observez son comportement.

  Juste une remarque : il existe une autre constante utilisable avec
  _g_t_k___w_i_n_d_o_w___n_e_w_(_) - GTK_WINDOW_DIALOG.  Ceci permet d'interagir de fan
  un peu diffente avec le gestionnaire de fenres et doit re utilispour
  les fenres temporaires comme les boes de dialogue, par exemple.





  44..  PPllaacceemmeenntt ddeess wwiiddggeettss

  Lorsqu'on cr une application, on veut mettre plus qu'un simple bouton
  dans une fenre. Notre premier exemple Bonjour le monde n'utilisait
  qu'un seul widget et on pouvait donc simplement faire un appel
  _g_t_k___c_o_n_t_a_i_n_e_r___a_d_d pour placer le widget dans la fenre. Mais si l'on
  dire en mettre plus, comment peut-on contrer l'endroit ole widget sera
  positionn? C'est ici que le placement entre en jeu.


  44..11..  TThhrriiee ddeess bbooeess ddee ppllaacceemmeenntt

  La majeure partie du placement est faites en crnt des boes comme dans
  l'exemple ci-dessus. Ce sont des widgets containers invisibles ol'on
  peut placer nos widgets. Elles existent sous deux formes : boes
  horizontales et boes verticales. Lorsque l'on place des widgets dans
  une boe horizontale, les objets sont ins horizontalement de gauche
  droite ou de droite gauche selon l'appel utilis Dans une boe
  verticale, les widgets sont plac de haut en bas ou vice versa. On peut
  utiliser n'importe quelle combinaison de boes l'intieur ou cd'autres
  boes pour crr l'effet dir

  Pour crr une nouvelle boe horizontale, on appelle _g_t_k___h_b_o_x___n_e_w_(_), et
  pour les boes verticales, _g_t_k___v_b_o_x___n_e_w_(_). Les fonctions
  _g_t_k___b_o_x___p_a_c_k___s_t_a_r_t_(_) et _g_t_k___b_o_x___p_a_c_k___e_n_d_(_) servent placer les objets
  l'intieur de ces containers.  La fonction _g_t_k___b_o_x___p_a_c_k___s_t_a_r_t_(_) placera
  de haut en bas dans une boe verticale et de gauche droite dans une boe
  horizontale.  _g_t_k___b_o_x___p_a_c_k___e_n_d_(_) fera le contraire en plant de bas en
  haut et de droite gauche. En utilisant ces fonctions, on peut aligner
  droite ou gauche nos widgets et me les manger de n'importe quelle fan
  pour obtenir l'effet dir Dans la plupart de nos exemples, on utilisera
  _g_t_k___b_o_x___p_a_c_k___s_t_a_r_t_(_). Un objet peut re un autre container ou un
  widget. En fait, de nombreux widgets (dont les boutons) sont eux-mes
  des containers, mais on utilise galement seulement un label dans un
  bouton.

  En utilisant ces appels, GTK sait ovous voulez placer vos widgets et
  il peut donc les dimensionner automatiquement et faire d'autres choses
  bien pratiques. Il existe aussi plusieurs options permettant de priser
  comment les widgets doivent re plac. Comme vous pouvez l'imaginer,
  cette mhode nous donne pas mal de libertpour placer et crr les
  widgets.


  44..22..  DDaaiillss ssuurr lleess bbooeess

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



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

  Chaque ligne contient une boe horizontale (_h_b_o_x) contenant plusieurs
  boutons. L'appel _g_t_k___b_o_x___p_a_c_k indique la fan dont sont plac tous les
  boutons dans la hbox.  Chaque bouton est placdans la hbox de la me fan
  (mes paramres que la fonction _g_t_k___b_o_x___p_a_c_k___s_t_a_r_t_(_)).

  Voici la dlaration de la fonction _g_t_k___b_o_x___p_a_c_k___s_t_a_r_t.




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




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

  Le paramre _e_x_p_a_n_d de _g_t_k___b_o_x___p_a_c_k___s_t_a_r_t_(_) ou _g_t_k___b_o_x___p_a_c_k___e_n_d_(_) contre
  la fan dont le widget est placdans la boe. S'il vaut TRUE, les widgets
  sont dispos dans la boe de fan en occuper tout l'espace. S'il vaut
  FALSE, la boe est rrie pour correspondre la taille du widget. Mettre
  _e_x_p_a_n_d FALSE vous permettra d'aligner droite et gauche vos widgets.
  Sinon, ils s'argiront pour occuper toute la boe. Le me effet pourrait
  re obtenu en utilisant uniquement une des deux fonctions
  _g_t_k___b_o_x___p_a_c_k___s_t_a_r_t ou _p_a_c_k___e_n_d.

  Le paramre _f_i_l_l des fonctions _g_t_k___b_o_x___p_a_c_k contre si de l'espace
  supplentaire doit re allouaux objets eux-mes (TRUE), ou si on doit
  rajouter de l'espace (_p_a_d_d_i_n_g) dans la boe autour des objets (FALSE).
  Il n'a de sens que si le paramre _e_x_p_a_n_d vaut TRUE.

  Lorsque l'on cr une nouvelle boe, on utilise une fonction comme :



       GtkWidget * gtk_hbox_new (gint homogeneous,
                                 gint spacing);




  Le paramre _h_o_m_o_g_e_n_e_o_u_s de _g_t_k___h_b_o_x___n_e_w (et c'est la me chose pour
  _g_t_k___v_b_o_x___n_e_w) vifie que chaque objet de la boe ait la me taille (i.e.
  la me largeur dans une hbox, la me hauteur dans une vbox). S'il vaut
  TRUE, le paramre _e_x_p_a_n_d des fonctions _g_t_k___b_o_x___p_a_c_k sera toujours mis
  TRUE.

  Quelle est alors la diffence entre les paramres _s_p_a_c_i_n_g
  (configurlorsque la boe est cre) et _p_a_d_d_i_n_g (configurlorque les ents
  sont plac) ? _s_p_a_c_i_n_g ajoute de l'espace entre les objets, et _p_a_d_d_i_n_g
  en ajoute de chaque cd'un objet. La figure suivante devrait lairer
  tout cela :


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

  Voici le code utilispour crr les images ci-dessus. J'y ai mis beaucoup
  de commentaires en espant que vous n'aurez pas de proble pour le
  relire. Compilez-le et jouez avec les diffents paramres.


  44..33..  PPrrooggrraammmmee ddee ddoonnssttrraattiioonn ddeess ppllaacceemmeennttss







  #include "gtk/gtk.h"

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

  /* Construction d'une nouvelle hbox remplie de boutons. Les paramres qui
   * nous intessent sont pass cette fonction.
   * On n'affiche pas la boe, mais tout ce qu'elle contient. */

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

      /* Crtion d'une hbox avec les paramres homogeneous et spacing
       * voulus. */

      box = gtk_hbox_new (homogeneous, spacing);

      /* Crtion d'une sie de boutons configur de fan appropri */

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

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

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

      /* Crtion d'un bouton portant un label dendant de la valeur
       * du paramre expand. */

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

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

      /* Me chose que ci-dessus mais sous forme abr. */

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

      /* Rupation du paramre padding sous forme de chae. */

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

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

      return box;
  }

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

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

      gtk_init (&argc, &argv);

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

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

      which = atoi (argv[1]);

      /* Crtion de notre fenre. */

      window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

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

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

      gtk_container_border_width (GTK_CONTAINER (window), 10);


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

      box1 = gtk_vbox_new (FALSE, 0);

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

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

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

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

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

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

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

          /* Affichage du label */

          gtk_widget_show (label);

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

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

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

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

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

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

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

          separator = gtk_hseparator_new ();

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

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

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

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

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

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

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

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

          /* Un autre sarateur */

          separator = gtk_hseparator_new ();

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

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

          break;

      case 2:

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

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

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

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

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

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

          separator = gtk_hseparator_new ();

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

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

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

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

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

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

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

          separator = gtk_hseparator_new ();

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

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

      case 3:

          /* Ceci est une donstration de la possibilitd'utiliser
           * gtk_box_pack_end() pour aligner les widgets droite.
           * On cr d'abord une nouvelle boe comme d'habitude. */

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

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

          label = gtk_label_new ("end");

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

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

          /* Affichage du label. */

          gtk_widget_show (label);

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

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

          /* Sarateur pour le bas. */

          separator = gtk_hseparator_new ();

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

          gtk_widget_set_usize (separator, 400, 5);

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

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

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

      quitbox = gtk_hbox_new (FALSE, 0);

      /* Notre bouton pour quitter. */

      button = gtk_button_new_with_label ("Quit");

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

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

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

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

      /* Placement de la quitbox dans la vbox (box1) */
      gtk_box_pack_start (GTK_BOX (box1), quitbox, FALSE, FALSE, 0);

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

      gtk_container_add (GTK_CONTAINER (window), box1);

      /* Affichage */

      gtk_widget_show (button);
      gtk_widget_show (quitbox);

      gtk_widget_show (box1);

      /* Affichage de la fenre en dernier */

      gtk_widget_show (window);

      /* Ne pas oublier notre fonction principale. */

      gtk_main ();

      /* Le contre revient ici lorsque gtk_main_quit() est appel,
       * jusqu'ce que  gtk_exit() soitutilis. */

      return 0;
  }






  44..44..  PPllaacceemmeenntt aavveecc lleess ttaabblleess

  udions une autre mhode de placement : les tables. Elles peuvent s'aver
  tr utiles dans certaines situations.

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

  La premie chose faire est, bien s, d'udier la fonction _g_t_k___t_a_b_l_e___n_e_w :



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




  Le premier paramre est le nombre de lignes de la table et le deuxie,
  le nombre de colonnes.

  Le paramre _h_o_m_o_g_e_n_e_o_u_s s'occupe de la fan dont les cases de la table
  seront dimensionns. Si homogeneous vaut TRUE, les cases prennent la
  taille du plus grand widget de la table. S'il vaut FALSE, la taille
  des cases dend du widget le plus haut de la ligne et du plus large de
  cette colonne.

  Le nombre de lignes et colonnes va de 0 n, on est le nombre spifidans
  l'appel _g_t_k___t_a_b_l_e___n_e_w. Ainsi, avec _r_o_w_s = 2 et _c_o_l_u_m_n_s = 2, la table
  ressemblera ceci :


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




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



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





  Ole premier paramre (_t_a_b_l_e) est la table que l'on a cre et le second
  (_c_h_i_l_d) est le widget que l'on veut placer dans la table.

  Les paramres _l_e_f_t___a_t_t_a_c_h et _r_i_g_h_t___a_t_t_a_c_h spifient l'emplacement du
  widget et le nombre de cases utiliser. Par exemple, si on veut placer
  un bouton dans le coin infieur droit de la table drite plus haut et
  que l'on dire ne remplir QUE cette case, _l_e_f_t___a_t_t_a_c_h vaudra 1,
  _r_i_g_h_t___a_t_t_a_c_h vaudra 2; _t_o_p___a_t_t_a_c_h vaudra 1 et _b_o_t_t_o_m___a_t_t_a_c_h vaudra 2.

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

  Les paramres _x_o_p_t_i_o_n_s et _y_o_p_t_i_o_n_s servent priser les options de
  placement et peuvent re combins par un OU logique pour permettre des
  options multiples.

  Ces options sont :

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

  +o  GTK_SHRINK - Si la table a moins de place qu'il ne lui en faut (g
     alement, cause d'un redimensionnement de la fenre par
     l'utilisateur), les widgets sont alors simplement pouss vers le bas
     de la fenre et disparaissent. Si GTK_SHRINK est spifi les widgets
     se ruiront en me temps que la table.

  +o  GTK_EXPAND - Cette option provoque l'extension de la table pour
     qu'elle utilise tout l'espace restant dans la fenre.

  Le paramres de _p_a_d_d_i_n_g jouent le me re que pour les boes, il crnt une
  zone libre, spifi en pixels, autour du widget.

  gtk_table_attach() a BEAUCOUP d'options.  Voici donc une
  fonction-raccourci :
       void gtk_table_attach_defaults (GtkTable   *table,
                                       GtkWidget  *widget,
                                       gint        left_attach,
                                       gint        right_attach,
                                       gint        top_attach,
                                       gint        bottom_attach);




  _x_o_p_t_i_o_n_s et _o_p_t_i_o_n_s valent par daut GTK_FILL | GTK_EXPAND, et _x_p_a_d_d_i_n_g
  et _y_p_a_d_d_i_n_g valent 0. Les autres paramres sont les mes que ceux de la
  fonction prente.

  Il existe aussi les fonctions _g_t_k___t_a_b_l_e___s_e_t___r_o_w___s_p_a_c_i_n_g_(_) et
  _g_t_k___t_a_b_l_e___s_e_t___c_o_l___s_p_a_c_i_n_g_(_). Elles permettent de placer des espaces
  apr une ligne ou une colonne.



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




  et


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




  Pour les colonnes, l'espace est ajoutdroite de la colonne et pour les
  lignes, il est ajouten dessous.

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



       void gtk_table_set_row_spacings (GtkTable *table,
                                        gint      spacing);




  Et,


       void gtk_table_set_col_spacings (GtkTable  *table,
                                        gint       spacing);




  Avec ces appels, la dernie ligne et la dernie colonne n'ont pas
  d'espace supplentaire.




  44..55..  EExxeemmppllee ddee ppllaacceemmeenntt aavveecc ttaabbllee

  Pour le moment, udiez l'exemple sur les tables (testgtk.c)
  distribuavec les sources de GTK.


  55..  VVuuee dd''eennsseemmbbllee ddeess wwiiddggeettss


  Les apes pour crr un widget en GTK sont :

  1. _g_t_k___*___n_e_w_(_) - une des fonctions disponibles pour crr un nouveau
     widget. Ces fonctions sont drites dans cette section.

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

  3. Configuration des attributs du widget.

  4. Placement du widget dans un container en utilisant un appel
     appropricomme _g_t_k___c_o_n_t_a_i_n_e_r___a_d_d_(_) ou _g_t_k___b_o_x___p_a_c_k___s_t_a_r_t_(_).

  5. Affichage du widget gre _g_t_k___w_i_d_g_e_t___s_h_o_w_(_).

  _g_t_k___w_i_d_g_e_t___s_h_o_w_(_) permet GTK de savoir que l'on a fini de configurer
  les attributs du widget et qu'il est pr re affich On peut aussi
  utiliser _g_t_k___w_i_d_g_e_t___h_i_d_e_(_) pour le faire disparare. L'ordre dans
  lequel on affiche les widgets n'est pas important, mais il est prable
  d'afficher la fenre en dernier pour qu'elle surgisse d'un seul coup
  plut que de voir les diffents widgets apparare l'ran au fur et mesure.
  Les fils d'un widget (une fenre est aussi un widget) ne seront pas
  affich tant que la fenre elle-me n'est pas affich par la fonction
  _g_t_k___w_i_d_g_e_t___s_h_o_w_(_).


  55..11..  CCoonnvveerrssiioonnss ddee ttyyppeess

  Vous remarquerez, au fur et mesure que vous progressez, que GTK
  utilise un syste de coercition de type. Celle-ci est toujours rlis en
  utilisant des macros qui vifient si l'objet donnpeut re converti et
  qui rlisent cette coercition. Les macros que vous rencontrerez le plus
  sont :


  +o  GTK_WIDGET(widget)

  +o  GTK_OBJECT(object)

  +o  GTK_SIGNAL_FUNC(function)

  +o  GTK_CONTAINER(container)

  +o  GTK_WINDOW(window)

  +o  GTK_BOX(box)

  Elles sont toutes utiliss pour convertir les paramres des fonctions.
  Vous les verrez dans les exemples et, en rle gale, vous saurez les
  utiliser simplement en regardant la dlaration d'une fonction.

  Comme vous pouvez le voir dans la hiarchie de classes ci-dessous, tous
  les _G_t_k_W_i_d_g_e_t_s divent d'une classe de base _G_t_k_O_b_j_e_c_t. Ceci signifie
  que vous pouvez utiliser un widget chaque fois qu'une fonction
  requiert un objet - il suffit d'utiliser la macro GTK_OBJECT().


  Par exemple :



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





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

  De nombreux widgets sont aussi des containers. Si vous regardez la
  hiarchie de classe ci-dessous, vous remarquerez que beaucoup de
  widgets viennent de la classe _G_t_k_C_o_n_t_a_i_n_e_r. N'importe lequel de ces
  widgets peut re utilisavec la macro GTK_CONTAINER pour re passen
  paramre une fonction qui attend un container.

  Malheureusement, ces macros ne peuvent re couvertes en dail dans ce
  didacticiel, Je vous recommande donc de jeter un coup d'oeil sur les
  fichier en-tes GTK : ils peuvent s'aver tr instructifs. En fait, il
  n'est pas difficile de comprendre comment fonctionne un widget, il
  suffit d'udier les dlarations des fonctions.



  55..22..  LLaa hhiiaarrcchhiiee ddeess wwiiddggeettss

  Voici l'arbre de la hiarchie de classes utiliss pour implanter les
  widgets.


































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






  55..33..  WWiiddggeettss ssaannss ffeennrree

  Les widgets suivants n'ont pas de fenre associ. Si vous voulez
  capturer des ements, vous devez utiliser _G_t_k_E_v_e_n_t_B_o_x.  Reportez-vous
  la section sur ``Le widget EventBox''



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




  Nous continuerons notre exploration de GTK en examinant chaque widget
  tour tour, crnt quelques fonctions simples pour les afficher. Une
  autre source intessante est le programme _t_e_s_t_g_t_k_._c livravec GTK. Il se
  trouve dans le rertoire _g_t_k_/


  66..  WWiiddggeettss bboouuttoonnss


  66..11..  BBoouuttoonnss nnoorrmmaauuxx

  On a dpresque vu tout ce qu'il y avait voir sur le widget bouton. Il
  est tr simple. Cependant, il y a deux fans de crr un bouton. On peut
  utiliser _g_t_k___b_u_t_t_o_n___n_e_w___w_i_t_h___l_a_b_e_l_(_) pour crr un bouton avec un label,
  ou _g_t_k___b_u_t_t_o_n___n_e_w_(_) pour crr un bouton vide. Dans ce dernier cas,
  c'est vous de placer un label ou un pixmap sur celui-ci. Pour ce
  faire, crz une boe, puis placez vos objets dans celle-ci en utilisant
  la fonction habituelle _g_t_k___b_o_x___p_a_c_k___s_t_a_r_t, utilisez alors
  _g_t_k___c_o_n_t_a_i_n_e_r___a_d_d pour placer la boe dans le bouton.


  Voici un exemple d'utilisation de _g_t_k___b_u_t_t_o_n___n_e_w_(_) pour crr un bouton
  contenant une image et un label. J'ai sardu reste le code qui cr une
  boe pour que vous puissiez l'utiliser dans vos programmes.































































  #include <gtk/gtk.h>


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

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

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

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

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

      style = gtk_widget_get_style(parent);

      /* Chargement de xpm pour crr une image */

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

      /* Crtion d'un label  */

      label = gtk_label_new (label_text);

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

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

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

      gtk_widget_show(pixmapwid);
      gtk_widget_show(label);

      return (box1);
  }

  /* Notre fonction de rappel habituelle */

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


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

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

      /* Crtion d'une fenre */

      window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

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

      /* Il est prable de faire cela pour toutes les fenres */

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


      /* Configuration du bord de la fenre */

      gtk_container_border_width (GTK_CONTAINER (window), 10);

      /* Crtion d'un bouton */

      button = gtk_button_new ();

      /* Vous devriez re habituvoir ces fonctions maintenant */

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

      /* Appel de notre fonction de crtion de boe */

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

      /* Placement et affichage de tous nos widgets */

      gtk_widget_show(box1);

      gtk_container_add (GTK_CONTAINER (button), box1);

      gtk_widget_show(button);

      gtk_container_add (GTK_CONTAINER (window), button);

      gtk_widget_show (window);

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

      return 0;
  }




  La fonction _x_p_m___l_a_b_e_l___b_o_x_(_) peut re utilis pour placer des xpms et des
  labels sur tout widget qui peut re container.


  66..22..  BBoouuttoonnss ccoommmmuuttaatteeuurrss

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

  Les boutons commutateurs sont la base des cases cocher ou des boutons
  radio, donc la plupart des appels utilis pour les boutons commutateurs
  sont hit par les cases cocher et les boutons radio. J'insisterai
  ldessus quand nous les aborderons.

  Crtion d'un bouton commutateur :



       GtkWidget* gtk_toggle_button_new (void);

       GtkWidget* gtk_toggle_button_new_with_label (gchar *label);




  Comme vous pouvez l'imaginer, elles fonctionnent comme celles des
  boutons normaux. La premie cr un bouton commutateur vide et la deuxie
  un bouton commutateur contenant dun label.

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



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

           } else {

               /* le bouton est enfonc*/
           }
       }






  L'appel qui suit peut re utilispour configurer l'at d'un bouton
  commutateur et de ses descendants, les cases cocher et les boutons
  radio. On lui passe notre bouton en premier paramre et TRUE ou FALSE
  pour spifier s'il doit re relhou enfonc Par daut, il est relh(FALSE).



       void gtk_toggle_button_set_state (GtkToggleButton *toggle_button,
                                         gint state);




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




  void gtk_toggle_button_toggled (GtkToggleButton *toggle_button);




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


  66..33..  CCaasseess ccoocchheerr

  Les cases cocher hitent de nombreuses propri et fonctions des boutons
  commutateurs, mais ont un aspect diffent. Au lieu d're des boutons
  contenant du texte, ce sont de petits carr avec un texte sur leur
  droite. Il sont souvent utilis pour valider ou non des options dans
  les applications.

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



       GtkWidget* gtk_check_button_new (void);

       GtkWidget* gtk_check_button_new_with_label (gchar *label);




  La fonction _n_e_w___w_i_t_h___l_a_b_e_l cr une case cocher avec un texte cotd'elle.

  La vification de l'at d'une case cocher est identique celle des
  boutons commutateurs.


  66..44..  BBoouuttoonnss rraaddiioo

  Les boutons radio ressemblent aux cases cocher sauf qu'ils sont group
  de fan ce qu'un seul d'entre-eux puisse re sectionnun moment donn Ils
  sont utilis par les applications lorsqu'il s'agit d'effectuer un choix
  dans une liste d'options.

  La crtion d'un bouton radio s'effectue gre l'un des appels suivants :



       GtkWidget* gtk_radio_button_new (GSList *group);

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




  On notera le paramre supplentaire de ces fonctions. Elles nessitent un
  groupe pour rliser correctement leur the. Le premier appel doit passer
  NULL au premier paramre puis on peut crr un groupe en utilisant :



       GSList* gtk_radio_button_group (GtkRadioButton *radio_button);






  On passe alors ce groupe en premier paramre des appels suivants aux
  fonctions de crtion.  Il est prable, aussi, de priser quel bouton doit
  re choisi par daut avec la fonction :



       void gtk_toggle_button_set_state (GtkToggleButton *toggle_button,
                                         gint state);




  Celle-ci est drite dans la section sur les boutons commutateurs et
  fonctionne exactement de la me fan.

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



  77..  WWiiddggeettss ddiivveerrss


  77..11..  LLaabbeellss

  Les labels sont tr utilis dans GTK et sont relativement simples. Ils
  n'ettent pas de signaux car ils n'ont pas de fenre X qui leur est
  associ. Si vous avez besoin de capturer des signaux ou de faire des
  coupures (clippings , utilisez un widget EventBox.

  Pour crr un label, on utilise :



       GtkWidget* gtk_label_new (char *str);




  Ol'unique paramre est la chae de caractes que l'on veut que le label
  affiche.

  Pour changer le texte d'un label apr sa crtion, on utilise la
  fonction :



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




  ole premier paramre est le label que l'on veut modifier, que l'on
  convertit en utilisant la macro GTK_LABEL(), et le second est la
  nouvelle chae.

  L'espace nessaire la nouvelle chae sera automatiquement ajustsi
  nessaire.

  Pour ruper la chae courante, on utilise la fonction :





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




  ole premier paramre est le label dont on veut ruper la chae et le
  second sert retourner cette chae.


  77..22..  LLee wwiiddggeett bbuullllee dd''aaiiddee

  Ce sont les petits textes qui surgissent lorsque vous laissez votre
  pointeur sur un bouton ou un autre widget pendant quelques secondes.
  Ils sont faciles utiliser, on ne donnera donc pas d'exemple. Si vous
  voulez voir du code, consultez le programme _t_e_s_t_g_t_k_._c distribuavec
  GTK.

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

  Le premier appel que vous utiliserez sera pour crr une nouvelle bulle
  d'aide. Vous n'avez besoin que de le faire une fois dans une fonction
  donn. Le _G_t_k_T_o_o_l_t_i_p que cette fonction retourne peut re utilispour crr
  plusieurs bulles d'aide.



       GtkTooltips *gtk_tooltips_new (void);




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



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




  Les paramres sont la bulle d'aide dcre, suivi du widget pour lequel
  vous voulez voir apparare cette bulle et le texte que vous voulez
  qu'elle contienne.

  Voici un petit exemple :



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





  D'autres fonctions peuvent re utiliss avec les bulles d'aide. Je ne
  ferais que les umer et les drire briement.



       void gtk_tooltips_destroy    (GtkTooltips *tooltips);




  Destruction de bulles d'aide.



       void gtk_tooltips_enable     (GtkTooltips *tooltips);




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



       void gtk_tooltips_disable    (GtkTooltips *tooltips);




  Dactivation d'un ensemble de bulles d'aide activs.



       void gtk_tooltips_set_delay  (GtkTooltips *tooltips,
                                     gint         delay);



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



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




  Change le texte d'une bulle d'aide dcre.



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




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

  Et c'est tout concernant les fonctions associs aux bulles d'aide.
  C'est plus que vous ne vouliez sement en savoir :)
  77..33..  BBaarrrreess ddee pprrooggrreessssiioonn

  Les barres de progression sont utiliss pour afficher la progression
  d'une opation. Elles sont tr simple utiliser comme vous pourrez le
  constater en udiant le code ci-dessous. Commenns d'abord par l'appel
  permettant de crr une nouvelle barre.



       GtkWidget *gtk_progress_bar_new (void);




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



       void gtk_progress_bar_update (GtkProgressBar *pbar, gfloat percentage);




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

  Les barres de progression sont galement utiliss avec les dais
  d'expiration ou autres fonctions identiques (voir la section sur
  ``Expirations, fonctions d'E/S et d'attente'') pour donner l'illusion
  du multi-thes. Toutes emploient la fonction _g_t_k___p_r_o_g_r_e_s_s___b_a_r___u_p_d_a_t_e de
  la me fan.

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































  #include <gtk/gtk.h>

  static int ptimer = 0;
  int pstat = TRUE;

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

  gint progress (gpointer data)
  {
      gfloat pvalue;

      /* rupation de la valeur courante de la barre */

      pvalue = GTK_PROGRESS_BAR (data)->percentage;

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

      gtk_progress_bar_update (GTK_PROGRESS_BAR (data), pvalue);

      return TRUE;
  }

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

  void progress_r (void)
  {
      pstat = FALSE;
  }

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

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

      gtk_init (&argc, &argv);

      window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

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

      gtk_container_border_width (GTK_CONTAINER (window), 10);

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

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

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

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

      /* Configure le dai d'expiration pour ger automatiquement la
       *  mise jour de la barre */

      ptimer = gtk_timeout_add (100, progress, pbar);

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

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

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

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

      gtk_widget_show(table);
      gtk_widget_show(window);

      gtk_main ();

      return 0;
  }




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



       pbar = gtk_progress_bar_new ();




  Cet appel cr une nouvelle barre, nomm _p_b_a_r.



       ptimer = gtk_timeout_add (100, progress, pbar);




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



       pvalue = GTK_PROGRESS_BAR (data)->percentage;





  Ce code assigne _p_v_a_l_u_e la valeur du pourcentage de la barre.



       gtk_progress_bar_update (GTK_PROGRESS_BAR (data), pvalue);




  Finalement, ce code met jour la barre avec la valeur de _p_v_a_l_u_e.

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


  77..44..  BBooeess ddee ddiiaalloogguuee


  Les widgets boes de dialogue sont tr simples : ce sont simplement des
  fenres avec plusieurs choses dplacs dedans. La structure d'une boe de
  dialogue est :



       struct GtkDialog
       {
             GtkWindow window;

             GtkWidget *vbox;
             GtkWidget *action_area;
       };




  Comme vous le voyez, cela cr simplement une fenre et la place dans une
  vbox suivie d'un sarateur et d'une hbox pour la zone d'action

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



       GtkWidget* gtk_dialog_new (void);




  Ainsi, pour crr un nouveau dialogue, on utilise :



       GtkWidget window;
       window = gtk_dialog_new ();




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




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




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



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




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



  77..55..  PPiixxmmaappss

  Les pixmaps sont des structures de donns contenant des images. Celles-
  ci peuvent re utiliss diffents endroits, mais le plus souvent comme
  ices dans le bureau X Window. Un bitmap est un pixmap de 2 couleurs.

  Pour utiliser des pixmaps avec GTK, on doit d'abord construire une
  structure _G_d_k_P_i_x_m_a_p en utilisant les fonctions de la couche GDK. Les
  pixmaps peuvent soit re crs partir de donns en memoire, ou partir de
  donns lues dans un fichier. Nous utiliserons chacun des appels pour
  crr un pixmap.



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




  Cette fonction sert crr un pixmap mono-plan (2 couleurs) partir de
  donns en moire. Chaque bit de la donn _d_a_t_a. _w_i_d_t_h et _h_e_i_g_h_t sont
  exprim en pixels. Le pointeur vers un _G_d_k_W_i_n_d_o_w pointe sur la fenre
  courante car les ressources d'un pixmap n'ont de signification que
  dans le contexte de l'ran oil doit s'afficher.










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




  Cette fonction est utilis pour crr un pixmap d'une profondeur donn
  (nombre de couleurs) partir de la donn spifi pas _d_a_t_a. _f_g et _b_g sont
  les couleurs utiliser pour l'avant et l'arrie-plan.



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




  Le format XPM est une reprentation des pixmaps reconnue par le syste X
  Window. Il est largement utiliset de nombreux utilitaires pour crr des
  fichiers d'images ce format sont disponibles. Le fichier _f_i_l_e_n_a_m_e doit
  contenir une image dans ce format qui sera charg dans la structure
  pixmap. Le masque _m_a_s_k indique quels sont les bits opaques du pixmap.
  Tous les autres bits sont coloris en utilisant la couleur spifi par
  _t_r_a_n_s_p_a_r_e_n_t___c_o_l_o_r. Un exemple d'utilisation est prentci-dessous.



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




  De petites images peuvent re intrs dans un programme sous la forme de
  donns _d_a_t_a au format XPM. Un pixmap est cr en utilisant ces donns au
  lieu de les lire dans un fichier. Un exemple de telles donns est :




















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






       void gdk_pixmap_destroy( GdkPixmap  *pixmap );




  Lorsqu'on a utilisun pixmap et que l'on en a plus besoin tout de
  suite, il est prable de liber la ressource en utilisant un appel
  _g_d_k___p_i_x_m_a_p___d_e_s_t_r_o_y.  Les pixmaps doivent re consids comme une
  ressource prieuse.

  Quand le pixmap est cr, on peut l'afficher comme un widget GTK. On
  doit crr un widget pixmap qui contiendra le pixmap GDK. Ceci est
  rlisde la fan suivante :



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




  Les autres fonctions pour les widgets pixmap sont :



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





  _g_t_k___p_i_x_m_a_p___s_e_t sert changer le pixmap pris en charge par le widget.
  _v_a_l est le pixmap cr par le GDK.

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






























































  #include <gtk/gtk.h>


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


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

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


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

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


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

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

      /* Cr la fenre principale et attache le signal "delete_event" pour
       * terminer l'application */

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

      /* Utilisation de GDK pour crr le pixmap */

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

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

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

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

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

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

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

      return 0;
  }





  Pour charger un fichier partir d'un fichier XPM appel_i_c_o_n_0_._x_p_m se
  trouvant dans le rertoire courant, on aurait cr le pixmap ainsi :



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

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





  Utilisation des formes

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

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





  #include <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 appel
   * (via le signal "delete_event"). */

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


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

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

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

      /* Crtion du pixmap et du widget pixmap */

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

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

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

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

      gtk_widget_shape_combine_mask( window, mask, 0, 0 );

      /* Affichage de la fenre */

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

      return 0;
  }




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



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

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




  88..  WWiiddggeettss ccoonnttaaiinneerrss

  88..11..  BBlloocc--nnootteess

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

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



       GtkWidget* gtk_notebook_new (void);




  Lorsque le bloc-notes a cr, il y a 12 fonctions permettant de
  travailler sur les blocs-notes. udions-les sarent.

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



       void gtk_notebook_set_tab_pos (GtkNotebook *notebook, GtkPositionType pos);




  _G_t_k_P_o_s_i_t_i_o_n_T_y_p_e peut prendre les valeurs suivantes qu'il n'est pas
  nessaire d'expliquer :

  +o  GTK_POS_LEFT

  +o  GTK_POS_RIGHT

  +o  GTK_POS_TOP

  +o  GTK_POS_BOTTOM

  GTK_POS_TOP est la valeur par daut.

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



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

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




  Ces fonctions ajoutent des pages au bloc-notes_*_n_o_t_e_b_o_o_k en les insant
  la fin (_a_p_p_e_n_d) ou au dut (_p_r_e_p_e_n_d). _*_c_h_i_l_d est le widget qui est
  placdans la page du bloc-notes, et _*_t_a_b___l_a_b_e_l est le label de la page
  qui est ajout.
  La troisie fonction ajoutant une page un bloc-notes conserve toutes
  les propri des deux prentes, mais elle nous permet en plus de spifier
  la position ol'on dire inser cette page.



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




  Les paramres sont les mes que ___a_p_p_e_n_d__ et ___p_r_e_p_e_n_d__ sauf qu'il y en a
  un de plus : _p_o_s_i_t_i_o_n. Celui-ci sert spifier l'endroit ocette page
  sera ins.

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



       void gtk_notebook_remove_page (GtkNotebook *notebook, gint page_num);




  Cette fonction e la page spifi par _p_a_g_e___n_u_m du widget _*_n_o_t_e_b_o_o_k.

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



       gint gtk_notebook_current_page (GtkNotebook *notebook);




  Les deux fonctions suivantes permettent de passer la page suivante ou
  prente d'un bloc-notes. Il suffit de faire l'appel de la fonction
  aduate avec le widget sur lequel on veut oper. Remarque : lorsqu'on
  est sur la dernie page du bloc-notes et que l'on appelle
  _g_t_k___n_o_t_e_b_o_o_k___n_e_x_t___p_a_g_e, on revient la premie page. De me, si l'on est
  sur la premie page et que l'onappelle _g_t_k___n_o_t_e_b_o_o_k___p_r_e_v___p_a_g_e, on se
  retrouve sur sa dernie page.



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




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



       void gtk_notebook_set_page (GtkNotebook *notebook, gint page_num);




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



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




  _s_h_o_w___t_a_b_s et _s_h_o_w___b_o_r_d_e_r peuvent valoir TRUE ou FALSE (0 ou 1).

  Voyons maintenant un exemple, il est tirdu code de _t_e_s_t_g_t_k_._c de la
  distribution GTK et montre l'utilisation des 13 fonctions. Ce petit
  programme cr une fenre contenant un bloc-notes et six boutons. Le
  bloc-notes contient 11 pages, ajouts par trois moyens diffents : la
  fin, au milieu et au dut. Les boutons permettent de faire tourner les
  indicateurs de page, ajouter/er les indicateurs et le contour, er une
  page, passer la page suivante et prente, et sortir du programme.














































  #include <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 rafrahir  le widget --
       * ce qui force le widget se redessiner. */

        gtk_widget_draw(GTK_WIDGET(notebook), NULL);
  }

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

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

      gtk_init (&argc, &argv);

      window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

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

      gtk_container_border_width (GTK_CONTAINER (window), 10);

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

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

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

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

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

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

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

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


      /* Ajoute une page un endroit pris. */

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

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

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

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

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

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

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

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

      gtk_notebook_set_page (GTK_NOTEBOOK(notebook), 3);


      /* Crtion des boutons */

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

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

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

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

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

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

      gtk_widget_show(table);
      gtk_widget_show(window);

      gtk_main ();

      return 0;
  }




  En espant que ceci vous aide crr des blocs-notes pour vos applications
  GTK.



  88..22..  FFeennrreess aavveecc bbaarrrreess ddee ddiilleemmeenntt

  Les fenres avec barres de dilement servent crr des zones dilantes
  l'intieur d'une vraie fenre. On peut inser n'importe quel widget dans
  ces fenres, ils seront accessibles quelle que soit leur taille en
  utilisant les barres de dilement.


  La fonction suivante sert crr une fenre avec barre de dilement :



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




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



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




  Cela permet de configurer le fonctionnement des barres de dilement. Le
  premier paramre est la fenre dilement que l'on veut modifier, le
  second configure le fonctionnement de la barre horizontale et le
  troisie celui de la barre verticale.

  Ce fonctionnement peut re GTK_POLICY AUTOMATIC ou GTK_POLICY_ALWAYS.
  GTK_POLICY_AUTOMATIC didera automatiquement de votre besoin en barres
  de dilement, alors que GTK_POLICY_ALWAYS mettra toujours celles-ci.

  Voici un exemple simple qui place 100 boutons commutateurs dans une
  fenre dilement. Je n'ai commentque les parties qui sont nouvelles pour
  vous.
































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

      /* Crtion d'une boe de dialogue pour y placer la fenre dilement.
       * Une boe de dialogue est une fenre comme les autres sauf qu'elle contient
       * une vbox et un sarateur horizontal. Ce n'est qu'un raccourci pour crr des
       * zones de dialogue. */

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

      /* Crtion d'une fenre dilement. */

      scrolled_window = gtk_scrolled_window_new (NULL, NULL);

      gtk_container_border_width (GTK_CONTAINER (scrolled_window), 10);

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

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

      /* Crtion d'une boe de dialogue */

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

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

      table = gtk_table_new (10, 10, FALSE);

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

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

      /* Place la table fans la fenre dilement */

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

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

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

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

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

      /* On met ce bouton en bouton par daut  */

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

      /* Rupe le bouton par daut. Le fait de presser la touche Entr      * activera le bouton. */

      gtk_widget_grab_default (button);
      gtk_widget_show (button);

      gtk_widget_show (window);

      gtk_main();

      return(0);
  }




  Essayez de changer la taille de la fenre et faites attention aux
  rctions des barres de dilement. On peut aussi utiliser la fonction
  _g_t_k___w_i_d_g_e_t___s_e_t___u_s_i_z_e_(_) pour configurer la taille par daut de la fenre
  et des autres widgets.



  99..  WWiiddggeettss lliisstteess

  Le widget _G_t_k_L_i_s_t sert de container vertical pour des widgets
  _G_t_k_L_i_s_t_I_t_e_m.

  Un widget _G_t_k_L_i_s_t posse sa propre fenre pour recevoir lesements et sa
  propre couleur de fond qui est habituellement blanche. Comme il est
  directement divde _G_t_k_C_o_n_t_a_i_n_e_r, il peut re traitcomme tel en utilisant
  la macro GTK_CONTAINER(List) : voir le widget _G_t_k_C_o_n_t_a_i_n_e_r pour en
  savoir plus.

  On doit d'abord connare l'utilisation des _G_L_i_s_t et des fonctions
  _g___l_i_s_t___*_(_) qui leur sont lis pour pouvoir utiliser pleinement le
  widget _G_t_k_L_i_s_t.

  Un champ de la structure d'un widget _G_t_k_L_i_s_t nous intesse
  particuliement :






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




  Le champ _s_e_l_e_c_t_i_o_n d'un _G_t_k_L_i_s_t pointe sur une liste cha de tous les
  items qui sont sectionn, ou vaut NULL si aucune section n'est faite.
  Ainsi, pour connare la section courante, on consulte le champ
  _G_T_K___L_I_S_T_(_)_-_>_s_e_l_e_c_t_i_o_n mais on ne doit pas le modifier car ses champs
  internes sont g par les fonctions _g_t_k___l_i_s_t___*_(_).

  Le champ _s_e_l_e_c_t_i_o_n___m_o_d_e dermine les options de section d'un _G_t_k_L_i_s_t et
  donc le contenu du champ du _G_T_K___L_I_S_T_(_)_-_>_s_e_l_e_c_t_i_o_n :

  _s_e_l_e_c_t_i_o_n___m_o_d_e peut avoir l'une des valeurs suivantes :

  +o  GTK_SELECTION_SINGLE - _s_e_l_e_c_t_i_o_n vaut NULL ou contient un pointeur
     vers un seul item sectionn

  +o  GTK_SELECTION_BROWSE - _s_e_l_e_c_t_i_o_n vaut NULL si la liste ne contient
     aucun widget ou seulement des widgets non sensitifs. Sinon, ce
     champ contient un pointeur vers une seule structure Glist, et donc
     vers exactement un item.

  +o  GTK_SELECTION_MULTIPLE - _s_e_l_e_c_t_i_o_n vaut NULL si aucun item n'est
     sectionnou pointe vers le premier item sectionn Ce dernier point
     son tour vers le second item, etc.

  +o  GTK_SELECTION_EXTENDED - _s_e_l_e_c_t_i_o_n vaut toujours NULL.

  La valeur par daut est GTK_SELECTION_MULTIPLE.


  99..11..  SSiiggnnaauuxx



       void GtkList::selection_changed (GtkList *LIST)




  Ce signal sera invoquchaque fois que le champ section d'un GtkList a
  chang Cela arrive lorsqu'un fils d'un GtkList a sectionnou dectionn



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




  Ce signal est invoqulorsqu'un fils du GtkList va re sectionn Ceci
  arrive principalement lors d'appels _g_t_k___l_i_s_t___s_e_l_e_c_t___i_t_e_m_(_)_,
  _g_t_k___l_i_s_t___s_e_l_e_c_t___c_h_i_l_d_(_) et lors d'appuis de boutons. Quelques fois, il
  est indirectement dlenchlorsque des fils sont ajout ou supprim du
  GtkList.



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




  Ce signal est invoqulorsqu'un fils du GtkList va re dectionn Cela
  arrive principalement lors d'appels _g_t_k___l_i_s_t___u_n_s_e_l_e_c_t___i_t_e_m_(_)_,
  _g_t_k___l_i_s_t___u_n_s_e_l_e_c_t___c_h_i_l_d_(_), et lors d'appuis de boutons. Quelques fois,
  il est indirectement dlenchlorsque des fils sont ajout ou supprim du
  GtkList.



  99..22..  FFoonnccttiioonnss



       guint gtk_list_get_type (void)




  Retourne l'identificateur de type GtkList



       GtkWidget* gtk_list_new (void)




  Cr un nouvel objet GtkList  Le nouveau widget est retournsous la forme
  d'un pointeur vers un objet GtkWidget  NULL est retournen cas
  d'erreur.



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




  Inse des items dans _L_I_S_T, partir de _P_O_S_I_T_I_O_N.  _I_T_E_M_S est une liste
  doublement cha ochaque noeud doit pointer vers un nouveau _G_t_k_L_i_s_t_I_t_e_m.
  Les noeuds _G_L_i_s_t de _I_T_E_M_S sont pris en charge par _L_I_S_T.




       void gtk_list_append_items (GtkList *LIST, GList *ITEMS)




  Inse des items la fin de _L_I_S_T selon le me principe que
  _g_t_k___l_i_s_t___i_n_s_e_r_t___i_t_e_m_s. Les noeuds _G_L_i_s_t de _I_T_E_M_S sont pris en charge
  par _L_I_S_T.



       void gtk_list_prepend_items (GtkList *LIST, GList *ITEMS)





  Inse des items au dut de _L_I_S_T selon le me principe que
  _g_t_k___l_i_s_t___i_n_s_e_r_t___i_t_e_m_s. Les noeuds _G_L_i_s_t de _I_T_E_M_S sont pris en charge
  par _L_I_S_T.



       void gtk_list_remove_items (GtkList *LIST, GList *ITEMS)




  e des items de _L_I_S_T. _I_T_E_M_S est une liste doublement cha dont chaque
  noeud pointe vers un fils direct de _L_I_S_T. Il est de la responsabilitde
  l'appelant de faire un appel _g___l_i_s_t___f_r_e_e_(_I_T_E_M_S_) apr cela. L'appelant
  doit aussi druire lui-me les items.



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




  e et druit des items de _L_I_S_T. Un widget est concernsi sa position
  courante dans _L_I_S_T est dans l'intervalle spifipar _S_T_A_R_T et _E_N_D.



       void gtk_list_select_item (GtkList *LIST, gint ITEM)




  Invoque le signal _G_t_k_L_i_s_t_:_:_s_e_l_e_c_t___c_h_i_l_d pour un item spifipar sa
  position courante dans _L_I_S_T.



       void gtk_list_unselect_item (GtkList *LIST, gint ITEM)




  Invoque le signal _G_t_k_L_i_s_t_:_:_u_n_s_e_l_e_c_t___c_h_i_l_d pour un item spifipar sa
  position courante dans _L_I_S_T.



       void gtk_list_select_child (GtkList *LIST, GtkWidget *CHILD)




  Invoque le signal _G_t_k_L_i_s_t_:_:_s_e_l_e_c_t___c_h_i_l_d pour le fils _C_H_I_L_D spifi



       void gtk_list_unselect_child (GtkList *LIST, GtkWidget *CHILD)




  Invoque le signal _G_t_k_L_i_s_t_:_:_u_n_s_e_l_e_c_t___c_h_i_l_d pour le fils _C_H_I_L_D spifi



  gint gtk_list_child_position (GtkList *LIST, GtkWidget *CHILD)




  Retourne la position de _C_H_I_L_D dans _L_I_S_T. Retourne -1 en cas d'erreur.



       void gtk_list_set_selection_mode (GtkList *LIST, GtkSelectionMode MODE)




  Configure _L_I_S_T dans le mode de section _M_O_D_E qui peut re
  GTK_SELECTION_SINGLE, GTK_SELECTION_BROWSE, GTK_SELECTION_MULTIPLE ou
  GTK_SELECTION_EXTENDED.



       GtkList* GTK_LIST (gpointer OBJ)




  Convertit un pointeur gique en <\em GtkList*\  Voir _S_t_a_n_d_a_r_d _M_a_c_r_o_s_:_:,
  pour plus d'informations.



       GtkListClass* GTK_LIST_CLASS (gpointer CLASS)




  Convertit un pointeur gique en GtkListClass*   Voir _S_t_a_n_d_a_r_d _M_a_c_r_o_s_:_:,
  pour plus d'informations.



       gint GTK_IS_LIST (gpointer OBJ)




  Dermine si un pointeur gique rence un objet GtkList  Voir _S_t_a_n_d_a_r_d
  _M_a_c_r_o_s_:_:, pour plus d'informations.


  99..33..  EExxeemmppllee

  Voici un programme affichant les changements de section dans une
  _G_t_k_L_i_s_t et permettant d'emprisonner des items en les sectionnant avec
  le bouton droit de la souris.












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

  /* Chae pour stocker les donns dans les items de la liste. */

  const   gchar   *list_item_data_key="list_item_data";


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

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


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

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


      /* initialise gtk (et donc gdk) */

      gtk_init(&argc, &argv);


      /* Crtion d'une fenre pour placer tous les widgets.
       * Connexion de  gtk_main_quit() l'ement "destroy" de
       * la fenre afin de prendre en charge les ements fermeture d'une
       * fenre du gestionnaire de fenre. */

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


      /* l'intieur de la fenre, on a besoin d'une boe pour placer
       * verticalement les widgets. */

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

      /* Fenre dilement pour placer le widget GtkList l'intieur. */

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

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

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

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

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

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

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

      /* Crtion d'un sarateur. */

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

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

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


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

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

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

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

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

      gtk_widget_show(window);

      /* Lancement de la boucle principale de gtk */

      gtk_main();

      /* On arrive ici apr que gtk_main_quit() ait appel lorsque
       * la fenre principale a druite. */

  }

  /* Gestionnaire de signal connectaux ements boutons presser/relher
   * du GtkList. */

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

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

          /* On recherche l'item sectionnce moment pris.
           * Ce sera notre prisonnier ! */

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

          /* On recherche les items dprisonniers et on les
           * remet dans la liste.
           * Il ne faut pas oublier de liber la liste doublement
           * cha que gtk_container_children() retourne. */

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

              list_item=dlist->data;

              gtk_widget_reparent(list_item, gtklist);

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

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

          if (new_prisoner) {
              GList   static_dlist;

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

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

  /* Gestionnaire de signal appellorsque le GtkList
   * et le signal "selection_changed". */

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

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

      dlist=GTK_LIST(gtklist)->selection;

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

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

      g_print("La section est ");

      /* On rupe l'item dans la liste doublement cha
       * puis on interroge la donn associ par list_item_data_key
       * et on l'affiche. */

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

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

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





  99..44..  WWiiddggeett iitteemm ddee lliissttee

  Le widget _G_t_k_L_i_s_t_I_t_e_m sert de container pour contenir un fils, lui
  fournissant des fonctions de section/delection exactement comme le
  widget GtkList les demande pour ses fils.

  Un _G_t_k_L_i_s_t_I_t_e_m a sa propre fenre pour recevoir les ements et a sa
  propre couleur de fond, habituellement blanche.

  Comme il est directement divd'un _G_t_k_I_t_e_m, il peut re traitcomme tel en
  utilisant la macro GTK_ITEM(ListItem), reportez-vous la section sur le
  widget GtkItem pour plus de dail sur celui-ci.  Habituellement, un
  _G_t_k_L_i_s_t_I_t_e_m contient juste un label pour identifier, par exemple, un
  nom de fichier dans un _G_t_k_L_i_s_t -- la fonction appropri
  _g_t_k___l_i_s_t___i_t_e_m___n_e_w___w_i_t_h___l_a_b_e_l_(_) est donc fournie. Le me effet peut re
  obtenu en crnt un _G_t_k_L_a_b_e_l part, en configurant son alignement avec
  _x_a_l_i_g_n=0 et _y_a_l_i_g_n=0.5 suivi d'un ajout ultieur au _G_t_k_L_i_s_t_I_t_e_m.

  Tout comme on n'est pas forcd'ajouter un _G_t_k_L_a_b_e_l un _G_t_k_L_i_s_t_I_t_e_m, on
  peut aussi ajouter un _G_t_k_V_B_o_x ou un _G_t_k_A_r_r_o_w etc. un _G_t_k_L_i_s_t_I_t_e_m.


  99..55..  SSiiggnnaauuxx

  Un _G_t_k_L_i_s_t_I_t_e_m ne cr pas de nouveaux signaux par lui-me, mais hite de
  ceux d'un _G_t_k_I_t_e_m. Voir _G_t_k_I_t_e_m_:_:, pour plus d'informations.



  99..66..  FFoonnccttiioonnss




       guint gtk_list_item_get_type (void)




  Retourne l'identificateur du type GtkListItem



       GtkWidget* gtk_list_item_new (void)




  Crtion d'un objet _G_t_k_L_i_s_t_I_t_e_m. Le nouveau widget est retournsous la
  forme d'un pointeur vers un objet _G_t_k_W_i_d_g_e_t. NULL est retournen cas
  d'erreur.



       GtkWidget* gtk_list_item_new_with_label (gchar *LABEL)




  Crtion d'un objet _G_t_k_L_i_s_t_I_t_e_m ayant un simple _G_t_k_L_a_b_e_l comme seul
  fils. Le nouveau widget est retournsous la forme d'un pointeur vers un
  objet _G_t_k_W_i_d_g_e_t. NULL est retournen cas d'erreur.



       void gtk_list_item_select (GtkListItem *LIST_ITEM)




  Cette fonction est surtout un emballage de _g_t_k___i_t_e_m___s_e_l_e_c_t _(_G_T_K___I_T_E_M
  _(_l_i_s_t___i_t_e_m_)_) qui ettra le signal _G_t_k_I_t_e_m_:_:_s_e_l_e_c_t.  Voir _G_t_k_I_t_e_m_:_:,
  pour plus d'informations.



       void gtk_list_item_deselect (GtkListItem *LIST_ITEM)




  Cette fonction est surtout un emballage de _g_t_k___i_t_e_m___d_e_s_e_l_e_c_t _(_G_T_K___I_T_E_M
  _(_l_i_s_t___i_t_e_m_)_) qui ettra le signal _G_t_k_I_t_e_m_:_:_d_e_s_e_l_e_c_t. Voir _G_t_k_I_t_e_m_:_:,
  pour plus d'informations.



       GtkListItem* GTK_LIST_ITEM (gpointer OBJ)




  Convertit un pointeur gique en _G_t_k_L_i_s_t_I_t_e_m_*. Voir _S_t_a_n_d_a_r_d _M_a_c_r_o_s_:_:
  pour plus d'informations.



       GtkListItemClass* GTK_LIST_ITEM_CLASS (gpointer CLASS)




  Convertit un pointeur gique en _G_t_k_L_i_s_t_I_t_e_m_C_l_a_s_s_*. Voir _S_t_a_n_d_a_r_d
  _M_a_c_r_o_s_:_: pour plus d'informations.



       gint GTK_IS_LIST_ITEM (gpointer OBJ)




  Dermine si un pointeur gique se re un objet _G_t_k_L_i_s_t_I_t_e_m.  Voir
  _S_t_a_n_d_a_r_d _M_a_c_r_o_s_:_: pour plus d'informations.


  99..77..  EExxeemmppllee

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



  1100..  WWiiddggeettss sseeccttiioonnss ddee ffiicchhiieerrss

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

  Pour crr une boe de section de fichier, on utilise :



       GtkWidget* gtk_file_selection_new (gchar *title);




  Pour configurer le nom de fichier, par exemple pour aller dans un
  rertoire pris ou donner un nom de fichier par daut, on utilise la
  fonction :



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




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



       gchar* gtk_file_selection_get_filename (GtkFileSelection *filesel);




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


  +o  dir_list

  +o  file_list

  +o  selection_entry

  +o  selection_text

  +o  main_vbox

  +o  ok_button

  +o  cancel_button

  +o  help_button

  Le plus souvent, on utilise les pointeurs _o_k___b_u_t_t_o_n_, _c_a_n_c_e_l___b_u_t_t_o_n, et
  _h_e_l_p___b_u_t_t_o_n pour priser leurs utilisations.
  Voici un exemple emprunt_t_e_s_t_g_t_k_._c et modifipour fonctionner tout seul.
  Comme vous le verrez, il n'y a pas grand chose faire pour crr un
  wigdget de section de fichier. Cependant, dans cet exemple, si le
  bouton Aide appara l'ran, il ne fait rien car aucun signal ne lui est
  attach



       #include <gtk/gtk.h>

       /* Rupe le nom de fichier sectionnet l'affiche sur la console. */

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

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

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

           gtk_init (&argc, &argv);

           /* Crtion d'un widget de section de fichier. */

           filew = gtk_file_selection_new ("File selection");

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

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

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

           /* Connexion de cancel_button pour druire le widget */

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

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

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

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





  1111..  WWiiddggeettss MMeennuu

  Il y a deux fans de crr des menus, la facile et la compliqu. Les deux
  ont leur utilit mais on peut galement utiliser l'_u_s_i_n_e _m_e_n_u_s (c'est la
  mhode facile...). La mhode compliqu consiste crr tous les menus en
  utilisant directement les appels. La mhode facile consiste utiliser
  les appels _g_t_k___m_e_n_u___f_a_c_t_o_r_y. C'est beaucoup plus simple, mais chaque
  approche a ses avantages et inconvients.

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


  1111..11..  CCrrttiioonn mmaannuueellllee ddee mmeennuuss

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

  Regardons les fonctions utiliss pour crr les menus. La premie sert crr
  un nouveau menu.



       GtkWidget *gtk_menu_bar_new()




  Cette fonction cr une nouvelle barre de menu. On utilise la fonction
  _g_t_k___c_o_n_t_a_i_n_e_r___a_d_d pour la placer dans une fenre, ou les fonctions
  _b_o_x___p_a_c_k pour la placer dans une boe - la me que pour les boutons.



       GtkWidget *gtk_menu_new();




  Cette fonction retourne un pointeur vers un nouveau menu, il n'est
  jamais montr(avec _g_t_k___w_i_d_g_e_t___s_h_o_w), il ne fait que contenir les items
  du menu. Ceci deviendra plus clair lorsque nous udierons l'exemple ci-
  dessous.

  Les deux appels suivants servent crr des items de menu qui seront plac
  dans le menu.



       GtkWidget *gtk_menu_item_new()




  et



       GtkWidget *gtk_menu_item_new_with_label(const char *label)




  Ces appels servent crr les menus qui doivent re affich. On doit bien
  faire la diffence entre un menu qui est cr avec _g_t_k___m_e_n_u___n_e_w_(_) et un
  item de menu cr avec les fonctions _g_t_k___m_e_n_u___i_t_e_m___n_e_w_(_). L'item de menu
  sera un vitable bouton avec une action associ alors qu'un menu sera un
  container contenant les items.

       gtk_menu_item_append()

       gtk_menu_item_set_submenu()




  Les fonctions _g_t_k___m_e_n_u___n_e_w___w_i_t_h___l_a_b_e_l_(_) et _g_t_k___m_e_n_u___n_e_w_(_) sont telles
  que vous les attendiez apr avoir udiles boutons. L'une cr un nouvel
  item de menu contenant dun label, et l'autre ne fait que crr un item
  de menu vide.

  Voici les apes pour crr une barre de menu avec des menus attach :

  +o  Crr un nouveau menu avec _g_t_k___m_e_n_u___n_e_w_(_)

  +o  Crr un item de menu avec _g_t_k___m_e_n_u___i_t_e_m___n_e_w_(_).  Ce sera la racine du
     menu, le texte apparaissant ici sera aussi sur la barre de menu.

  +o  Utiliser plusieurs appels _g_t_k___m_e_n_u___i_t_e_m___n_e_w_(_) pour chaque item que
     l'on dire dans le menu. Utiliser _g_t_k___m_e_n_u___i_t_e_m___a_p_p_e_n_d_(_) pour placer
     chacun de ces items les uns apr les autres. Cela cr une liste
     d'items de menu.

  +o  Utiliser _g_t_k___m_e_n_u___i_t_e_m___s_e_t___s_u_b_m_e_n_u_(_) pour attacher les items de
     menus nouvellement crs l'item de menu racine (celui crla seconde
     ape).

  +o  Crr une nouvelle barre de menu avec _g_t_k___m_e_n_u___b_a_r___n_e_w_(_). Cette ape
     ne doit re faite qu'une fois lorsque l'on cr une sie de menu sur
     une seule barre de menus.

  +o  Utiliser _g_t_k___m_e_n_u___b_a_r___a_p_p_e_n_d_(_) pour placer le menu racine dans la
     barre de menu.

  La crtion d'un menu surgissant est presque identique. La diffence est
  que le menu n'est pas postautomatiquement par une barre de menu, mais
  explicitement en appelant la fonction _g_t_k___m_e_n_u___p_o_p_u_p_(_) par un ement
  bouton press

  Suivez ces apes

  +o  Crr une fonction de gestion d'ement. Elle doit avoir le prototype

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


  et elle utilisera l'ement _e_v_e_n_t pour savoir ofaire surgir le menu.

  +o  Ce gestionnaire, si l'ement est un appui sur un bouton souris,
     traite _e_v_e_n_t comme un ement bouton (ce qu'il est) et l'utilise, de
     la fan indiqu dans le code d'exemple, pour passer l'information
     _g_t_k___m_e_n_u___p_o_p_u_p_(_).

  +o  Lier ce gestionnaire un widget avec :

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


  o_w_i_d_g_e_t est le widget auquel vous le liez, _h_a_n_d_l_e_r est le gestion-
  naire, et _m_e_n_u est un menu cr avec _g_t_k___m_e_n_u___n_e_w_(_). Cela peut re un
  menu qui est aussi postpar une barre de menu, comme le montre l'exem-
  ple.


  1111..22..  EExxeemmppllee ddee mmeennuu mmaannuueell

































































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

      /* Crtion d'un fenre */

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

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

      menu = gtk_menu_new();

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

      root_menu = gtk_menu_item_new_with_label("Menu racine");

      gtk_widget_show(root_menu);

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

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

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

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

              menu_items = gtk_menu_item_new_with_label(buf);

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

              gtk_menu_append(GTK_MENU (menu), menu_items);

              /* On fait quelque chose d'intessant lorsque l'item est
               * sectionn */

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

              /* Affichage du widget. */

              gtk_widget_show(menu_items);
          }

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

      gtk_menu_item_set_submenu(GTK_MENU_ITEM (root_menu), menu);

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

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

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

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

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

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

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

      gtk_menu_bar_append(GTK_MENU_BAR (menu_bar), root_menu);

      /* Affichage de la fenre. */

      gtk_widget_show(window);

      gtk_main ();

      return 0;
  }



  /* On rond un appui sur le bouton en postant un nouveau menu passcomme
   * un widget.
   *
   * On remarque que le paramre "widget" est le menu poster, PAS le bouton
   * qui a press */


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

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

          /* On indique l'appelant que l'on a gcet ement. */

          return TRUE;
      }

      /* On indique l'appelant que l'on n'a pas gcet ement. */

      return FALSE;
  }


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

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




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


  1111..33..  UUttiilliissaattiioonn ddee GGttkkMMeennuuFFaaccttoorryy

  Maintenant que nous avons explorla voie difficile, nous allons voir
  l'utilisation des appels _g_t_k___m_e_n_u___f_a_c_t_o_r_y_.


  1111..44..  EExxeemmppllee dd''uussiinnee mmeennuu

  Voici un exemple utilisant l'usine menu de GTK. Le premier fichier est
  _m_e_n_u_s_._h.  Nous sarerons _m_e_n_u_s_._c et _m_a_i_n_._c cause des variables globales
  utiliss dans le fichier _m_e_n_u_s_._c.



       #ifndef __MENUS_H__
       #define __MENUS_H__

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

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

       #ifdef __cplusplus
       }
       #endif /* __cplusplus */

       #endif /* __MENUS_H__ */




  Voici le fichier _m_e_n_u_s_._c :









  #include <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 utilis pour crr les menus. Le premier champ
   * est la chae de dinition du menu. Le second, la touche de raccourci
   * utilis pour accer cette fonction du menu avec le clavier.
   * Le troisie est la fonction de rappel utiliser lorsque l'item de menu
   * est choisi (par la touche de raccourci ou avec la souris). Le dernier
   * ent est la donn passer la fonction de rappel. */


  static GtkMenuEntry menu_items[] =
  {
          {"<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'ents de menu_item */

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

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

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

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

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

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

          gtk_menu_factory_add_subfactory(factory, subfactory[0], "<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 sensitivitd'un menu qui n'existe pas : %s", path);
  }




  Voici _m_a_i_n_._h :



       #ifndef __MAIN_H__
       #define __MAIN_H__


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

       void file_quit_cmd_callback(GtkWidget *widget, gpointer data);

       #ifdef __cplusplus
       }
       #endif /* __cplusplus */

       #endif /* __MAIN_H__ */




  Et, enfin, _m_a_i_n_._c :






















  #include <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 donstration du fonctionnement des fonctions de rappel
   * lorsqu'on utilise l'usine menus. Souvent, on met tous les rappels
   * des menus dans un fichier sar ce qui assure une meilleure
   * organisation. */

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




  Un _m_a_k_e_f_i_l_e pour que cela soit plus facile compiler :












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

  O_FILES = menus.o main.o

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

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

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




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



  1122..  WWiiddggeettss nnoonn ddooccuummeenntt

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

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

  Lorsque vous avez compris toutes les fonctions d'un nouveau widget non
  document pensez rire un didacticiel pour que les autres puissent b
  icier du temps que vous y avez pass


  1122..11..  EEnnttrrss ddee tteexxttee



  1122..22..  SSeeccttiioonnss ddee ccoouulleeuurrss



  1122..33..  CCoonnttrree dd''iinntteerrvvaallllee



  1122..44..  RRlleess



  1122..55..  BBooeess ddee tteexxttee



  1122..66..  PPrriissuuaalliissaattiioonn

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






























































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

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

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

  Encore plus : pour certains plug-ins une intervention humaine en
  temps rl, spifique aux images, peut s'aver nessaire. Dans le
  plug-in SuperNova, par exemple, on demande l'utilisateur d'entrer
  les coordonns du centre de la future supernova. La fan la plus
  simple de faire cela, vraiment, est de prenter une prisualisation
  l'utilisateur et de lui demander de choisir interactivement le
  point. Utilisons les prisualisations !

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

  Quand ne pas utiliser les prisualisations

  N'utilisez pas les prisualisations pour les graphes, les trac,
  etc. GDK est bien plus rapide pour . N'utilisez les que pour les
  images !

  Utilisons les prisualisations !

  Vous pouvez mettre une prisualisation dans peu pr n'importe
  quoi. Dans une vbox, une hbox, un bouton, etc. Mais elles donnent leur
  meilleur d'elles-mes dans des cadres resserr autour d'elles. Les
  prisualisations n'ont, par elles-mes, aucun contour et semblent
  plates sans eux. (Bien s, si c'est cet aspect que vous
  voulez...). Les cadres serr fournissent les bordures nessaires.

                                 [Image][Image]

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


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

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

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

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

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

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

    my_preview_rendering_function(preview);
    return frame;
  }

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

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

                                     [Image]

  La solution consiste placer la prisualisation et le label dans une
  table de 2x2 en les attachant avec les paramres suivants (c'est
  l'une des possibilit, bien s. La clconsiste ne pas mettre
  GTK_FILL dans le second attachement)bsp;:
  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 rultat :

                                     [Image]

  Divers

  Rendre une prisualisation cliquable se fait tr facilement en la plant dans un bouton. Cela ajoute aussi une bordure agrble autour de la prisualisation et vous n'avez me pas besoin de la mettre dans un cadre.  Voir le plug-in Filter Pack Simulation comme exemple.

  Remplir une prisualisation

  Afin de nous familiariser avec les bases de ce remplissage, crns 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;                             /* Coordonns      */
    double r, alpha, x, y;

    if (preview==NULL) return; /* J'ajoute galement ceci quand je */
                               /* veux iter des plantages stupides  */
                               /* Vous devez vous assurer que tout a  */
                               /* 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);

      /* Inse "row" dans "preview" en partant du point de */
      /* coordonns (0,j) premie colonne, j_ie ligne allant de SIZE */
      /* pixels vers la droite */
    }

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

  Ceux qui n'utilisent pas GIMP en ont suffisamment vu pour faire
  dbeaucoup de choses. Pour ceux qui l'utilisent, j'ai quelques
  prisions ajouter.
  Prisualisation d'image

  Il est pratique de conserver une version ruite de l'image ayant
  juste assez de pixels pour remplir la prisualisation. Ceci est
  possible en choisissant chaque ie pixel on est le ratio de la
  taille de l'image par rapport la taille de la visualisation. Toutes
  les opations suivantes (y compris le remplissage des
  prisualisations) sont alors rliss seulement sur le nombre ruit
  de pixels. Ce qui suit est mon implantation de la ruction 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 ruit l'image la taille de prisualisation choisie */
    /* La taille de la prisualisation est dermin par LongerSize, i.e.  */
    /* la plus grande des deux dimensions. Ne fonctionne qu'avec des images  */
    /* RGB !                                                                 */

    gint RH, RW;          /* Hauteur et Largeur ruites                     */
    gint width, height;   /* Largeur et Hauteur de la surface ruire      */
    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 entie    */

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

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

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

    /* Si on veut prisualiser une section avec une surface qui l'entoure, */
    /* on doit l'agrandir un petit peu. Considez  comme une devinette.   */

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

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

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

    /* Les lignes ci-dessous derminent la dimension qui sera le cot*/
    /* le plus long. Cette id est emprunt au plug-in Supernova.    */
    /* Je soupnne que j'aurais pu y penser moi-me, mais la vit */
    /* doit re dite. Le plagiat pue !                                */

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

    /* L'image entie est ruite dans une chae ! */

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

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

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

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

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

        /* Pas de section = chaque point est complement sectionn! */

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

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

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

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

  La suite est une fonction de prisualisation qui utilise le me type
  <em/ReducedImage/ !  On remarque qu'elle utilise une fausse
  transparence (au moyen de <em/fake_transparancy/ qui est dini 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 prisualisationbsp;:

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


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

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

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

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

  Fonctions applicables

  guint           gtk_preview_get_type           (void);
  /* Aucune id */
  void            gtk_preview_uninit             (void);
  /* Aucune id */
  GtkWidget*      gtk_preview_new                (GtkPreviewType   type);
  /* Drite ci-dessous */
  void            gtk_preview_size               (GtkPreview      *preview,
                                                  gint             width,
                                                  gint             height);
  /* Permet de changer la taille d'une prisualisation existante */
  /* Apparamment, il y a un bug dans GTK qui rend ce traitement   */
  /* hasardeux. Une mhode pour corriger ce proble consiste  */
  /* changer manuellement la taille de la fenre contenant la    */
  /* prisualisation apr avoir changla taille de la          */
  /* prisualisation.                                            */

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

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

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

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

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

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

  That's all, folks!






  1122..77..  CCoouurrbbeess



  1133..  WWiiddggeett EEvveennttBBooxx

  Il n'est disponible que dans _g_t_k_+_9_7_0_9_1_6_._t_a_r_._g_z et les distributions
  ultieures.

  Certains widgets GTK n'ont pas de fenre X associ, il se dessinent donc
  sur leurs parents. cause de cela, ils ne peuvent recevoir d'ements et,
  s'ils ont une taille incorrecte, ils ne peuvent pas se mettre en place
  correctement : on peut alors avoir des surimpressions douteuses, etc.
  Si vous avez besoin de ces widgets, _E_v_e_n_t_B_o_x est fait pour vous.

  Au premier abord, le widget _E_v_e_n_t_B_o_x peut apparare comme totalement
  dud'int. Il ne dessine rien l'ran et ne rond aucun enement. Cependant,
  il joue un re - il fournit une fenre X pour son widget fils. Ceci est
  important car de nombreux widgets GTK n'ont pas de fenre X associ. Ne
  pas avoir de fenre permet d'onomiser de la moire mais a aussi quelques
  inconvients. Un widget sans fenre ne peut recevoir d'ement, et ne
  rlise aucune mise en place de ce qu'il contient. Bien que le nom
  EventBox insiste sur la fonction de gestion d'ement, le widget peut
  aussi re utilispour la mise en place (et plus... voir l'exemple ci-
  dessous).


  Pour crr un widget EventBox, on utilise :



       GtkWidget* gtk_event_box_new (void);





  Un widget fils peut alors re ajoutcet _E_v_e_n_t_B_o_x :



       gtk_container_add (GTK_CONTAINER(event_box), widget);





  L'exemple suivant montre l'utilisation d'un _E_v_e_n_t_B_o_x - un label est cr
  et mis en place sur une petite boe, et configurpour qu'un clic souris
  sur le label provoque la fin du programme.




















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

      /* Crtion d'un EventBox et ajout de celui-ci dans la fenre. */

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

      /* Crtion d'un long label */

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

      /* Placement serr */

      gtk_widget_set_usize (label, 110, 20);

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

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

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

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

      gtk_widget_show (window);

      gtk_main ();

      return 0;
  }





  1144..  CCoonnffiigguurraattiioonn ddeess aattttrriibbuuttss ddee wwiiddggeett

  Cette section drit les fonctions opant sur les widgets. Elles peuvent
  re utiliss pour configurer le style, l'espacement, la taille, etc.

  (Je devrais peut re faire une section uniquement consacr aux
  raccourcis clavier.)

       void       gtk_widget_install_accelerator (GtkWidget           *widget,
                                                  GtkAcceleratorTable *table,
                                                  gchar               *signal_name,
                                                  gchar                key,
                                                  guint8               modifiers);

       void       gtk_widget_remove_accelerator  (GtkWidget           *widget,
                                                  GtkAcceleratorTable *table,
                                                  gchar               *signal_name);

       void       gtk_widget_activate            (GtkWidget           *widget);

       void       gtk_widget_set_name            (GtkWidget           *widget,
                                                  gchar               *name);
       gchar*     gtk_widget_get_name            (GtkWidget           *widget);

       void       gtk_widget_set_sensitive       (GtkWidget           *widget,
                                                  gint                 sensitive);

       void       gtk_widget_set_style           (GtkWidget           *widget,
                                                  GtkStyle            *style);

       GtkStyle*    gtk_widget_get_style     (GtkWidget *widget);

       GtkStyle*    gtk_widget_get_default_style    (void);

       void       gtk_widget_set_uposition       (GtkWidget           *widget,
                                                  gint                 x,
                                                  gint                 y);
       void       gtk_widget_set_usize           (GtkWidget           *widget,
                                                  gint                 width,
                                                  gint                 height);

       void       gtk_widget_grab_focus          (GtkWidget           *widget);

       void       gtk_widget_show                (GtkWidget           *widget);

       void       gtk_widget_hide                (GtkWidget           *widget);







  1155..  TTeemmppoorriissaattiioonnss,, ffoonnccttiioonnss dd''EE//SS eett dd''aatttteennttee


  1155..11..  TTeemmppoorriissaattiioonnss

  Vous pouvez vous demander comment faire pour que GTK fasse quelque
  chose d'utile lorsqu'il est dans _g_t_k___m_a_i_n. En fait, on a plusieurs
  options. L'utilisation des fonctions suivantes permet de crr une
  temporisation qui sera appel tous les _i_n_t_e_r_v_a_l millisecondes.



       gint gtk_timeout_add (guint32 interval,
                             GtkFunction function,
                             gpointer data);




  Le premier paramre est le nombre de millisecondes entre les appels
  notre fonction. Le deuxie est la fonction appeler et le troisie est la
  donn pass cette fonction de rappel. La valeur retourn est un marqueur
  de type entier qui pourra re utilispour arrer la temporisation en
  appelant :



       void gtk_timeout_remove (gint tag);




  On peut aussi stopper la fonction de temporisation en faisant
  retourner zo ou FALSE notre fonction de rappel. idemment, cela veut
  dire que si vous voulez que votre fonction continue re appel, elle
  doit retourner une valeur non nulle, ou TRUE.

  La dlaration de votre fonction de rappel doit ressembler  :



       gint timeout_callback (gpointer data);





  1155..22..  SSuurrvveeiillllaannccee ddeess EE//SS

  Une autre caractistique intessante du GTK est la possibilitde vifier
  les donns d'un descripteur de fichier (celles retourns par _o_p_e_n(2) ou
  _s_o_c_k_e_t(2)). C'est particuliement pratique pour les applications reau.
  La fonction suivante permet cette vification :



       gint gdk_input_add (gint source,
                           GdkInputCondition condition,
                           GdkInputFunction  function,
                           gpointer data);




  Le premier paramre est le descripteur de fichier que l'on veut udier,
  le second spifie ce qu'on veut que le GDK recherche. Cela peut re :

  GDK_INPUT_READ - Appel _f_u_n_c_t_i_o_n lorsqu'il y a une donn pre re lue dans
  le descripteur de fichier.

  GDK_INPUT_WRITE - Appel de _f_u_n_c_t_i_o_n lorsque le descripteur de fichier
  est pr pour une riture.

  Je suis s que vous vous doutez, maintenant, que le troisie paramre est
  la fonction que l'on veut appeler lorsque les conditions ci-dessus
  sont satisfaites. Le dernier paramre est la donn passer cette
  fonction.

  La valeur retourn est un marqueur qui pourra re utilispour dire au GDK
  de cesser de surveiller ce descripteur l'aide de la fonction :



       void gdk_input_remove (gint tag);



  La fonction de rappel doit re dlar de la fan suivante :



       void input_callback (gpointer data, gint source,
                            GdkInputCondition condition);






  1155..33..  FFoonnccttiioonnss dd''aatttteennttee

  Que se passe-t'il si vous avez une fonction qui doit re appel lorsque
  rien d'autre ne se passe ? On utilise la fonction suivante qui force
  GTK appeler _f_u_n_c_t_i_o_n lorsqu'on est en phase d'inaction ;



       gint gtk_idle_add (GtkFunction function,
                          gpointer data);






       void gtk_idle_remove (gint tag);




  Je n'expliquerai pas la signification des paramres car ils ressemblent
  beaucoup ceux dvus ci-dessus. La fonction point par le premier paramre
  de _g_t_k___i_d_l_e___a_d_d_(_) sera appel chaque occasion. Comme pour les autres,
  retourner FALSE emphera la fonction d'attente d're appel.


  1166..  GGeessttiioonn ddeess sseeccttiioonnss

  1166..11..  IInnttrroodduuccttiioonn

  Un type de communication inter-processus g par GTK est les _s_e_c_t_i_o_n_s.
  Une section identifie un morceau de donns, par exemple une portion de
  texte sectionn par l'utilisateur avec la souris. Seule une application
  sur un ran (le _p_r_o_p_r_i_a_i_r_e) peut posser une section particulie un
  moment donn ainsi lorsqu'une section est rlam par une application, le
  propriaire prent doit indiquer l'utilisateur que la section a
  abandonn. Les autres applications peuvent demander le contenu d'une
  section sous diffentes formes appels _c_i_b_l_e_s. Il peut y avoir un nombre
  quelconque de sections, mais la plupart des applications X n'en gent
  qu'une, la _s_e_c_t_i_o_n _p_r_i_m_a_i_r_e.


  Dans la plupart des cas, une application GTK n'a pas besoin de ger
  elle-me les sections. Les widgets standards, comme le widget Entr de
  texte, possent dla capacitde rlamer la section lorsqu'il le faut (par
  exemple, lorsque l'utilisateur glisse au dessus d'un texte) et de
  ruper le contenu de la section denue par un autre widget ou une autre
  application (par exemple, lorsque l'utilisateur clique avec le deuxie
  bouton de la souris). Cependant, il peut il y avoir des cas dans
  lesquels vous voulez donner aux autres widgets la possibilitde fournir
  la section, ou vous direz ruper des cibles non supports par daut.


  Un concept fondamental dans la comprension du fonctionnement des
  sections est celui d'_a_t_o_m_e. Un atome est un entier qui dinit de fan
  unique une chae (sur un affichage particulier). Certains atomes sont
  prinis par le serveur X et, dans certains cas, des constantes dinies
  dans _g_t_k_._h correspondent ces atomes. Par exemple, la constante
  GDK_PRIMARY_SELECTION correspond la chae "PRIMARY".  Dans d'autres
  cas, on doit utiliser les fonctions _g_d_k___a_t_o_m___i_n_t_e_r_n_(_), pour obtenir
  l'atome correspondant une chae, et _g_d_k___a_t_o_m___n_a_m_e_(_), pour obtenir le
  nom d'un atome. Les sections et les cibles sont identifi par des
  atomes.


  1166..22..  RRuuppaattiioonn ddee llaa sseeccttiioonn

  La rupation de la section est un processus asynchrone. Pour darrer le
  processus, on appelle :



       gint gtk_selection_convert   (GtkWidget           *widget,
                                     GdkAtom              selection,
                                     GdkAtom              target,
                                     guint32              time)




  Cela _c_o_n_v_e_r_t_i_t la section dans la forme spifi par _t_a_r_g_e_t. Si tout est
  possible, le paramre _t_i_m_e sera le moment de l'ement qui a dlenchla
  section. Ceci aide s'assurer que les ements arrivent dans l'ordre
  ol'utilisateur les a demand Cependant, si cela n'est pas possible (par
  exemple, lorsque la conversion a dlench par un signal "clicked"),
  alors on peut utiliser la macro GDK_CURRENT_TIME.


  Quand le propriaire de la section rond la reque, un signal
  "selection_received" est envoynotre application. Le gestionnaire de ce
  signal reit un pointeur vers une structure GtkSelectionData dinie
  ainsi :



       struct _GtkSelectionData
       {
         GdkAtom selection;
         GdkAtom target;
         GdkAtom type;
         gint    format;
         guchar *data;
         gint    length;
       };




  _s_e_l_e_c_t_i_o_n et _t_a_r_g_e_t sont les valeurs que l'on a donndans notre appel
  _g_t_k___s_e_l_e_c_t_i_o_n___c_o_n_v_e_r_t_(_). _t_y_p_e est un atome qui identifie le type de
  donns retournpar le propriaire de la section. Quelques valeurs
  possibles sont : "STRING", une chae de caractes latin-1, "ATOM", une
  sie d'atomes, "INTEGER", un entier, etc. La plupart des cibles ne
  peuvent retourner qu'un type. _f_o_r_m_a_t donne la longueur des unit (les
  caractes, par exemple) en bits. Habituellement, on ne se prccupe pas
  de cela lorsqu'on reit des donns. _d_a_t_a est un pointeur vers la donn
  retourn et _l_e_n_g_t_h donne la longueur en octets de la donn retourn. Si
  _l_e_n_g_t_h est native, cela indique qu'une erreur est survenue et que la
  section ne peut re rup. Ceci peut arriver si aucune application n'est
  propriaire de la section, ou si vous avez demandune cible que
  l'application ne sait pas ger. Le tampon est garanti d're un octet
  plus long que _l_e_n_g_t_h ; l'octet supplentaire sera toujours zo, et il
  n'est donc pas nessaire de faire une copie de chae simplement pour
  qu'elle soit termin par zo (comme doivent l're toutes les chaes C).


  Dans l'exemple qui suit, on rupe la cible spiale "TARGETS", qui est
  une liste de toutes les cibles en lesquelles la section peut re
  convertie.
























































  #include <gtk/gtk.h>

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

  /* Gestionnaire de signal invoqulorsque l'utilisateur clique sur
   * le bouton Obtenir les cibles  */

  void get_targets (GtkWidget *widget, gpointer data)
  {
    static GdkAtom targets_atom = GDK_NONE;

    /* Obtention de l'atome correspondant la chae "TARGETS" */

    if (targets_atom == GDK_NONE)
      targets_atom = gdk_atom_intern ("TARGETS", FALSE);

    /* Demande de la cible "TARGETS" pour la section primaire */

    gtk_selection_convert (widget, GDK_SELECTION_PRIMARY, targets_atom,
                           GDK_CURRENT_TIME);
  }

  /* Gestionnaire de signal appelquand le propriaire des sections
   * retourne la donn. */

  void selection_received (GtkWidget *widget, GtkSelectionData *selection_data,
                      gpointer data)
  {
    GdkAtom *atoms;
    GList *item_list;
    int i;

    /* **** IMPORTANT **** On vifie si la rupation s'est bien pass. */

    if (selection_data->length < 0)
      {
        g_print ("Selection retrieval failed\n");
        return;
      }

    /* On s'assure que l'on a obtenu la donn sous la forme attendue. */

    if (selection_data->type != GDK_SELECTION_TYPE_ATOM)
      {
        g_print ("La section \"TARGETS\" n'a pas retourn sous la forme d'atomes !\n");
        return;
      }

    /* Affichage des atomes res. */

    atoms = (GdkAtom *)selection_data->data;

    item_list = NULL;
    for (i=0; 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);

    /* Crtion de la fenre de l'application. */

    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title (GTK_WINDOW (window), "Sections");
    gtk_container_border_width (GTK_CONTAINER (window), 10);

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

    /* Crtion d'un bouton pour obtenir les cibles */

    button = gtk_button_new_with_label ("Obtenir les cibles");
    gtk_container_add (GTK_CONTAINER (window), button);

    gtk_signal_connect (GTK_OBJECT(button), "clicked",
                        GTK_SIGNAL_FUNC (get_targets), NULL);
    gtk_signal_connect (GTK_OBJECT(button), "selection_received",
                        GTK_SIGNAL_FUNC (selection_received), NULL);

    gtk_widget_show (button);
    gtk_widget_show (window);

    gtk_main ();

    return 0;
  }





  1166..33..  FFoouurrnniirr llaa sseeccttiioonn


  Fournir la section est un peu plus compliqu On doit enregistrer les
  gestionnaires qui seront appel lorsque notre section est demand. Pour
  chaque paire section/cible que l'on gera, on fera un appel ~:



       void gtk_selection_add_handler (GtkWidget           *widget,
                                       GdkAtom              selection,
                                       GdkAtom              target,
                                       GtkSelectionFunction function,
                                       GtkRemoveFunction    remove_func,
                                       gpointer             data);




  _w_i_d_g_e_t, _s_e_l_e_c_t_i_o_n et _t_a_r_g_e_t identifient les reques que ce gestionnaire
  gera. S'il ne vaut pas NULL, _r_e_m_o_v_e___f_u_n_c sera appellorsque le
  gestionnaire de signal est supprim Ceci est utile, par exemple, pour
  des langages interpr qui doivent garder une trace du nombre de rences
  _d_a_t_a.

  La fonction de rappel _f_u_n_c_t_i_o_n doit avoir la signature suivante :



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




  Le _G_t_k_S_e_l_e_c_t_i_o_n_D_a_t_a est le me qu'au dessus, mais, cette fois, nous
  sommes responsables de l'initialisation de ses champs _t_y_p_e, _f_o_r_m_a_t,
  _d_a_t_a, et _l_e_n_g_t_h. (Le champ _f_o_r_m_a_t est important ici - le serveur X
  l'utilise pour savoir si la donn doit re hang par octet ou non.
  Habituellement, ce sera 8 (un caracte), ou 32 (un entier)). Cette
  initialisation est faite en utilisant l'appel :



       void gtk_selection_data_set (GtkSelectionData *selection_data,
                                    GdkAtom           type,
                                    gint              format,
                                    guchar           *data,
                                    gint              length);




  Cette fonction s'occupe de faire une copie correcte des donns afin que
  l'on n'ait pas se soucier du reste. (On ne doit pas remplir ces champs
  la main).


  Lorsque cela est demandpar l'utilisateur, on rlame la possession de la
  section en appelant :



       gint gtk_selection_owner_set (GtkWidget           *widget,
                                     GdkAtom              selection,
                                     guint32              time);




  Si une autre application rlame la possession de la section, on recevra
  un "selection_clear_event".

  Comme exemple de fourniture de section, l'exemple suivant ajoute une
  fonctionnalitde section un bouton commutateur. Lorsque ce bouton est
  appuy le programme rlame la section primaire. La seule cible support
  (part certaines cibles fournies par GTK lui-me, comme TARGETS  est
  STRING  Lorsque celle-ci est demand, on retourne une reprentation de
  l'heure sous forme de chae.











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

  /* Fonction de rappel appel lorsque l'utilisateur commute la section. */

  void selection_toggled (GtkWidget *widget, gint *have_selection)
  {
    if (GTK_TOGGLE_BUTTON(widget)->active)
      {
        *have_selection = gtk_selection_owner_set (widget,
                                                   GDK_SELECTION_PRIMARY,
                                                   GDK_CURRENT_TIME);
        /* Si la demande de section houe, on remet le bouton en position sortie. */

        if (!*have_selection)
          gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON(widget), FALSE);
      }
    else
      {
        if (*have_selection)
          {
            /* Avant de nettoyer la selection en mettant son propriaire NULL,
             * on vifie que nous sommes bien son propriaire actuel. */

            if (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == widget->window)
              gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY,
                                       GDK_CURRENT_TIME);
            *have_selection = FALSE;
          }
      }
  }

  /* Appel lorsqu'une autre application demande la section. */

  gint selection_clear (GtkWidget *widget, GdkEventSelection *event,
                   gint *have_selection)
  {
    *have_selection = FALSE;
    gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON(widget), FALSE);

    return TRUE;
  }

  /* Fournit l'heure comme section. */

  void selection_handle (GtkWidget *widget,
                    GtkSelectionData *selection_data,
                    gpointer data)
  {
    gchar *timestr;
    time_t current_time;

    current_time = time (NULL);
    timestr = asctime (localtime(&current_time));

    /* Lorsqu'on retourne une chae, elle ne doit pas se terminer par
     * 0, ce sera fait pour nous. */

    gtk_selection_data_set (selection_data, GDK_SELECTION_TYPE_STRING,
                            8, timestr, strlen(timestr));
  }

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

    GtkWidget *selection_button;

    static int have_selection = FALSE;

    gtk_init (&argc, &argv);

    /* Crtion de la fenre principale. */

    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title (GTK_WINDOW (window), "Event Box");
    gtk_container_border_width (GTK_CONTAINER (window), 10);

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

    /* Crtion d'un bouton commutateur pour qu'il agisse comme une section. */

    selection_button = gtk_toggle_button_new_with_label ("Demande de section");
    gtk_container_add (GTK_CONTAINER (window), selection_button);
    gtk_widget_show (selection_button);

    gtk_signal_connect (GTK_OBJECT(selection_button), "toggled",
                        GTK_SIGNAL_FUNC (selection_toggled), &have_selection);
    gtk_signal_connect (GTK_OBJECT(selection_button), "selection_clear_event",
                        GTK_SIGNAL_FUNC (selection_clear), &have_selection);

    gtk_selection_add_handler (selection_button, GDK_SELECTION_PRIMARY,
                               GDK_SELECTION_TYPE_STRING,
                               selection_handle, NULL, NULL);

    gtk_widget_show (selection_button);
    gtk_widget_show (window);

    gtk_main ();

    return 0;
  }







  1177..  gglliibb

  La _g_l_i_b fournit de nombreuses fonctions et dinitions utiles, pres re
  utiliss lorsqu'on cr des applications GDK et GTK. Je les umerais
  toutes avec une bre explication. Beaucoup sont des rliques des
  fonctions standards de la _l_i_b_c, et je ne les daillerais donc pas trop.
  Ceci doit surtout servir de rence afin de savoir ce qui est disponible
  pour re utilis


  1177..11..  DDiinniittiioonnss

  Les dinitions pour les bornes de la plupart des types standards sont :









  G_MINFLOAT
  G_MAXFLOAT
  G_MINDOUBLE
  G_MAXDOUBLE
  G_MINSHORT
  G_MAXSHORT
  G_MININT
  G_MAXINT
  G_MINLONG
  G_MAXLONG




  Voici aussi les redinitions de types. Celles qui ne sont pas spifis
  sont configurs dynamiquement selon l'architecture. itez surtout de
  compter sur la taille d'un pointeur si vous voulez un programme
  portable ! Un pointeur sur un Alpha fait 8 octets, mais il en fait 4
  sur un Intel.



       char   gchar;
       short  gshort;
       long   glong;
       int    gint;
       char   gboolean;

       unsigned char   guchar;
       unsigned short  gushort;
       unsigned long   gulong;
       unsigned int    guint;

       float   gfloat;
       double  gdouble;
       long double gldouble;

       void* gpointer;

       gint8
       guint8
       gint16
       guint16
       gint32
       guint32





  1177..22..  LLiisstteess ddoouubblleemmeenntt cchhaass

  Les fonctions suivantes servent crr, ger et druire des listes
  doublement chas. Je suppose que vous savez ce qu'est une liste cha car
  leur explication n'entre pas dans le cadre de ce document. Bien s, il
  n'y a pas besoin de les connare pour une utilisation gale de GTK, mais
  c'est bien de savoir comment elles fonctionnent.









  GList* g_list_alloc       (void);

  void   g_list_free        (GList     *list);

  void   g_list_free_1      (GList     *list);

  GList* g_list_append      (GList     *list,
                             gpointer   data);

  GList* g_list_prepend     (GList     *list,
                             gpointer   data);

  GList* g_list_insert      (GList     *list,
                             gpointer   data,
                             gint       position);

  GList* g_list_remove      (GList     *list,
                             gpointer   data);

  GList* g_list_remove_link (GList     *list,
                             GList     *link);

  GList* g_list_reverse     (GList     *list);

  GList* g_list_nth         (GList     *list,
                             gint       n);

  GList* g_list_find        (GList     *list,
                             gpointer   data);

  GList* g_list_last        (GList     *list);

  GList* g_list_first       (GList     *list);

  gint   g_list_length      (GList     *list);

  void   g_list_foreach     (GList     *list,
                             GFunc      func,
                             gpointer   user_data);







  1177..33..  LLiisstteess ssiimmpplleemmeenntt cchhaass

  La plupart des fonctions pour les listes simplement chas ci-dessous
  sont identiques celles vues plus haut. Voici une liste comple :
















  GSList* g_slist_alloc       (void);

  void    g_slist_free        (GSList   *list);

  void    g_slist_free_1      (GSList   *list);

  GSList* g_slist_append      (GSList   *list,
                               gpointer  data);

  GSList* g_slist_prepend     (GSList   *list,
                               gpointer  data);

  GSList* g_slist_insert      (GSList   *list,
                               gpointer  data,
                               gint      position);

  GSList* g_slist_remove      (GSList   *list,
                               gpointer  data);

  GSList* g_slist_remove_link (GSList   *list,
                               GSList   *link);

  GSList* g_slist_reverse     (GSList   *list);

  GSList* g_slist_nth         (GSList   *list,
                               gint      n);

  GSList* g_slist_find        (GSList   *list,
                               gpointer  data);

  GSList* g_slist_last        (GSList   *list);

  gint    g_slist_length      (GSList   *list);

  void    g_slist_foreach     (GSList   *list,
                               GFunc     func,
                               gpointer  user_data);






  1177..44..  GGeessttiioonn ddee llaa mmooiirree



       gpointer g_malloc      (gulong    size);




  Remplace _m_a_l_l_o_c_(_).  On n'a pas besoin de vifier la valeur de retour
  car cela est fait pour nous dans cette fonction.



       gpointer g_malloc0     (gulong    size);




  Identique la prente, mais initialise la moire zo avant de retourner un
  pointeur vers la zone rerv.


       gpointer g_realloc     (gpointer  mem,
                               gulong    size);




  Rlloue _s_i_z_e octets de moire partir de _m_e_m. idemment, la moire doit
  avoir allou auparavant.



       void     g_free        (gpointer  mem);




  Libe la moire. Facile.



       void     g_mem_profile (void);




  Produit un profil de la moire utilis, mais requiert l'ajout de _#_d_e_f_i_n_e
  _M_E_M___P_R_O_F_I_L_E au dut de _g_l_i_b_/_g_m_e_m_._c, de refaire un _m_a_k_e et un _m_a_k_e
  _i_n_s_t_a_l_l.



       void     g_mem_check   (gpointer  mem);




  Vifie qu'un emplacement moire est valide. Nessite que l'on ajoute
  _#_d_e_f_i_n_e _M_E_M___C_H_E_C_K au dut de _g_m_e_m_._c que l'on refasse un _m_a_k_e et un _m_a_k_e
  _i_n_s_t_a_l_l.


  1177..55..  TTiimmeerrss

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







  1177..66..  GGeessttiioonn ddeess cchhaaeess

  Un ensemble complet de fonction de gestion des chaes. Elles semblent
  toutes tr intessantes et sont sement meilleures, bien des ards, que
  les fonctions C standards, mais elle nessitent de la documentation.



       GString* g_string_new       (gchar   *init);
       void     g_string_free      (GString *string,
                                    gint     free_segment);

       GString* g_string_assign    (GString *lval,
                                    gchar   *rval);

       GString* g_string_truncate  (GString *string,
                                    gint     len);

       GString* g_string_append    (GString *string,
                                    gchar   *val);

       GString* g_string_append_c  (GString *string,
                                    gchar    c);

       GString* g_string_prepend   (GString *string,
                                    gchar   *val);

       GString* g_string_prepend_c (GString *string,
                                    gchar    c);

       void     g_string_sprintf   (GString *string,
                                    gchar   *fmt,
                                    ...);

       void     g_string_sprintfa  (GString *string,
                                    gchar   *fmt,
                                    ...);






  1177..77..  UUttiilliittaaiirreess eett ffoonnccttiioonnss dd''eerrrreeuurrss



       gchar* g_strdup    (const gchar *str);




  Remplace la fonction _s_t_r_d_u_p. Elle copie le contenu de la chae
  d'origine dans la moire venant d're allou et retourne un pointeur sur
  cette zone.



       gchar* g_strerror  (gint errnum);




  Je recommande de l'utiliser pour tous les messages d'erreur. Elle est
  beaucoup plus propre et plus portable que _p_e_r_r_o_r_(_) ou les autres. La
  sortie est habituellement de la forme :
       nom du programme:fonction qui a houfichier ou autre descripteur:strerror




  Voici un exemple d'appel utilisdans le programme Bonjour tout le monde
  ! ~:



       g_print("bonjour_monde:open:%s:%s\n", filename, g_strerror(errno));






       void g_error   (gchar *format, ...);




  Affiche un message d'erreur. Le format est comme _p_r_i_n_t_f, mais il
  ajoute ** ERROR **: au dut du message et sort du programme. n'utiliser
  que pour les erreurs fatales.



       void g_warning (gchar *format, ...);




  Comme au dessus, mais ajoute ** WARNING **:  et ne termine pas le
  programme.



       void g_message (gchar *format, ...);




  Affiche  message: avant la chae pass en paramre.



       void g_print   (gchar *format, ...);




  Remplace _p_r_i_n_t_f_(_).

  Enfin la dernie fonction :



       gchar* g_strsignal (gint signum);




  Affiche le nom du signal syste Unix correspondant au numo de signal.
  Utile pour les fonctions giques de gestion de signaux.

  Tout ce qui est ci-dessus est plus ou moins vol_g_l_i_b_._h. Si quelqu'un
  s'occupe de documenter une fonction, qu'il m'envoit un courrier !



  1188..  FFiicchhiieerrss rrcc ddee GGTTKK

  GTK a sa propre mhode pour ger les configurations par daut des
  applications, en utilisant des fichiers rc. Ceux-ci peuvent re utilis
  pour configurer les couleurs de presque tous les widgets, et pour
  mettre des pixmaps sur le fond de certains widgets.


  1188..11..  FFoonnccttiioonnss ppoouurr lleess ffiicchhiieerrss rrcc

  Au darrage de votre application, ajoutez un appel ~:


       void gtk_rc_parse (char *filename);




  en lui passant le nom de votre fichier rc. Ceci forcera GTK analyser
  ce fichier et utiliser les configurations de styles pour les types de
  widgets qui y sont dinis.

  Si vous voulez avoir un ensemble particulier de widgets qui prenne le
  pas sur le style des autres, ou une autre division logique de widgets,
  utilisez un appel ~:


       void gtk_widget_set_name (GtkWidget *widget,
                                 gchar *name);




  En lui passant comme premier paramre le widget que vous avez cr, et le
  nom que vous voulez lui donner comme second paramre. Ceci vous
  permettra de changer les attributs de ce widget par son nom dans le
  fichier rc.

  Si vous utilisez un appel comme celui-ci :



       button = gtk_button_new_with_label ("Bouton Spial");
       gtk_widget_set_name (button, "bouton special");




  Ce bouton s'appelle bouton special et peut re accpar son nom dans le
  fichier rc en tant que bouton special.GtkButton   [<--- Vifiez !]

  Le fichier rc ci-dessous configure les propri de la fenre principale
  et fait hiter tous les fils de celle-ci du style drit par
  bouton_principal  Le code utilisdans l'application est :



       window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
       gtk_widget_set_name (window, "fenetre principale");


  Et le style est dini dans le fichier rc avec :



       widget "fenetre principale.*GtkButton*" style "bouton_principal"




  Ce qui configure tous les widgets _G_t_k_B_u_t_t_o_n de fenre principale avec
  le style bouton_principal dini dans le fichier rc.

  Ainsi que vous pouvez le voir, il s'agit d'un syste puissant et
  flexible. Utilisez votre imagination pour en tirer le meilleur.


  1188..22..  FFoorrmmaatt ddeess ffiicchhiieerrss rrcc ddee GGTTKK

  Le format du fichier GTK est illustrdans l'exemple suivant. Il s'agit
  du fichier _t_e_s_t_g_t_k_r_c de la distribution GTK mais j'ai ajoutquelques
  commentaires et autres choses. Vous pouvez inclure cette explication
  votre application pour permettre l'utilisateur de rler finement son
  application.

  Il y a plusieurs directives pour changer les attributs d'un widget.

  +o  fg - configure la couleur de premier plan d'un widget.

  +o  bg - configure la couleur d'arrie plan d'un widget.

  +o  bg_pixmap - configure l'arrie plan d'un widget avec un pixmap.

  +o  font - configure la fonte utiliser pour un widget.

  De plus, un widget peut se trouver dans diffents ats et l'on peut
  configurer des couleurs, pixmaps et fontes diffentes pour chacun
  d'eux. Ces ats sont :

  +o  NORMAL - L'at normal d'un widget, sans la souris au dessus de lui,
     non press etc.

  +o  PRELIGHT - Lorsque la souris se trouve au dessus du widget, les
     couleurs dinies pour cet at sont actives.

  +o  ACTIVE - Lorsque le widget est pressou cliqu il devient actif et
     les attributs associ cet at sont appliqu.

  +o  INSENSITIVE - Quand un widget est configurpour re insensible et
     qu'il ne peut re activ il prend ces attributs.

  +o  SELECTED - Lorsqu'un objet est choisi, il prend ces attributs.

  Lorsqu'on utilise les mots-cl _f_g et _b_g pour configurer les couleurs
  des widgets, le format est :



        fg[<STATE>] = { Red, Green, Blue }




  OSTATE est l'un des ats vus plus haut (PRELIGHT, ACTIVE etc), et o_R_e_d,
  _G_r_e_e_n et _B_l_u_e sont des valeurs comprises entre 0 et 1.0. { 1.0, 1.0,
  1.0 } reprente la couleur blanche.  Ces valeurs doivent re de type rl
  ou elles seront consids comme valant 0, ainsi un simple 1 ne marchera
  pas, il faut mettre 1.0  Un 0 simple convient car ce n'est pas un
  proble s'il n'est pas reconnu puisque toutes les valeurs non reconnues
  sont mises 0.

  _b_g___p_i_x_m_a_p est tr similaire, sauf que les couleurs sont remplacs par un
  nom de fichier.

  _p_i_x_m_a_p___p_a_t_h est une liste de chemins sar par des :  Ces chemins seront
  parcourus pour chaque pixmap que l'on spifie.


  La directive _f_o_n_t est simplement :


       font = "<font name>"




  Ola seule partie difficile est d'arriver comprendre la chae contenant
  le nom de la fonte. L'utilisation de _x_f_o_n_t_s_e_l ou d'un autre utilitaire
  semblable peut aider.

  _w_i_d_g_e_t___c_l_a_s_s configure le style d'une classe de widgets. Ces classes
  sont lists dans la section sur la hiarchie des widgets.

  La directive _w_i_d_g_e_t configure un ensemble spifique de widgets selon un
  style donn annulant tout style de configuration pour la classe de
  widget donn. Ces widgets sont enregistr dans l'application en
  utilisant l'appel _g_t_k___w_i_d_g_e_t___s_e_t___n_a_m_e_(_). Ceci vous permet de spifier
  les attributs d'un widget, widget par widget, au lieu de configurer
  les attributs d'une classe entie de widgets. Je vous demande
  instamment de documenter tous ces widgets spiaux pour que les
  utilisateurs puisse les adapter leurs besoins.

  Lorsque le mot-cl_p_a_r_e_n_t est utiliscomme attribut, le widget prendra
  les attributs de son parent dans l'application.

  Lorsqu'on dinit un style, on peut assigner les attributs d'un style d
  dini ce nouveau style.



       style "bouton_principal" = "button"
       {
         font = "-adobe-helvetica-medium-r-normal--*-100-*-*-*-*-*-*"
         bg[PRELIGHT] = { 0.75, 0, 0 }
       }




  Cet exemple prend le style "button" et cr un nouveau style
  "bouton_principal"en changeant simplement la fonte et la couleur de
  fond pour l'at PRELIGHT.

  Bien s, un bon nombre de ces attributs ne s'applique pas tous les
  widgets. C'est une question de bon sens. Tout ce qui peut s'appliquer
  s'applique.


  1188..33..  EExxeemmppllee ddee ffiicchhiieerr rrcc




  # 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 ats possibles. Remarquez que certains ne s'appliquent
  # pas certains widgets.
  #
  # NORMAL - L'at 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
  # dinies pour cet at sont actives.
  #
  # ACTIVE - Lorsque le widget est pressou cliqu il devient actif et les
  # attributs associ cet at sont appliqu.
  #
  # INSENSITIVE - Quand un widget est configurpour re insensible, et qu'il
  # ne peut re activ il prend ces attributs.
  #
  # SELECTED - Lorsqu'un objet est choisi, il prend ces attributs.
  #
  # Avec ces ats, on peut configurer les attributs des widgets dans chacun
  # de ces ats en utilisant les directives suivantes.
  #
  # fg - configure la couleur de premier plan d'un widget.
  # bg - configure la couleur d'arrie plan d'un widget.
  # bg_pixmap - configure l'arrie 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 assignaux widgets rls la fin du fichier.

  style "window"
  {
    #Configure l'espace autour de la fenre avec le pixmap spifi
    #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'at "NORMAL".

    fg[NORMAL] = { 1.0, 0, 0 }

    #Configure le pixmap d'arrie plan de ce widget celui de son parent.
    bg_pixmap[NORMAL] = "<parent>"
  }

  style "button"
  {
    # Voici tous les ats possibles pour un bouton. Le seul qui ne peut
    # s'appliquer est l'at 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 hite des attributs du style "button" puis on
  # rase la fonte et la couleur de fond pour crr 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 dini 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 dinis
  # plus haut.
  # Les types de widget sont list dans la hiarchie des classes, mais
  # peut probablement re list dans ce document pour que l'utilisateur
  # puisse s'y rer.

  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 re documentpour en tirer profit.
  widget "main window.*GtkButton*" style "main_button"






  1199..  rriittuurree ddee vvooss pprroopprreess wwiiddggeettss


  1199..11..  VVuuee dd''eennsseemmbbllee

  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 ovous aurez besoin de crr votre propre type de widget. Comme
  GTK utilise l'hitage de widget de fan intensive et qu'il y a dun
  widget ressemblant celui que vous voulez, il est souvent possible de
  crr un nouveau type de widget en seulement quelques lignes de code.
  Mais, avant de travailler sur un nouveau widget, il faut vifier
  d'abord que quelqu'un ne l'a pas drit. Ceci iter la duplication des
  efforts et maintient au minimum le nombre de widgets, ce qui permet de
  garder la cohence du code et de l'interface des diffentes
  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 bicier. Le
  meilleur endroit pour faire cela est, sans doute, la gtk-list.


  1199..22..  AAnnaattoommiiee dd''uunn wwiiddggeett

  Afin de crr un nouveau widget, il importe de comprendre comment
  fonctionnent les objets GTK. Cette section ne se veut re qu'un rapide
  survol. Consultez la documentation de rence pour plus de dails.


  Les widgets sont implant selon une mhode orient objet. Cependant, ils
  sont rits en C standard. Ceci amiore beaucoup la portabilitet la
  stabilit par contre cela signifie que celui qui rit des widgets doit
  faire attention certains dails d'implantation. Les informations
  communes toutes les instances d'une classe de widget (tous les widgets
  boutons, par exemple) sont stocks dans la _s_t_r_u_c_t_u_r_e _d_e _l_a _c_l_a_s_s_e.  Il
  n'y en a qu'une copie dans laquelle sont stocks les informations sur
  les signaux de la classe (fonctionnement identique aux fonctions
  virtuelles en C). Pour permettre l'hitage, le premier champ de la
  structure de classe doit re une copie de la structure de classe du pe.
  La dlaration de la structure de classe de _G_t_k_B_u_t_t_o_n ressemble ceci :



       struct _GtkButtonClass
       {
         GtkContainerClass parent_class;

         void (* pressed)  (GtkButton *button);
         void (* released) (GtkButton *button);
         void (* clicked)  (GtkButton *button);
         void (* enter)    (GtkButton *button);
         void (* leave)    (GtkButton *button);
       };





  Lorsqu'un bouton est traitcomme un container (par exemple, lorsqu'il
  change de taille), sa structure de classe peut re convertie en
  _G_t_k_C_o_n_t_a_i_n_e_r_C_l_a_s_s et les champs aduats utilis pour ger 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 diffentes pour chaque instance du widget. Nous l'appelerons
  _s_t_r_u_c_t_u_r_e _d_'_o_b_j_e_t. Pour la classe _B_u_t_t_o_n, elle ressemble ~:

       struct _GtkButton
       {
         GtkContainer container;

         GtkWidget *child;

         guint in_button : 1;
         guint button_down : 1;
       };





  Notez que, comme pour la structure de classe, le premier champ est la
  structure d'objet de la classe parente, cette structure peut donc re
  convertie dans la structure d'objet de la classe parente si besoin
  est.


  1199..33..  CCrrttiioonn dd''uunn wwiiddggeett ccoommppooss..XXSS 111144 1199..33.. CCrrttiioonn dd''uunn wwiiddggeett ccoomm--
  ppooss..XXEE

  1199..33..11..  IInnttrroodduuccttiioonn

  Un type de widget qui peut re intessant crr est un widget qui est
  simplement un agrat d'autres widgets GTK. Ce type de widget ne fait
  rien qui ne pourrait re fait sans crr de nouveaux widgets, mais offre
  une mhode pratique pour empaqueter les ents d'une interface
  utilisateur afin de la rtiliser facilement. Les widgets _F_i_l_e_S_e_l_e_c_t_i_o_n
  et _C_o_l_o_r_S_e_l_e_c_t_i_o_n de la distribution standard sont des exemples de ce
  type de widget.


  L'exemple de widget que nous allons crr dans cette section crra un
  widget _T_i_c_t_a_c_t_o_e, un tableau de 3x3 boutons commutateurs qui dlenche
  un signal lorsque tous les boutons d'une ligne, d'une colonne, ou
  d'une diagonale sont press.


  1199..33..22..  CChhooiixx dd''uunnee ccllaassssee ppaarreenntt

  La classe parent d'un widget composest, typiquement, la classe
  container contenant tous les ents du widget compos Par exemple, la
  classe parent du widget _F_i_l_e_S_e_l_e_c_t_i_o_n est la classe _D_i_a_l_o_g. Comme nos
  boutons seront mis sous la forme d'un tableau, il semble naturel
  d'utiliser la classe _G_t_k_T_a_b_l_e comme parent. Malheureusement, cela ne
  peut marcher. La crtion d'un widget est divis en deux fonctions --
  _W_I_D_G_E_T_N_A_M_E___n_e_w_(_) que l'utilisateur appelle, et _W_I_D_G_E_T_N_A_M_E___i_n_i_t_(_) qui
  rlise le travail d'initialisation du widget indendamment des paramre
  passla fonction _new(). Les widgets fils n'appellent que la fonction
  ___i_n_i_t de leur widget parent. Mais cette division du travail ne
  fonctionne pas bien avec les tableaux qui, lorsqu'ils sont crs, ont
  besoin de connare leue nombre de lignes et de colonnes. Sauf dupliquer
  la plupart des fonctionnalit de _g_t_k___t_a_b_l_e___n_e_w_(_) dans notre widget
  _T_i_c_t_a_c_t_o_e, nous ferions mieux d'iter de le diver de _G_t_k_T_a_b_l_e. Pour
  cette raison, nous la diverons plut de _G_t_k_V_B_o_x et nous placerons notre
  table dans la VBox.


  1199..33..33..  TThhee hheeaaddeerr ffiillee

  Chaque classe de widget posse un fichier en-te qui dlare les
  structures d'objet et de classe pour ce widget, en plus de fonctions
  publiques. Quelques caractistiques mitent d're indiqus. Afin d'iter
  des dinitions multiples, on enveloppe le fichier en-te 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 dlarons 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 vifient si un
  objet est un widget Tictactoe.


  Voici le fichier en-te 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__ */





  1199..33..44..  LLaa ffoonnccttiioonn  __ggeett__ttyyppee(())

  Continuons maintenant avec l'implantation de notre widget. La fonction
  centrale pour chaque widget est _W_I_D_G_E_T_N_A_M_E___g_e_t___t_y_p_e_(_). Cette fonction,
  lorsqu'elle est appel pour la premie fois, informe le GTK de la classe
  et rupe un ID permettant d'identifier celle-ci de fan unique. Lors des
  appels suivants, elle ne fait que retourner cet ID.













  guint
  tictactoe_get_type ()
  {
    static guint ttt_type = 0;

    if (!ttt_type)
      {
        GtkTypeInfo ttt_info =
        {
          "Tictactoe",
          sizeof (Tictactoe),
          sizeof (TictactoeClass),
          (GtkClassInitFunc) tictactoe_class_init,
          (GtkObjectInitFunc) tictactoe_init,
          (GtkArgFunc) NULL,
        };

        ttt_type = gtk_type_unique (gtk_vbox_get_type (), &ttt_info);
      }

    return ttt_type;
  }





  La structure _G_t_k_T_y_p_e_I_n_f_o est dinie de la fan 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-mes. Nous ignorerons
  le champ _a_r_g___f_u_n_c ici : il a un re important permettant aux options
  des widgets d're correctement initialiss partir des langages interpr,
  mais cette fonctionnalitest encore tr peu implant. Lorsque GTK dispose
  d'une copie correctement remplie de cette structure, il sait comment
  crr des objets d'un type particulier de widget.


  1199..33..55..  LLaa ffoonnccttiioonn __ccllaassss__iinniitt(())

  La fonction _W_I_D_G_E_T_N_A_M_E___c_l_a_s_s___i_n_i_t_(_) initialise les champs de la
  structure de classe du widget et configure tous les signaux de cette
  classe. Pour notre widget Tictactoe, cet appel est :








  enum {
    TICTACTOE_SIGNAL,
    LAST_SIGNAL
  };

  static gint tictactoe_signals[LAST_SIGNAL] = { 0 };

  static void
  tictactoe_class_init (TictactoeClass *class)
  {
    GtkObjectClass *object_class;

    object_class = (GtkObjectClass*) class;

    tictactoe_signals[TICTACTOE_SIGNAL] = gtk_signal_new ("tictactoe",
                                           GTK_RUN_FIRST,
                                           object_class->type,
                                           GTK_SIGNAL_OFFSET (TictactoeClass, tictactoe),
                                           gtk_signal_default_marshaller, GTK_ARG_NONE, 0);


    gtk_object_class_add_signals (object_class, tictactoe_signals, LAST_SIGNAL);

    class->tictactoe = NULL;
  }





  Notre widget n'a qu'un signal : "tictactoe", invoqulorsqu'une ligne,
  une colonne ou une diagonale est complement remplie. Tous les widgets
  compos n'ont pas besoin de signaux. Si vous lisez ceci pour la premie
  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,
                                                  ...);




  cr un nouveau signal. Les paramres sont :


  +o  _n_a_m_e : Le nom du signal signal.

  +o  _r_u_n___t_y_p_e : Indique si le gestionnaire par daut doit re lancavant ou
     apr le gestionnaire de l'utilisateur. Le plus souvent, ce sera
     GTK_RUN_FIRST, ou GTK_RUN_LAST, bien qu'il y ait d'autres
     possibilit.

  +o  _o_b_j_e_c_t___t_y_p_e : L'ID de l'objet auquel s'applique ce signal (il
     s'appliquera aussi au descendants de l'objet).


  +o  _f_u_n_c_t_i_o_n___o_f_f_s_e_t : L'offset d'un pointeur vers le gestionnaire par
     daut dans la structure de classe.

  +o  _m_a_r_s_h_a_l_l_e_r : Fonction utilis pour invoquer le gestionnaire de
     signal. Pour les gestionnaires de signaux n'ayant pas d'autres
     paramres que l'objet etteur et les donns utilisateur, on peut
     utiliser la fonction prinie _g_t_k___s_i_g_n_a_l___d_e_f_a_u_l_t___m_a_r_s_h_a_l_l_e_r_(_).

  +o  _r_e_t_u_r_n___v_a_l : Type de la valeur retourn.

  +o  _n_p_a_r_a_m_s : Nombre de paramres du gestionnaire de signal (autres que
     les deux par daut mentionn plus haut).

  +o  _._._. : Types des paramres.

  Lorsque l'on spifie les types, on utilise l'umation _G_t_k_A_r_g_T_y_p_e :



       typedef enum
       {
         GTK_ARG_INVALID,
         GTK_ARG_NONE,
         GTK_ARG_CHAR,
         GTK_ARG_SHORT,
         GTK_ARG_INT,
         GTK_ARG_LONG,
         GTK_ARG_POINTER,
         GTK_ARG_OBJECT,
         GTK_ARG_FUNCTION,
         GTK_ARG_SIGNAL
       } GtkArgType;





  _g_t_k___s_i_g_n_a_l___n_e_w_(_) retourne un identificateur entier pour le signal, que
  l'on stocke dans le tableau _t_i_c_t_a_c_t_o_e___s_i_g_n_a_l_s, indicpar une umation
  (conventionnellement, les ents de l'umation 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.

  Apr avoir cr nos signaux, nous devons demander GTK d'associer ceux-ci
  la classe Tictactoe. Ceci est fait en appelant
  _g_t_k___o_b_j_e_c_t___c_l_a_s_s___a_d_d___s_i_g_n_a_l_s_(_). Puis nous configurons le pointeur qui
  pointe sur le gestionnaire par daut du signal "tictactoe" NULL, pour
  indiquer qu'il n'y a pas d'action par daut.


  1199..33..66..  LLaa ffoonnccttiioonn __iinniitt(())


  Chaque classe de widget a aussi besoin d'une fonction pour initialiser
  la structure d'objet. Habituellement, cette fonction a le re, plut
  limit d'initialiser les champs de la structure avec des valeurs par
  daut. Cependant, pour les widgets compos, cette fonction cr 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]);
        }
  }





  1199..33..77..  EEtt llee rreessttee......


  Il reste une fonction que chaque widget (sauf pour les types widget de
  base, comme _G_t_k_B_i_n, qui ne peuvent re instanci) besoin d'avoir --
  celle que l'utilisateur appelle pour crr un objet de ce type. Elle est
  conventionnellement appel _W_I_D_G_E_T_N_A_M_E___n_e_w_(_). Pour certains widgets, par
  pour ceux de Tictactoe, cette fonction prend des paramres et rlise
  certaines initialisations dendantes des paramres. Les deux autres
  fonctions sont spifiques au widget Tictactoe.


  _t_i_c_t_a_c_t_o_e___c_l_e_a_r_(_) est une fonction publique qui remet tous les boutons
  du widget en position relh. Notez l'utilisation de
  _g_t_k___s_i_g_n_a_l___h_a_n_d_l_e_r___b_l_o_c_k___b_y___d_a_t_a_(_) pour empher notre gestionnaire de
  signaux des boutons commutateurs d're dlenchsans besoin.


  _t_i_c_t_a_c_t_o_e___t_o_g_g_l_e_(_) est le gestionnaire de signal invoqulorsqu'on
  clique sur un bouton. Il vifie s'il y a des combinaisons gagnantes
  concernant le bouton qui vient d're commutet, si c'est le cas, et 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"

  /* Invoqulorsqu'une ligne, une colonne ou une diagonale est comple */

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

    /* Crtion 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;
  }





  1199..44..  CCrrttiioonn dd''uunn wwiiddggeett ppaarrttiirr ddee zzoo

  1199..44..11..  IInnttrroodduuccttiioonn


  Dans cette section, nous en apprendrons plus sur la fan dont les
  widgets s'affichent eux-mes l'ran et comment ils interagissent avec
  les ements. Comme exemple, nous crrons un widget d'appel tephonique
  interactif avec un pointeur que l'utilisateur pourra dlacer pour
  initialiser la valeur.


  1199..44..22..  AAffffiicchheerr uunn wwiiddggeett ll''rraann

  Il y a plusieurs apes mises en jeu lors de l'affichage. Lorsque le
  widget est cr par l'appel _W_I_D_G_E_T_N_A_M_E___n_e_w_(_), plusieurs autres fonctions
  supplentaires sont requises.


  +o  _W_I_D_G_E_T_N_A_M_E___r_e_a_l_i_z_e_(_) s'occupe de crr une fenre X pour le widget,
     s'il en a une.

  +o  _W_I_D_G_E_T_N_A_M_E___m_a_p_(_) est invoqu apr l'appel de _g_t_k___w_i_d_g_e_t___s_h_o_w_(_). Elle
     s'assure que le widget est bien tracl'ran (_m_a_p_p_f_P_)_. _D_a_n_s _l_e _c_a_s
     _d_'_u_n_e _c_l_a_s_s_e _c_o_n_t_a_i_n_e_r_, _e_l_l_e _d_o_i_t _a_u_s_s_i _a_p_p_e_l_e_r _d_e_s _f_o_n_c_t_i_o_n_s
     _m_a_p_(_)_> _p_o_u_r _c_h_a_q_u_e _w_i_d_g_e_t _f_i_l_s_.

  +o  _W_I_D_G_E_T_N_A_M_E___d_r_a_w_(_) est invoqu lorsque _g_t_k___w_i_d_g_e_t___d_r_a_w_(_) est
     appelpour le widget ou l'un de ces ancres. Elle rlise les vitables
     appels aux fonctions de dessin pour tracer le widget l'ran. Pour
     les widgets containers, cette fonction doit appeler
     _g_t_k___w_i_d_g_e_t___d_r_a_w_(_) pour ses widgets fils.

  +o  _W_I_D_G_E_T_N_A_M_E___e_x_p_o_s_e_(_) est un gestionnaire pour les ements
     d'exposition du widget. Il rlise les appels nessaires aux fonctions
     de dessin pour tracer la partie expos l'ran. Pour les widgets
     containers, cette fonction doit ger les ements d'exposition pour
     ses widgets enfants n'ayant pas leurs propres fenres (s'ils ont
     leurs propres fenres, X gera les ements d'exposition nessaires).


  Vous avez pu noter que les deux dernies fonctions sont assez
  similaires -- chacune se charge de tracer le widget l'ran. En fait, de
  nombreux types de widgets ne se prccupent pas vraiment de la diffence
  entre les deux. La fonction _d_r_a_w_(_) par daut de la classe widget ge
  simplement unement d'exposition synthique pour la zone redessiner.
  Cependant, certains types de widgets peuvent onomiser du travail en
  faisant la diffence entre les deux fonctions. Par exemple, si un
  widget a plusieurs fenres X et puisque les ements d'exposition
  identifient la fenre expos, il peut redessiner seulement la fenre
  concern, ce qui n'est pas possible avec des appels _d_r_a_w_(_).


  Les widgets container, me s'ils ne se soucient pas eux-mes de la
  diffence, ne peuvent pas utiliser simplement la fonction _d_r_a_w_(_) car
  leurs widgets enfants tiennent compte de cette diffence. Cependant, ce
  serait du gaspillage de dupliquer le code de tracpour les deux
  fonctions. Conventionnellement, de tels widgets possent une fonction
  nomm _W_I_D_G_E_T_N_A_M_E___p_a_i_n_t_(_) qui rlise le vitable travail de tracdu widget
  et qui est appel 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 fenre, nous pouvons utiliser l'approche la
  plus simple : utiliser la fonction _d_r_a_w_(_) par daut et n'implanter que
  la fonction _e_x_p_o_s_e_(_).


  1199..44..33..  OOrriiggiinneess dduu wwiiddggeett DDiiaall

  Exactement comme les animaux terrestres ne sont que des variantes des
  premiers amphibiens qui rampent hors de la boue, les widgets GTK sont
  des variantes d'autres widgets, drits. Ainsi, bien que cette section
  s'appelle crr un widget partir de zo  le widget Dial commence rllement
  avec le code source du widget Range. Celui-ci a pris comme point de
  dart car ce serait bien que notre Dial ait la me interface que les
  widgets Scale qui ne sont que des descendants spialis du widget Range.
  Par consuent, bien que le code source soit prentci-dessous sous une
  forme achev, cela n'implique pas qu'il a rit _d_e_u_s _e_x _m_a_c_h_i_n_a. De plus,
  si vous ne savez pas comment fonctionnent les widgets Scale du point
  de vue du programmeur de l'application, il est prable de les udier
  avant de continuer.



  1199..44..44..  LLeess bbaasseess

  Un petite partie de notre widget devrait ressembler au widget
  Tictactoe. Nous avons d'abord le fichier en-te :






























































  /* 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 stocks. On sait donc quand quelque
       chose change */
    gfloat old_value;
    gfloat old_lower;
    gfloat old_upper;
    /* L'objet ajustment qui stocke les donns 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 donns, mais part , les
  choses sont plut similaires.



  Puis, apr avoir inclus les fichiers en-te et dlarquelques 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

  /* Dlararations des prototypes */

  [ omis pour gagner de la place ]

  /* Donns locales */

  static GtkWidgetClass *parent_class = NULL;

  guint
  gtk_dial_get_type ()
  {
    static guint dial_type = 0;

    if (!dial_type)
      {
        GtkTypeInfo dial_info =
        {
          "GtkDial",
          sizeof (GtkDial),
          sizeof (GtkDialClass),
          (GtkClassInitFunc) gtk_dial_class_init,
          (GtkObjectInitFunc) gtk_dial_init,
          (GtkArgFunc) NULL,
        };

        dial_type = gtk_type_unique (gtk_widget_get_type (), &dial_info);
      }

    return dial_type;
  }

  static void
  gtk_dial_class_init (GtkDialClass *class)
  {
    GtkObjectClass *object_class;
    GtkWidgetClass *widget_class;

    object_class = (GtkObjectClass*) class;
    widget_class = (GtkWidgetClass*) class;

    parent_class = gtk_type_class (gtk_widget_get_type ());

    object_class->destroy = gtk_dial_destroy;

    widget_class->realize = gtk_dial_realize;
    widget_class->expose_event = gtk_dial_expose;
    widget_class->size_request = gtk_dial_size_request;
    widget_class->size_allocate = gtk_dial_size_allocate;
    widget_class->button_press_event = gtk_dial_button_press;
    widget_class->button_release_event = gtk_dial_button_release;
    widget_class->motion_notify_event = gtk_dial_motion_notify;
  }

  static void
  gtk_dial_init (GtkDial *dial)
  {
    dial->button = 0;
    dial->policy = GTK_UPDATE_CONTINUOUS;
    dial->timer = 0;
    dial->radius = 0;
    dial->pointer_width = 0;
    dial->angle = 0.0;
    dial->old_value = 0.0;
    dial->old_lower = 0.0;
    dial->old_upper = 0.0;
    dial->adjustment = NULL;
  }

  GtkWidget*
  gtk_dial_new (GtkAdjustment *adjustment)
  {
    GtkDial *dial;

    dial = gtk_type_new (gtk_dial_get_type ());

    if (!adjustment)
      adjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);

    gtk_dial_set_adjustment (dial, adjustment);

    return GTK_WIDGET (dial);
  }

  static void
  gtk_dial_destroy (GtkObject *object)
  {
    GtkDial *dial;

    g_return_if_fail (object != NULL);
    g_return_if_fail (GTK_IS_DIAL (object));

    dial = GTK_DIAL (object);

    if (dial->adjustment)
      gtk_object_unref (GTK_OBJECT (dial->adjustment));

    if (GTK_OBJECT_CLASS (parent_class)->destroy)
      (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
  }




  Notez que cette fonction _i_n_i_t_(_) fait moins de choses que pour le
  widget Tictactoe car ce n'est pas un widget composet que la fonction
  _n_e_w_(_) en fait plus car elle a maintenant un paramre. Notez aussi que
  lorsque nous stockons un pointeur vers l'objet Adjustement, nous
  increntons son nombre de rences (et nous le drentons lorsque nous ne
  l'utilisons plus) afin que GTK puisse savoir quand il pourra re druit
  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);
  }





  1199..44..55..  ggttkk__ddiiaall__rreeaalliizzee(())

  Nous arrivons maintenant quelques nouveaux types de fonctions.
  D'abord, nous avons une fonction qui rlise la crtion de la fenre X.
  Notez que l'on passe un masque la fonction _g_d_k___w_i_n_d_o_w___n_e_w_(_) pour
  spifier quels sont les champs de la structure GdkWindowAttr qui
  contiennent des donns (les autres recevront des valeurs par daut).
  Notez aussi la fan dont est cr le masque d'ement du widget. On appelle
  _g_t_k___w_i_d_g_e_t___g_e_t___e_v_e_n_t_s_(_) pour ruper le masque d'ement que l'utilisateur
  a spifipour ce widget (avec _g_t_k___w_i_d_g_e_t___s_e_t___e_v_e_n_t_s_(_)) et ajouter les
  ements qui nous intessent.


  Apr avoir cr la fenre, nous configurons son style et son fond et
  mettons un pointeur vers le widget dans le champ user de la GdkWindow.
  Cette dernie ape permet GTK de distribuer les ements pour cette fenre
  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);
       }





  1199..44..66..  NNoottiiaattiioonn ddee llaa ttaaiillllee

  Avant le premier affichage de la fenre contenant un widget et chaque
  fois que la forme de la fenre change, GTK demande chaque widget fils
  la taille qu'il dire avoir. Cette reque est g par la fonction
  _g_t_k___d_i_a_l___s_i_z_e___r_e_q_u_e_s_t_(_). Comme notre widget n'est pas un widget
  container, et n'a pas de contraintes rlles sur sa taille, nous ne
  faisons que retourner une valeur raisonnable par daut.



       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 demandune taille idle, le forme de la
  fenre est calcul et chaque widget fils est averti de sa taille.
  Habituellement, ce sera autant que la taille requise, mais si, par
  exemple, l'utilisateur a redimensionnla fenre, cette taille peut
  occasionnellement re plus petite que la taille requise. La
  notification de la taille est g par la fonction
  _g_t_k___d_i_a_l___s_i_z_e___a_l_l_o_c_a_t_e_(_). Notez qu'en me temps qu'elle calcule les
  tailles de certains composants pour une utilisation future, cette
  routine fait aussi le travail de base consistant dlacer 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;
           }
       }



  .


  1199..44..77..  ggttkk__ddiiaall__eexxppoossee(())

  Comme cela est mentionnplus haut, tout le dessin de ce widget est
  rlisdans le gestionnaire pour les ements d'exposition. Il n'y a pas
  grand chose de plus dire ldessus, sauf constater l'utilisation de la
  fonction _g_t_k___d_r_a_w___p_o_l_y_g_o_n pour dessiner le pointeur avec une forme en
  trois dimensions selon les couleurs stocks 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;
  }





  1199..44..88..  GGeessttiioonn ddeess eemmeennttss

  Le reste du code du widget ge diffents types d'ements et n'est pas
  trop diffent de ce que l'on trouve dans la plupart des applications
  GTK. Deux types d'ements peuvent survenir -- l'utilisateur peut
  cliquer sur le widget avec la souris et faire glisser pour dlacer le
  pointeur, ou bien la valeur de l'objet Adjustment peut changer cause
  d'une circonstance extieure.


  Lorsque l'utilisateur clique sur le widget, on vifie si le clic s'est
  bien passpr du pointeur et si c'est le cas, on stocke alors le bouton
  avec lequel l'utilisateur a cliqudans le champ _b_u_t_t_o_n de la structure
  du widget et on rupe tous les ements souris avec un appel
  _g_t_k___g_r_a_b___a_d_d_(_). Un dlacement ultieur de la souris provoque le recalcul
  de la valeur de contre (par la fonction _g_t_k___d_i_a_l___u_p_d_a_t_e___m_o_u_s_e). Selon
  la politique qui a choisie, les ements "value_changed" sont, soit g
  instantanent (_G_T_K___U_P_D_A_T_E___C_O_N_T_I_N_U_O_U_S), apr un dai ajoutau timer avec
  _g_t_k___t_i_m_e_o_u_t___a_d_d_(_) (_G_T_K___U_P_D_A_T_E___D_E_L_A_Y_E_D), ou seulement lorsque le bouton
  est relh(_G_T_K___U_P_D_A_T_E___D_I_S_C_O_N_T_I_N_U_O_U_S).







































  static gint
  gtk_dial_button_press (GtkWidget      *widget,
                         GdkEventButton *event)
  {
    GtkDial *dial;
    gint dx, dy;
    double s, c;
    double d_parallel;
    double d_perpendicular;

    g_return_val_if_fail (widget != NULL, FALSE);
    g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
    g_return_val_if_fail (event != NULL, FALSE);

    dial = GTK_DIAL (widget);

    /* Dermine si le bouton pressest dans la rion du pointeur.
       On fait cela en calculant les distances paralle et perpendiculaire
       du point ola souris a press 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 extieurs sont
  communiqu notre widget par les signaux "changed" et "value_changed".
  Les gestionnaires pour ces fonctions appellent _g_t_k___d_i_a_l___u_p_d_a_t_e_(_) pour
  valider les paramres, calculer le nouvel angle du pointeur et
  redessiner le widget (en appelant _g_t_k___w_i_d_g_e_t___d_r_a_w_(_)).









  static void
  gtk_dial_update (GtkDial *dial)
  {
    gfloat new_value;

    g_return_if_fail (dial != NULL);
    g_return_if_fail (GTK_IS_DIAL (dial));

    new_value = dial->adjustment->value;

    if (new_value < dial->adjustment->lower)
      new_value = dial->adjustment->lower;

    if (new_value > dial->adjustment->upper)
      new_value = dial->adjustment->upper;

    if (new_value != dial->adjustment->value)
      {
        dial->adjustment->value = new_value;
        gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
      }

    dial->angle = 7.*M_PI/6. - (new_value - dial->adjustment->lower) * 4.*M_PI/3. /
      (dial->adjustment->upper - dial->adjustment->lower);

    gtk_widget_draw (GTK_WIDGET(dial), NULL);
  }

  static void
  gtk_dial_adjustment_changed (GtkAdjustment *adjustment,
                                gpointer       data)
  {
    GtkDial *dial;

    g_return_if_fail (adjustment != NULL);
    g_return_if_fail (data != NULL);

    dial = GTK_DIAL (data);

    if ((dial->old_value != adjustment->value) ||
        (dial->old_lower != adjustment->lower) ||
        (dial->old_upper != adjustment->upper))
      {
        gtk_dial_update (dial);

        dial->old_value = adjustment->value;
        dial->old_lower = adjustment->lower;
        dial->old_upper = adjustment->upper;
      }
  }

  static void
  gtk_dial_adjustment_value_changed (GtkAdjustment *adjustment,
                                      gpointer       data)
  {
    GtkDial *dial;

    g_return_if_fail (adjustment != NULL);
    g_return_if_fail (data != NULL);

    dial = GTK_DIAL (data);

    if (dial->old_value != adjustment->value)
      {
        gtk_dial_update (dial);

        dial->old_value = adjustment->value;
      }
  }





  1199..44..99..  AAmmiioorraattiioonnss ppoossssiibblleess


  Le widget Dial drit jusqu'maintenant exute peu pr 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-tes et la praration. Cependant, certaines
  amiorations peuvent re apports ce widget :


  +o  Si vous testez ce widget, vous vous apercevrez qu'il y a un peu de
     scintillement lorsque le pointeur est dlac Ceci est dau fait que le
     widget entier est effac puis redessin chaque mouvement du pointeur.
     Souvent, la meilleure fan de ger ce proble est de dessiner sur un
     pixmap non affich puis de copier le rultat final sur l'ran en une
     seule ape (le widget _P_r_o_g_r_e_s_s_B_a_r se dessine de cette fan).

  +o  L'utilisateur devrait pouvoir utiliser les flhes du curseur vers le
     haut et vers le bas pour increnter et drenter la valeur.

  +o  Ce serait bien si le widget avait des boutons pour augmenter et
     diminuer la valeur dans de petites ou de grosses proportions. Bien
     qu'il serait possible d'utiliser les widgets _B_u_t_t_o_n pour cela, nous
     voudrions aussi que les boutons s'auto-rent lorsqu'ils sont
     maintenus appuy, comme font les flhes d'une barre de dilement. La
     majeure partie du code pour implanter ce type de comportement peut
     se trouver dans le widget _G_t_k_R_a_n_g_e.

  +o  Le widget Dial pourrait re fait dans un widget container avec un
     seul widget fils positionn en bas, entre les boutons mentionn ci-
     dessus. L'utilisateur pourrait alors ajouter au choix, un widget
     label ou entr pour afficher la valeur courante de l'appel.


  1199..55..  EEnn ssaavvooiirr pplluuss

  Seule une petite partie des nombreux dails de la crtion des widgets a
  pu re drite. Si vous direz rire vos propres widgets, la meilleure
  source d'exemples est le source de GTK lui-me. Posez-vous quelques
  questions sur les widgets que vous voulez rire : est-ce un widget
  container ? posse-t-il sa propre fenre ? est-ce une modification d'un
  widget existant ? Puis, trouvez un widget identique et commencez faire
  les modifications. Bonne chance !


  2200..  SSccrriibbbbllee,, uunn pprrooggrraammmmee ssiimmppllee ddee ddeessssiinn

  2200..11..  PPrreennttaattiioonn

  Dans cette section, nous construirons un programme simple de dessin.
  Ce faisant, nous examinerons comment ger les ements souris, comment
  dessiner dans une fenre, et comment mieux dessiner en utilisant un
  pixmap en arrie plan. Apr avoir cr ce programme, nous l'endrons en lui
  ajoutant le support des piphiques _X_i_n_p_u_t, comme les tables de trac GTK
  dispose de routines de support qui facilitent beaucoup l'obtention des
  informations endues (comme la pression et l'inclinaison du stylet)
  partir de tels piphiques.

  2200..22..  GGeessttiioonn dd''eemmeenntt

  Les signaux GTK que nous avons dvus concernent les actions de haut
  niveau, comme la section d'un choix d'un menu. Cependant, il est
  quelques fois utile de connare les cas de bas niveau, comme le
  dlacement de la souris, ou la pression d'une touche. Il existe aussi
  des signaux GTK correspondant ces_e_m_e_n_t_s bas niveau. Les gestionnaires
  de ces signaux ont un paramre supplentaire qui est un pointeur vers
  une structure contenant des informations sur l'ement. Par exemple, les
  gestionnaires des ements de dlacement recoivent un paramre vers une
  structure _G_d_k_E_v_e_n_t_M_o_t_i_o_n qui ressemble (en partie) ceci :



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




  _t_y_p_e sera initialisavec le type de l'ement, ici _G_D_K___M_O_T_I_O_N___N_O_T_I_F_Y,
  _w_i_n_d_o_w est la fenre dans laquelle l'ement est survenu. _x et _y donnent
  les coordonns de l'ement et _s_t_a_t_e spifie l'at du modificateur lorsque
  l'ement s'est produit (c'est-dire quelles sont les touches de
  modification et les boutons souris qui ont press). Il s'agit d'un OU
  bit bit de l'une des valeurs suivantes :



       GDK_SHIFT_MASK
       GDK_LOCK_MASK
       GDK_CONTROL_MASK
       GDK_MOD1_MASK
       GDK_MOD2_MASK
       GDK_MOD3_MASK
       GDK_MOD4_MASK
       GDK_MOD5_MASK
       GDK_BUTTON1_MASK
       GDK_BUTTON2_MASK
       GDK_BUTTON3_MASK
       GDK_BUTTON4_MASK
       GDK_BUTTON5_MASK





  Comme pour les autres signaux, on appelle _g_t_k___s_i_g_n_a_l___c_o_n_n_e_c_t_(_) pour
  derminer ce qui se passe lorsqu'un ement survient. Mais nous devons
  aussi faire en sorte que GTK sache de quels ements nous voulons re
  avertis. Pour ce faire, on appelle la fonction :



       void       gtk_widget_set_events          (GtkWidget           *widget,
                                                  gint                 events);

  Le deuxie champ spifie les ements qui nous intessent. Il s'agit d'un
  OU bit bit de constantes qui indiquent diffent types d'ements. Pour r
  ence ultieure, les types d'ements 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 re observ lorsqu'on appelle
  _g_t_k___w_i_d_g_e_t___s_e_t___e_v_e_n_t_s_(_). D'abord, elle doit re appel avant que la
  fenre X d'un widget GTK soit cre. En pratique, cela signifie que l'on
  doit l'appeler immiatement apr avoir cr le widget. Ensuite, le widget
  doit avoir une fenre X associ. Pour des raisons d'efficacit de
  nombreux types de widgets n'ont pas de fenre propre, mais se dessinent
  dans la fenre 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 ements pour ces widgets, on doit utiliser un widget
  _E_v_e_n_t_B_o_x. Voir la section sur ``Le widget EventBox'' pour plus de
  dails.
  Pour notre programme de dessin, on veut savoir quand le bouton de la
  souris est presset quand la souris est dlac, nous indiquons donc
  _G_D_K___P_O_I_N_T_E_R___M_O_T_I_O_N___M_A_S_K et _G_D_K___B_U_T_T_O_N___P_R_E_S_S___M_A_S_K. On veut aussi savoir
  quand il est nessaire de redessiner notre fenre, on indique donc
  _G_D_K___E_X_P_O_S_U_R_E___M_A_S_K. Bien que nous voulions re avertis via un ement
  _C_o_n_f_i_g_u_r_e lorsque la taille de notre fenre change, on n'a pas besoin
  de priser le flag _G_D_K___S_T_R_U_C_T_U_R_E___M_A_S_K correspondant car il est
  automatiquement spifipour chaque fenre.


  Il arrive cependant qu'il puisse y avoir un proble en indiquant
  seulement _G_D_K___P_O_I_N_T_E_R___M_O_T_I_O_N___M_A_S_K. Cela fera que le serveur ajoutera
  un nouvelement de dlacement la file des ements chaque fois que
  l'utilisateur dlace la souris. Si cela nous prend 0,1 seconde pour ger
  unement de dlacement, si le serveur X n'ajoute un nouvel ement de
  dlacement dans la queue que toutes les 0,05 secondes, nous serons vite
  la trae de l'utilisateur. Si l'utilisateur dessine pendant 5 secondes,
  cela nous prendra 5 secondes de plus pour le traiter apr qu'il ait
  relhle bouton de la souris ! Ce que l'on voudrait, c'est ne ruper
  qu'un ement de dlacement pour chaque ement que l'on traite. Pour cela,
  il faut priser _G_D_K___P_O_I_N_T_E_R___M_O_T_I_O_N___H_I_N_T___M_A_S_K.


  Avec _G_D_K___P_O_I_N_T_E_R___M_O_T_I_O_N___H_I_N_T___M_A_S_K, le serveur nous envoit un ement de
  dlacement la premie fois que la pointeur se dlace apr re entrdans la
  fenre, ou apr un ement d'appui ou de relhement d'un bouton. Lesements
  de dlacement suivants seront supprim jusqu'ce que l'on demande
  explicitement la position du pointeur en utilisant la fonction :



       GdkWindow*    gdk_window_get_pointer     (GdkWindow       *window,
                                                 gint            *x,
                                                 gint            *y,
                                                 GdkModifierType *mask);




  (Il existe une autre fonction, _g_t_k___w_i_d_g_e_t___g_e_t___p_o_i_n_t_e_r_(_) qui posse une
  interface simple, mais n'est pas tr utile car elle ne fait que ruper
  la position de la souris et ne se prccupe pas de savoir si les boutons
  sont press).


  Le code pour configurer les ements pour notre fenre 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 tr 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;
       }






  2200..33..  LLee wwiiddggeett DDrraawwiinnggAArreeaa eett llee ddeessssiinn

  Revenons au processus de dessin sur l'ran. Le widget que l'on utilise
  pour ceci est le widget _D_r_a_w_i_n_g_A_r_e_a. Un tel widget est essentiellement
  une fenre 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 daut peut re donn au widget par l'appel :



       void       gtk_drawing_area_size       (GtkDrawingArea      *darea,
                                               gint                 width,
                                               gint                 height);


  Cette taille par daut peu re surcharg en appelant
  _g_t_k___w_i_d_g_e_t___s_e_t___u_s_i_z_e_(_) et celle-ci, son tour, peut re surcharg si
  l'utilisateur modifie manuellement la taille de la fenre contenant la
  zone de dessin.


  Il faut noter que lorsque l'on cr un widget _D_r_a_w_i_n_g_A_r_e_a, nous sommes
  _c_o_m_p_l_e_m_e_n_t responsable du dessin du contenu. Si notre fenre est cach
  puis redouverte, nous recevrons un ement d'exposition et devrons
  redessiner ce qui a cachauparavant.


  Devoir se rappeler tout ce qui a dessinl'ran pour pouvoir correctement
  le redessiner peut s'aver, c'est le moins que l'on puisse dire, pible.
  De plus, cela peut re visible si des portions de la fenre sont effacs
  puis redessins ape par ape. La solution ce proble est d'utiliser un
  _p_i_x_m_a_p _d_'_a_r_r_i_e_-_p_l_a_n qui n'est pas sur l'ran. Au lieu de dessiner
  directement l'ran, on dessine sur une image stock dans la moire du
  serveur et qui n'est pas affich, puis, lorsque l'image change ou
  lorsque de nouvelles portions de l'image sont affichs, on copie les
  parties aduates sur l'ran.


  Pour crr un pixmap moire, on appelle la fonction :



       GdkPixmap* gdk_pixmap_new               (GdkWindow  *window,
                                                gint        width,
                                                gint        height,
                                                gint        depth);




  Le paramre _w_i_n_d_o_w_s indique une fenre GTK de laquelle ce pixmap tire
  certaines de ses propri. _w_i_d_t_h et _h_e_i_g_h_t prisent la taille du pixmap.
  _d_e_p_t_h prise la  _p_r_o_f_o_n_d_e_u_r _d_e _c_o_u_l_e_u_r, c'est-dire le nombre de bits
  par pixel, de la nouvelle fenre. Si cette profondeur vaut _-_1, elle
  correspondra celle de _w_i_n_d_o_w.


  Nous crns le pixmap dans notre gestionnaire "configure_event". Cet
  ement est gchaque fois que la fenre change de taille, y compris
  lorsqu'elle initialement cre.





















  /* Pixmap d'arrie-plan pour la zone de dessin */
  static GdkPixmap *pixmap = NULL;

  /* Crtion d'un nouveau pixmap d'arrie-plan de la taille voulue */
  static gint
  configure_event (GtkWidget *widget, GdkEventConfigure *event)
  {
    if (pixmap)
      {
        gdk_pixmap_destroy(pixmap);
      }
    pixmap = gdk_pixmap_new(widget->window,
                            widget->allocation.width,
                            widget->allocation.height,
                            -1);
    gdk_draw_rectangle (pixmap,
                        widget->style->white_gc,
                        TRUE,
                        0, 0,
                        widget->allocation.width,
                        widget->allocation.height);

    return TRUE;
  }




  L'appel _g_d_k___d_r_a_w___r_e_c_t_a_n_g_l_e_(_) remet le pixmap blanc. Nous en dirons un
  peu plus dans un moment.


  Notre gestionnaire d'ement d'exposition copie alors simplement la
  partie concerns du pixmap sur l'ran (on dermine la zone qu'il faut
  redessiner en utilisant le champ _e_v_e_n_t_-_>_a_r_e_a de l'ement
  d'exposition) :



       /* Remplit l'ran partir du pixmap d'arrie-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'ran jour avec notre pixmap, mais
  comment dessine-t-on rllement ce que l'on veut dans le pixmap ? Il
  existe un grand nombre d'appels dans la bibliothue GDK de GTK pour
  dessiner sur des _d_e_s_s_i_n_a_b_l_e_s. Un dessinable est simplement quelque
  chose sur lequel on peut dessiner. Cela peut re une fenre, un pixmap,
  ou un bitmap (une image en noir et blanc). Nous avons dvu plus haut
  deux de ces appels, _g_d_k___d_r_a_w___r_e_c_t_a_n_g_l_e_(_) et _g_d_k___d_r_a_w___p_i_x_m_a_p_(_). La
  liste comple 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 rence ou le fichier en-te _<_g_d_kgdk.h>/
  pour plus de dails sur ces fonctions.  Celles-ci partagent toutes les
  mes deux paramres. Le premier est le dessinable sur lequel dessiner,
  le second est un _c_o_n_t_e_x_t_e _g_r_a_p_h_i_q_u_e (GC).


  Un contexte graphique encapsule l'information sur des choses comme les
  couleurs de premier et d'arrie plan et la largeur de ligne. GDK posse
  un ensemble complet de fonctions pour crr et manipuler les contextes
  graphiques, mais, pour simplifier, nous n'utiliserons que les
  contextes graphiques prinis. Chaque widget a un style associ(qui peut
  re modifidans un fichier gtkrc, voir la section sur les fichiers rc de
  GTK). Celui-ci, entre autres choses, stocke plusieurs contextes
  graphiques. Quelques exemples d'accces contextes sont :



       widget->style->white_gc
       widget->style->black_gc
       widget->style->fg_gc[GTK_STATE_NORMAL]
       widget->style->bg_gc[GTK_WIDGET_STATE(widget)]




  Les champs _f_g___g_c_, _b_g___g_c_, _d_a_r_k___g_c et _l_i_g_h_t___g_c sont index par un paramre
  de type _G_t_k_S_t_a_t_e_T_y_p_e qui peut prendre les valeurs :



       GTK_STATE_NORMAL,
       GTK_STATE_ACTIVE,
       GTK_STATE_PRELIGHT,
       GTK_STATE_SELECTED,
       GTK_STATE_INSENSITIVE




  Par exemple, pour _G_T_K___S_T_A_T_E___S_E_L_E_C_T_E_D, la couleur de premier plan par
  daut est blanc et la couleur d'arrie plan par daut est bleu fonc


  Notre fonction _d_r_a_w___b_r_u_s_h_(_), qui rlise le dessin l'ran est alors :







  /* Dessine un rectangle l'ran */
  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);
  }




  Apr avoir dessinle rectangle reprentant la brosse sur le pixmap, nous
  appelons la fonction :



       void       gtk_widget_draw                (GtkWidget           *widget,
                                                  GdkRectangle        *area);




  qui indique X que la zone donn par le paramre _a_r_e_a a besoin d're mise
  jour. X gera entuellement un ement d'exposition (en combinant peut-re
  les zones pass dans plusieurs appels _g_t_k___w_i_d_g_e_t___d_r_a_w_(_)) ce qui forcera
  notre gestionnaire d'ement d'exposition copier les parties aduates
  l'ran.


  Nous avons maintenant couvert entiement le programme de dessin, sauf
  quelques dails banals comme la crtion de la fenre principale. Le code
  source complet est disponible l'endroit ovous avez obtenu ce
  didacticiel.


  2200..44..  AAjjoouutteerr llee ssuuppppoorrtt XXIInnppuutt

  Il est maintenant possible d'acheter des piphiques bon march comme les
  tablettes graphiques qui permettent d'exprimer beaucoup plus
  facilement son talent qu'avec une souris. La fan la plus simple pour
  utiliser de tels piphiques est simplement de le faire comme un
  remplacement de la souris, mais cela ne tire pas partie des nombreux
  avantages de ces piphiques, comme :


  +o  Sensibilitla pression ;

  +o  rapport d'inclinaison ;

  +o  positionnement au dessous du pixel ;

  +o  entrs 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 dinition comple de, par exemple, la structure
  _G_d_k_E_v_e_n_t_M_o_t_i_o_n, on voit qu'elle posse des champs pour supporter des
  informations endues sur les piphiques.



       struct _GdkEventMotion
       {
         GdkEventType type;
         GdkWindow *window;
         guint32 time;
         gdouble x;
         gdouble y;
         gdouble pressure;
         gdouble xtilt;
         gdouble ytilt;
         guint state;
         gint16 is_hint;
         GdkInputSource source;
         guint32 deviceid;
       };




  _p_r_e_s_s_u_r_e indique la pression comme un nombre rl compris entre 0 et 1.
  _x_t_i_l_t et _y_t_i_l_t peuvent prendre des valeurs entre -1 et 1,
  correspondant au degrd'inclinaison dans chaque direction, _s_o_u_r_c_e et
  _d_e_v_i_c_e_i_d prisent de deux fans diffentes le piphique pour lequel l'
  ement est survenus. _s_o_u_r_c_e donne une simple information sur le type du
  piphique. Il peut prendre l'une des valeurs suivantes :



       GDK_SOURCE_MOUSE
       GDK_SOURCE_PEN
       GDK_SOURCE_ERASER
       GDK_SOURCE_CURSOR




  deviceid prise un ID numique unique pour le piphique. Il peut re
  utilispour trouver des informations supplentaires sur le piphique en
  utilisant l'appel _g_d_k___i_n_p_u_t___l_i_s_t___d_e_v_i_c_e_s_(_) (voir ci-dessous). La
  valeur spiale _G_D_K___C_O_R_E___P_O_I_N_T_E_R sert digner le piphique de pointage
  principal (habituellement la souris).


  2200..44..11..  VVaalliiddeerr ll''iinnffoorrmmaattiioonn ssuupppplleennttaaiirree ssuurr uunn ppiipphhiiqquuee

  Pour indiquer GTK que l'on dire obtenir des informations supplentaires
  sur le piphique, on a simplement besoin d'ajouter une ligne nos
  programmes.



       gtk_widget_set_extension_events (drawing_area, GDK_EXTENSION_EVENTS_CURSOR);




  En donnant la valeur _G_D_K___E_X_T_E_N_S_I_O_N___E_V_E_N_T_S___C_U_R_S_O_R, on indique que nous
  dirons les ements d'extension, mais seulement si l'on ne doit pas
  dessiner notre propre curseur. Voir la section ``Sophistications
  supplentaires'' ci-dessous pour des plus de dails sur le dessin du
  curseur. Nous pourrions aussi donner les valeurs
  _G_D_K___E_X_T_E_N_S_I_O_N___E_V_E_N_T_S___A_L_L si nous voulons dessiner notre propre
  curseur, ou GDK_EXTENSION_EVENTS_NONE pour revenir la situation par
  daut.


  Toutefois, nous ne sommes pas complement la fin de l'histoire. Par
  daut, aucun piphique d'extension n'est autoris Nous avons besoin d'un
  manisme pour que les utilisateurs puissent autoriser et configurer
  leur extensions. GTK dispose du widget _I_n_p_u_t_D_i_a_l_o_g pour automatiser
  cette the. La procure suivante ge un widget InputDialog. Elle cr le
  dialogue s'il n'est pas prent 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 fan dont nous gons ce dialogue. En le
  connectant au signal "destroy", nous nous assurons que nous ne
  garderons pas un pointeur sur le dialogue apr l'avoir druit -- cela
  pourrait provoquer une erreur de segmentation).



  InputDialog a deux boutons "Close" et "Save", qui n'ont pas d'actions
  qui leur sont assigns par daut. 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.


  2200..44..22..  UUttiilliisseerr ll''iinnffoorrmmaattiioonn ssuupppplleennttaaiirree dd''uunn ppiipphhiiqquuee

  Lorsque l'on a validle piphique, on peut simplement utiliser
  l'information supplentaire des champs des structures d'ement. En fait,
  il est toujours prident d'utiliser ces informations car ces champs
  auront des valeurs par daut judicieuses me lorsque les ements
  supplentaires ne sont pas autoris.


  La seule modification consiste appeler _g_d_k___i_n_p_u_t___w_i_n_d_o_w___g_e_t___p_o_i_n_t_e_r_(_)
  au lieu de _g_d_k___w_i_n_d_o_w___g_e_t___p_o_i_n_t_e_r. Cela est nessaire car
  _g_d_k___w_i_n_d_o_w___g_e_t___p_o_i_n_t_e_r ne retourne pas l'information supplentaire.



       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 priser l'ID du piphique
  ainsi que la fenre. Habituellement, on aura obtenu cet ID par le champ
  _d_e_v_i_c_e_i_d d'une structure d'ement. Cette fonction retournera une valeur
  cohente lorsque les ements ne sont pas autoris (dans ce cas,
  _e_v_e_n_t_-_>_d_e_v_i_c_e_i_d aura la valeur _G_D_K___C_O_R_E___P_O_I_N_T_E_R).

  La structure de base des gestionnaires d'ements de dlacement et de
  bouton pressne change donc pas trop -- il faut juste ajouter le code
  permettant de traiter l'information supplentaire.






























  static gint
  button_press_event (GtkWidget *widget, GdkEventButton *event)
  {
    print_button_press (event->deviceid);

    if (event->button == 1 && pixmap != NULL)
      draw_brush (widget, event->source, event->x, event->y, event->pressure);

    return TRUE;
  }

  static gint
  motion_notify_event (GtkWidget *widget, GdkEventMotion *event)
  {
    gdouble x, y;
    gdouble pressure;
    GdkModifierType state;

    if (event->is_hint)
      gdk_input_window_get_pointer (event->window, event->deviceid,
                                    &x, &y, &pressure, NULL, NULL, &state);
    else
      {
        x = event->x;
        y = event->y;
        pressure = event->pressure;
        state = event->state;
      }

    if (state & GDK_BUTTON1_MASK && pixmap != NULL)
      draw_brush (widget, event->source, x, y, pressure);

    return TRUE;
  }




  On doit aussi faire quelquechose de cette nouvelle information. Notre
  nouvelle fonction _d_r_a_w___b_r_u_s_h_(_) dessine avec une couleur diffente pour
  chaque _e_v_e_n_t_-_>_s_o_u_r_c_e et change la taille du pinceau selon la pression.

























  /* Dessine un rectangle l'ran, la taille dend de la pression,
     et la couleur dend du type de piphique */
  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);
  }





  2200..44..33..  EEnn ssaavvooiirr pplluuss ssuurr uunn ppiipphhiiqquuee

  Notre programme affichera le nom du piphique qui a gchaque appui de
  bouton. Pour trouver le nom d'un piphique, nous appelons la fonction :



       GList *gdk_input_list_devices               (void);




  qui retourne une GList (un type de liste cha de la bibliothue glib) de
  structures GdkDeviceInfo. La structure GdkDeviceInfo est dinie de la
  fan 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 intesse est _n_a_m_e qui est, tout
  simplement, le nom que X donne au piphique. L'autre champ, qui n'est
  pas une information de configuration, est _h_a_s___c_u_r_s_o_r. Si _h_a_s___c_u_r_s_o_r
  est faux, on doit dessiner notre propre curseur, mais puisque nous
  avons pris_G_D_K___E_X_T_E_N_S_I_O_N___E_V_E_N_T_S___C_U_R_S_O_R, nous n'avons pas nous en
  prccuper.


  Notre fonction _p_r_i_n_t___b_u_t_t_o_n___p_r_e_s_s_(_) ne fait parcourir la liste retourn
  jusqu'trouver une correspondance, puis affiche le nom du piphique.



       static void
       print_button_press (guint32 deviceid)
       {
         GList *tmp_list;

         /* gdk_input_list_devices retourne une liste interne, nous ne devons donc
           pas la liber apr */
         tmp_list = gdk_input_list_devices();

         while (tmp_list)
           {
             GdkDeviceInfo *info = (GdkDeviceInfo *)tmp_list->data;

             if (info->deviceid == deviceid)
               {
                 printf("Bouton presssur le piphique '%s'\n", info->name);
                 return;
               }

             tmp_list = tmp_list->next;
           }
       }




  Ceci termine les modifications de notre programme XInputize  Comme
  pour la premie version, le code complet est disponible l'endroit ovous
  avez obtenu ce didacticiel.


  2200..44..44..  SSoopphhiissttiiccaattiioonnss ssuupppplleennttaaiirreess

  Bien que notre programme supporte maintenant XInput, il y manque des
  caractistiques que l'on souhaite trouver dans les applications
  comples. D'abord, l'utilisateur ne veut probablement pas avoir
  configurer ses piphiques chaque fois qu'il lance le programme et nous
  devons donc lui permettre de sauvegarder la configuration du piphique.
  Ceci est rlisen parcourant ce que retourne _g_d_k___i_n_p_u_t___l_i_s_t___d_e_v_i_c_e_s_(_) et
  en rivant la configuration dans un fichier.


  Pour restaurer un at au prochain darrage du programme, GDK dispose de
  fonctions pour changer la configuration des piphiques :



       gdk_input_set_extension_events()
       gdk_input_set_source()
       gdk_input_set_mode()
       gdk_input_set_axes()
       gdk_input_set_key()




  (La liste retourn par _g_d_k___i_n_p_u_t___l_i_s_t___d_e_v_i_c_e_s_(_) ne doit pas re modifi
  directement). Un exemple est donndans le programme de dessin _g_s_u_m_i
  (disponible l'adresse http://www.msc.cornell.edu/~otaylor/gsumi/). De
  plus, ce serait pratique d'avoir une mhode standard pour faire cela
  pour toutes les applications. Ceci appartient probablement un niveau l
  ement supieur GTK, peut-re dans la bibliothue GNOME.


  Une autre grosse omission que nous avons mentionn plus haut est
  l'absence de dessin du curseur. Les plates-formes autres qu'XFree86
  n'autorisent pas encore l'utilisation simultan d'un piphique 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 ldessus. 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 : derminer si le piphique courant a besoin ou non d'un curseur
  dessinet derminer si le piphique courant est proximit(si celui-ci est
  une tablette de dessin, il est pratique de faire disparare le curseur
  lorsque le stylet est en dehors de la tablette. Lorsque le piphique
  voit le stylet, on dit qu'il est proximit. La premie vification est
  faite en recherchant dans la liste des piphiques, comme nous l'avons
  fait pour obtenir le nom du piphique. La deuxie est rlis en
  choisissant desements "proximity_out". Une exemple de dessin d'un
  curseur personnel est donndans le programme _t_e_s_t_i_n_p_u_t de la
  distribution GTK.



  2211..  CCoonnsseeiillss ppoouurr ll''rriittuurree dd''aapppplliiccaattiioonnss GGTTKK

  Cette section est simplement un regroupement de lignes de conduites g
  ales et sages, ainsi que d'astuces pour crr des applications GTK
  correctes. Elle est totalement inutile pour l'instant car il ne s'agit
  que d'une phrase :)

  Utilisez _a_u_t_o_c_o_n_f et _a_u_t_o_m_a_k_e de GNU !  Ce sont vos amis :) J'ai en
  projet de les prenter briement ici.




  2222..  CCoonnttrriibbuuttiioonnss

  Ce document, comme beaucoup d'autres beaux logiciels, a cr (NdT : et
  traduit) par des volontaires boles. Si vous vous y connaissez sur
  certains aspects de GTK qui ne sont pas encore document, soyez gentils
  de contribuer ce document.


  Si vous didez de contribuer, envoyez-moi (Ian Main) votre texte
  slow@intergate.bc.ca. La totalitde ce document est libre et tout ajout
  que vous pourriez y faire doit l're alement. Ainsi, tout le monde peut
  utiliser n'importe quelle partie de vos exemples dans les programmes,
  les copies de ce document peuvent re distribus volont etc.

  Merci.



  2233..  RReemmeerrcciieemmeennttss

  Je voudrai remercier les personnes suivantes pour leurs contributions
  ce texte :


  +o  Bawer Dagdeviren, chamele0n@geocities.com pour le didacticiel sur
     les menus.

  +o  Raph Levien, raph@acm.org pour _b_o_n_j_o_u_r _t_o_u_t _l_e _m_o_n_d_e la GTK, le
     placement des widgets et pour sa sagesse. Il a aussi geusement
     donnun abri ce didacticiel.

  +o  Peter Mattis, petm@xcf.berkeley.edu pour le programme GTK le plus
     simple et pour sa capacitle faire :)

  +o  Werner Koch werner.koch@guug.de pour la conversion du texte
     original en SGML, et pour la hiarchie 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'riture 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 tr prieuse.

  +o  Tim Janik timj@psynet.net pour son beau travail sur le widget
     Lists.  Merci Tim :)

  +o  Rajat Datta rajat@ix.netcom.com pour son excellent travail sur le
     didacticiel sur les Pixmaps.

  +o  Michael K. Johnson johnsonm@redhat.com pour ses infos et le code
     pour les menus.

  Et tous ceux d'entre vous qui ont commentet aidamiorer ce document.

  Merci.





  2244..  CCooppyyrriigghhtt

  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 gale GNU telle
  qu'elle est publi par la Free Software Foundation ; soit la version 2
  de la licence, ou (comme vous voulez) toute version ultieure.

  Ce programme est distribudans l'espoir qu'il sera utile, mais SANS
  AUCUNE GARANTIE ; me sans la garantie de COMMERCIALITou d'ADUATION A
  UN BUT PARTICULIER. Voir la licence publique gale GNU pour plus de
  dails.

  Vous devriez avoir re une copie de la licence publique gale GNU avec
  ce programme ; si ce n'est pas le cas, rivez la Free Software
  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.


  2244..11..  CCee ddooccuummeenntt aa aaddaapptteenn FFrraanniiss ppaarriicc JJaaccoobboonnii <<mailto:jaco@dot-
  com.fr>. Toute remarque sur cette adaptation sera la bienvenue.  Notes
  du traducteur

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








































