\documentclass[17pt,a4paper]{foils}
\newif\ifpdf
\ifx\pdfoutput\undefined
  \pdffalse % we are not running PDFLaTeX
\else
  \pdfoutput=1 % we are running PDFLaTeX
  \pdftrue
  \pdfcompresslevel=9
\fi

%%INRIA ou Fac?
\newif\ifinria
\inriatrue
%\inriafalse
%% seminaire ou audition?
\newif\ifseminaire
\seminairetrue
%\seminairefalse
%% traitement du signal ou info?
\newif\ifts
\tstrue
%\tsfalse

\usepackage[latin1]{inputenc}% pour preciser le codage d'entree, ici latin1
\usepackage{latexsym}% caractères supplémentaires
\usepackage{amsmath}
\usepackage{graphicx}
\usepackage{color}
%\graphicspath{{figures/}}
\usepackage{multicol}

\newif\iffrench
\frenchtrue

\iffrench
  \usepackage{french}
  \ProcessOptions\relax
  \abbreviations
\else
  \usepackage{xspace}
  \usepackage[english,frenchb]{babel}
  %commandes de semi-compatibilité
  \providecommand{\fsc}{\bsc}
  \providecommand{\lsc}{\textsc}
  \providecommand{\lettrinef}[2]{\lettrine{#1}{#2}} 
  \providecommand{\french}{\selectlanguage{frenchb}}
  \providecommand{\english}{\selectlanguage{english}}
  \providecommand{\inferieura}{<}
  \providecommand{\superieura}{>}
  \newenvironment{order}{\begin{enumerate}}{\end{enumerate}}
  \newenvironment{resume}{\begin{center}Résumé\vspace*{-.5em}\end{center}\begin{quotation}}{\end{quotation}}
  \newenvironment{abstract}{\begin{center}Abstract\vspace*{-.5em}\end{center}\begin{quotation}}{\end{quotation}}
  \usepackage{makeidx}
\fi


\ifpdf
  \usepackage{ae,aecompl}
  \usepackage[cm]{aeguill}

  % to generate PDF thumbnails (optionnal)
  \usepackage{thumbpdf}
\fi

\usepackage[T1]{fontenc}% pour utiliser les fontes a la norme Cork (DC,...)
                        % permet notamment la cesure des mots accentues
\usepackage[plainpages=false]{hyperref}

\definecolor{dkblue}{rgb}{0,0,0.5}
\newcommand\titlecolor{dkblue}

\newcommand{\ttt}{\texttt}

\title{\color{\titlecolor}Introduction à GTK}
\author{Frédéric Devernay\\
  \ttt{Frederic.Devernay at inria dot fr}}
\date{8 novembre 1999}
\MyLogo{}
%\Restriction{}
%\rightfooter{}
%\leftheader{}
%\rightheader{}

\begin{document}
\maketitle

{%\small
\begin{abstract}
\noindent Nous verrons d'abord l'organisation générale de GTK et de
GNOME. Nous détaillerons ensuite l'utilisation de la bibliothèque de
portabilité GLib, puis quelques exemples d'utilisation de GTK.

\noindent Pourquoi GNOME et GTK ? 
\begin{itemize}
\item Parce que GTK ne dépend pas du langage utilisé ($\neq$ Qt), est
  portable et extensible, et que le source est disponible.
\item   Parce que
  GNOME est basé sur des concepts largement répandus dans l'industrie
  (CORBA, XML), mais peu enseignés.
\item \emph{Et en plus c'est beau !
    (\english\href{http://gtk.themes.org/}{thèmes}, \french etc.)}
\end{itemize}
\end{abstract}
}

\foilhead[-1.5cm]{\color{\titlecolor}L'architecture logicielle de GTK/GNOME}

%\begin{figure}
  \begin{center}
    \includegraphics[width=\textwidth]{gnome-arch}
{\small La hiérarchie des bibliothèques de GNOME}
    
  \end{center}

\begin{itemize}
\item Les bibliothèques GNOME sont haut niveau (notion d'application,
  fichiers de config...)
\item GTK+ est composé de GTK (widget toolkit) et GDK (primitives de
  dessin bas niveau)
\item GLib est une bibliothèque de portabilité (types, fonctions
  standard) et d'utilitaires (listes, tables de hachage, ...)
\end{itemize}

\foilhead{\color{\titlecolor}Les bibliothèques GNOME}

\begin{itemize}
\item \textbf{libgnome :} utilitaires indépendants de la toolkit ;
\item \textbf{libgnomeui :} ... dependants de la toolkit (combos, ...)
  ;
\item \textbf{libgnorba :} utilisation de corba, via ORBit ;
\item \textbf{gtk-xmhtml :} widget HTML pour GTK ;
\item \textbf{xvt :} widget d'émulation de terminal ;
\item \textbf{libvfs :} file système virtuel utilisé dans Midnight
  Commander ;
\item \textbf{libart\_lgpl :} bibliothèque graphique avec anti-aliasing.
\end{itemize}

Mais commençons par le début : \emph{GLib}, puis \emph{GTK}, puis
\emph{GNOME}.

Caractéristique commune de toutes ces bibliothèques : peu de
documentation. Resources disponibles :
\begin{itemize}
\english
\item \href{http://www.gtk.org/}{www.gtk.org} (GTK)
\item \href{http://developer.gnome.org/doc/}{developer.gnome.org/doc}
  (GNOME)
\item
  \href{http://www-mips.unice.fr/~devernay/cours/IHM/}{www-mips.unice.fr/~devernay/cours/IHM} (le site du cours)
\french
\item les \emph{includes} de GTK, GLib, ...
\end{itemize}

\foilhead{\color{\titlecolor}GLib}

\noindent Les noms de fonction commencent par \ttt{g\_} (g\_strdup), 
les types par \ttt{g} (gint32), les structures sont capitalisées et
commencent par \ttt{G} (GHashTable).

\noindent \textbf{Typedefs :}
{\small
\begin{itemize}
\item \ttt{gint8} : entier signé 8 bits
\item \ttt{guint8} : entier non signé 8 bits
\item ... \ttt{gint16}, \ttt{guint16}, \ttt{gint32}, \ttt{guint32},
  \ttt{gint64} et \ttt{guint64} (dispo si \ttt{G\_HAVE\_GINT64} est
  défini), \ttt{gchar}, \ttt{guchar}, \ttt{gshort}, \ttt{gushort}, \ttt{glong},
  \ttt{gulong}, \ttt{gint}, \ttt{guint}, \ttt{gfloat}, \ttt{gdouble},
  \ttt{gboolean} (en fait un \ttt{int}, prend les valeurs \ttt{TRUE}
  ou \ttt{FALSE}), \ttt{gpointer} (= \ttt{void *}),
  \ttt{gconstpointer} (= \ttt{const void *})
\end{itemize}
}

\noindent \textbf{Portabilité et Utilitaires :}
{\small
\begin{itemize}
\item \ttt{gchar* g\_strdup (const gchar*)} = \ttt{stdup()}
\item \ttt{g\_malloc}, \ttt{g\_free}
\item \ttt{void g\_usleep (gulong count)} suspend l'exécution pendant
  '\ttt{count}' microsecondes
\item \ttt{g\_snprintf} = \ttt{snprintf} \emph{à utiliser pour
    raisons éviter les 'buffer overflows'} (sécurité++)
\item \ttt{g\_new(type,count)} (= \ttt{(type)
    g\_malloc(count*sizeof(type))}), \ttt{g\_new0} id. mais met à zéro
\item \ttt{gchar* g\_strconcat (const gchar *str, ..., NULL)}
  concatène les chaînes en paramètres (alloue  une nouvelle chaîne)
\item \ttt{g\_strdup\_printf} comme un sprintf, mais alloue la chaîne
\item \ttt{g\_strstrip} coupe les espaces au début et à la fin d'une
  chaîne, \emph{n'alloue pas, ne désalloue pas}
\end{itemize}
}

\foilhead{\color{\titlecolor}GLib : Les containers}

{\small
\begin{itemize}
\item GList : liste doublement chaînée
\item GSList : liste simplement chaînée
\item GHashTable : table de hachage
\item GCache : cache
\item GTree : arbre binaire équilibré
\item GNode : arbre \emph{n}-aire
\item GString : chaîne de caractères de taille dynamique
\item GArray : tableau de taille dynamique
\item GPtrArray : tableau de pointeurs de taille dynamique
\item GByteArray : tableau d'octets (guint8) de taille dynamique
\end{itemize}
}

\noindent\textbf{Listes}
{\small
\begin{itemize}
\item \texttt{GList*
    g\_list\_append             (GList          *list,
    gpointer     data)} ajoute data à list. list peut être NULL
  (création)
\item g\_list\_prepend, g\_list\_remove, g\_list\_find, g\_list\_next,
  g\_list\_previous, g\_list\_free (ne libère pas les data !)
\end{itemize}
}

\noindent\textbf{Chaînes de caractères}
{\tiny
\begin{verbatim}
/* le char* est dans le champ str de GString. */
GString* g_string_new       (const gchar *init);
void     g_string_free      (GString     *string, gint free_segment);
GString* g_string_append    (GString     *string, const gchar *val);
GString* g_string_prepend   (GString     *string, const gchar *val);
void     g_string_sprintf   (GString     *string, const gchar *format,
                             ...) G_GNUC_PRINTF (2, 3);
void     g_string_sprintfa  (GString     *string, const gchar *format,
                             ...) G_GNUC_PRINTF (2, 3);
\end{verbatim}
}

\noindent\textbf{etc...}
{\small Autres containers (/usr/include/glib.h), constantes et macros
  de conversion portables... même sous MSWindows (/usr/lib/glib/include/glibconfig.h).}

\foilhead{\color{\titlecolor}GTK : introduction}

\begin{itemize}
\item Il existe des interfaces C++ (GTK--), python (pyGTK), perl
  (perlGTK), ..., mais nous allons utiliser l'interface C (les autres
  lui ressemblent).
  
\item  GTK+ est écrit en C orienté-objet : implémente les notions de
  classe, d'héritage (simple), de méthodes virtuelles, de typage fort
  (à l'exécution) et de
  fonctions de rappel (callbacks).
\end{itemize}

\noindent Premier exemple : affiche une fenêtre 200x200
{\tiny
\begin{verbatim}
#include <gtk/gtk.h>

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

    /* recherche sur la ligne de commande les arguments --gtk-module
       --g-fatal-warnings --display --sync --no-xshm --name --class */
    gtk_init (&argc, &argv);
    
    /* crée une fenêtre et l'affiche */
    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_widget_show (window);
    
    /* boucle d'évènements */
    gtk_main ();
    
    return 0;
}
\end{verbatim}
}

\foilhead{\color{\titlecolor}GTK : Bonjour tout le monde}

{\tiny
\begin{verbatim}

#include <gtk/gtk.h>

    /* fonction de rappel. Dans cet exemple, les paramètres sont ignorés...
     *  Les fonctions de rappel sont détaillées plus loin. */

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

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

    /* Si l'on renvoit TRUE dans le gestionnaire du signal "delete_event",
     * GTK émettra le signal "destroy". Retourner FALSE signifie que l'on
     * ne veut pas que la fenêtre soit détruite. 
     * Utilisé pour faire apparaître des boîtes de dialogue du type
     * « Êtes-vous sûr de vouloir quitter ? » */
    
    /* Remplacez FALSE par TRUE et la fenêtre principale sera détruite 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 déclarer les widgets. */

    GtkWidget *window;
    GtkWidget *button;
    
    /* Cette fonction est appelée dans toutes les applications GTK. 
     * Les paramètres passés en ligne de commande sont analysés et 
     * retournés à l'application. */

    gtk_init (&argc, &argv);
    
    /* Création d'une nouvelle fenêtre. */

    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    
    /* Lorsque la fenêtre reçoit le signal "delete_event" 
     * (envoyé par le gestionnaire de fenêtres en utilisant l'option
     * « close » ou la barre de titre), on lui demande d'appeler la
     * fonction delete_event() définie plus haut. La donnée passée en
     * paramètre à la fonction de rappel est NULL et est ignoré dans le
     * rappel. */

    gtk_signal_connect (GTK_OBJECT (window), "delete_event",
                        GTK_SIGNAL_FUNC (delete_event), NULL);
    
    /* Ici, on connecte l'évenement "destroy" à un gestionnaire de signal.
     * Cet événement arrive lorsqu'on appelle gtk_widget_destroy() sur la
     * fenêtre, 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 fenêtre. */

    gtk_container_border_width (GTK_CONTAINER (window), 10);
    
    /* Création 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() définie plus haut en lui passant NULL en paramètre. */

    gtk_signal_connect (GTK_OBJECT (button), "clicked",
                        GTK_SIGNAL_FUNC (hello), NULL);
    
    /* Ceci provoquera la destruction de la fenêtre 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 fenêtres. */

    gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
                               GTK_SIGNAL_FUNC (gtk_widget_destroy),
                               GTK_OBJECT (window));
    
    /* Insertion du bouton dans la fenêtre (container gtk). */

    gtk_container_add (GTK_CONTAINER (window), button);
    
    /* L'étape finale consiste à afficher ce nouveau widget... */

    gtk_widget_show (button);
    
    /* ... et la fenêtre. */

    gtk_widget_show (window);
    
    /* Toutes les applications GTK doivent avoir un gtk_main(). 
     * Le déroulement du programme se termine là et attend qu'un
     * événement survienne (touche pressée ou événement souris). */

    gtk_main ();
    
    return 0;
}
\end{verbatim}
}

\noindent\textbf{Compilation}
{\tiny
\begin{verbatim}
gcc -Wall -g -I/usr/lib/glib/include bonjour.c -o bonjour_monde \
    -L/usr/X11R6/lib -lgtk -lgdk -lglib -lXext -lX11 -lm
ou
gcc bonjour.c -o bonjour_monde `gtk-config --cflags --libs`
\end{verbatim}
}

\foilhead{\color{\titlecolor}GTK : théorie des signaux et rappels}

GTK est dirigé par les événements : il reste dans gtk\_main() jusqu'à
ce qu'un événement arrive (ex. bouton). Il faut connecter le
gestionnaire de signaux sur la fonction de rappel :
{\small
\begin{verbatim}
/* retourne un identificateur de la fonction de rappel */
gint gtk_signal_connect( GtkObject     *object, /* widget émetteur */
                         gchar         *name,   /* nom du signal */
                         GtkSignalFunc  func,   /* fctn de rappel */
                         gpointer       func_data ); /* données */
/* déconnecte le signal */
void gtk_signal_disconnect( GtkObject *object,
                            gint id );
/* déconnecte tous les signaux */
void gtk_signal_handlers_destroy( GtkObject *object );
\end{verbatim}
}

\noindent La fonction de rappel a (en général) la forme :
{\small
\begin{verbatim}
void callback_func( GtkWidget *widget, /* widget émetteur */
                    gpointer   callback_data ); /* données */
\end{verbatim}
}
\noindent Autre forme (utile pour appeler une fctn GTK prenant un
objet) :
{\small
\begin{verbatim}
gint gtk_signal_connect_object( GtkObject     *object,
                                gchar         *name,
                                GtkSignalFunc  func,
                                GtkObject     *slot_object );
void callback_func( GtkObject *object );
\end{verbatim}
}

\foilhead{\color{\titlecolor}GTK : événements}

{\small\noindent event, button\_press\_event, button\_release\_event,
motion\_notify\_event, delete\_event, destroy\_event, expose\_event,
key\_press\_event, key\_release\_event, enter\_notify\_event,
leave\_notify\_event, configure\_event, focus\_in\_event,
focus\_out\_event, map\_event, unmap\_event, property\_notify\_event,
selection\_clear\_event, selection\_request\_event,
selection\_notify\_event, proximity\_in\_event, proximity\_out\_event,
drag\_begin\_event, drag\_request\_event, drag\_end\_event,
drop\_enter\_event, drop\_leave\_event, drop\_\-data\_available\_event,
other\_event

\begin{verbatim}
/* fonction de rappel */
void callback_func( GtkWidget *widget,
                    GdkEvent  *event,
                    gpointer   callback_data );
\end{verbatim}

\noindent\texttt{GdkEvent.type : enum \{   GDK\_\-NOTHING, GDK\_\-DELETE, GDK\_\-DESTROY, GDK\_\-EXPOSE, GDK\_\-MOTION\_\-NOTIFY, GDK\_\-BUTTON\_\-PRESS, GDK\_\-2BUTTON\_\-PRESS, GDK\_\-3BUTTON\_\-PRESS, GDK\_\-BUTTON\_\-RELEASE, GDK\_\-KEY\_\-PRESS, GDK\_\-KEY\_\-RELEASE, GDK\_\-ENTER\_\-NOTIFY, GDK\_\-LEAVE\_\-NOTIFY, GDK\_\-FOCUS\_\-CHANGE, GDK\_\-CONFIGURE, GDK\_\-MAP, GDK\_\-UNMAP, GDK\_\-PROPERTY\_\-NOTIFY, GDK\_\-SELECTION\_\-CLEAR, GDK\_\-SELECTION\_\-REQUEST, GDK\_\-SELECTION\_\-NOTIFY, GDK\_\-PROXIMITY\_\-IN, GDK\_\-PROXIMITY\_\-OUT, GDK\_\-DRAG\_\-BEGIN, GDK\_\-DRAG\_\-REQUEST, GDK\_\-DROP\_\-ENTER, GDK\_\-DROP\_\-LEAVE, GDK\_\-DROP\_\-DATA\_\-AVAIL, GDK\_\-CLIENT\_\-EVENT, GDK\_\-VISIBILITY\_\-NOTIFY, GDK\_\-NO\_\-EXPOSE, GDK\_\-OTHER\_\-EVENT       /* Deprecated, use filters instead */
\}}

\begin{verbatim}
/* Connection d'un événement vers une fonction de rappel */
gtk_signal_connect( GTK_OBJECT(button), "button_press_event",
                    GTK_SIGNAL_FUNC(button_press_callback), 
                    NULL);
static gint button_press_callback (GtkWidget      *widget, 
                                GdkEventButton *event,
                                gpointer        data);
\end{verbatim}
}

\foilhead{\color{\titlecolor}GTK : Bonjour tout le monde amélioré}

{\tiny
\begin{verbatim}
#include <gtk/gtk.h>

/* Notre nouveau rappel amélioré. La donnée passée à cette fonction est
 * imprimée sur stdout. */

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

/* Un autre rappel */

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

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

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

    /* Cette fonction est appelée dans toutes les applications GTK. 
     * Les paramètre passés en ligne de commande sont analysés et 
     * retournés à l'application. */

    gtk_init (&argc, &argv);

    /* Création d'une nouvelle fenêtre. */

    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

    /* Nouvel appel qui intitule notre nouvelle fenêtre 
     * "Salut les boutons !" */

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

    /* Configuration d'un gestionnaire pour "delete_event" afin de
     * quitter immédiatement GTK. */

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


    /* Configuration de la largeur du contour de la fenêtre. */
    
    gtk_container_border_width (GTK_CONTAINER (window), 10);

    /* Création d'une boîte pour y placer les widgets.
     * Ceci est décrit en détails plus loin dans la section 
     * « placement ». La boîte n'est pas matérialisée, elle est juste
     * utilisée comme moyen d'arranger les widgets. */

    box1 = gtk_hbox_new(FALSE, 0);

    /* On met la boîte dans la fenêtre principale. */

    gtk_container_add (GTK_CONTAINER (window), box1);

    /* On crée 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 chaîne « Bouton 1 » comme paramètre. */

    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 boîte invisible qui a été placée dans la fenêtre. */

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

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

    gtk_widget_show(button);

    /* On fait la même chose pour créer un deuxième bouton. */

    button = gtk_button_new_with_label ("Bouton 2");

    /* On appelle la même fonction de rappel avec un paramètre différent,
     * un pointeur sur la chaîne « 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 préférable d'afficher la fenêtre 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 fête commence ! */

    gtk_main ();

    return 0;
}
\end{verbatim}
}

\begin{itemize}
\item Utilise le placement des widgets, cf. \emph{gtk\_box\_pack\_start()}.
\item \emph{gtk\_window\_new(GTK\_WINDOW\_DIALOG)} crée une boîte de dialogue.
\end{itemize}

\foilhead{\color{\titlecolor}Placement des widgets}

{\tiny
\begin{verbatim}
GtkWidget* gtk_hbox_new      (gboolean homogeneous,
                              gint spacing);
void gtk_box_pack_[start|end] (GtkBox        *box,
                               GtkWidget    *child,
                               gboolean      expand,
                               gboolean      fill,
                               guint         padding);
\end{verbatim}
}

\begin{center}
\includegraphics{placement1}
\includegraphics{placement2}
\includegraphics{placement3}
\end{center}

\foilhead{\color{\titlecolor}Tables}
\noindent Utilisation d'une grille pour placer les widgets : lignes de 0 à nrows, colonnes de 0 à ncolumns. homogeneous = boîtes prennent la taille du plus grand widget.

\noindent Création avec gtk\_table\_new, placement avec gtk\_table\_attach
{\tiny
\begin{verbatim}
GtkWidget* gtk_table_new (gint rows,          
                          gint columns,       
                          gint homogeneous);  
 0          1          2     -> colonnes
0+----------+----------+    |
 |          |          |    V lignes
1+----------+----------+
 |          |          |
2+----------+----------+
void gtk_table_attach (GtkTable      *table, /* table */
                       GtkWidget     *child, /* widget à placer */
                       gint           left_attach, /* position */
                       gint           right_attach,
                       gint           top_attach,
                       gint           bottom_attach,
                       gint           xoptions, /* options */
                       gint           yoptions,
                       gint           xpadding, /* marge */
                       gint           ypadding);
\end{verbatim}
}
\noindent options : OU logique de GTK\_FILL (le widget s'élargit pour occuper tte la case), GTK\_SHRINK (les widgets sont réduits plutôt que descendus lors d'un redimensionnement), GTK\_EXPAND (la table utilise tout l'espace restant dans la fenêtre).

\foilhead{\color{\titlecolor}Tables : plus simple}
{\tiny
\begin{verbatim}
/* options = GTK_FILL|GTK_EXPAND, padding=0 */
void gtk_table_attach_defaults (GtkTable   *table,
                                GtkWidget  *widget,
                                gint        left_attach,
                                gint        right_attach,
                                gint        top_attach,
                                gint        bottom_attach);

/* espaces après une ligne ou une colonne */
void gtk_table_set_row_spacing (GtkTable      *table,
                                gint           row,
                                gint           spacing);  /* en dessous */
void gtk_table_set_col_spacing  (GtkTable      *table,
                                 gint           column,
                                 gint           spacing); /* a droite */
/* gtk_table_set_row_spacings, gtk_table_set_col_spacings = ttes les cols */
\end{verbatim}

\begin{center}
  \includegraphics[width=5cm]{gtk_tut_table}
\end{center}
\begin{verbatim}
    button = gtk_button_new_with_label ("button 1"); [...]
    gtk_table_attach_defaults (GTK_TABLE(table), button, 0, 1, 0, 1); [...]
    button = gtk_button_new_with_label ("button 2"); [...]
    gtk_table_attach_defaults (GTK_TABLE(table), button, 1, 2, 0, 1); [...]
    button = gtk_button_new_with_label ("Quit"); [...]
    gtk_table_attach_defaults (GTK_TABLE(table), button, 0, 2, 1, 2); [...]
\end{verbatim}
}
\foilhead{\color{\titlecolor}Vision d'ensemble des widgets}

\noindent Étapes de la création d'un widget GTK :
\begin{enumerate}
\item {\em gtk\_*\_new()\/}
\item Connexion des signaux voulus avec les gestionnaires adéquats
\item Placement dans un container/une boîte/une table
\item Créer/afficher les widgets fils
\item Affichage : {\em gtk\_widget\_show()\/}
\end{enumerate}

\noindent gtk\_widget\_hide() fait disparaître un widget

Conversions de types (avec vérification), pour obtenir le type requis par la déclaration de la fonction :
\begin{itemize}
\item  GTK\_WIDGET(widget)
\item  GTK\_OBJECT(object)
\item  GTK\_SIGNAL\_FUNC(function)
\item  GTK\_CONTAINER(container)
\item  GTK\_WINDOW(window)
\item  GTK\_BOX(box)
\end{itemize}

\foilhead{\color{\titlecolor}Hiérarchie de classes des widgets}


\begin{multicols}{2}
  
{\tiny
\begin{verbatim}
 GtkObject
  +GtkWidget
  | +GtkMisc
  | | +GtkLabel *
  | | | +GtkAccelLabel
  | | | `GtkTipsQuery
  | | +GtkArrow *
  | | +GtkImage * 
  | | `GtkPixmap *
  | +GtkContainer
  | | +GtkBin *
  | | | +GtkAlignment *
  | | | +GtkFrame *
  | | | | `GtkAspectFrame *
  | | | +GtkButton
  | | | | +GtkToggleButton
  | | | | | `GtkCheckButton
  | | | | |   `GtkRadioButton
  | | | | `GtkOptionMenu
  | | | +GtkItem *
  | | | | +GtkMenuItem
  | | | | | +GtkCheckMenuItem
  | | | | | | `GtkRadioMenuItem
  | | | | | `GtkTearoffMenuItem
  | | | | +GtkListItem
  | | | | `GtkTreeItem
  | | | +GtkWindow
  | | | | +GtkColorSelectionDialog
  | | | | +GtkDialog
  | | | | | `GtkInputDialog
  | | | | +GtkDrawWindow
  | | | | +GtkFileSelection
  | | | | +GtkFontSelectionDialog
  | | | | `GtkPlug
  | | | +GtkEventBox
  | | | +GtkHandleBox
  | | | +GtkScrolledWindow *
  | | | `GtkViewport
  | | +GtkBox *
  | | | +GtkButtonBox
  | | | | +GtkHButtonBox
  | | | | `GtkVButtonBox
  | | | +GtkVBox *
  | | | | +GtkColorSelection
  | | | | `GtkGammaCurve
  | | | `GtkHBox *
  | | |   +GtkCombo
  | | |   `GtkStatusbar
  | | +GtkCList
  | | | `GtkCTree
  | | +GtkFixed
  | | +GtkNotebook
  | | | `GtkFontSelection
  | | +GtkPaned
  | | | +GtkHPaned
  | | | `GtkVPaned
  | | +GtkLayout
  | | +GtkList
  | | +GtkMenuShell
  | | | +GtkMenuBar
  | | | `GtkMenu
  | | +GtkPacker
  | | +GtkSocket
  | | +GtkTable *
  | | +GtkToolbar
  | | `GtkTree
  | +GtkCalendar
  | +GtkDrawingArea
  | | `GtkCurve
  | +GtkEditable
  | | +GtkEntry
  | | | `GtkSpinButton
  | | `GtkText
  | +GtkRuler
  | | +GtkHRuler
  | | `GtkVRuler
  | +GtkRange
  | | +GtkScale
  | | | +GtkHScale
  | | | `GtkVScale
  | | `GtkScrollbar
  | |   +GtkHScrollbar
  | |   `GtkVScrollbar
  | +GtkSeparator *
  | | +GtkHSeparator *
  | | `GtkVSeparator *
  | +GtkPreview
  | `GtkProgress
  |   `GtkProgressBar
  +GtkData
  | +GtkAdjustment
  | `GtkTooltips
  `GtkItemFactory
\end{verbatim}

\noindent (*): Widgets sans fenêtres, doivent être englobés dans une GtkEventBox si on veut capturer leur événements.
}
\end{multicols}

\textbf{Une bonne doc sur les widgets et leur utilisation :} le programme testgtk.c

\foilhead{\color{\titlecolor}Boutons}

\noindent \textbf{Boutons normaux :} deux possibilités pour les créer,
\begin{enumerate}
\item gtk\_button\_new\_with\_label()
\item gtk\_button\_new(), mettre l'intérieur du bouton dans une box
  (gtk\_box\_pack\_start()) puis l'ajouter au bouton avec
  gtk\_container\_add(). Exemple : boutons.c
\end{enumerate}

\noindent \textbf{Boutons commutateurs et cases à cocker :} \newline
(toggle
buttons \& check buttons) émettent le signal
``toggled''
{\tiny
\begin{verbatim}
GtkWidget* gtk_toggle_button_new (void);
GtkWidget* gtk_toggle_button_new_with_label (gchar *label);
GtkWidget* gtk_check_button_new (void);
GtkWidget* gtk_check_button_new_with_label (gchar *label);

/* exemple de fonction de rappel pour le signal "toggled" */
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 relâché. */
    } else {
        /* le bouton est enfoncé */
    }
}

/* configuration du bouton, émet "clicked" si changement */
void gtk_toggle_button_set_active (GtkToggleButton *toggle_button,
                                  gint state); /*_set_state en GTK 1.0 */

/* change l'état et émet "toggled" */
void gtk_toggle_button_toggled (GtkToggleButton *toggle_button);
\end{verbatim}
}

\foilhead{\color{\titlecolor}Boutons (2)}

\noindent \textbf{Boutons radio :} (exemple radiobuttons.c)
{\small
\begin{verbatim}
GtkWidget* gtk_radio_button_new (GSList *group);
GtkWidget* gtk_radio_button_new_with_label (GSList *group,
                                            gchar *label);

/* group = NULL au premier appel, puis on crée le groupe avec : */
GSList* gtk_radio_button_group (GtkRadioButton *radio_button);

/* ou bien */
button2 = gtk_radio_button_new_with_label(
            gtk_radio_button_group (GTK_RADIO_BUTTON (button1)),
            "button2");

/* positionner le bouton actif avec : */
void gtk_toggle_button_set_active( GtkToggleButton *toggle_button,
                                  gint             state );
\end{verbatim}
}

\foilhead{\color{\titlecolor}Ajustements}


{\small

\noindent Permettent de connecter un widget de << réglage >> (scrollbar,
valeur ajustable, bouton de réglage) \`a un widget utilisant les valeurs
correspondantes (zone de text, viewport) pour afficher, par ex., une
zone de données.

\noindent \textbf{GtkAdjustment :} Stocke la valeur concernée et les
intervalles autorisés, peut émettre des signaux lorsque la valeur ou les
intervalles sont changés.
{\tiny
\begin{verbatim}
GtkObject *gtk_adjustment_new( gfloat value,
                               gfloat lower, /* intervalle */
                               gfloat upper, /*   autorisé */
                               gfloat step_increment, /* petit/ */
                               gfloat page_increment, /* grand incrément */
                               gfloat page_size ); /* taille zone visible */
\end{verbatim}
}
\noindent \textbf{Utilisation facile :}
\begin{verbatim}
text = gtk_text_new (NULL, NULL);
vscrollbar = gtk_vscrollbar_new (GTK_TEXT(text)->vadj);
\end{verbatim}

\noindent \textbf{Changement de valeur << à la main >> :}
\begin{verbatim}
void gtk_adjustment_set_value( GtkAdjustment *adjustment,
                               gfloat         value );
\end{verbatim}
l'ajustement émet le signal ``value\_changed''. Exemple :
{\tiny
\begin{verbatim}
void cb_rotate_picture (GtkAdjustment *adj, GtkWidget *picture)
{ set_picture_rotation (picture, adj->value); ... } ...
gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
                    GTK_SIGNAL_FUNC (cb_rotate_picture), picture);
\end{verbatim}
}
\noindent \emph{de même}, lorsque les champs ``upper'' ou ``lower'' sont changés,
le signal ``changed'' est émis. \emph{S'ils sont changés à la main, utiliser
gtk\_signal\_emit\_by\_name(GTK\_OBJECT (adjustment), "changed")}.
}

\foilhead{\color{\titlecolor}Intervalles}

Scale ou ScrollBar : \includegraphics{range}
{\tiny
\begin{verbatim}
/* adj=NULL pour en créer un nouveau */
GtkWidget *gtk_[h|v]scrollbar_new( GtkAdjustment *adj );

/* affiche ou non la valeur */
void gtk_scale_set_draw_value(GtkScale *scale, gint draw_value);

/* nombre de décimales à afficher */
void gtk_scale_set_digits(GtkScale *scale, gint digits);

/* endroit où afficher la valeur */
/* pos = GTK_POS_[LEFT|RIGHT|TOP|BOTTOM] */
void gtk_scale_set_value_pos(GtkScale *scale, GtkPositionType  pos);
\end{verbatim}
}

\noindent \textbf{Fonctionnalités communes :}
{\tiny
\begin{verbatim}
/* quand faut-il émettre le signal "changed" ? */
/* policy=GTK_UPDATE_POLICY_CONTINUOUS     -à chaque mvmt */
/* policy=GTK_UPDATE_POLICY_DISCONTINUOUS -quand on lache */
/* policy=GTK_UPDATE_POLICY_DELAYED    -quand on s'arrête */
void gtk_range_set_update_policy( GtkRange *range, GtkUpdateType policy);

/* récupérer/changed l'ajustement */
GtkAdjustment* gtk_range_get_adjustment(GtkRange *range);
void gtk_range_set_adjustment(GtkRange *range, GtkAdjustment *adjustment);
\end{verbatim}
}
{\tiny
\noindent Si on change l'ajustement, connecter les bons signaux au nouveau, et
appeler gtk\_range\_adjustment\_changed().

\noindent \textbf{Clavier :}
\begin{itemize}
\item intervalles verticaux : flèches haut/bas (1 step), PgUp/PgDwn
  (1page)
\item intervalles horizontaux : flèches gauche/droite (1 step), Home
  (début), End (fin)
\item scale : control-gauche/droite (1 step)
\item scrollbar : control-Home/End (1 step)
\end{itemize}
}

\foilhead{\color{\titlecolor}Widgets divers}
\noindent Voir leur utilisation dans le \english
\href{http://www.gtk.org/tutorial/}{tutorial} \french (chapitre 9,
Misc. Widgets) ou dans testgtk.c.

\begin{itemize}
\item Labels
\item Flèches
\item Bulles d'aide
\item Barres de progression
\item Dialogues
\item Pixmaps
\item Règles
\item Barres d'état
\item Entrées de texte
\item Boutons rotatifs
\item Boîte combo
\item Sélection de couleurs
\item Sélection de fichiers
\end{itemize}

\foilhead{\color{\titlecolor}Widgets conteneurs}
{\small
\noindent \textbf{Event Box :} permet d'associer des évènements à un
widget sans fenêtre, et de faire le clipping (utile si on retaille un
widget, avec gtk\_widget\_set\_usize()).
{\tiny
\begin{verbatim}
GtkWidget *gtk_event_box_new( void );
gtk_container_add( GTK_CONTAINER(event_box), child_widget );
/* ce qu'on ne peut pas faire avec un widget sans window : */
gtk_signal_connect (GTK_OBJECT(event_box), "button_press_event",
                              GTK_SIGNAL_FUNC (gtk_exit), NULL);
gdk_window_set_cursor (event_box->window, gdk_cursor_new (GDK_HAND1));
\end{verbatim}
}
\noindent \textbf{Widget Alignement :} permet d'aligner un widget
(position et taille, entre 0.0 et 1.0) à l'intérieur de sa fenêtre.
{\tiny
\begin{verbatim}
GtkWidget* gtk_alignment_new( gfloat xalign, yalign, xscale, yscale);
void gtk_alignment_set( GtkAlignment *alignment, xa, ya, xs, ys);
\end{verbatim}
}
\noindent \textbf{Conteneurs fixe et  d'arrangement :} pour placer un widget à une
position précise en pixels. Le conteneur d'arrangement implémente une
zone quasi-infinie, qui peut être scrollée. De plus, il se redessine
(se met à jour) tout seul quand ses valeurs sont changées.
{\tiny
\begin{verbatim}
GtkWidget* gtk_[fixed|layout]_new( void );
void gtk_[fixed|layout]_put( [GtkFixed *fixed|GtkLayout *layout],
                             GtkWidget *widget, gint16 x, y );
void gtk_[fixed|layout]_move( [GtkFixed *fixed|GtkLayout *layout],
                               GtkWidget *widget, gint16 x, y );

/* fixer la taille : */
void gtk_layout_set_size( GtkLayout *layout, guint width, height );

/* empecher/remettre la mise à jour */
void gtk_layout_freeze( GtkLayout *layout );
void gtk_layout_thaw( GtkLayout *layout );

/* positionner/récupérer les ajustements */
GtkAdjustment* gtk_layout_get_[h|v]adjustment( GtkLayout *layout );
void gtk_layout_set_[h|v]adjustment( GtkLayout *layout,
                                     GtkAdjustment *adjustment );
\end{verbatim}
}

\noindent \textbf{Cadres :} \emph{(frame)} englobe un ou plusieurs widgets avec une
boîte, éventuellement étiquetée. Les cadres d'aspect \emph{(aspect frame)}
permettent en plus de conserver l'aspect (largeur/hauteur) du widget fils.

\noindent \textbf{Fenêtres coulissantes :} \emph{(panned window)}
Fenêtre divisée en deux zones séparées par une ligne dont la position
est règlable.
\begin{verbatim}
GtkWidget *gtk_[h|v]paned_new (void);
void gtk_paned_add[1|2] (GtkPaned *paned, GtkWidget *child);
void gtk_paned_set_handle_size(GtkPaned *paned, guint16 size);
void gtk_paned_set_gutter_size(GtkPaned *paned, guint16 size);
\end{verbatim}

\noindent \textbf{Viewport :} permet de visualiser une partie d'un
widget seulement. On utilise plutôt scrolled\_window. Différence avec
layout ?

\noindent \textbf{Scrolled Windows :} Un viewport avec des scrollbars.

\noindent \textbf{Boîtes de boutons :} \emph{(button boxes)} pour
grouper facilement des boutons.

\noindent \textbf{Barre d'outils :} \emph{(toolbar)} Permet
typiquement de ranger ensemble des boutons avec des icones, du texte,
et des bulles d'aide, dont l'aspeces
t global peut être changé facilement 
(barre de navigation netscape).

\noindent \textbf{Bloc-notes :} \emph{(notebook)} Collection de pages
superposées, avec des onglets pour les sélectionner (préférences
windows...).


}
\foilhead{\color{\titlecolor}Widget CList}

\noindent Liste multi-colonnes pouvant gérer des milliers de
colonnes, avec un titre éventuellement actif (à la netscape
Mail/News). Doit être placé dans une GtkScrolledWindow si on a besoin
de scrollbars.

{\tiny
\begin{verbatim}
GtkWidget *gtk_clist_new ( gint columns );
GtkWidget *gtk_clist_new_with_titles( gint   columns,
                                      gchar *titles[] );
/* comportement */
void gtk_clist_set_selection_mode( GtkCList         *clist,
                                   GtkSelectionMode  mode );
mode = GTK_SELECTION_[SINGLE|BROWSE|MULTIPLE*|EXTENDED]
/* aspect */
void gtk_clist_set_shadow_type( GtkCList      *clist,
                                GtkShadowType  border );
borde = GTK_SHADOW_[NONE|IN|OUT|ETCHED_IN|ETCHED_OUT]
\end{verbatim}
}

{\small \noindent Les titres peuvent être rendus actifs (comme des boutons) ou passifs
(comme des labels), être montrés ou cachés, et on peut changer le
titre ou mettre un widget quelconque dans un titre :}
{\tiny
\begin{verbatim}
void gtk_clist_column_title_[active|passive]( GtkCList *clist,
                                     gint     column );
void gtk_clist_column_titles_[active|passive]( GtkCList *clist );
void gtk_clist_column_titles_[show|hide]( GtkCList *clist );
void gtk_clist_set_column_title( GtkCList *clist,
                                 gint      column,
                                 gchar    *title );
void gtk_clist_set_column_widget( GtkCList  *clist,
                                  gint       column,
                                  GtkWidget *widget );
\end{verbatim}
}

{\small \noindent Réglages des paramètres de la clist :
gtk\_clist\_set\_column\_justification(), gtk\_clist\-\_set\_column\_width(),
gtk\_clist\_set\_row\_height(), gtk\_clist\_set\_foreground(),
gtk\-\_clist\_set\_background().

\noindent Tester si une ligne est visible entièrement ou partiellement :
gtk\_clist\_row\_is\_visible(). Aller à une ligne/colonne :
gtk\_clist\_moveto().}

{\tiny
\begin{verbatim}
/* Ajouter au début / à la fin / insérer une colonne */
gint gtk_clist_[prepend|append](GtkCList *clist, gchar*text[]);
void gtk_clist_insert(GtkCList *clist, gint row, gchar*text[]);

/* Enlever une/toutes les colonnes */
void gtk_clist_remove( GtkCList *clist, gint row );
void gtk_clist_clear( GtkCList *clist );

/* geler/réactiver les mises à jour de l'affichage */
void gtk_clist_freeze( GtkCList * clist );
void gtk_clist_thaw( GtkCList * clist );

/* mettre du texte/un pixmap/les deux dans une cellule */
void gtk_clist_set_text(clist,row,column, gchar *text);
void gtk_clist_set_pixmap(clist,row,column,
                          GdkPixmap *pixmap, GdkBitmap *mask );
void gtk_clist_set_pixtext(clist,row,column,gchar *text,
           guint8 spacing, GdkPixmap *pixmap, GdkBitmap *mask);

/* pour les récupérer, pareil avec _get_, et aussi... */
GtkCellType gtk_clist_get_cell_type(clist, row, column );

/* associer un pointeur vers des données à une ligne */
void gtk_clist_set_row_data(clist,row, gpointer data);
void gtk_clist_set_row_data_full(clist, row, data,
                           GtkDestroyNotify  destroy );
gpointer gtk_clist_get_row_data(clist, row);
gint gtk_clist_find_row_from_data(clist, data);

/* changer la sélection en cours  */
void gtk_clist_select_row(clist, row, column);
void gtk_clist_unselect_row(clist, row, column);
\end{verbatim}
}
{\small
Les signaux (en plus de ceux de GtkContainer) : select\_row, unselect\_row,
click\_column (voir appendice A du tutorial pour le prototype des
callbacks).
}

\foilhead{\color{\titlecolor}Widget arbre}
{\small
\begin{verbatim}
/* création d'un GtkTree*/
GtkWidget *gtk_tree_new( void );

/* création d'un GtkTreeItem */
GtkWidget *gtk_tree_item_new_with_label( gchar *label );

/* ajout à l'arbre (un par un... eh oui !) */
void gtk_tree_[append|prepend](tree, tree_item);
void gtk_tree_insert(tree, tree_item, gint position);

/* suppression : détruit aussi les sous-arbres ! */
/* sinon, utiliser gtk_container_remove() */
void gtk_tree_remove_items(tree, GList *items);
void gtk_tree_clear_items( tree, gint start, gint end);

/* ajout d'un sous-arbre sous un TreeItem */
void gtk_tree_item_set_subtree(tree_item, GtkWidget *subtree);

/* comportement vs. selection */
void gtk_tree_set_selection_mode(tree, GtkSelectionMode mode);
\end{verbatim}
}

{ \noindent Signaux de GtkTree : selection\_changed, select\_child,
unselect\_child.

\noindent Signaux de GtkTreeItem : ceux de GtkItem (select, deselect, toggle) +
expand, collapse
}
%\foilhead{\color{\titlecolor}Widgets sélection de fichiers}

\foilhead{\color{\titlecolor}Widget menu}

\noindent Deux manières de les créer : utiliser l'itemfactory (facile) ou à la
main (cf. Tutorial).

{\tiny
\begin{verbatim}
/* This is the GtkItemFactoryEntry structure used to generate new menus.
   Item 1: The menu path. The letter after the underscore indicates an
           accelerator key once the menu is open.
   Item 2: The accelerator key for the entry
   Item 3: The callback function.
   Item 4: The callback action.  This changes the parameters with
           which the function is called.  The default is 0.
   Item 5: The item type, used to define what kind of an item it is.
           Here are the possible values:

           NULL               -> "<Item>"
           ""                 -> "<Item>"
           "<Title>"          -> create a title item
           "<Item>"           -> create a simple item
           "<CheckItem>"      -> create a check item
           "<ToggleItem>"     -> create a toggle item
           "<RadioItem>"      -> create a radio item
           <path>             -> path of a radio item to link against
           "<Separator>"      -> create a separator
           "<Branch>"         -> create an item to hold sub items (optional)
           "<LastBranch>"     -> create a right justified branch 
*/

static GtkItemFactoryEntry menu_items[] = {
  { "/_File",         NULL,         NULL, 0, "<Branch>" },
  { "/File/_New",     "<control>N", print_hello, 0, NULL },
  { "/File/_Open",    "<control>O", print_hello, 0, NULL },
  { "/File/_Save",    "<control>S", print_hello, 0, NULL },
  { "/File/Save _As", NULL,         NULL, 0, NULL },
  { "/File/sep1",     NULL,         NULL, 0, "<Separator>" },
  { "/File/Quit",     "<control>Q", gtk_main_quit, 0, NULL },
  { "/_Options",      NULL,         NULL, 0, "<Branch>" },
  { "/Options/Test",  NULL,         NULL, 0, NULL },
  { "/_Help",         NULL,         NULL, 0, "<LastBranch>" },
  { "/_Help/About",   NULL,         NULL, 0, NULL },
};

void get_main_menu( GtkWidget  *window,
                    GtkWidget **menubar )
{
  GtkItemFactory *item_factory;
  GtkAccelGroup *accel_group;
  gint nmenu_items = sizeof (menu_items) / sizeof (menu_items[0]);

  accel_group = gtk_accel_group_new ();

  /* This function initializes the item factory.
     Param 1: The type of menu - can be GTK_TYPE_MENU_BAR, GTK_TYPE_MENU,
              or GTK_TYPE_OPTION_MENU.
     Param 2: The path of the menu.
     Param 3: A pointer to a gtk_accel_group.  The item factory sets up
              the accelerator table while generating menus.
  */

  item_factory = gtk_item_factory_new (GTK_TYPE_MENU_BAR, "<main>", 
                                       accel_group);

  /* This function generates the menu items. Pass the item factory,
     the number of items in the array, the array itself, and any
     callback data for the the menu items. */
  gtk_item_factory_create_items (item_factory, nmenu_items, menu_items, NULL);

  /* Attach the new accelerator group to the window. */
  gtk_accel_group_attach (accel_group, GTK_OBJECT (window));

  if (menubar)
    /* Finally, return the actual menu bar created by the item factory. */ 
    *menubar = gtk_item_factory_get_widget (item_factory, "<main>");
}
\end{verbatim}
}


\foilhead{\color{\titlecolor}Widget Text}

{\small
\begin{verbatim}
GtkWidget *gtk_text_new( GtkAdjustment *hadj,
                         GtkAdjustment *vadj );

void gtk_text_set_adjustments(text, hadj, vadj);

/* widget text avec scrollbar : */
  vscrollbar = gtk_vscrollbar_new (GTK_TEXT(text)->vadj);
  gtk_box_pack_start(GTK_BOX(hbox), vscrollbar, FALSE, FALSE, 0);
  gtk_widget_show (vscrollbar);

/* texte éditable ou affichage seulement ? */
void gtk_text_set_editable( GtkText *text,
                            gboolean editable );

/* retour à la ligne auto */
void gtk_text_set_word_wrap( GtkText *text,
                             gint     word_wrap );

/* position... */
void gtk_text_set_point( GtkText *text, guint index );
guint gtk_text_get_point( GtkText *text );
guint gtk_text_get_length( GtkText *text );

/* insertion... */
void gtk_text_insert( GtkText    *text,
                      GdkFont    *font, /* ou NULL */
                      GdkColor   *fore, /* ou NULL */
                      GdkColor   *back, /* ou NULL */
                      const char *chars,
                      gint        length );

/* eviter la mise à jour automatique */
void gtk_text_freeze( GtkText *text );
void gtk_text_thaw( GtkText *text );         

/* delete : retourne TRUE ou FALSE */
gint gtk_text_[backward|forward]_delete(text, nchars);

/* retourne caractère à la position index dans widget t */
GTK_TEXT_INDEX(t, index)

/* retourne une chaine de caracteres extraite. faire g_free() */
gchar *gtk_editable_get_chars( GtkEditable *editable,
                               start_pos, end_pos [-1 = EOT] );
\end{verbatim}

\noindent \textbf{raccourcis clavier :}
\begin{itemize}
\item  Ctrl-A   Beginning of line
\item  Ctrl-E   End of line
\item  Ctrl-N   Next Line
\item  Ctrl-P   Previous Line
\item  Ctrl-B   Backward one character
\item  Ctrl-F   Forward one character
\item  Alt-B    Backward one word
\item  Alt-F    Forward one word
\item  Ctrl-H   Delete Backward Character (Backspace)
\item  Ctrl-D   Delete Forward Character (Delete)
\item  Ctrl-W   Delete Backward Word
\item  Alt-D    Delete Forward Word
\item  Ctrl-K   Delete to end of line
\item  Ctrl-U   Delete line
\item  Ctrl-X   Cut to clipboard
\item  Ctrl-C   Copy to clipboard
\item  Ctrl-V   Paste from clipboard
\end{itemize}
}

\foilhead{\color{\titlecolor}Configuration des attributs de widget}

\foilhead{\color{\titlecolor}Temporisations, fonctions d'E/S et d'attente}

\foilhead{\color{\titlecolor}Gestion des sélections}

\foilhead{\color{\titlecolor}Fichiers rc de GTK}

\foilhead{\color{\titlecolor}Écriture de vos propres widgets}

\foilhead{\color{\titlecolor}Scribble, un programme simple de dessin}

\foilhead{\color{\titlecolor}Conseils pour l'écriture d'applications GTK}

\end{document}
%%% Local Variables: 
%%% mode: latex
%%% TeX-master: t
%%% End: 
