mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-12-29 06:51:10 +00:00
8601 lines
277 KiB
Plaintext
8601 lines
277 KiB
Plaintext
|
<!doctype linuxdoc system>
|
|||
|
<article>
|
|||
|
<title>Didacticiel
|
|||
|
<author>Ian Main, <tt><htmlurl url="mailto:slow@intergate.bc.ca"
|
|||
|
name="slow@intergate.bc.ca"></tt>
|
|||
|
<date>January 24, 1998.
|
|||
|
|
|||
|
|
|||
|
<sect>Introduction
|
|||
|
<p>
|
|||
|
GTK (GIMP Toolkit) a <20>t<EFBFBD> d'abord d<>velopp<70> pour <20>tre une bo<62>te <20>
|
|||
|
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 <20> GIMP toolkit <20> car il
|
|||
|
fut cr<63><72> pour d<>velopper GIMP, mais il est d<>sormais utilis<69> dans
|
|||
|
plusieurs projets de logiciels libres. Les auteurs sont :
|
|||
|
<itemize>
|
|||
|
<item> Peter Mattis <tt><htmlurl url="mailto:petm@xcf.berkeley.edu"
|
|||
|
name="petm@xcf.berkeley.edu"></tt>
|
|||
|
<item> Spencer Kimball <tt><htmlurl url="mailto:spencer@xcf.berkeley.edu"
|
|||
|
name="spencer@xcf.berkeley.edu"></tt>
|
|||
|
<item> Josh MacDonald <tt><htmlurl url="mailto:jmacd@xcf.berkeley.edu"
|
|||
|
name="jmacd@xcf.berkeley.edu"></tt>
|
|||
|
</itemize>
|
|||
|
|
|||
|
<p>
|
|||
|
GTK est essentiellement une interface de programmation (API) orient<6E>e
|
|||
|
objet. Bien qu'il soit enti<74>rement <20>crit en C, il est implant<6E> en
|
|||
|
utilisant la notion de classes et de fonctions de rappel (pointeurs
|
|||
|
de fonctions).
|
|||
|
<p>
|
|||
|
Un troisi<73>me composant, appel<65> glib, remplace certains appels
|
|||
|
standard et comporte quelques fonctions suppl<70>mentaires pour g<>rer les
|
|||
|
listes cha<68>n<EFBFBD>es, etc. Les fonctions de remplacement sont utilis<69>es
|
|||
|
pour accro<72>tre la portabilit<69> de GTK car certaines de ces fonctions,
|
|||
|
comme g_strerror(), ne sont pas disponibles ou ne sont pas standard
|
|||
|
sur d'autres Unix. D'autres comportent des am<61>liorations par rapport
|
|||
|
aux versions de la libc : g_malloc(), par exemple, facilite le
|
|||
|
d<EFBFBD>buggage.<p>
|
|||
|
|
|||
|
Ce didacticiel tente de d<>crire du mieux possible GTK, mais il n'est pas
|
|||
|
exhaustif. Il suppose une bonne connaissance du langage C, et de la fa<66>on de
|
|||
|
cr<EFBFBD>er des programmes C. Il serait tr<74>s pr<70>cieux au lecteur d'avoir d<>j<EFBFBD> une
|
|||
|
exp<EFBFBD>rience de la programmation X, mais cela n'est pas n<>cessaire. Si
|
|||
|
l'apprentissage de GTK marque vos d<>buts dans l'approche des widgets, n'h<>sitez
|
|||
|
pas <20> faire des commentaires sur ce didacticiel et sur les probl<62>mes qu'il vous
|
|||
|
a pos<6F>. Il y a aussi une API C++ pour GTK (GTK--), si vous pr<70>f<EFBFBD>rez utiliser
|
|||
|
ce langage, consultez plut<75>t la documentation qui la concerne. Une
|
|||
|
encapsulation en Objective C et des liaisons Guile sont <20>galement disponibles,
|
|||
|
mais ne seront pas abord<72>es ici.
|
|||
|
<p>
|
|||
|
J'appr<70>cierais beaucoup avoir un <20>cho des probl<62>mes que vous avez
|
|||
|
rencontr<EFBFBD> pour apprendre GTK <20> partir de ce document. De plus, toute
|
|||
|
suggestion sur son am<61>lioration est la bienvenue.
|
|||
|
|
|||
|
<sect>Bien d<>buter
|
|||
|
<p>
|
|||
|
La premi<6D>re chose <20> faire est, bien s<>r, de r<>cup<75>rer les sources de
|
|||
|
GTK et de les installer. Vous pouvez en obtenir la derni<6E>re version
|
|||
|
sur <tt/ftp.gimp.org/ dans le r<>pertoire <tt>/pub/gtk</tt>. D'autres
|
|||
|
sources d'informations se trouvent sur
|
|||
|
<tt>http://www.gimp.org/gtk</tt>. GTK utilise <em/autoconf/ de GNU
|
|||
|
pour se configurer. Lorsque vous l'aurez d<>tarr<72>, tapez
|
|||
|
<em>./configure --help</em> pour consulter la liste des options.
|
|||
|
<p>
|
|||
|
Pour commencer notre introduction <20> GTK, nous d<>buterons avec le
|
|||
|
programme le plus simple qui soit. Celui-ci cr<63>era une fen<65>tre de
|
|||
|
200x200 pixels et ne pourra se terminer qu'en le tuant <20> partir du
|
|||
|
shell.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
#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;
|
|||
|
}
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Tous les programmes inclueront <20>videmment le fichier
|
|||
|
<tt>gtk/gtk.h</tt> qui d<>clare les variables, fonctions, structures,
|
|||
|
etc. qui seront utilis<69>es par votre application GTK.
|
|||
|
<p>
|
|||
|
La ligne :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
gtk_init (&argc, &argv);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
appelle la fonction <em/gtk_init(gint *argc, gchar ***argv)/ qui sera
|
|||
|
appel<EFBFBD>e dans toutes les applications GTK. Cette fonction configure
|
|||
|
certaines choses pour nous, comme l'aspect visuel et les couleurs par
|
|||
|
d<EFBFBD>faut, puis appelle <em/gdk_init(gint *argc, gchar ***argv)/. Cette
|
|||
|
derni<EFBFBD>re initialise la biblioth<74>que pour qu'elle puisse <20>tre utilis<69>e,
|
|||
|
configure les gestionnaires de signaux par d<>faut et v<>rifie les
|
|||
|
param<EFBFBD>tres pass<73>s <20> notre application via la ligne de commande en
|
|||
|
recherchant l'un des suivants :
|
|||
|
|
|||
|
<itemize>
|
|||
|
<item> <tt/--display/
|
|||
|
<item> <tt/--debug-level/
|
|||
|
<item> <tt/--no-xshm/
|
|||
|
<item> <tt/--sync/
|
|||
|
<item> <tt/--show-events/
|
|||
|
<item> <tt/--no-show-events/
|
|||
|
</itemize>
|
|||
|
<p>
|
|||
|
Elle les supprime alors de la liste des param<61>tres, en laissant tout
|
|||
|
ce qu'elle ne reconna<6E>t pas pour que notre application l'analyse ou
|
|||
|
l'ignore. Ceci cr<63>e un ensemble de param<61>tres standards accept<70>s par
|
|||
|
toutes les applications GTK.
|
|||
|
<p>
|
|||
|
Les deux lignes de code suivantes cr<63>ent et affichent une fen<65>tre.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|||
|
gtk_widget_show (window);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Le param<61>tre <tt/GTK_WINDOW_TOPLEVEL/ pr<70>cise que l'on veut que la
|
|||
|
fen<EFBFBD>tre cr<63><72>e suive l'aspect et le placement d<>finis par le
|
|||
|
gestionnaire de fen<65>tres. Plut<75>t que de cr<63>er une fen<65>tre de 0x0, une
|
|||
|
fen<EFBFBD>tre sans fen<65>tre fille est de 200x200 par d<>faut : on peut
|
|||
|
ainsi la manipuler facilement.
|
|||
|
<p>
|
|||
|
La fonction <em/gtk_widget_show()/ informe GTK que l'on a configur<75>
|
|||
|
le widget et qu'il peut l'afficher.
|
|||
|
<p>
|
|||
|
La ligne suivante lance la boucle principale de traitement de GTK.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
gtk_main ();
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
<em/gtk_main()/ est un autre appel que vous verrez dans toute
|
|||
|
application GTK. Lorsque le contr<74>le atteind ce point, GTK se met en
|
|||
|
attente d'<27>v<EFBFBD>nements X (click sur un bouton, ou appui d'une touche, par
|
|||
|
exemple), de timeouts ou d'entr<74>es-sorties fichier. Dans notre exemple
|
|||
|
simple, cependant, les <20>v<EFBFBD>nements sont ignor<6F>s.
|
|||
|
|
|||
|
|
|||
|
<sect1><3E> Bonjour tout le monde <20> en GTK
|
|||
|
<p>
|
|||
|
OK, <20>crivons un programme avec un widget (bouton). C'est le classique <20> Bonjour tout le monde <20> <20> la sauce GTK.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
|
|||
|
#include <gtk/gtk.h>
|
|||
|
|
|||
|
/* fonction de rappel. Dans cet exemple, les param<61>tres sont ignor<6F>s...
|
|||
|
* Les fonctions de rappel sont d<>taill<6C>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 <20>mettra le signal "destroy". Retourner FALSE signifie que l'on
|
|||
|
* ne veut pas que la fen<65>tre soit d<>truite.
|
|||
|
* Utilis<69> pour faire appara<72>tre des bo<62>tes de dialogue du type
|
|||
|
* <20> <20>tes-vous s<>r de vouloir quitter ? <20> */
|
|||
|
|
|||
|
/* Remplacez FALSE par TRUE et la fen<65>tre principale sera d<>truite par
|
|||
|
* un signal <20> delete_event <20>. */
|
|||
|
|
|||
|
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<65>e dans toutes les applications GTK.
|
|||
|
* Les param<61>tres pass<73>s en ligne de commande sont analys<79>s et
|
|||
|
* retourn<72>s <20> l'application. */
|
|||
|
|
|||
|
gtk_init (&argc, &argv);
|
|||
|
|
|||
|
/* Cr<43>ation d'une nouvelle fen<65>tre. */
|
|||
|
|
|||
|
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|||
|
|
|||
|
/* Lorsque la fen<65>tre re<72>oit le signal "delete_event"
|
|||
|
* (envoy<6F> par le gestionnaire de fen<65>tres en utilisant l'option
|
|||
|
* <20> close <20> ou la barre de titre), on lui demande d'appeler la
|
|||
|
* fonction delete_event() d<>finie plus haut. La donn<6E>e pass<73>e en
|
|||
|
* param<61>tre <20> la fonction de rappel est NULL et est ignor<6F> dans le
|
|||
|
* rappel. */
|
|||
|
|
|||
|
gtk_signal_connect (GTK_OBJECT (window), "delete_event",
|
|||
|
GTK_SIGNAL_FUNC (delete_event), NULL);
|
|||
|
|
|||
|
/* Ici, on connecte l'<27>venement "destroy" <20> un gestionnaire de signal.
|
|||
|
* Cet <20>v<EFBFBD>nement arrive lorsqu'on appelle gtk_widget_destroy() sur la
|
|||
|
* fen<65>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<65>tre. */
|
|||
|
|
|||
|
gtk_container_border_width (GTK_CONTAINER (window), 10);
|
|||
|
|
|||
|
/* Cr<43>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<61>tre. */
|
|||
|
|
|||
|
gtk_signal_connect (GTK_OBJECT (button), "clicked",
|
|||
|
GTK_SIGNAL_FUNC (hello), NULL);
|
|||
|
|
|||
|
/* Ceci provoquera la destruction de la fen<65>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<65>tres. */
|
|||
|
|
|||
|
gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
|
|||
|
GTK_SIGNAL_FUNC (gtk_widget_destroy),
|
|||
|
GTK_OBJECT (window));
|
|||
|
|
|||
|
/* Insertion du bouton dans la fen<65>tre (container gtk). */
|
|||
|
|
|||
|
gtk_container_add (GTK_CONTAINER (window), button);
|
|||
|
|
|||
|
/* L'<27>tape finale consiste <20> afficher ce nouveau widget... */
|
|||
|
|
|||
|
gtk_widget_show (button);
|
|||
|
|
|||
|
/* ... et la fen<65>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
|
|||
|
* <20>v<EFBFBD>nement survienne (touche press<73>e ou <20>v<EFBFBD>nement souris). */
|
|||
|
|
|||
|
gtk_main ();
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
<sect1>Compilation de <20> Bonjour tout le monde <20>
|
|||
|
<p>
|
|||
|
Supposons que vous avez sauvegard<72> le code pr<70>c<EFBFBD>dent dans un fichier
|
|||
|
nomm<EFBFBD> <em/bonjour.c/, pour le compiler tapez la commande
|
|||
|
suivante :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
gcc -Wall -g bonjour.c -o bonjour_monde -L/usr/X11R6/lib \
|
|||
|
-lgtk -lgdk -lglib -lXext -lX11 -lm
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
Les biblioth<74>ques invoqu<71>es ci-dessus doivent toutes <20>tre dans vos
|
|||
|
chemins de recherche par d<>faut, sinon, ajoutez <tt/-L<library
|
|||
|
directory>/ pour que <em/gcc/ recherche dans ces r<>pertoires les
|
|||
|
biblioth<EFBFBD>ques n<>cessaires. Sur mon syst<73>me Debian GNU/Linux, par exemple,
|
|||
|
je dois ajouter <tt>-L/usr/X11R6/lib</> pour qu'il trouve les
|
|||
|
biblioth<EFBFBD>ques X11 (NdT : et c'est pareil sur mon syst<73>me Red Hat
|
|||
|
Linux...).
|
|||
|
<p>
|
|||
|
L'ordre des biblioth<74>ques est important. L'<27>diteur de liens doit
|
|||
|
conna<EFBFBD>tre les fonctions d'une biblioth<74>que dont il a besoin avant de
|
|||
|
les traiter.
|
|||
|
<p>
|
|||
|
Si vous compilez en utilisant des biblioth<74>ques statiques, l'ordre
|
|||
|
dans lequel vous listez les biblioth<74>ques devient tr<74>s
|
|||
|
important. L'exemple donn<6E> ci-dessus devrait fonctionner dans tous les
|
|||
|
cas.
|
|||
|
<p>
|
|||
|
Les biblioth<74>ques que l'on utilise sont :
|
|||
|
<itemize>
|
|||
|
<item>La biblioth<74>que glib (<tt/-lglib/), qui contient diverses
|
|||
|
fonctions. Seule <em/g_print()/ est utilis<69>e dans cet exemple. GTK est
|
|||
|
construit au dessus de <em/glib/ et vous aurez donc toujours besoin de
|
|||
|
celle-ci. Voir la section concernant <ref id="sec_glib" name="glib">
|
|||
|
pour plus de d<>tails.
|
|||
|
|
|||
|
<item>La biblioth<74>que GDK (<tt/-lgdk/), l'enveloppe de Xlib.
|
|||
|
|
|||
|
<item>La biblioth<74>que GTK (<tt/-lgtk/), la biblioth<74>que des widgets,
|
|||
|
construite au dessus de GDK.
|
|||
|
|
|||
|
<item>La biblioth<74>que Xlib (<tt/-lX11/ utilis<69>e par GDK.
|
|||
|
|
|||
|
<item>La biblioth<74>que Xext (<tt/-lXext/). Cette derni<6E>re contient le
|
|||
|
code pour les pixmaps en m<>moire partag<61>e et les autres extensions X.
|
|||
|
|
|||
|
<item>La biblioth<74>que math<74>matique (<tt/-lm/). Elle est utilis<69>e pour
|
|||
|
diff<EFBFBD>rentes raisons par GTK.
|
|||
|
</itemize>
|
|||
|
|
|||
|
<sect1>Th<54>orie des signaux et des rappels
|
|||
|
<p>
|
|||
|
Avant de voir en d<>tail le programme <20> Bonjour tout le monde <20>, nous
|
|||
|
parlerons d'abord des <20>v<EFBFBD>nements et des fonctions de rappel. GTK est
|
|||
|
dirig<EFBFBD> par les <20>v<EFBFBD>nements, ce qui signifie qu'il restera inactif dans
|
|||
|
<em/gtk_main/ jusqu'<27> ce qu'un <20>v<EFBFBD>nement survienne et que le contr<74>le
|
|||
|
soit pass<73> <20> la fonction appropri<72>e.
|
|||
|
<p>
|
|||
|
Ce passage du contr<74>le est r<>alis<69> en utilisant le concept de <20> signal
|
|||
|
<EFBFBD>. Lorsqu'un <20>v<EFBFBD>nement survient, comme l'appui sur un bouton, le
|
|||
|
signal appropri<72> sera <20> <20>mis <20> par le widget qui a <20>t<EFBFBD> press<73>. C'est
|
|||
|
de cette fa<66>on que GTK r<>alise la plupart de son travail. Pour qu'un
|
|||
|
bouton r<>alise une action, on configure un gestionnaire de signal pour
|
|||
|
capturer ces signaux et appeler la fonction ad<61>quate. Ceci est fait
|
|||
|
en utilisant une fonction comme :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
gint gtk_signal_connect (GtkObject *object,
|
|||
|
gchar *name,
|
|||
|
GtkSignalFunc func,
|
|||
|
gpointer func_data);
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
O<EFBFBD> le premier param<61>tre est le widget qui <20>mettra le signal, et le
|
|||
|
deuxi<EFBFBD>me est le nom du signal que l'on souhaite intercepter. Le
|
|||
|
troisi<EFBFBD>me param<61>tre est la fonction que l'on veut appeler quand le
|
|||
|
signal est captur<75>, et le quatri<72>me sont les donn<6E>es que l'on souhaite
|
|||
|
passer <20> cette fonction.
|
|||
|
<p>
|
|||
|
La fonction sp<73>cifi<66>e par le troisi<73>me param<61>tre s'appelle une <20>
|
|||
|
fonction de rappel <20> et doit <20>tre de la forme :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void callback_func(GtkWidget *widget, gpointer *callback_data);
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
O<EFBFBD> le premier param<61>tre sera un pointeur vers le widget qui a <20>mis le
|
|||
|
signal, et le second un pointeur vers les donn<6E>es pass<73>es par le
|
|||
|
dernier param<61>tre de la fonction <em/gtk_signal_connect()/ d<>crite
|
|||
|
plus haut.
|
|||
|
<p>
|
|||
|
Un autre appel utilis<69> dans l'exemple <20> Bonjour tout le monde <20> est :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
gint gtk_signal_connect_object (GtkObject *object,
|
|||
|
gchar *name,
|
|||
|
GtkSignalFunc func,
|
|||
|
GtkObject *slot_object);
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
<em/gtk_signal_connect_object()/ est la m<>me chose que
|
|||
|
<em/gtk_signal_connect()/ sauf que la fonction de rappel utilise un
|
|||
|
seul param<61>tre : un pointeur vers un objet GTK. Lorsqu'on utilise
|
|||
|
cette fonction pour connecter des signaux, le rappel doit <20>tre de
|
|||
|
cette forme :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void callback_func (GtkObject *object);
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
O<EFBFBD> l'objet est d'ordinaire un widget. En g<>n<EFBFBD>ral, on ne configure pas
|
|||
|
de rappels pour <em/gtk_signal_connect_object/. D'habitude, ceux-ci sont
|
|||
|
utilis<EFBFBD>s pour appeler une fonction GTK acceptant un simple widget ou
|
|||
|
objet comme param<61>tre, 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 diff<66>rent de param<61>tres. De nombreuses fonctions de la
|
|||
|
biblioth<EFBFBD>que GTK n'acceptent qu'un simple pointeur vers un
|
|||
|
<em/GtkWidget/ comme param<61>tre et vous pouvez donc utiliser
|
|||
|
<em/gtk_signal_connect_object()/ pour celles-ci, tandis que pour vos
|
|||
|
fonctions vous pouvez avoir besoin d'avoir de fournir plus de donn<6E>es
|
|||
|
aux fonctions de rappel.
|
|||
|
|
|||
|
<sect1><3E> Bonjour tout le monde <20> pas <20> pas
|
|||
|
<p>
|
|||
|
Maintenant que nous connaissons la th<74>orie, clarifions un peu en progressant <20> travers le programme <20> Bonjour tout le monde <20>.
|
|||
|
<p>
|
|||
|
Voici la fonction de rappel appel<65>e lorsque le bouton est <20> clicked
|
|||
|
<EFBFBD>. Dans notre exemple, on ignore le widget et les donn<6E>es mais il
|
|||
|
n'est pas difficile de faire quelque chose avec. Le prochain exemple
|
|||
|
utilisera le param<61>tre des donn<6E>es pour nous dire quel bouton a <20>t<EFBFBD>
|
|||
|
press<EFBFBD>.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void hello (GtkWidget *widget, gpointer *data)
|
|||
|
{
|
|||
|
g_print ("Bonjour tout le monde\n");
|
|||
|
}
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
<p>
|
|||
|
Cette fonction de rappel est un peu sp<73>ciale. L'<27>v<EFBFBD>nement
|
|||
|
"delete_event" survient lorsque le gestionnaire de fen<65>tres l'envoie <20>
|
|||
|
l'application. On doit choisir ce qu'il faut faire de ces
|
|||
|
<EFBFBD>v<EFBFBD>nements. On peut les ignorer, leur donner une r<>ponse, ou
|
|||
|
simplement quitter l'application.
|
|||
|
|
|||
|
La valeur que l'on retourne dans cette fonction de rappel permet <20> GTK
|
|||
|
de savoir ce qu'il a <20> faire. En retournant FALSE, on l'informe que
|
|||
|
l'on ne veut pas que le signal "destroy" soit <20>mis, afin de laisser
|
|||
|
notre application tourner. En retournant TRUE, on lui demande
|
|||
|
d'<27>mettre "destroy" qui appellera <20> son tour notre gestionnaire du
|
|||
|
signal "destroy".
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
gint delete_event(GtkWidget *widget, GdkEvent *event, gpointer data)
|
|||
|
{
|
|||
|
g_print ("le signal delete_event est survenu.\n");
|
|||
|
|
|||
|
return (FALSE);
|
|||
|
}
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
<p>
|
|||
|
Voici une autre fonction de rappel qui ne fait que quitter
|
|||
|
l'application en appelant <em/gtk_main_quit()/. Il n'y a pas grand
|
|||
|
chose de plus <20> dire car elle est plut<75>t triviale :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void destroy (GtkWidget *widget, gpointer *data)
|
|||
|
{
|
|||
|
gtk_main_quit ();
|
|||
|
}
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
Je suppose que vous connaissez la fonction <em/main()/... oui, comme
|
|||
|
les autres programmes C, toutes les applications GTK en ont une.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
int main (int argc, char *argv[])
|
|||
|
{
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
La partie qui suit d<>clare deux pointeurs sur des structures de type
|
|||
|
<em/GtkWidget/. Ceux-ci sont utilis<69>s plus loin pour cr<63>er une
|
|||
|
fen<EFBFBD>tre et un bouton.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
GtkWidget *window;
|
|||
|
GtkWidget *button;
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
Et revoici notre <em/gtk_init/. Comme pr<70>c<EFBFBD>demment, il initialise le toolkit
|
|||
|
et analyse les param<61>tres de la ligne de commande. Il supprime chaque
|
|||
|
param<EFBFBD>tre reconnu de la liste et modifie <em/argc/ et <em/argv/ pour
|
|||
|
faire comme si ces param<61>tres n'avaient jamais exist<73>, laissant notre
|
|||
|
application analyser les param<61>tres restants.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
gtk_init (&argc, &argv);
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
Cr<EFBFBD>ation d'une nouvelle fen<65>tre. C'est plut<75>t classique. La m<>moire
|
|||
|
est allou<6F>e pour une structure <em/GtkWidget/ et <em/window/ pointe
|
|||
|
donc sur celle-ci. Cela configure une nouvelle fen<65>tre, mais celle-ci
|
|||
|
ne sera pas affich<63>e tant que l'on n'a pas appel<65>
|
|||
|
<em/gtk_widget_show(window)/ vers la fin de notre programme.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
Voici maintenant un exemple de connexion d'un gestionnaire de signal <20>
|
|||
|
un objet : la fen<65>tre. Le signal "destroy" est captur<75>. Il est
|
|||
|
<EFBFBD>mis lorsqu'on utilise le gestionnaire de fen<65>tres pour tuer la
|
|||
|
fen<EFBFBD>tre (et que l'on retourne TRUE dans le gestionnaire
|
|||
|
"delete_event"), ou lorsqu'on utilise l'appel
|
|||
|
<em/gtk_widget_destroy()/ en lui passant le widget <em/window/ comme
|
|||
|
objet <20> d<>truire. Ici, on appelle juste la fonction <em/destroy()/
|
|||
|
d<EFBFBD>finie ci-dessus avec le param<61>tre NULL, ce qui quitte GTK pour
|
|||
|
nous.
|
|||
|
<p>
|
|||
|
<tt/GTK_OBJECT/ et <tt/GTK_SIGNAL_FUNC/ sont des macros qui r<>alisent
|
|||
|
les conversions et les v<>rifications de types pour nous. Elles rendent
|
|||
|
aussi le code plus lisible.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
gtk_signal_connect (GTK_OBJECT (window), "destroy",
|
|||
|
GTK_SIGNAL_FUNC (destroy), NULL);
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
La fonction suivante sert <20> configurer un attribut d'un objet
|
|||
|
container. Elle configure simplement la fen<65>tre pour qu'elle ait une
|
|||
|
zone vide autour d'elle de 10 pixels de large o<> aucun widget ne
|
|||
|
pourra se trouver. Il existe d'autres fonctions similaires que nous
|
|||
|
verrons dans la section sur la
|
|||
|
<ref id="sec_setting_widget_attributes"
|
|||
|
name="Configuration des attributs des widgets">
|
|||
|
<p>
|
|||
|
<EFBFBD> nouveau, <tt/GTK_CONTAINER/ est une macro r<>alisant la conversion de type.
|
|||
|
<tscreen><verb>
|
|||
|
gtk_container_border_width (GTK_CONTAINER (window), 10);
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
Cet appel cr<63>e un nouveau bouton. Il alloue l'espace m<>moire pour une
|
|||
|
nouvelle structure GtkWidget, l'initialise et fait pointer <em/button/
|
|||
|
vers elle. Ce bouton portera le label <20> Bonjour tout le monde <20>
|
|||
|
lorsqu'il sera affich<63>.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
button = gtk_button_new_with_label ("Bonjour tout le monde");
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
Maintenant, prenons ce bouton et faisons lui faire quelque chose
|
|||
|
d'utile. On lui attache un gestionnaire de signal pour que, lorsqu'il
|
|||
|
<EFBFBD>mettra le signal "clicked", notre fonction <em/hello()/ soit
|
|||
|
appel<EFBFBD>e. On ignore les param<61>tres et on ne passe donc que la valeur
|
|||
|
NULL <20> la fonction de rappel <em/hello()/. <20>videmment, le signal
|
|||
|
"clicked" est <20>mis lorsqu'on clique sur le bouton avec la souris.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
gtk_signal_connect (GTK_OBJECT (button), "clicked",
|
|||
|
GTK_SIGNAL_FUNC (hello), NULL);
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
On utilisera aussi ce bouton pour quitter notre programme, ce qui
|
|||
|
permettra d'illustrer la fa<66>on dont le signal "destroy" peut venir
|
|||
|
soit du gestionnaire de fen<65>tres, soit de notre programme. Quand le
|
|||
|
bouton est "clicked" comme cela est d<>crit plus haut, il appelle
|
|||
|
d'abord la fonction de rappel <em/hello()/ puis celle-ci dans l'ordre
|
|||
|
dans lequel elles sont configur<75>es. On peut avoir autant de fonctions
|
|||
|
de rappel que l'on d<>sire, elles seront ex<65>cut<75>es selon leur ordre de
|
|||
|
connexion. Puisque la fonction <em/gtk_widget_destroy()/ n'accepte que
|
|||
|
<em/GtkWidget *widget/ comme param<61>tre, on utilise ici la fonction
|
|||
|
<em/gtk_signal_connect_object()/ <20> la place de
|
|||
|
<em/gtk_signal_connect()/.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
|
|||
|
GTK_SIGNAL_FUNC (gtk_widget_destroy),
|
|||
|
GTK_OBJECT (window));
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
Voici un appel de placement, qui sera expliqu<71> en d<>tail plus tard,
|
|||
|
mais qui est plut<75>t facile <20> comprendre. Il indique simplement <20> GTK
|
|||
|
que le bouton doit <20>tre plac<61> dans la fen<65>tre o<> il s'affichera.
|
|||
|
|
|||
|
<tscreen><verb> gtk_container_add (GTK_CONTAINER (window), button);
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
Maintenant, nous avons tout configur<75> comme on le souhaitait :
|
|||
|
les gestionnaires de signaux sont en place et le bouton est mis dans
|
|||
|
la fen<65>tre o<> il doit se trouver. On demande alors <20> GTK de <20> montrer
|
|||
|
<EFBFBD> les widgets <20> l'<27>cran. Le widget <em/window/ est affich<63> en dernier
|
|||
|
afin que la fen<65>tre enti<74>re surgisse d'un coup plut<75>t que voir d'abord
|
|||
|
la fen<65>tre s'afficher puis ensuite le bouton appara<72>tre <20>
|
|||
|
l'int<6E>rieur. Il faut dire qu'avec des exemples simples comme celui-ci,
|
|||
|
vous ne ferez pas la diff<66>rence.
|
|||
|
<tscreen><verb>
|
|||
|
gtk_widget_show(button);
|
|||
|
|
|||
|
gtk_widget_show (window);
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
Bien s<>r, on appelle <em/gtk_main()/ qui attendra les <20>v<EFBFBD>nements
|
|||
|
venant du serveur X et demandera aux widgets d'<27>mettre les signaux
|
|||
|
lorsque ces <20>v<EFBFBD>nements surviendront.
|
|||
|
<tscreen><verb>
|
|||
|
gtk_main ();
|
|||
|
</verb></tscreen>
|
|||
|
Enfin, le <em/return/ final. Il est ex<65>cut<75> lorsque <em/gtk_quit()/ est appel<65>.
|
|||
|
<tscreen><verb>
|
|||
|
return 0;
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
Lorsque l'on clique sur un bouton GTK, le widget <20>met un
|
|||
|
signal "clicked". Afin de pouvoir utiliser cette information, notre
|
|||
|
programme configure un gestionnaire pour capturer ce signal. Ce
|
|||
|
gestionnaire appelle la fonction de notre choix. Dans notre exemple,
|
|||
|
lorsque le bouton que l'on a cr<63><72> est "clicked", la fonction
|
|||
|
<em/hello()/ est appel<65>e avec le param<61>tre NULL, puis le gestionnaire
|
|||
|
suivant de ce signal est <20> son tour appel<65>. Il appelle la fonction
|
|||
|
<em/gtk_widget_destroy()/ en lui passant le widget <em/window/ comme
|
|||
|
param<EFBFBD>tre, ce qui provoque la destruction de celui-ci. Ceci
|
|||
|
force la fen<65>tre <20> envoyer un signal "destroy", qui est captur<75> <20> son
|
|||
|
tour et appelle notre fonction de rappel <em/destroy()/ qui ferme
|
|||
|
simplement GTK.
|
|||
|
<p>
|
|||
|
Une autre fa<66>on de proc<6F>der consiste <20> utiliser le gestionnaire de
|
|||
|
fen<EFBFBD>tres pour d<>truire la fen<65>tre. Cela provoquera l'<27>mission du
|
|||
|
signal "delete_event" qui sera pris en charge par notre gestionnaire
|
|||
|
<em/delete_event()/. S'il retourne FALSE, la fen<65>tre restera telle
|
|||
|
quelle et rien ne se passera. Retourner TRUE forcera GTK <20> <20>mettre
|
|||
|
le signal "destroy" qui, bien s<>r, appelera la fonction de rappel
|
|||
|
<em/destroy()/ provoquant la sortie du GTK. <p>
|
|||
|
On remarquera que ces signaux ne sont pas les m<>mes que les signaux
|
|||
|
syst<EFBFBD>mes Unix et ne sont pas implant<6E>s en utilisant ceux-ci, bien que
|
|||
|
la terminologie employ<6F>e soit presque identique.
|
|||
|
|
|||
|
|
|||
|
<sect>Continuons
|
|||
|
<p>
|
|||
|
<sect1>Types de donn<6E>es
|
|||
|
<p>
|
|||
|
Vous avez probablement not<6F> certaines choses qui n<>cessitent des
|
|||
|
explications dans les exemples pr<70>c<EFBFBD>dents. les <em/gint/, <em/gchar/,
|
|||
|
etc. que vous avez pu voir sont des red<65>finitions de <em/int/ et
|
|||
|
<em/char/, respectivement. Leur raison d'<27>tre est de s'affranchir des
|
|||
|
d<EFBFBD>pendances ennuyeuses concernant la taille des types de donn<6E>es
|
|||
|
simples lorsqu'on r<>alise des calculs. Un bon exemple est <em/gint32/
|
|||
|
qui d<>signera un entier cod<6F> sur 32 bits pour toutes les plateformes,
|
|||
|
que ce soit une station Alpha 64 bits ou un PC i386 32 bits. Les
|
|||
|
red<EFBFBD>finitions de type sont tr<74>s simples et intuitives. Elles sont
|
|||
|
toutes d<>crites dans le fichier <em>glib/glib.h</em> (qui est inclus
|
|||
|
par <em/gtk.h/).
|
|||
|
<p>
|
|||
|
On notera aussi la possibilit<69> d'utiliser un <em/GtkWidget/ lorsque la
|
|||
|
fonction attend un <em/GtkObject/. GTK poss<73>de une architecture
|
|||
|
orient<EFBFBD>e objet, et un widget est un objet.
|
|||
|
|
|||
|
<sect1>Compl<70>ments sur les gestionnaires de signaux
|
|||
|
<p>
|
|||
|
Regardons <20> nouveau la d<>claration de <em/gtk_signal_connect/.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
gint gtk_signal_connect (GtkObject *object, gchar *name,
|
|||
|
GtkSignalFunc func, gpointer func_data);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Vous avez remarqu<71> que le valeur de retour est de type <em/gint/ ? Il
|
|||
|
s'agit d'un marqueur qui identifie votre fonction de rappel. Comme on
|
|||
|
le disait plus haut, on peut avoir autant de fonctions de rappel que
|
|||
|
l'on a besoin, par signal et par objet, et chacune sera ex<65>cut<75>e <20> son
|
|||
|
tour, dans l'ordre dans lequel elle a <20>t<EFBFBD> attach<63>e. Ce marqueur vous
|
|||
|
permet d'<27>ter ce rappel de la liste en faisant &;:
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_signal_disconnect (GtkObject *object, gint id);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Ainsi, en passant le widget dont on veut supprimer le gestionnaire et
|
|||
|
le marqueur ou identificateur retourn<72> par l'une des fonctions
|
|||
|
<em/signal_connect/, on peut d<>connecter un gestionnaire de signal.
|
|||
|
<p>
|
|||
|
Une autre fonction permettant de supprimer tous les gestionnaires de
|
|||
|
signaux pour un objet est :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
gtk_signal_handlers_destroy (GtkObject *object);
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
Cet appel n'a pas trop besoin d'explications. Il <20>te simplement tous
|
|||
|
les gestionnaires de signaux de l'objet pass<73> en param<61>tre.
|
|||
|
|
|||
|
|
|||
|
<sect1>Un <20> Bonjour tout le monde <20> am<61>lior<6F>
|
|||
|
<p>
|
|||
|
<EFBFBD>tudions une version l<>g<EFBFBD>rement am<61>lior<6F>e avec de meilleurs exemples
|
|||
|
de fonctions de rappel. Ceci permettra aussi d'introduire le sujet
|
|||
|
suivant : le placement des wigdets.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
#include <gtk/gtk.h>
|
|||
|
|
|||
|
/* Notre nouveau rappel am<61>lior<6F>. La donn<6E>e pass<73>e <20> cette fonction est
|
|||
|
* imprim<69>e sur stdout. */
|
|||
|
|
|||
|
void rappel (GtkWidget *widget, gpointer *data)
|
|||
|
{
|
|||
|
g_print ("Re-Bonjour - %s a <20>t<EFBFBD> press<73>\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<65>e dans toutes les applications GTK.
|
|||
|
* Les param<61>tre pass<73>s en ligne de commande sont analys<79>s et
|
|||
|
* retourn<72>s <20> l'application. */
|
|||
|
|
|||
|
gtk_init (&argc, &argv);
|
|||
|
|
|||
|
/* Cr<43>ation d'une nouvelle fen<65>tre. */
|
|||
|
|
|||
|
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|||
|
|
|||
|
/* Nouvel appel qui intitule notre nouvelle fen<65>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<6D>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<65>tre. */
|
|||
|
|
|||
|
gtk_container_border_width (GTK_CONTAINER (window), 10);
|
|||
|
|
|||
|
/* Cr<43>ation d'une bo<62>te pour y placer les widgets.
|
|||
|
* Ceci est d<>crit en d<>tails plus loin dans la section
|
|||
|
* <20> placement <20>. La bo<62>te n'est pas mat<61>rialis<69>e, elle est juste
|
|||
|
* utilis<69>e comme moyen d'arranger les widgets. */
|
|||
|
|
|||
|
box1 = gtk_hbox_new(FALSE, 0);
|
|||
|
|
|||
|
/* On met la bo<62>te dans la fen<65>tre principale. */
|
|||
|
|
|||
|
gtk_container_add (GTK_CONTAINER (window), box1);
|
|||
|
|
|||
|
/* On cr<63>e un nouveau bouton portant le label <20> Bouton 1 <20>. */
|
|||
|
|
|||
|
button = gtk_button_new_with_label ("Bouton 1");
|
|||
|
|
|||
|
/* Lorsque le bouton est cliqu<71>, on appelle la fonction <20> rappel <20>
|
|||
|
* avec un pointeur sur la cha<68>ne <20> Bouton 1 <20> comme param<61>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<62>te invisible qui a <20>t<EFBFBD> plac<61>e dans la fen<65>tre. */
|
|||
|
|
|||
|
gtk_box_pack_start(GTK_BOX(box1), button, TRUE, TRUE, 0);
|
|||
|
|
|||
|
/* N'oubliez jamais cette <20>tape qui indique <20> GTK que la configuration
|
|||
|
* de ce bouton est termin<69>e et qu'il peut <20>tre affich<63>. */
|
|||
|
|
|||
|
gtk_widget_show(button);
|
|||
|
|
|||
|
/* On fait la m<>me chose pour cr<63>er un deuxi<78>me bouton. */
|
|||
|
|
|||
|
button = gtk_button_new_with_label ("Bouton 2");
|
|||
|
|
|||
|
/* On appelle la m<>me fonction de rappel avec un param<61>tre diff<66>rent,
|
|||
|
* un pointeur sur la cha<68>ne <20> Bouton 2 <20>. */
|
|||
|
|
|||
|
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<70>f<EFBFBD>rable d'afficher la fen<65>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;
|
|||
|
}
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
Compilez ce programme en utilisant les m<>mes param<61>tres que pour
|
|||
|
l'exemple pr<70>c<EFBFBD>dent. Vous remarquerez que, maintenant, il est plus
|
|||
|
difficile de quitter le programme : vous devez utiliser le
|
|||
|
gestionnaire de fen<65>tres ou une commande shell pour le d<>truire. Un
|
|||
|
bon exercice pour le lecteur serait d'ins<6E>rer un troisi<73>me bouton <20>
|
|||
|
Quitter <20> qui permettrait de sortir du programme. Vous pouvez aussi
|
|||
|
jouer avec les options de <em/gtk_box_pack_start()/ en lisant la
|
|||
|
section suivante. Essayez de redimensionner la fen<65>tre, et observez
|
|||
|
son comportement.
|
|||
|
<p>
|
|||
|
Juste une remarque : il existe une autre constante utilisable
|
|||
|
avec <em/gtk_window_new()/ - GTK_WINDOW_DIALOG. Ceci permet
|
|||
|
d'interagir de fa<66>on un peu diff<66>rente avec le gestionnaire de
|
|||
|
fen<EFBFBD>tres et doit <20>tre utilis<69> pour les fen<65>tres temporaires comme les
|
|||
|
bo<EFBFBD>tes de dialogue, par exemple.
|
|||
|
|
|||
|
<sect>Placement des widgets
|
|||
|
<p>
|
|||
|
Lorsqu'on cr<63>e une application, on veut mettre plus qu'un simple
|
|||
|
bouton dans une fen<65>tre. Notre premier exemple <20> Bonjour le monde <20>
|
|||
|
n'utilisait qu'un seul widget et on pouvait donc simplement faire un
|
|||
|
appel <20> <em/gtk_container_add/ pour <20> placer <20> le widget dans la
|
|||
|
fen<EFBFBD>tre. Mais si l'on d<>sire en mettre plus, comment peut-on contr<74>ler
|
|||
|
l'endroit o<> le widget sera positionn<6E> ? C'est ici que le placement
|
|||
|
entre en jeu.
|
|||
|
|
|||
|
<sect1>Th<54>orie des bo<62>tes de placement
|
|||
|
<p>
|
|||
|
La majeure partie du placement est faites en cr<63>ant des bo<62>tes comme
|
|||
|
dans l'exemple ci-dessus. Ce sont des widgets containers invisibles o<>
|
|||
|
l'on peut placer nos widgets. Elles existent sous deux formes :
|
|||
|
bo<EFBFBD>tes horizontales et bo<62>tes verticales. Lorsque l'on place des
|
|||
|
widgets dans une bo<62>te horizontale, les objets sont ins<6E>r<EFBFBD>s
|
|||
|
horizontalement de gauche <20> droite ou de droite <20> gauche selon l'appel
|
|||
|
utilis<EFBFBD>. Dans une bo<62>te verticale, les widgets sont plac<61>s de haut en
|
|||
|
bas ou vice versa. On peut utiliser n'importe quelle combinaison de
|
|||
|
bo<EFBFBD>tes <20> l'int<6E>rieur ou <20> c<>t<EFBFBD> d'autres bo<62>tes pour cr<63>er l'effet
|
|||
|
d<EFBFBD>sir<EFBFBD>.
|
|||
|
<p>
|
|||
|
Pour cr<63>er une nouvelle bo<62>te horizontale, on appelle
|
|||
|
<em/gtk_hbox_new()/, et pour les bo<62>tes verticales,
|
|||
|
<em/gtk_vbox_new()/. Les fonctions <em/gtk_box_pack_start()/ et
|
|||
|
<em/gtk_box_pack_end()/ servent <20> placer les objets <20> l'int<6E>rieur de
|
|||
|
ces containers. La fonction <em/gtk_box_pack_start()/ placera de haut
|
|||
|
en bas dans une bo<62>te verticale et de gauche <20> droite dans une bo<62>te
|
|||
|
horizontale. <em/gtk_box_pack_end()/ fera le contraire en pla<6C>ant de
|
|||
|
bas en haut et de droite <20> gauche. En utilisant ces fonctions, on peut
|
|||
|
aligner <20> droite ou <20> gauche nos widgets et m<>me les m<>langer de
|
|||
|
n'importe quelle fa<66>on pour obtenir l'effet d<>sir<69>. Dans la plupart de
|
|||
|
nos exemples, on utilisera <em/gtk_box_pack_start()/. Un objet peut
|
|||
|
<EFBFBD>tre un autre container ou un widget. En fait, de nombreux widgets
|
|||
|
(dont les boutons) sont eux-m<>mes des containers, mais on utilise
|
|||
|
g<EFBFBD>n<EFBFBD>ralement seulement un label dans un bouton.
|
|||
|
<p>
|
|||
|
En utilisant ces appels, GTK sait o<> vous voulez placer vos widgets et
|
|||
|
il peut donc les dimensionner automatiquement et faire d'autres choses
|
|||
|
bien pratiques. Il existe aussi plusieurs options permettant de
|
|||
|
pr<EFBFBD>ciser comment les widgets doivent <20>tre plac<61>s. Comme vous pouvez
|
|||
|
l'imaginer, cette m<>thode nous donne pas mal de libert<72> pour placer et
|
|||
|
cr<EFBFBD>er les widgets.
|
|||
|
|
|||
|
<sect1>D<>tails sur les bo<62>tes
|
|||
|
<p>
|
|||
|
<EFBFBD> cause de cette libert<72>, le placement des bo<62>tes avec GTK peut
|
|||
|
para<EFBFBD>tre d<>routant au premier abord. Il existe beaucoup d'options et
|
|||
|
il n'est pas tout de suite <20>vident de comprendre comment elles
|
|||
|
s'accordent toutes ensemble. En fait, il y a 5 styles de base
|
|||
|
diff<EFBFBD>rents.
|
|||
|
|
|||
|
<p>
|
|||
|
<?
|
|||
|
<IMG ALIGN="center" SRC="packbox1.gif"
|
|||
|
VSPACE="15" HSPACE="10" ALT="Box Packing Example Image" WIDTH="528"
|
|||
|
HEIGHT="235">
|
|||
|
>
|
|||
|
|
|||
|
Chaque ligne contient une bo<62>te horizontale (<em/hbox/) contenant
|
|||
|
plusieurs boutons. L'appel <20> <em/gtk_box_pack/ indique la fa<66>on dont
|
|||
|
sont plac<61>s tous les boutons dans la hbox. Chaque bouton est plac<61>
|
|||
|
dans la hbox de la m<>me fa<66>on (m<>mes param<61>tres que la fonction
|
|||
|
<em/gtk_box_pack_start()/).
|
|||
|
<p>
|
|||
|
Voici la d<>claration de la fonction <em/gtk_box_pack_start/.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_box_pack_start (GtkBox *box,
|
|||
|
GtkWidget *child,
|
|||
|
gint expand,
|
|||
|
gint fill,
|
|||
|
gint padding);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Le premier param<61>tre est la bo<62>te dans laquelle on place l'objet, le
|
|||
|
second est cet objet. Tous les objets sont tous des boutons jusqu'<27>
|
|||
|
maintenant, on place donc des boutons dans des bo<62>tes.
|
|||
|
<p>
|
|||
|
Le param<61>tre <em/expand/ de <em/gtk_box_pack_start()/ ou
|
|||
|
<em/gtk_box_pack_end()/ contr<74>le la fa<66>on dont le widget est plac<61>
|
|||
|
dans la bo<62>te. S'il vaut TRUE, les widgets sont dispos<6F>s dans la bo<62>te
|
|||
|
de fa<66>on <20> en occuper tout l'espace. S'il vaut FALSE, la bo<62>te est
|
|||
|
r<EFBFBD>tr<EFBFBD>cie pour correspondre <20> la taille du widget. Mettre <em/expand/ <20>
|
|||
|
FALSE vous permettra d'aligner <20> droite et <20> gauche vos
|
|||
|
widgets. Sinon, ils s'<27>largiront pour occuper toute la bo<62>te. Le m<>me
|
|||
|
effet pourrait <20>tre obtenu en utilisant uniquement une des deux
|
|||
|
fonctions <em/gtk_box_pack_start/ ou <em/pack_end/.
|
|||
|
<p>
|
|||
|
Le param<61>tre <em/fill/ des fonctions <em/gtk_box_pack/ contr<74>le si de
|
|||
|
l'espace suppl<70>mentaire doit <20>tre allou<6F> aux objets eux-m<>mes (TRUE),
|
|||
|
ou si on doit rajouter de l'espace (<em/padding/) dans la bo<62>te autour
|
|||
|
des objets (FALSE). Il n'a de sens que si le param<61>tre <em/expand/
|
|||
|
vaut TRUE.
|
|||
|
<p>
|
|||
|
Lorsque l'on cr<63>e une nouvelle bo<62>te, on utilise une fonction comme :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
GtkWidget * gtk_hbox_new (gint homogeneous,
|
|||
|
gint spacing);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Le param<61>tre <em/homogeneous/ de <em/gtk_hbox_new/ (et c'est la m<>me
|
|||
|
chose pour <em/gtk_vbox_new/) v<>rifie que chaque objet de la bo<62>te ait
|
|||
|
la m<>me taille (i.e. la m<>me largeur dans une hbox, la m<>me hauteur
|
|||
|
dans une vbox). S'il vaut TRUE, le param<61>tre <em/expand/ des fonctions
|
|||
|
<em/gtk_box_pack/ sera toujours mis <20> TRUE.
|
|||
|
<p>
|
|||
|
Quelle est alors la diff<66>rence entre les param<61>tres <em/spacing/
|
|||
|
(configur<75> lorsque la bo<62>te est cr<63><72>e) et <em/padding/ (configur<75>
|
|||
|
lorque les <20>l<EFBFBD>ments sont plac<61>s) ? <em/spacing/ ajoute de l'espace
|
|||
|
entre les objets, et <em/padding/ en ajoute de chaque c<>t<EFBFBD> d'un
|
|||
|
objet. La figure suivante devrait <20>clairer tout cela :
|
|||
|
|
|||
|
<?
|
|||
|
<IMG ALIGN="center" SRC="packbox2.gif"
|
|||
|
VSPACE="15" HSPACE="10" ALT="Box Packing Example Image" WIDTH="509"
|
|||
|
HEIGHT="213">
|
|||
|
>
|
|||
|
|
|||
|
Voici le code utilis<69> pour cr<63>er les images ci-dessus. J'y ai mis beaucoup de
|
|||
|
commentaires en esp<73>rant que vous n'aurez pas de probl<62>me pour le
|
|||
|
relire. Compilez-le et jouez avec les diff<66>rents param<61>tres.
|
|||
|
|
|||
|
<sect1>Programme de d<>monstration des placements
|
|||
|
<p>
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
#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 param<61>tres qui
|
|||
|
* nous int<6E>ressent sont pass<73>s <20> cette fonction.
|
|||
|
* On n'affiche pas la bo<62>te, 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];
|
|||
|
|
|||
|
/* Cr<43>ation d'une hbox avec les param<61>tres homogeneous et spacing
|
|||
|
* voulus. */
|
|||
|
|
|||
|
box = gtk_hbox_new (homogeneous, spacing);
|
|||
|
|
|||
|
/* Cr<43>ation d'une s<>rie de boutons configur<75>s de fa<66>on appropri<72>e */
|
|||
|
|
|||
|
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);
|
|||
|
|
|||
|
/* Cr<43>ation d'un bouton portant un label d<>pendant de la valeur
|
|||
|
* du param<61>tre 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);
|
|||
|
|
|||
|
/* M<>me chose que ci-dessus mais sous forme abr<62>g<EFBFBD>e. */
|
|||
|
|
|||
|
button = gtk_button_new_with_label (fill ? "TRUE," : "FALSE,");
|
|||
|
gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
|
|||
|
gtk_widget_show (button);
|
|||
|
|
|||
|
/* R<>cup<75>ration du param<61>tre padding sous forme de cha<68>ne. */
|
|||
|
|
|||
|
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, <20> ne jamais oublier ! :) */
|
|||
|
|
|||
|
gtk_init (&argc, &argv);
|
|||
|
|
|||
|
if (argc != 2) {
|
|||
|
fprintf (stderr, "usage : %s num, o<> num vaut 1, 2, ou 3.\n", *argv);
|
|||
|
|
|||
|
/* Nettoyage dans GTK et sortie avec un code d'erreur de 1 */
|
|||
|
gtk_exit (1);
|
|||
|
}
|
|||
|
|
|||
|
which = atoi (argv[1]);
|
|||
|
|
|||
|
/* Cr<43>ation de notre fen<65>tre. */
|
|||
|
|
|||
|
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|||
|
|
|||
|
/* Il ne faut jamais oublier de connecter le signal "destroy" <20> la
|
|||
|
* fen<65>tre principale. C'est tr<74>s important pour disposer d'un
|
|||
|
* comportement intuitif ad<61>quat. */
|
|||
|
|
|||
|
gtk_signal_connect (GTK_OBJECT (window), "delete_event",
|
|||
|
GTK_SIGNAL_FUNC (delete_event), NULL);
|
|||
|
|
|||
|
gtk_container_border_width (GTK_CONTAINER (window), 10);
|
|||
|
|
|||
|
|
|||
|
/* Cr<43>ation d'une bo<62>te verticale (vbox) pour y placer les bo<62>tes
|
|||
|
* horizontales.
|
|||
|
* Ceci permet de placer les bo<62>tes horizontales contenant les boutons
|
|||
|
* les unes au dessus des autres dans cette vbox. */
|
|||
|
|
|||
|
box1 = gtk_vbox_new (FALSE, 0);
|
|||
|
|
|||
|
/* L'exemple <20> afficher. Ils correspondent aux images ci-dessus. */
|
|||
|
|
|||
|
switch (which) {
|
|||
|
case 1:
|
|||
|
/* Cr<43>ation d'un label. */
|
|||
|
|
|||
|
label = gtk_label_new ("gtk_hbox_new (FALSE, 0);");
|
|||
|
|
|||
|
/* Alignement du label <20> gauche. On pr<70>cisera 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 bo<62>te verticale (vbox box1). Il ne
|
|||
|
* faut pas oublier que les widgets qui s'ajoutent <20> une vbox sont
|
|||
|
* plac<61>s 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 bo<62>te :
|
|||
|
* 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 bo<62>te :
|
|||
|
* 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);
|
|||
|
|
|||
|
/* Param<61>tres : 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);
|
|||
|
|
|||
|
/* Cr<43>ation d'un s<>parateur, on verra cela plus tard, mais ils sont
|
|||
|
* simples <20> utiliser. */
|
|||
|
|
|||
|
separator = gtk_hseparator_new ();
|
|||
|
|
|||
|
/* Placement du s<>parateur dans la vbox. Ne pas oublier que tous les
|
|||
|
* widgets sont plac<61>s dans une vbox et qu'il seront plac<61>s
|
|||
|
* verticalement. */
|
|||
|
|
|||
|
gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
|
|||
|
gtk_widget_show (separator);
|
|||
|
|
|||
|
/* Cr<43>ation 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);
|
|||
|
|
|||
|
/* Param<61>tres : 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);
|
|||
|
|
|||
|
/* Param<61>tres : 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 s<>parateur */
|
|||
|
|
|||
|
separator = gtk_hseparator_new ();
|
|||
|
|
|||
|
/* Les 3 derniers param<61>tres 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:
|
|||
|
|
|||
|
/* Cr<43>ation d'un label, box1 est une vbox identique <20>
|
|||
|
* celle cr<63><72>e au d<>but 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);
|
|||
|
|
|||
|
/* Param<61>tres : 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);
|
|||
|
|
|||
|
/* Param<61>tres : 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 param<61>tres 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);
|
|||
|
|
|||
|
/* Param<61>tres : 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);
|
|||
|
|
|||
|
/* Param<61>tres : 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 param<61>tres 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 d<>monstration de la possibilit<69> d'utiliser
|
|||
|
* gtk_box_pack_end() pour aligner les widgets <20> droite.
|
|||
|
* On cr<63>e d'abord une nouvelle bo<62>te comme d'habitude. */
|
|||
|
|
|||
|
box2 = make_box (FALSE, 0, FALSE, FALSE, 0);
|
|||
|
|
|||
|
/* On cr<63>e le label qui sera mis <20> la fin. */
|
|||
|
|
|||
|
label = gtk_label_new ("end");
|
|||
|
|
|||
|
/* On le place en utilisant gtk_box_pack_end(), il est ainsi
|
|||
|
* mis <20> droite de la hbox cr<63><72>e par l'appel <20> 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);
|
|||
|
|
|||
|
/* S<>parateur pour le bas. */
|
|||
|
|
|||
|
separator = gtk_hseparator_new ();
|
|||
|
|
|||
|
/* Configuration du s<>parateur en 400x5 pixels.
|
|||
|
* La hbox que l'on a cr<63><72>e aura donc 400 pixels de large,
|
|||
|
* et le label "end" sera s<>par<61> des autres de la hbox.
|
|||
|
* Sinon, tous les widgets de la hbox seraient plac<61>s les plus
|
|||
|
* pr<70>s possible les uns des autres. */
|
|||
|
|
|||
|
gtk_widget_set_usize (separator, 400, 5);
|
|||
|
|
|||
|
/* Placement du s<>parateur dans la vbox (box1)
|
|||
|
* cr<63><72>e au debut de main(). */
|
|||
|
|
|||
|
gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
|
|||
|
gtk_widget_show (separator);
|
|||
|
}
|
|||
|
|
|||
|
/* Cr<43>ation 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 d<>truire la fen<65>tre. Ceci enverra le
|
|||
|
* signal "destroy" <20> la fen<65>tre. Ce signal sera <20> son tour captur<75>
|
|||
|
* par notre gestionnaire de signal d<>fini plus haut. */
|
|||
|
|
|||
|
gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
|
|||
|
GTK_SIGNAL_FUNC (gtk_widget_destroy),
|
|||
|
GTK_OBJECT (window));
|
|||
|
|
|||
|
/* Placement du bouton dans la <20> quitbox <20>.
|
|||
|
* Les 3 derniers param<61>tres 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 fen<65>tre principale. */
|
|||
|
|
|||
|
gtk_container_add (GTK_CONTAINER (window), box1);
|
|||
|
|
|||
|
/* Affichage */
|
|||
|
|
|||
|
gtk_widget_show (button);
|
|||
|
gtk_widget_show (quitbox);
|
|||
|
|
|||
|
gtk_widget_show (box1);
|
|||
|
|
|||
|
/* Affichage de la fen<65>tre en dernier */
|
|||
|
|
|||
|
gtk_widget_show (window);
|
|||
|
|
|||
|
/* Ne pas oublier notre fonction principale. */
|
|||
|
|
|||
|
gtk_main ();
|
|||
|
|
|||
|
/* Le contr<74>le revient ici lorsque gtk_main_quit() est appel<65>e,
|
|||
|
* jusqu'<27> ce que gtk_exit() soitutilis<69>e. */
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
<p>
|
|||
|
<sect1>Placement avec les tables
|
|||
|
<p>
|
|||
|
<EFBFBD>tudions une autre m<>thode de placement : les tables. Elles
|
|||
|
peuvent s'av<61>rer tr<74>s utiles dans certaines situations.
|
|||
|
|
|||
|
En utilisant des tables, on cr<63>e une grille dans laquelle on peut
|
|||
|
placer les widgets. Ceux-ci peuvent occuper tous les endroits que l'on
|
|||
|
d<EFBFBD>sire.
|
|||
|
|
|||
|
La premi<6D>re chose <20> faire est, bien s<>r, d'<27>tudier la fonction
|
|||
|
<em/gtk_table_new/ :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
GtkWidget* gtk_table_new (gint rows,
|
|||
|
gint columns,
|
|||
|
gint homogeneous);
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
Le premier param<61>tre est le nombre de lignes de la table et le
|
|||
|
deuxi<EFBFBD>me, le nombre de colonnes.
|
|||
|
|
|||
|
Le param<61>tre <em/homogeneous/ s'occupe de la fa<66>on dont les cases de
|
|||
|
la table seront dimensionn<6E>es. Si homogeneous vaut TRUE, les cases
|
|||
|
prennent la taille du plus grand widget de la table. S'il vaut FALSE,
|
|||
|
la taille des cases d<>pend du widget le plus haut de la ligne et du
|
|||
|
plus large de cette colonne.
|
|||
|
|
|||
|
Le nombre de lignes et colonnes va de 0 <20> n, o<> n est le nombre sp<73>cifi<66> dans
|
|||
|
l'appel <20> <em/gtk_table_new/. Ainsi, avec <em/rows/ = 2 et
|
|||
|
<em/columns/ = 2, la table ressemblera <20> ceci :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
0 1 2
|
|||
|
0+----------+----------+
|
|||
|
| | |
|
|||
|
1+----------+----------+
|
|||
|
| | |
|
|||
|
2+----------+----------+
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
On notera que le syst<73>me de coordonn<6E>es part du coin en haut <20>
|
|||
|
gauche. Pour placer un widget dans une case, ou utilise la fonction
|
|||
|
suivante :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
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);
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
O<EFBFBD> le premier param<61>tre (<em/table/) est la table que l'on a cr<63><72>e et
|
|||
|
le second (<em/child/) est le widget que l'on veut placer dans la
|
|||
|
table.
|
|||
|
|
|||
|
Les param<61>tres <em/left_attach/ et <em/right_attach/ sp<73>cifient
|
|||
|
l'emplacement du widget et le nombre de cases <20> utiliser. Par exemple,
|
|||
|
si on veut placer un bouton dans le coin inf<6E>rieur droit de la table
|
|||
|
d<EFBFBD>crite plus haut et que l'on d<>sire ne remplir QUE cette case,
|
|||
|
<em/left_attach/ vaudra 1, <em/right_attach/ vaudra 2; <em/top_attach/
|
|||
|
vaudra 1 et <em/bottom_attach/ vaudra 2.
|
|||
|
|
|||
|
Si on veut un widget occupant toute la ligne sup<75>rieure de notre table, on utilisera
|
|||
|
les valeurs 0, 2, 0, 1.
|
|||
|
|
|||
|
Les param<61>tres <em/xoptions/ et <em/yoptions/ servent <20> pr<70>ciser les
|
|||
|
options de placement et peuvent <20>tre combin<69>es par un OU logique pour
|
|||
|
permettre des options multiples.
|
|||
|
|
|||
|
Ces options sont :
|
|||
|
<itemize>
|
|||
|
<item>GTK_FILL - Si la case de la table est plus large que le widget, et que
|
|||
|
GTK_FILL est sp<73>cifi<66>, le widget s'<27>largira pour occuper toute la place
|
|||
|
disponible.
|
|||
|
|
|||
|
<item>GTK_SHRINK - Si la table a moins de place qu'il ne lui en faut
|
|||
|
(g<>n<EFBFBD>ralement, <20> cause d'un redimensionnement de la fen<65>tre par
|
|||
|
l'utilisateur), les widgets sont alors simplement pouss<73>s vers le bas
|
|||
|
de la fen<65>tre et disparaissent. Si GTK_SHRINK est sp<73>cifi<66>, les
|
|||
|
widgets se r<>duiront en m<>me temps que la table.
|
|||
|
|
|||
|
<item>GTK_EXPAND - Cette option provoque l'extension de la table pour
|
|||
|
qu'elle utilise tout l'espace restant dans la fen<65>tre.
|
|||
|
</itemize>
|
|||
|
|
|||
|
Le param<61>tres de <em/padding/ jouent le m<>me r<>le que pour les bo<62>tes,
|
|||
|
il cr<63>ent une zone libre, sp<73>cifi<66>e en pixels, autour du widget.
|
|||
|
|
|||
|
gtk_table_attach() a BEAUCOUP d'options. Voici donc une fonction-raccourci :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_table_attach_defaults (GtkTable *table,
|
|||
|
GtkWidget *widget,
|
|||
|
gint left_attach,
|
|||
|
gint right_attach,
|
|||
|
gint top_attach,
|
|||
|
gint bottom_attach);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
<em/xoptions/ et <em/options/ valent par d<>faut GTK_FILL | GTK_EXPAND,
|
|||
|
et <em/xpadding/ et <em/ypadding/ valent 0. Les autres param<61>tres sont
|
|||
|
les m<>mes que ceux de la fonction pr<70>c<EFBFBD>dente.
|
|||
|
|
|||
|
Il existe aussi les fonctions <em/gtk_table_set_row_spacing()/ et
|
|||
|
<em/gtk_table_set_col_spacing()/. Elles permettent de placer des
|
|||
|
espaces apr<70>s une ligne ou une colonne.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_table_set_row_spacing (GtkTable *table,
|
|||
|
gint row,
|
|||
|
gint spacing);
|
|||
|
</verb></tscreen>
|
|||
|
et
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_table_set_col_spacing (GtkTable *table,
|
|||
|
gint column,
|
|||
|
gint spacing);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Pour les colonnes, l'espace est ajout<75> <20> droite de la colonne et pour
|
|||
|
les lignes, il est ajout<75> en dessous.
|
|||
|
|
|||
|
On peut aussi configurer un espacement pour toutes les lignes et/ou
|
|||
|
colonnes avec :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_table_set_row_spacings (GtkTable *table,
|
|||
|
gint spacing);
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
Et,
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_table_set_col_spacings (GtkTable *table,
|
|||
|
gint spacing);
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
Avec ces appels, la derni<6E>re ligne et la derni<6E>re colonne n'ont pas
|
|||
|
d'espace suppl<70>mentaire.
|
|||
|
|
|||
|
<sect1>Exemple de placement avec table
|
|||
|
<p>
|
|||
|
Pour le moment, <20>tudiez l'exemple sur les tables (testgtk.c) distribu<62>
|
|||
|
avec les sources de GTK.
|
|||
|
|
|||
|
<sect>Vue d'ensemble des widgets
|
|||
|
<p>
|
|||
|
<p>
|
|||
|
Les <20>tapes pour cr<63>er un widget en GTK sont :
|
|||
|
<enum>
|
|||
|
<item> <em/gtk_*_new()/ - une des fonctions disponibles pour cr<63>er un
|
|||
|
nouveau widget. Ces fonctions sont d<>crites dans cette section.
|
|||
|
|
|||
|
<item> Connexion de tous les signaux que l'on souhaite utiliser avec
|
|||
|
les gestionnaires ad<61>quats.
|
|||
|
|
|||
|
<item> Configuration des attributs du widget.
|
|||
|
|
|||
|
<item> Placement du widget dans un container en utilisant un appel appropri<72> comme
|
|||
|
<em/gtk_container_add()/ ou <em/gtk_box_pack_start()/.
|
|||
|
|
|||
|
<item> Affichage du widget gr<67>ce <20> <em/gtk_widget_show()/.
|
|||
|
</enum>
|
|||
|
<p>
|
|||
|
<em/gtk_widget_show()/ permet <20> GTK de savoir que l'on a fini de
|
|||
|
configurer les attributs du widget et qu'il est pr<70>t <20> <20>tre
|
|||
|
affich<EFBFBD>. On peut aussi utiliser <em/gtk_widget_hide()/ pour le faire
|
|||
|
dispara<EFBFBD>tre. L'ordre dans lequel on affiche les widgets n'est pas
|
|||
|
important, mais il est pr<70>f<EFBFBD>rable d'afficher la fen<65>tre en dernier
|
|||
|
pour qu'elle surgisse d'un seul coup plut<75>t que de voir les diff<66>rents
|
|||
|
widgets appara<72>tre <20> l'<27>cran au fur et <20> mesure. Les fils d'un widget
|
|||
|
(une fen<65>tre est aussi un widget) ne seront pas affich<63>s tant que la
|
|||
|
fen<EFBFBD>tre elle-m<>me n'est pas affich<63>e par la fonction
|
|||
|
<em/gtk_widget_show()/.
|
|||
|
|
|||
|
<sect1> Conversions de types
|
|||
|
<p>
|
|||
|
Vous remarquerez, au fur et <20> mesure que vous progressez, que GTK
|
|||
|
utilise un syst<73>me de coercition de type. Celle-ci est toujours
|
|||
|
r<EFBFBD>alis<EFBFBD>e en utilisant des macros qui v<>rifient si l'objet donn<6E> peut
|
|||
|
<EFBFBD>tre converti et qui r<>alisent cette coercition. Les macros que vous
|
|||
|
rencontrerez le plus sont :
|
|||
|
|
|||
|
<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)
|
|||
|
</itemize>
|
|||
|
|
|||
|
Elles sont toutes utilis<69>es pour convertir les param<61>tres des
|
|||
|
fonctions. Vous les verrez dans les exemples et, en r<>gle g<>n<EFBFBD>rale,
|
|||
|
vous saurez les utiliser simplement en regardant la
|
|||
|
d<EFBFBD>claration d'une fonction.
|
|||
|
|
|||
|
Comme vous pouvez le voir dans la hi<68>rarchie de classes ci-dessous,
|
|||
|
tous les <em/GtkWidgets/ d<>rivent d'une classe de base
|
|||
|
<em/GtkObject/. Ceci signifie que vous pouvez utiliser un widget <20>
|
|||
|
chaque fois qu'une fonction requiert un objet - il suffit d'utiliser
|
|||
|
la macro GTK_OBJECT().
|
|||
|
|
|||
|
Par exemple :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
gtk_signal_connect(GTK_OBJECT(button), "clicked",
|
|||
|
GTK_SIGNAL_FUNC(fonction_rappel), donnee_de_rappel);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
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
|
|||
|
hi<EFBFBD>rarchie de classe ci-dessous, vous remarquerez que beaucoup de
|
|||
|
widgets viennent de la classe <em/GtkContainer/. N'importe lequel de
|
|||
|
ces widgets peut <20>tre utilis<69> avec la macro GTK_CONTAINER pour <20>tre
|
|||
|
pass<EFBFBD> en param<61>tre <20> une fonction qui attend un container.
|
|||
|
|
|||
|
Malheureusement, ces macros ne peuvent <20>tre couvertes en d<>tail dans
|
|||
|
ce didacticiel, Je vous recommande donc de jeter un coup d'oeil sur
|
|||
|
les fichier en-t<>tes GTK : ils peuvent s'av<61>rer tr<74>s
|
|||
|
instructifs. En fait, il n'est pas difficile de comprendre comment
|
|||
|
fonctionne un widget, il suffit d'<27>tudier les d<>clarations des
|
|||
|
fonctions.
|
|||
|
|
|||
|
<p>
|
|||
|
<sect1>La hi<68>rarchie des widgets
|
|||
|
<p>
|
|||
|
Voici l'arbre de la hi<68>rarchie de classes utilis<69>es pour implanter les widgets.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
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
|
|||
|
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
|
|||
|
<sect1>Widgets sans fen<65>tre
|
|||
|
<p>
|
|||
|
Les widgets suivants n'ont pas de fen<65>tre associ<63>e. Si vous voulez
|
|||
|
capturer des <20>v<EFBFBD>nements, vous devez utiliser <em/GtkEventBox/.
|
|||
|
Reportez-vous <20> la section sur
|
|||
|
<ref id="sec_The_EventBox_Widget" name="Le widget EventBox">
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
GtkAlignment
|
|||
|
GtkArrow
|
|||
|
GtkBin
|
|||
|
GtkBox
|
|||
|
GtkImage
|
|||
|
GtkItem
|
|||
|
GtkLabel
|
|||
|
GtkPaned
|
|||
|
GtkPixmap
|
|||
|
GtkScrolledWindow
|
|||
|
GtkSeparator
|
|||
|
GtkTable
|
|||
|
GtkViewport
|
|||
|
GtkAspectFrame
|
|||
|
GtkFrame
|
|||
|
GtkVPaned
|
|||
|
GtkHPaned
|
|||
|
GtkVBox
|
|||
|
GtkHBox
|
|||
|
GtkVSeparator
|
|||
|
GtkHSeparator
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
Nous continuerons notre exploration de GTK en examinant chaque widget
|
|||
|
tour <20> tour, cr<63>ant quelques fonctions simples pour les afficher. Une
|
|||
|
autre source int<6E>ressante est le programme <em/testgtk.c/ livr<76> avec
|
|||
|
GTK. Il se trouve dans le r<>pertoire <em>gtk/</em>
|
|||
|
|
|||
|
<sect>Widgets boutons
|
|||
|
<p>
|
|||
|
<sect1>Boutons normaux
|
|||
|
<p>
|
|||
|
On a d<>j<EFBFBD> presque vu tout ce qu'il y avait <20> voir sur le widget
|
|||
|
bouton. Il est tr<74>s simple. Cependant, il y a deux fa<66>ons de cr<63>er un
|
|||
|
bouton. On peut utiliser <em/gtk_button_new_with_label()/ pour cr<63>er
|
|||
|
un bouton avec un label, ou <em/gtk_button_new()/ pour cr<63>er un bouton
|
|||
|
vide. Dans ce dernier cas, c'est <20> vous de placer un label ou un
|
|||
|
pixmap sur celui-ci. Pour ce faire, cr<63>ez une bo<62>te, puis placez vos
|
|||
|
objets dans celle-ci en utilisant la fonction habituelle
|
|||
|
<em/gtk_box_pack_start/, utilisez alors <em/gtk_container_add/ pour
|
|||
|
placer la bo<62>te dans le bouton.
|
|||
|
<p>
|
|||
|
Voici un exemple d'utilisation de <em/gtk_button_new()/ pour cr<63>er un
|
|||
|
bouton contenant une image et un label. J'ai s<>par<61> du reste le code
|
|||
|
qui cr<63>e une bo<62>te pour que vous puissiez l'utiliser dans vos
|
|||
|
programmes.
|
|||
|
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
#include <gtk/gtk.h>
|
|||
|
|
|||
|
|
|||
|
/* Cr<43>ation d'une hbox avec une image et un label. Cette fonction
|
|||
|
* retourne la bo<62>te... */
|
|||
|
|
|||
|
GtkWidget *xpm_label_box (GtkWidget *parent, gchar *xpm_filename,
|
|||
|
gchar *label_text)
|
|||
|
{
|
|||
|
GtkWidget *box1;
|
|||
|
GtkWidget *label;
|
|||
|
GtkWidget *pixmapwid;
|
|||
|
GdkPixmap *pixmap;
|
|||
|
GdkBitmap *mask;
|
|||
|
GtkStyle *style;
|
|||
|
|
|||
|
/* Cr<43>ation 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<6E>t la vraie raison, qu'il
|
|||
|
* m'<27>claire sur ce point. */
|
|||
|
|
|||
|
style = gtk_widget_get_style(parent);
|
|||
|
|
|||
|
/* Chargement de xpm pour cr<63>er une image */
|
|||
|
|
|||
|
pixmap = gdk_pixmap_create_from_xpm (parent->window, &mask,
|
|||
|
&style->bg[GTK_STATE_NORMAL],
|
|||
|
xpm_filename);
|
|||
|
pixmapwid = gtk_pixmap_new (pixmap, mask);
|
|||
|
|
|||
|
/* Cr<43>ation d'un label */
|
|||
|
|
|||
|
label = gtk_label_new (label_text);
|
|||
|
|
|||
|
/* placement de l'image et du label dans la bo<62>te */
|
|||
|
|
|||
|
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 <20>t<EFBFBD> press<73>\n", (char *) data);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
int main (int argc, char *argv[])
|
|||
|
{
|
|||
|
/* GtkWidget est le type utilis<69> pour d<>clarer les widgets */
|
|||
|
|
|||
|
GtkWidget *window;
|
|||
|
GtkWidget *button;
|
|||
|
GtkWidget *box1;
|
|||
|
|
|||
|
gtk_init (&argc, &argv);
|
|||
|
|
|||
|
/* Cr<43>ation d'une fen<65>tre */
|
|||
|
|
|||
|
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|||
|
|
|||
|
gtk_window_set_title (GTK_WINDOW (window), "Pixmap'd Buttons!");
|
|||
|
|
|||
|
/* Il est pr<70>f<EFBFBD>rable de faire cela pour toutes les fen<65>tres */
|
|||
|
|
|||
|
gtk_signal_connect (GTK_OBJECT (window), "destroy",
|
|||
|
GTK_SIGNAL_FUNC (gtk_exit), NULL);
|
|||
|
|
|||
|
|
|||
|
/* Configuration du bord de la fen<65>tre */
|
|||
|
|
|||
|
gtk_container_border_width (GTK_CONTAINER (window), 10);
|
|||
|
|
|||
|
/* Cr<43>ation d'un bouton */
|
|||
|
|
|||
|
button = gtk_button_new ();
|
|||
|
|
|||
|
/* Vous devriez <20>tre habitu<74> <20> voir ces fonctions maintenant */
|
|||
|
|
|||
|
gtk_signal_connect (GTK_OBJECT (button), "clicked",
|
|||
|
GTK_SIGNAL_FUNC (callback), (gpointer) "cool button");
|
|||
|
|
|||
|
/* Appel de notre fonction de cr<63>ation de bo<62>te */
|
|||
|
|
|||
|
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;
|
|||
|
}
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
La fonction <em/xpm_label_box()/ peut <20>tre utilis<69>e pour placer des
|
|||
|
xpms et des labels sur tout widget qui peut <20>tre container.
|
|||
|
|
|||
|
<sect1> Boutons commutateurs
|
|||
|
<p>
|
|||
|
Les boutons commutateurs ressemblent beaucoup aux boutons normaux, sauf
|
|||
|
qu'ils seront toujours alternativement dans un <20>tat ou dans un
|
|||
|
autre. Le changement d'<27>tat s'effectue par un click. Ils peuvent <20>tre
|
|||
|
enfonc<EFBFBD>s et, lorsqu'on clique dessus, ils se rel<65>vent. Re-cliquez,
|
|||
|
et ils se renfoncent.
|
|||
|
|
|||
|
Les boutons commutateurs sont la base des cases <20> cocher ou des boutons
|
|||
|
radio, donc la plupart des appels utilis<69>s pour les boutons commutateurs
|
|||
|
sont h<>rit<69>s par les cases <20> cocher et les boutons radio. J'insisterai
|
|||
|
l<EFBFBD> dessus quand nous les aborderons.
|
|||
|
|
|||
|
Cr<EFBFBD>ation d'un bouton commutateur :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
GtkWidget* gtk_toggle_button_new (void);
|
|||
|
|
|||
|
GtkWidget* gtk_toggle_button_new_with_label (gchar *label);
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
Comme vous pouvez l'imaginer, elles fonctionnent comme celles des
|
|||
|
boutons normaux. La premi<6D>re cr<63>e un bouton commutateur vide et la
|
|||
|
deuxi<EFBFBD>me un bouton commutateur contenant d<>j<EFBFBD> un label.
|
|||
|
<p>
|
|||
|
Pour r<>cup<75>rer l'<27>tat d'un commutateur et cela comprend aussi les
|
|||
|
cases <20> cocher et les boutons radio, on utilise une macro comme nous
|
|||
|
le montrons dans l'exemple qui suit et qui teste l'<27>tat du commutateur
|
|||
|
dans une fonction de rappel. Le signal qui nous int<6E>resse et qui est
|
|||
|
<EFBFBD>mis par les boutons commutateurs (ce qui comprend aussi les cases <20>
|
|||
|
cocher et les boutons radio), est le signal "toggled". Pour v<>rifier
|
|||
|
l'<27>tat de ces boutons, on configure un gestionnaire de signal qui
|
|||
|
capture "toggled" et utilise la macro pour d<>terminer l'<27>tat. La
|
|||
|
fonction de rappel ressemblera <20> ceci :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
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<65>ch<63>. */
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
/* le bouton est enfonc<6E> */
|
|||
|
}
|
|||
|
}
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
<!--
|
|||
|
|
|||
|
COMMENTED!
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
guint gtk_toggle_button_get_type (void);
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
No idea... they all have this, but I dunno what it is :)
|
|||
|
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_toggle_button_set_mode (GtkToggleButton *toggle_button,
|
|||
|
gint draw_indicator);
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
No idea.
|
|||
|
-->
|
|||
|
|
|||
|
<p>
|
|||
|
L'appel qui suit peut <20>tre utilis<69> pour configurer l'<27>tat d'un bouton
|
|||
|
commutateur et de ses descendants, les cases <20> cocher et les boutons
|
|||
|
radio. On lui passe notre bouton en premier param<61>tre et TRUE ou
|
|||
|
FALSE pour sp<73>cifier s'il doit <20>tre rel<65>ch<63> ou enfonc<6E>. Par d<>faut, il
|
|||
|
est rel<65>ch<63> (FALSE).
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_toggle_button_set_state (GtkToggleButton *toggle_button,
|
|||
|
gint state);
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
On notera que lorsqu'on utilise cette fonction, et que l'<27>tat est
|
|||
|
modifi<EFBFBD>, cela force le bouton <20> <20>mettre un signal "clicked".
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_toggle_button_toggled (GtkToggleButton *toggle_button);
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
Cet appel ne fait que commuter le bouton et <20>mettre le signal "toggled".
|
|||
|
|
|||
|
<sect1> Cases <20> cocher
|
|||
|
<p>
|
|||
|
Les cases <20> cocher h<>ritent de nombreuses propri<72>t<EFBFBD>s et fonctions des
|
|||
|
boutons commutateurs, mais ont un aspect diff<66>rent. Au lieu d'<27>tre des
|
|||
|
boutons contenant du texte, ce sont de petits carr<72>s avec un texte sur
|
|||
|
leur droite. Il sont souvent utilis<69>s pour valider ou non des options
|
|||
|
dans les applications.
|
|||
|
|
|||
|
Les deux fonctions de cr<63>ation sont identiques <20> celles des boutons normaux.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
GtkWidget* gtk_check_button_new (void);
|
|||
|
|
|||
|
GtkWidget* gtk_check_button_new_with_label (gchar *label);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
La fonction <em/new_with_label/ cr<63>e une case <20> cocher avec un texte <20>
|
|||
|
cot<EFBFBD> d'elle.
|
|||
|
|
|||
|
La v<>rification de l'<27>tat d'une case <20> cocher est identique <20> celle
|
|||
|
des boutons commutateurs.
|
|||
|
|
|||
|
<sect1> Boutons radio
|
|||
|
<p>
|
|||
|
Les boutons radio ressemblent aux cases <20> cocher sauf qu'ils sont
|
|||
|
group<EFBFBD>s de fa<66>on <20> ce qu'un seul d'entre-eux puisse <20>tre s<>lectionn<6E> <20>
|
|||
|
un moment donn<6E>. Ils sont utilis<69>s par les applications lorsqu'il
|
|||
|
s'agit d'effectuer un choix dans une liste d'options.
|
|||
|
|
|||
|
La cr<63>ation d'un bouton radio s'effectue gr<67>ce <20> l'un des appels
|
|||
|
suivants :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
GtkWidget* gtk_radio_button_new (GSList *group);
|
|||
|
|
|||
|
GtkWidget* gtk_radio_button_new_with_label (GSList *group,
|
|||
|
gchar *label);
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
On notera le param<61>tre suppl<70>mentaire de ces fonctions. Elles
|
|||
|
n<EFBFBD>cessitent un groupe pour r<>aliser correctement leur t<>che. Le
|
|||
|
premier appel doit passer NULL au premier param<61>tre puis on peut cr<63>er un
|
|||
|
groupe en utilisant :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
GSList* gtk_radio_button_group (GtkRadioButton *radio_button);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
<p>
|
|||
|
On passe alors ce groupe en premier param<61>tre des appels suivants aux fonctions de cr<63>ation. Il est pr<70>f<EFBFBD>rable, aussi, de pr<70>ciser quel bouton doit <20>tre choisi par d<>faut avec la fonction :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_toggle_button_set_state (GtkToggleButton *toggle_button,
|
|||
|
gint state);
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
Celle-ci est d<>crite dans la section sur les boutons commutateurs et fonctionne
|
|||
|
exactement de la m<>me fa<66>on.
|
|||
|
<p>
|
|||
|
[Mettre ici un exemple d'utilisation de tout cela car je crois que cela ferait
|
|||
|
beaucoup de bien...]
|
|||
|
|
|||
|
|
|||
|
<sect> Widgets divers
|
|||
|
<p>
|
|||
|
<sect1> Labels
|
|||
|
<p>
|
|||
|
Les labels sont tr<74>s utilis<69>s dans GTK et sont relativement
|
|||
|
simples. Ils n'<27>mettent pas de signaux car ils n'ont pas de fen<65>tre X
|
|||
|
qui leur est associ<63>e. Si vous avez besoin de capturer des signaux ou
|
|||
|
de faire des coupures (<28> clippings <20>), utilisez un widget EventBox.
|
|||
|
|
|||
|
Pour cr<63>er un label, on utilise :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
GtkWidget* gtk_label_new (char *str);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
O<EFBFBD> l'unique param<61>tre est la cha<68>ne de caract<63>res que l'on veut que le
|
|||
|
label affiche.
|
|||
|
|
|||
|
Pour changer le texte d'un label apr<70>s sa cr<63>ation, on utilise la fonction :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_label_set (GtkLabel *label,
|
|||
|
char *str);
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
o<EFBFBD> le premier param<61>tre est le label que l'on veut modifier, que l'on
|
|||
|
convertit en utilisant la macro GTK_LABEL(), et le second est la
|
|||
|
nouvelle cha<68>ne.
|
|||
|
|
|||
|
L'espace n<>cessaire <20> la nouvelle cha<68>ne sera automatiquement ajust<73>
|
|||
|
si n<>cessaire.
|
|||
|
|
|||
|
Pour r<>cup<75>rer la cha<68>ne courante, on utilise la fonction :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_label_get (GtkLabel *label,
|
|||
|
char **str);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
o<EFBFBD> le premier param<61>tre est le label dont on veut r<>cup<75>rer la cha<68>ne
|
|||
|
et le second sert <20> retourner cette cha<68>ne.
|
|||
|
|
|||
|
<sect1>Le widget bulle d'aide
|
|||
|
<p>
|
|||
|
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 <20> utiliser, on ne donnera donc pas d'exemple. Si vous
|
|||
|
voulez voir du code, consultez le programme <em/testgtk.c/ distribu<62>
|
|||
|
avec GTK.
|
|||
|
<p>
|
|||
|
Certains widgets (comme les labels) ne fonctionnent pas avec les
|
|||
|
bulles d'aide.
|
|||
|
<p>
|
|||
|
Le premier appel que vous utiliserez sera pour cr<63>er une nouvelle
|
|||
|
bulle d'aide. Vous n'avez besoin que de le faire une fois dans une
|
|||
|
fonction donn<6E>e. Le <em/GtkTooltip/ que cette fonction retourne peut
|
|||
|
<EFBFBD>tre utilis<69> pour cr<63>er plusieurs bulles d'aide.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
GtkTooltips *gtk_tooltips_new (void);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Lorsque vous avez cr<63><72> une nouvelle bulle d'aide et le widget sur lequel vous
|
|||
|
voulez l'utiliser, vous n'avez qu'<27> faire cet appel pour la configurer :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_tooltips_set_tips (GtkTooltips *tooltips,
|
|||
|
GtkWidget *widget,
|
|||
|
gchar *tips_text);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Les param<61>tres sont la bulle d'aide d<>j<EFBFBD> cr<63><72>e, suivi du widget pour
|
|||
|
lequel vous voulez voir appara<72>tre cette bulle et le texte que vous
|
|||
|
voulez qu'elle contienne.
|
|||
|
<p>
|
|||
|
Voici un petit exemple :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
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");
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
|
|||
|
D'autres fonctions peuvent <20>tre utilis<69>es avec les bulles d'aide. Je ne ferais que les <20>num<75>rer et les d<>crire bri<72>vement.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_tooltips_destroy (GtkTooltips *tooltips);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Destruction de bulles d'aide.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_tooltips_enable (GtkTooltips *tooltips);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Activation d'un ensemble de bulles d'aide d<>sactiv<69>es.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_tooltips_disable (GtkTooltips *tooltips);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
D<EFBFBD>sactivation d'un ensemble de bulles d'aide activ<69>es.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_tooltips_set_delay (GtkTooltips *tooltips,
|
|||
|
gint delay);
|
|||
|
|
|||
|
</verb></tscreen> Configure le nombre de millisecondes pendant lequel
|
|||
|
le pointeur soit se trouver sur le widget avant que la bulle d'aide
|
|||
|
n'apparaisse. Par d<>faut, ce d<>lai est de 1000 millisecondes, soit 1
|
|||
|
seconde.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_tooltips_set_tips (GtkTooltips *tooltips,
|
|||
|
GtkWidget *widget,
|
|||
|
gchar *tips_text);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Change le texte d'une bulle d'aide d<>j<EFBFBD> cr<63><72>e.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_tooltips_set_colors (GtkTooltips *tooltips,
|
|||
|
GdkColor *background,
|
|||
|
GdkColor *foreground);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Configure les couleurs de fond et de premier plan des bulles
|
|||
|
d'aides. Je ne sais toujours pas comment sp<73>cifier les couleurs...
|
|||
|
<p>
|
|||
|
Et c'est tout concernant les fonctions associ<63>es aux bulles
|
|||
|
d'aide. C'est plus que vous ne vouliez s<>rement en savoir :)
|
|||
|
|
|||
|
<sect1> Barres de progression
|
|||
|
<p>
|
|||
|
Les barres de progression sont utilis<69>es pour afficher la progression
|
|||
|
d'une op<6F>ration. Elles sont tr<74>s simple <20> utiliser comme vous pourrez
|
|||
|
le constater en <20>tudiant le code ci-dessous. Commen<65>ons d'abord par
|
|||
|
l'appel permettant de cr<63>er une nouvelle barre.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
GtkWidget *gtk_progress_bar_new (void);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Maintenant que la barre est cr<63><72>e, nous pouvons l'utiliser.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_progress_bar_update (GtkProgressBar *pbar, gfloat percentage);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Le premier param<61>tre est la barre de progression sur laquelle on veut
|
|||
|
agir, et le second est le pourcentage <20> effectu<74> <20>, signifiant le
|
|||
|
remplissage de la barres de 0 <20> 100 % (r<>el compris entre 0 et 1).
|
|||
|
|
|||
|
Les barres de progression sont g<>n<EFBFBD>ralement utilis<69>es avec les d<>lais
|
|||
|
d'expiration ou autres fonctions identiques (voir la section sur <ref
|
|||
|
id="sec_timeouts" name="Expirations, fonctions d'E/S et d'attente">)
|
|||
|
pour donner l'illusion du multi-t<>ches. Toutes emploient la fonction
|
|||
|
<em/gtk_progress_bar_update/ de la m<>me fa<66>on.
|
|||
|
|
|||
|
Voici un exemple de barre de progression mise <20> jour par des
|
|||
|
expirations. Ce code montre aussi comment r<>initialiser une barre.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
#include <gtk/gtk.h>
|
|||
|
|
|||
|
static int ptimer = 0;
|
|||
|
int pstat = TRUE;
|
|||
|
|
|||
|
/* Cette fonction incr<63>mente et met <20> jour la barre de progression,
|
|||
|
* elle la r<>initialise si pstat vaut FALSE */
|
|||
|
|
|||
|
gint progress (gpointer data)
|
|||
|
{
|
|||
|
gfloat pvalue;
|
|||
|
|
|||
|
/* r<>cup<75>ration 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 r<>initialisation 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<43>e 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 d<>lai d'expiration pour g<>rer automatiquement la
|
|||
|
* mise <20> jour de la barre */
|
|||
|
|
|||
|
ptimer = gtk_timeout_add (100, progress, pbar);
|
|||
|
|
|||
|
/* Ce bouton indique <20> la barre qu'elle doit se r<>initialiser */
|
|||
|
|
|||
|
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;
|
|||
|
}
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Dans ce petit programme, il y a quatre parties concernant le
|
|||
|
fonctionnement g<>n<EFBFBD>ral des barres de progression, nous les <20>tudierons
|
|||
|
dans l'ordre de leurs appels.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
pbar = gtk_progress_bar_new ();
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Cet appel cr<63>e une nouvelle barre, nomm<6D>e <em/pbar/.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
ptimer = gtk_timeout_add (100, progress, pbar);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Cet appel utilise des d<>lais d'expiration pour permettre un intervalle
|
|||
|
de temps constant. ces d<>lais ne sont pas n<>cessaires <20> l'utilisation
|
|||
|
des barres de progression.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
pvalue = GTK_PROGRESS_BAR (data)->percentage;
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Ce code assigne <20> <em/pvalue/ la valeur du pourcentage de la barre.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
gtk_progress_bar_update (GTK_PROGRESS_BAR (data), pvalue);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Finalement, ce code met <20> jour la barre avec la valeur de <em/pvalue/.
|
|||
|
|
|||
|
Et c'est tout ce qu'il y a <20> savoir sur les barres de
|
|||
|
progression. Amusez-vous bien.
|
|||
|
|
|||
|
<sect1> Bo<42>tes de dialogue
|
|||
|
<p>
|
|||
|
|
|||
|
Les widgets bo<62>tes de dialogue sont tr<74>s simples : ce sont
|
|||
|
simplement des fen<65>tres avec plusieurs choses d<>j<EFBFBD> plac<61>es dedans. La
|
|||
|
structure d'une bo<62>te de dialogue est :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
struct GtkDialog
|
|||
|
{
|
|||
|
GtkWindow window;
|
|||
|
|
|||
|
GtkWidget *vbox;
|
|||
|
GtkWidget *action_area;
|
|||
|
};
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Comme vous le voyez, cela cr<63>e simplement une fen<65>tre et la place dans
|
|||
|
une vbox suivie d'un s<>parateur et d'une hbox pour la <20> zone d'action <20>.
|
|||
|
|
|||
|
Le widget bo<62>te de dialogue peut servir <20> produire des messages pour
|
|||
|
l'utilisateur ainsi qu'<27> d'autres t<>ches. Il est vraiment rudimentaire
|
|||
|
et il n'y a qu'une seule fonction pour les bo<62>tes de dialogue :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
GtkWidget* gtk_dialog_new (void);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Ainsi, pour cr<63>er un nouveau dialogue, on utilise :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
GtkWidget window;
|
|||
|
window = gtk_dialog_new ();
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Ceci cr<63>era la bo<62>te de dialogue et c'est maintenant <20> vous de
|
|||
|
l'utiliser. Vous pouvez, par exemple, placer un bouton dans la zone
|
|||
|
d'action en faisant quelque chose comme :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
button = ...
|
|||
|
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->action_area), button,
|
|||
|
TRUE, TRUE, 0);
|
|||
|
gtk_widget_show (button);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Et vous pouvez aussi ajouter un label <20> la zone de la vboxb :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
label = gtk_label_new ("Les bo<62>tes de dialogues sont pratiques");
|
|||
|
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->vbox), label, TRUE,
|
|||
|
TRUE, 0);
|
|||
|
gtk_widget_show (label);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Comme exemple d'utilisation d'une bo<62>te de dialogue, vous pourriez
|
|||
|
mettre deux boutons dans la zone d'action (un bouton <20> Annuler <20> et un
|
|||
|
bouton <20> Ok <20>) et un label dans la zone de la vbox posant une question
|
|||
|
<EFBFBD> l'utilisateur ou signalant une erreur, etc. Vous pouvez alors
|
|||
|
attacher un signal diff<66>rent <20> chacun des boutons et r<>aliser
|
|||
|
l'op<6F>ration que l'utilisateur a choisie.
|
|||
|
|
|||
|
|
|||
|
<sect1> Pixmaps
|
|||
|
<p>
|
|||
|
Les pixmaps sont des structures de donn<6E>es contenant des
|
|||
|
images. Celles-ci peuvent <20>tre utilis<69>es <20> diff<66>rents endroits, mais
|
|||
|
le plus souvent comme ic<69>nes 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 <em/GdkPixmap/ en utilisant les fonctions de la couche
|
|||
|
GDK. Les pixmaps peuvent soit <20>tre cr<63><72>s <20> partir de donn<6E>es en
|
|||
|
memoire, ou <20> partir de donn<6E>es lues dans un fichier. Nous utiliserons
|
|||
|
chacun des appels pour cr<63>er un pixmap.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
GdkPixmap *gdk_bitmap_create_from_data( GdkWindow *window,
|
|||
|
gchar *data,
|
|||
|
gint width,
|
|||
|
gint height );
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
Cette fonction sert <20> cr<63>er un pixmap mono-plan (2 couleurs) <20> partir
|
|||
|
de donn<6E>es en m<>moire. Chaque bit de la donn<6E>e <em/data/. <em/width/
|
|||
|
et <em/height/ sont exprim<69>s en pixels. Le pointeur vers un
|
|||
|
<em/GdkWindow/ pointe sur la fen<65>tre courante car les ressources d'un
|
|||
|
pixmap n'ont de signification que dans le contexte de l'<27>cran o<> il
|
|||
|
doit s'afficher.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
GdkPixmap* gdk_pixmap_create_from_data( GdkWindow *window,
|
|||
|
gchar *data,
|
|||
|
gint width,
|
|||
|
gint height,
|
|||
|
gint depth,
|
|||
|
GdkColor *fg,
|
|||
|
GdkColor *bg );
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Cette fonction est utilis<69>e pour cr<63>er un pixmap d'une profondeur
|
|||
|
donn<EFBFBD>e (nombre de couleurs) <20> partir de la donn<6E>e sp<73>cifi<66>e pas
|
|||
|
<em/data/. <em/fg/ et <em/bg/ sont les couleurs <20> utiliser pour
|
|||
|
l'avant et l'arri<72>re-plan.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
GdkPixmap* gdk_pixmap_create_from_xpm( GdkWindow *window,
|
|||
|
GdkBitmap **mask,
|
|||
|
GdkColor *transparent_color,
|
|||
|
const gchar *filename );
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Le format XPM est une repr<70>sentation des pixmaps reconnue par le syst<73>me X Window. Il est largement utilis<69> et de nombreux utilitaires pour cr<63>er des fichiers d'images <20> ce format sont disponibles. Le fichier <em/filename/ doit contenir une image dans ce format qui sera charg<72>e dans la structure pixmap. Le masque <em/mask/ indique quels sont les bits opaques du pixmap. Tous les autres bits sont coloris<69>s en utilisant la couleur sp<73>cifi<66>e par <em/transparent_color/. Un exemple d'utilisation est pr<70>sent<6E> ci-dessous.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
GdkPixmap* gdk_pixmap_create_from_xpm_d (GdkWindow *window,
|
|||
|
GdkBitmap **mask,
|
|||
|
GdkColor *transparent_color,
|
|||
|
gchar **data);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
De petites images peuvent <20>tre int<6E>gr<67>es dans un programme sous la forme de
|
|||
|
donn<EFBFBD>es <em/data/ au format XPM. Un pixmap est cr<63><72> en utilisant ces donn<6E>es au
|
|||
|
lieu de les lire dans un fichier. Un exemple de telles donn<6E>es est :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
/* 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. ",
|
|||
|
" ......... ",
|
|||
|
" ",
|
|||
|
" "};
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void gdk_pixmap_destroy( GdkPixmap *pixmap );
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
Lorsqu'on a utilis<69> un pixmap et que l'on en a plus besoin tout de suite, il est pr<70>f<EFBFBD>rable de lib<69>rer la ressource en utilisant un appel <20> <em/gdk_pixmap_destroy/. Les pixmaps doivent <20>tre consid<69>r<EFBFBD>es comme une ressource pr<70>cieuse.
|
|||
|
|
|||
|
Quand le pixmap est cr<63><72>, on peut l'afficher comme un widget GTK. On
|
|||
|
doit cr<63>er un widget pixmap qui contiendra le pixmap GDK. Ceci est
|
|||
|
r<EFBFBD>alis<EFBFBD> de la fa<66>on suivante :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
GtkWidget* gtk_pixmap_new( GdkPixmap *pixmap,
|
|||
|
GdkBitmap *mask );
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
Les autres fonctions pour les widgets pixmap sont :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
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);
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
<em/gtk_pixmap_set/ sert <20> changer le pixmap pris en charge par le widget. <em/val/ est le pixmap cr<63><72> par le GDK.
|
|||
|
|
|||
|
Voici un exemple illustrant l'utilisation d'un pixmap dans un bouton :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
|
|||
|
#include <gtk/gtk.h>
|
|||
|
|
|||
|
|
|||
|
/* donn<6E>es XPM d'une ic<69>ne "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<65>e
|
|||
|
* via le signal "delete_event" */
|
|||
|
|
|||
|
void close_application( GtkWidget *widget, GdkEvent *event, gpointer *data )
|
|||
|
{
|
|||
|
gtk_main_quit();
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* Invoqu<71>e lorsque le bouton est cliqu<71>. Affiche simplement
|
|||
|
* un message. */
|
|||
|
|
|||
|
void button_clicked( GtkWidget *widget, gpointer *data )
|
|||
|
{
|
|||
|
printf( "bouton cliqu<71>\n" );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
int main( int argc, char *argv[] )
|
|||
|
{
|
|||
|
/* GtkWidget est le type pour d<>clarer les widgets */
|
|||
|
|
|||
|
GtkWidget *window, *pixmapwid, *button;
|
|||
|
GdkPixmap *pixmap;
|
|||
|
GdkBitmap *mask;
|
|||
|
GtkStyle *style;
|
|||
|
|
|||
|
/* Cr<43>e la fen<65>tre 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 cr<63>er 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 );
|
|||
|
|
|||
|
/* Cr<43>ation d'un widget pixmap GTK pour contenir le pixmap GDK */
|
|||
|
|
|||
|
pixmapwid = gtk_pixmap_new( pixmap, mask );
|
|||
|
gtk_widget_show( pixmapwid );
|
|||
|
|
|||
|
/* Cr<43>ation 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 fen<65>tre */
|
|||
|
gtk_main ();
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
|
|||
|
Pour charger un fichier <20> partir d'un fichier XPM appel<65>
|
|||
|
<em/icon0.xpm/ se trouvant dans le r<>pertoire courant, on aurait cr<63><72>
|
|||
|
le pixmap ainsi :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
/* Charge un pixmap <20> 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 );
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
|
|||
|
Utilisation des formes
|
|||
|
<p>
|
|||
|
Un d<>savantage de l'utilisation des pixmaps est que l'objet affich<63>
|
|||
|
est toujours rectangulaire, quelle que soit l'image. On voudrait
|
|||
|
pouvoir cr<63>er des bureaux et des applications avec des ic<69>nes ayant
|
|||
|
des formes plus naturelles. Par exemple, pour une interface de jeu, on
|
|||
|
aimerait avoir des boutons ronds <20> pousser. Pour faire cela, on doit
|
|||
|
utiliser des fen<65>tres avec des formes.
|
|||
|
|
|||
|
Une fen<65>tre avec forme est simplement un pixmap dont les pixels du
|
|||
|
fond sont transparents. Ainsi, lorsque l'image d'arri<72>re-plan est
|
|||
|
multicolore, on ne la cache pas avec le bord de notre ic<69>ne. L'exemple
|
|||
|
suivant affiche une image de brouette sur le bureau.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
|
|||
|
#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<65>e
|
|||
|
* (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<63>e la fen<65>tre principale et attache le signal "delete_event"
|
|||
|
* pour terminer l'application. On notera que la fen<65>tre 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);
|
|||
|
|
|||
|
/* Cr<43>ation 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-m<>me */
|
|||
|
|
|||
|
gtk_widget_shape_combine_mask( window, mask, 0, 0 );
|
|||
|
|
|||
|
/* Affichage de la fen<65>tre */
|
|||
|
|
|||
|
gtk_widget_set_uposition( window, 20, 400 );
|
|||
|
gtk_widget_show( window );
|
|||
|
gtk_main ();
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
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 <20>
|
|||
|
un clic souris qui provoquera l'arr<72>t de l'application.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
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 );
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
|
|||
|
<sect> Widgets containers
|
|||
|
|
|||
|
<sect1> Bloc-notes
|
|||
|
<p>
|
|||
|
Le widget bloc-notes est un ensemble de <20> pages <20> qui se
|
|||
|
chevauchent. Chaque page contient des informations
|
|||
|
diff<EFBFBD>rentes. R<>cemment, 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
|
|||
|
s<EFBFBD>paration de leur affichage.
|
|||
|
|
|||
|
Le premier appel de fonction que l'on doit conna<6E>tre est, vous
|
|||
|
l'aviez devin<69>, celui qui cr<63>e un widget bloc-notes.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
GtkWidget* gtk_notebook_new (void);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Lorsque le bloc-notes a <20>t<EFBFBD> cr<63><72>, il y a 12 fonctions permettant de
|
|||
|
travailler sur les blocs-notes. <20>tudions-les s<>par<61>ment.
|
|||
|
|
|||
|
La premi<6D>re permet de positionner les indicateurs de pages. Ceux-ci
|
|||
|
(d<>sign<67>s par le mot <20> tab <20> (signet)), peuvent se trouver en haut, en
|
|||
|
bas, <20> gauche ou <20> droite des pages.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_notebook_set_tab_pos (GtkNotebook *notebook, GtkPositionType pos);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
<em/GtkPositionType/ peut prendre les valeurs suivantes qu'il n'est
|
|||
|
pas n<>cessaire d'expliquer :
|
|||
|
<itemize>
|
|||
|
<item> GTK_POS_LEFT
|
|||
|
<item> GTK_POS_RIGHT
|
|||
|
<item> GTK_POS_TOP
|
|||
|
<item> GTK_POS_BOTTOM
|
|||
|
</itemize>
|
|||
|
|
|||
|
GTK_POS_TOP est la valeur par d<>faut.
|
|||
|
|
|||
|
La fonction suivante permet d'ajouter des pages <20> un bloc-notes. Il y
|
|||
|
a trois fa<66>ons d'ajouter des pages. Regardons les deux premi<6D>res qui
|
|||
|
sont tr<74>s semblables.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_notebook_append_page (GtkNotebook *notebook, GtkWidget *child,
|
|||
|
GtkWidget *tab_label);
|
|||
|
|
|||
|
void gtk_notebook_prepend_page (GtkNotebook *notebook, GtkWidget *child,
|
|||
|
GtkWidget *tab_label);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Ces fonctions ajoutent des pages au bloc-notes<em/*notebook/ en les
|
|||
|
ins<EFBFBD>rant <20> la fin (<em/append/) ou au d<>but
|
|||
|
(<em/prepend/). <em/*child/ est le widget qui est plac<61> dans la page
|
|||
|
du bloc-notes, et <em/*tab_label/ est le label de la page qui est
|
|||
|
ajout<EFBFBD>e.
|
|||
|
|
|||
|
La troisi<73>me fonction ajoutant une page <20> un bloc-notes conserve
|
|||
|
toutes les propri<72>t<EFBFBD>s des deux pr<70>c<EFBFBD>dentes, mais elle nous permet en
|
|||
|
plus de sp<73>cifier la position o<> l'on d<>sire ins<6E>rer cette page.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_notebook_insert_page (GtkNotebook *notebook, GtkWidget *child,
|
|||
|
GtkWidget *tab_label, gint position);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Les param<61>tres sont les m<>mes que <em/_append_/ et <em/_prepend_/ sauf
|
|||
|
qu'il y en a un de plus : <em/position/. Celui-ci sert <20>
|
|||
|
sp<EFBFBD>cifier l'endroit o<> cette page sera ins<6E>r<EFBFBD>e.
|
|||
|
|
|||
|
Maintenant que nous savons ins<6E>rer une page, voyons comment en supprimer une.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_notebook_remove_page (GtkNotebook *notebook, gint page_num);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Cette fonction <20>te la page sp<73>cifi<66>e par <em/page_num/ du widget
|
|||
|
<em/*notebook/.
|
|||
|
|
|||
|
Pour conna<6E>tre la page courante d'un bloc-notes, on dispose de la
|
|||
|
fonction :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
gint gtk_notebook_current_page (GtkNotebook *notebook);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Les deux fonctions suivantes permettent de passer <20> la page suivante
|
|||
|
ou pr<70>c<EFBFBD>dente d'un bloc-notes. Il suffit de faire l'appel de la
|
|||
|
fonction ad<61>quate avec le widget sur lequel on veut
|
|||
|
op<EFBFBD>rer. Remarque : lorsqu'on est sur la derni<6E>re page du
|
|||
|
bloc-notes et que l'on appelle <em/gtk_notebook_next_page/, on revient
|
|||
|
<EFBFBD> la premi<6D>re page. De m<>me, si l'on est sur la premi<6D>re page et que
|
|||
|
l'onappelle <em/gtk_notebook_prev_page/, on se retrouve sur sa
|
|||
|
derni<EFBFBD>re page.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_notebook_next_page (GtkNoteBook *notebook);
|
|||
|
void gtk_notebook_prev_page (GtkNoteBook *notebook);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
La fonction qui suit permet de choisir la page <20> active <20>. Si vous
|
|||
|
voulez ouvrir le bloc-notes <20> la page 5, par exemple, vous utiliserez
|
|||
|
cette fonction. Sans elle, le bloc-notes s'ouvre sur sa premi<6D>re page
|
|||
|
par d<>faut.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_notebook_set_page (GtkNotebook *notebook, gint page_num);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Les deux fonctions suivantes ajoutent ou <20>tent les indicateurs de page
|
|||
|
et le contour du bloc-notes, respectivement.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_notebook_set_show_tabs (GtkNotebook *notebook, gint show_tabs);
|
|||
|
void gtk_notebook_set_show_border (GtkNotebook *notebook, gint show_border);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
<em/show_tabs/ et <em/show_border/ peuvent valoir TRUE ou FALSE (0 ou 1).
|
|||
|
|
|||
|
Voyons maintenant un exemple, il est tir<69> du code de <em/testgtk.c/ de
|
|||
|
la distribution GTK et montre l'utilisation des 13 fonctions. Ce petit
|
|||
|
programme cr<63>e une fen<65>tre contenant un bloc-notes et six boutons. Le
|
|||
|
bloc-notes contient 11 pages, ajout<75>es par trois moyens
|
|||
|
diff<EFBFBD>rents : <20> la fin, au milieu et au d<>but. Les boutons
|
|||
|
permettent de faire tourner les indicateurs de page, ajouter/<2F>ter les
|
|||
|
indicateurs et le contour, <20>ter une page, passer <20> la page suivante et
|
|||
|
pr<EFBFBD>c<EFBFBD>dente, et sortir du programme.
|
|||
|
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
|
|||
|
#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 rafra<72>chir le widget --
|
|||
|
* ce qui force le widget <20> 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);
|
|||
|
|
|||
|
/* Cr<43>ation 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 <20> 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 <20> un endroit pr<70>cis. */
|
|||
|
|
|||
|
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 d<>but 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 d<>part (page 4) */
|
|||
|
|
|||
|
gtk_notebook_set_page (GTK_NOTEBOOK(notebook), 3);
|
|||
|
|
|||
|
|
|||
|
/* Cr<43>ation 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 pr<70>c<EFBFBD>dente");
|
|||
|
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;
|
|||
|
}
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
En esp<73>rant que ceci vous aide <20> cr<63>er des blocs-notes pour vos
|
|||
|
applications GTK.
|
|||
|
|
|||
|
|
|||
|
<sect1> Fen<65>tres avec barres de d<>filement
|
|||
|
<p>
|
|||
|
Les fen<65>tres avec barres de d<>filement servent <20> cr<63>er des zones
|
|||
|
d<EFBFBD>filantes <20> l'int<6E>rieur d'une vraie fen<65>tre. On peut ins<6E>rer
|
|||
|
n'importe quel widget dans ces fen<65>tres, ils seront accessibles quelle
|
|||
|
que soit leur taille en utilisant les barres de d<>filement.
|
|||
|
|
|||
|
|
|||
|
La fonction suivante sert <20> cr<63>er une fen<65>tre avec barre de
|
|||
|
d<EFBFBD>filement :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
GtkWidget* gtk_scrolled_window_new (GtkAdjustment *hadjustment,
|
|||
|
GtkAdjustment *vadjustment);
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
Le premier param<61>tre est l'ajustement horizontal, et le second
|
|||
|
l'ajustement vertical. Ils sont presque toujours positionn<6E>s <20> NULL.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_scrolled_window_set_policy (GtkScrolledWindow *scrolled_window,
|
|||
|
GtkPolicyType hscrollbar_policy,
|
|||
|
GtkPolicyType vscrollbar_policy);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Cela permet de configurer le fonctionnement des barres de
|
|||
|
d<EFBFBD>filement. Le premier param<61>tre est la fen<65>tre <20> d<>filement que l'on
|
|||
|
veut modifier, le second configure le fonctionnement de la barre
|
|||
|
horizontale et le troisi<73>me celui de la barre verticale.
|
|||
|
|
|||
|
Ce fonctionnement peut <20>tre GTK_POLICY AUTOMATIC ou GTK_POLICY_ALWAYS.
|
|||
|
GTK_POLICY_AUTOMATIC d<>cidera automatiquement de votre besoin en
|
|||
|
barres de d<>filement, alors que GTK_POLICY_ALWAYS mettra toujours
|
|||
|
celles-ci.
|
|||
|
|
|||
|
Voici un exemple simple qui place 100 boutons commutateurs dans une
|
|||
|
fen<EFBFBD>tre <20> d<>filement. Je n'ai comment<6E> que les parties qui sont
|
|||
|
nouvelles pour vous.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
#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);
|
|||
|
|
|||
|
/* Cr<43>ation d'une bo<62>te de dialogue pour y placer la fen<65>tre <20> d<>filement.
|
|||
|
* Une bo<62>te de dialogue est une fen<65>tre comme les autres sauf qu'elle contient
|
|||
|
* une vbox et un s<>parateur horizontal. Ce n'est qu'un raccourci pour cr<63>er 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);
|
|||
|
|
|||
|
/* Cr<43>ation d'une fen<65>tre <20> d<>filement. */
|
|||
|
|
|||
|
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 d<>cide automatiquement s'il faut ou non des barres,
|
|||
|
* GTK_POLICY_ALWAYS met toujours des barres
|
|||
|
* Le premier param<61>tre correspond <20> la barre horizontale,
|
|||
|
* le second <20> la barre verticale. */
|
|||
|
|
|||
|
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
|
|||
|
GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
|
|||
|
|
|||
|
/* Cr<43>ation d'une bo<62>te de dialogue */
|
|||
|
|
|||
|
gtk_box_pack_start (GTK_BOX (GTK_DIALOG(window)->vbox), scrolled_window,
|
|||
|
TRUE, TRUE, 0);
|
|||
|
gtk_widget_show (scrolled_window);
|
|||
|
|
|||
|
/* Cr<43>ation 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 fen<65>tre <20> d<>filement */
|
|||
|
|
|||
|
gtk_container_add (GTK_CONTAINER (scrolled_window), table);
|
|||
|
gtk_widget_show (table);
|
|||
|
|
|||
|
/* Cr<43>e 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 <20> Fermer <20> en bas de la bo<62>te 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 <20> bouton par d<>faut <20>. */
|
|||
|
|
|||
|
GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
|
|||
|
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->action_area), button, TRUE, TRUE, 0);
|
|||
|
|
|||
|
/* R<>cup<75>re le bouton par d<>faut. Le fait de presser la touche <20> Entr<74>e <20>
|
|||
|
* activera le bouton. */
|
|||
|
|
|||
|
gtk_widget_grab_default (button);
|
|||
|
gtk_widget_show (button);
|
|||
|
|
|||
|
gtk_widget_show (window);
|
|||
|
|
|||
|
gtk_main();
|
|||
|
|
|||
|
return(0);
|
|||
|
}
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
Essayez de changer la taille de la fen<65>tre et faites attention aux
|
|||
|
r<EFBFBD>actions des barres de d<>filement. On peut aussi utiliser la fonction
|
|||
|
<em/gtk_widget_set_usize()/ pour configurer la taille par d<>faut de la
|
|||
|
fen<EFBFBD>tre et des autres widgets.
|
|||
|
|
|||
|
|
|||
|
<sect>Widgets listes
|
|||
|
<p>
|
|||
|
Le widget <em/GtkList/ sert de container vertical pour des widgets <em/GtkListItem/.
|
|||
|
|
|||
|
Un widget <em/GtkList/ poss<73>de sa propre fen<65>tre pour recevoir les
|
|||
|
<EFBFBD>v<EFBFBD>nements et sa propre couleur de fond qui est habituellement
|
|||
|
blanche. Comme il est directement d<>riv<69> de <em/GtkContainer/, il peut
|
|||
|
<EFBFBD>tre trait<69> comme tel en utilisant la macro GTK_CONTAINER(List) :
|
|||
|
voir le widget <em/GtkContainer/ pour en savoir plus.
|
|||
|
|
|||
|
On doit d'abord conna<6E>tre l'utilisation des <em/GList/ et des
|
|||
|
fonctions <em/g_list_*()/ qui leur sont li<6C>es pour pouvoir utiliser
|
|||
|
pleinement le widget <em/GtkList/.
|
|||
|
|
|||
|
Un champ de la structure d'un widget <em/GtkList/ nous int<6E>resse particuli<6C>rement :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
struct _GtkList
|
|||
|
{
|
|||
|
...
|
|||
|
GList *selection;
|
|||
|
guint selection_mode;
|
|||
|
...
|
|||
|
};
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Le champ <em/selection/ d'un <em/GtkList/ pointe sur une liste cha<68>n<EFBFBD>e
|
|||
|
de tous les items qui sont s<>lectionn<6E>s, ou vaut NULL si aucune
|
|||
|
s<EFBFBD>lection n'est faite. Ainsi, pour conna<6E>tre la s<>lection courante, on
|
|||
|
consulte le champ <em/GTK_LIST()->selection/ mais on ne doit pas le
|
|||
|
modifier car ses champs internes sont g<>r<EFBFBD>s par les fonctions
|
|||
|
<em/gtk_list_*()/.
|
|||
|
|
|||
|
Le champ <em/selection_mode/ d<>termine les options de s<>lection d'un
|
|||
|
<em/GtkList/ et donc le contenu du champ du
|
|||
|
<em/GTK_LIST()->selection/ :
|
|||
|
|
|||
|
<em/selection_mode/ peut avoir l'une des valeurs suivantes :
|
|||
|
<itemize>
|
|||
|
<item> GTK_SELECTION_SINGLE - <em/selection/ vaut NULL ou contient un
|
|||
|
pointeur vers un seul item s<>lectionn<6E>.
|
|||
|
|
|||
|
<item> GTK_SELECTION_BROWSE - <em/selection/ vaut NULL si la liste ne
|
|||
|
contient aucun widget ou seulement des widgets non sensitifs. Sinon, ce
|
|||
|
champ contient un pointeur vers une seule structure Glist, et donc
|
|||
|
vers exactement un item.
|
|||
|
|
|||
|
<item> GTK_SELECTION_MULTIPLE - <em/selection/ vaut NULL si aucun item
|
|||
|
n'est s<>lectionn<6E> ou pointe vers le premier item s<>lectionn<6E>. Ce
|
|||
|
dernier point <20> son tour vers le second item, etc.
|
|||
|
|
|||
|
<item> GTK_SELECTION_EXTENDED - <em/selection/ vaut toujours NULL.
|
|||
|
</itemize>
|
|||
|
<p>
|
|||
|
La valeur par d<>faut est GTK_SELECTION_MULTIPLE.
|
|||
|
|
|||
|
<sect1>Signaux
|
|||
|
<p>
|
|||
|
<tscreen><verb>
|
|||
|
void GtkList::selection_changed (GtkList *LIST)
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Ce signal sera invoqu<71> <20> chaque fois que le champ s<>lection d'un
|
|||
|
GtkList a chang<6E>. Cela arrive lorsqu'un fils d'un GtkList a <20>t<EFBFBD>
|
|||
|
s<EFBFBD>lectionn<EFBFBD> ou d<>s<EFBFBD>lectionn<6E>.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void GtkList::select_child (GtkList *LIST, GtkWidget *CHILD)
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Ce signal est invoqu<71> lorsqu'un fils du GtkList va <20>tre
|
|||
|
s<EFBFBD>lectionn<EFBFBD>. Ceci arrive principalement lors d'appels <20>
|
|||
|
<em/gtk_list_select_item(), gtk_list_select_child()/ et lors d'appuis
|
|||
|
de boutons. Quelques fois, il est indirectement d<>clench<63> lorsque des
|
|||
|
fils sont ajout<75>s ou supprim<69>s du GtkList.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void GtkList::unselect_child (GtkList *LIST, GtkWidget *CHILD)
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Ce signal est invoqu<71> lorsqu'un fils du GtkList va <20>tre
|
|||
|
d<EFBFBD>s<EFBFBD>lectionn<EFBFBD>. Cela arrive principalement lors d'appels <20>
|
|||
|
<em/gtk_list_unselect_item(), gtk_list_unselect_child()/, et lors
|
|||
|
d'appuis de boutons. Quelques fois, il est indirectement d<>clench<63>
|
|||
|
lorsque des fils sont ajout<75>s ou supprim<69>s du GtkList.
|
|||
|
|
|||
|
|
|||
|
<sect1>Fonctions
|
|||
|
<p>
|
|||
|
<tscreen><verb>
|
|||
|
guint gtk_list_get_type (void)
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Retourne l'identificateur de type <20> GtkList <20>.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
GtkWidget* gtk_list_new (void)
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Cr<EFBFBD>e un nouvel objet <20> GtkList <20>. Le nouveau widget est retourn<72> sous
|
|||
|
la forme d'un pointeur vers un objet <20> GtkWidget <20>. NULL est retourn<72>
|
|||
|
en cas d'erreur.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_list_insert_items (GtkList *LIST, GList *ITEMS, gint POSITION)
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Ins<EFBFBD>re des items dans <em/LIST/, <20> partir de <em/POSITION/.
|
|||
|
<em/ITEMS/ est une liste doublement cha<68>n<EFBFBD>e o<> chaque noeud doit
|
|||
|
pointer vers un nouveau <em/GtkListItem/. Les noeuds <em/GList/ de
|
|||
|
<em/ITEMS/ sont pris en charge par <em/LIST/.
|
|||
|
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_list_append_items (GtkList *LIST, GList *ITEMS)
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Ins<EFBFBD>re des items <20> la fin de <em/LIST/ selon le m<>me principe que
|
|||
|
<em/gtk_list_insert_items/. Les noeuds <em/GList/ de <em/ITEMS/ sont
|
|||
|
pris en charge par <em/LIST/.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_list_prepend_items (GtkList *LIST, GList *ITEMS)
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Ins<EFBFBD>re des items au d<>but de <em/LIST/ selon le m<>me principe que
|
|||
|
<em/gtk_list_insert_items/. Les noeuds <em/GList/ de <em/ITEMS/ sont
|
|||
|
pris en charge par <em/LIST/.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_list_remove_items (GtkList *LIST, GList *ITEMS)
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
<EFBFBD>te des items de <em/LIST/. <em/ITEMS/ est une liste doublement
|
|||
|
cha<EFBFBD>n<EFBFBD>e dont chaque noeud pointe vers un fils direct de <em/LIST/. Il
|
|||
|
est de la responsabilit<69> de l'appelant de faire un appel <20>
|
|||
|
<em/g_list_free(ITEMS)/ apr<70>s cela. L'appelant doit aussi d<>truire
|
|||
|
lui-m<>me les items.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_list_clear_items (GtkList *LIST, gint START, gint END)
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
<EFBFBD>te et d<>truit des items de <em/LIST/. Un widget est concern<72> si sa
|
|||
|
position courante dans <em/LIST/ est dans l'intervalle sp<73>cifi<66> par
|
|||
|
<em/START/ et <em/END/.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_list_select_item (GtkList *LIST, gint ITEM)
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Invoque le signal <em/GtkList::select_child/ pour un item sp<73>cifi<66> par
|
|||
|
sa position courante dans <em/LIST/.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_list_unselect_item (GtkList *LIST, gint ITEM)
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Invoque le signal <em/GtkList::unselect_child/ pour un item sp<73>cifi<66> par
|
|||
|
sa position courante dans <em/LIST/.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_list_select_child (GtkList *LIST, GtkWidget *CHILD)
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Invoque le signal <em/GtkList::select_child/ pour le fils <em/CHILD/ sp<73>cifi<66>.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_list_unselect_child (GtkList *LIST, GtkWidget *CHILD)
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Invoque le signal <em/GtkList::unselect_child/ pour le fils <em/CHILD/ sp<73>cifi<66>.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
gint gtk_list_child_position (GtkList *LIST, GtkWidget *CHILD)
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Retourne la position de <em/CHILD/ dans <em/LIST/. Retourne -1 en cas d'erreur.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_list_set_selection_mode (GtkList *LIST, GtkSelectionMode MODE)
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Configure <em/LIST/ dans le mode de s<>lection <em/MODE/ qui peut <20>tre
|
|||
|
GTK_SELECTION_SINGLE, GTK_SELECTION_BROWSE, GTK_SELECTION_MULTIPLE ou
|
|||
|
GTK_SELECTION_EXTENDED.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
GtkList* GTK_LIST (gpointer OBJ)
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Convertit un pointeur g<>n<EFBFBD>rique en <20> <\em GtkList*\ <20>. Voir
|
|||
|
<em/Standard Macros::/, pour plus d'informations.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
GtkListClass* GTK_LIST_CLASS (gpointer CLASS)
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Convertit un pointeur g<>n<EFBFBD>rique en <20> GtkListClass* <20>. Voir
|
|||
|
<em/Standard Macros::/, pour plus d'informations.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
gint GTK_IS_LIST (gpointer OBJ)
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
D<EFBFBD>termine si un pointeur g<>n<EFBFBD>rique r<>f<EFBFBD>rence un objet <20> GtkList <20>. Voir
|
|||
|
<em/Standard Macros::/, pour plus d'informations.
|
|||
|
|
|||
|
<sect1>Exemple
|
|||
|
<p>
|
|||
|
Voici un programme affichant les changements de s<>lection dans une
|
|||
|
<em/GtkList/ et permettant d'<27> emprisonner <20> des items en les
|
|||
|
s<EFBFBD>lectionnant avec le bouton droit de la souris.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
/* 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>
|
|||
|
|
|||
|
/* Cha<68>ne pour stocker les donn<6E>es 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);
|
|||
|
|
|||
|
|
|||
|
/* Cr<43>ation d'une fen<65>tre pour placer tous les widgets.
|
|||
|
* Connexion de gtk_main_quit() <20> l'<27>v<EFBFBD>nement "destroy" de
|
|||
|
* la fen<65>tre afin de prendre en charge les <20>v<EFBFBD>nements <20> fermeture d'une
|
|||
|
* fen<65>tre <20> du gestionnaire de fen<65>tre. */
|
|||
|
|
|||
|
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);
|
|||
|
|
|||
|
|
|||
|
/* <20> l'int<6E>rieur de la fen<65>tre, on a besoin d'une bo<62>te 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);
|
|||
|
|
|||
|
/* Fen<65>tre <20> d<>filement pour placer le widget GtkList <20> l'int<6E>rieur. */
|
|||
|
|
|||
|
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);
|
|||
|
|
|||
|
/* Cr<43>ation du widget GtkList
|
|||
|
* Connexion du gestionnaire de signal sigh_print_selection() au signal
|
|||
|
* "selection_changed" du GtkList pour afficher les items s<>lectionn<6E>s
|
|||
|
* <20> chaque fois que la s<>lection 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);
|
|||
|
|
|||
|
/* Cr<43>ation d'une <20> Prison <20> 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
|
|||
|
* <20> mise au arr<72>ts <20> des items du GtkList. */
|
|||
|
|
|||
|
gtk_signal_connect(GTK_OBJECT(gtklist),
|
|||
|
"button_release_event",
|
|||
|
GTK_SIGNAL_FUNC(sigh_button_event),
|
|||
|
frame);
|
|||
|
|
|||
|
/* Cr<43>ation d'un s<>parateur. */
|
|||
|
|
|||
|
separator=gtk_hseparator_new();
|
|||
|
gtk_container_add(GTK_CONTAINER(vbox), separator);
|
|||
|
gtk_widget_show(separator);
|
|||
|
|
|||
|
/* Cr<43>ation d'un bouton et connexion de son signal "clicked" <20> la
|
|||
|
* destruction de la fen<65>tre. */
|
|||
|
|
|||
|
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));
|
|||
|
|
|||
|
|
|||
|
/* Cr<43>ation 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 <20> 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);
|
|||
|
}
|
|||
|
/* Cr<43>ation de 5 autres labels. Cette fois-ci, on utilise
|
|||
|
* gtk_list_item_new_with_label(). On ne peut interroger la cha<68>ne
|
|||
|
* 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 m<>me
|
|||
|
* cha<68>ne de texte pour l'ajouter au items que l'on place dans une liste
|
|||
|
* doublement cha<68>n<EFBFBD>e (GList). On les ajoute alors par un simple appel <20>
|
|||
|
* gtk_list_append_items().
|
|||
|
* Comme on utilise g_list_prepend() pour mettre les items dans la liste
|
|||
|
* doublement cha<68>n<EFBFBD>e, leur ordre sera d<>croissant (au lieu d'<27>tre croissant si
|
|||
|
* on utilisait g_list_append()). */
|
|||
|
|
|||
|
dlist=NULL;
|
|||
|
for (; i<10; i++) {
|
|||
|
sprintf(buffer, "Item avec le label %d", i);
|
|||
|
list_item=gtk_list_item_new_with_label(buffer);
|
|||
|
dlist=g_list_prepend(dlist, list_item);
|
|||
|
gtk_widget_show(list_item);
|
|||
|
gtk_object_set_data(GTK_OBJECT(list_item),
|
|||
|
list_item_data_key,
|
|||
|
"Item avec label int<6E>gr<67>");
|
|||
|
}
|
|||
|
gtk_list_append_items(GTK_LIST(gtklist), dlist);
|
|||
|
|
|||
|
/* Enfin, on veut voir la fen<65>tre... */
|
|||
|
|
|||
|
gtk_widget_show(window);
|
|||
|
|
|||
|
/* Lancement de la boucle principale de gtk */
|
|||
|
|
|||
|
gtk_main();
|
|||
|
|
|||
|
/* On arrive ici apr<70>s que gtk_main_quit() ait <20>t<EFBFBD> appel<65>e lorsque
|
|||
|
* la fen<65>tre principale a <20>t<EFBFBD> d<>truite. */
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
/* Gestionnaire de signal connect<63> aux <20>v<EFBFBD>nements boutons presser/rel<65>cher
|
|||
|
* du GtkList. */
|
|||
|
|
|||
|
void
|
|||
|
sigh_button_event (GtkWidget *gtklist,
|
|||
|
GdkEventButton *event,
|
|||
|
GtkWidget *frame)
|
|||
|
{
|
|||
|
/* On ne fait quelque chose que si le troisi<73>me bouton (celui de droite) a <20>t<EFBFBD>
|
|||
|
* rel<65>ch<63>. */
|
|||
|
|
|||
|
if (event->type==GDK_BUTTON_RELEASE &&
|
|||
|
event->button==3) {
|
|||
|
GList *dlist, *free_list;
|
|||
|
GtkWidget *new_prisoner;
|
|||
|
|
|||
|
/* On recherche l'item s<>lectionn<6E> <20> ce moment pr<70>cis.
|
|||
|
* 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 d<>j<EFBFBD> prisonniers et on les
|
|||
|
* remet dans la liste.
|
|||
|
* Il ne faut pas oublier de lib<69>rer la liste doublement
|
|||
|
* cha<68>n<EFBFBD>e 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'<27>te du GtkList et on le place
|
|||
|
* dans le cadre <20> Prison <20>. On doit d<>s<EFBFBD>lectionner l'item avant.
|
|||
|
|
|||
|
if (new_prisoner) {
|
|||
|
GList static_dlist;
|
|||
|
|
|||
|
static_dlist.data=new_prisoner;
|
|||
|
static_dlist.next=NULL;
|
|||
|
static_dlist.prev=NULL;
|
|||
|
|
|||
|
gtk_list_unselect_child(GTK_LIST(gtklist),
|
|||
|
new_prisoner);
|
|||
|
gtk_widget_reparent(new_prisoner, frame);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Gestionnaire de signal appel<65> lorsque le GtkList
|
|||
|
* <20>met le signal "selection_changed". */
|
|||
|
|
|||
|
void
|
|||
|
sigh_print_selection (GtkWidget *gtklist,
|
|||
|
gpointer func_data)
|
|||
|
{
|
|||
|
GList *dlist;
|
|||
|
|
|||
|
/* Recherche dans la liste doublement cha<68>n<EFBFBD>e des items s<>lectionn<6E>s
|
|||
|
* du GtkList, <20> faire en lecture seulement ! */
|
|||
|
|
|||
|
dlist=GTK_LIST(gtklist)->selection;
|
|||
|
|
|||
|
/* S'il n'y a pas d'items s<>lectionn<6E>, il n'y a rien d'autre <20> faire que
|
|||
|
* de le dire <20> l'utilisateur. */
|
|||
|
|
|||
|
if (!dlist) {
|
|||
|
g_print("S<>lection nettoy<6F>e\n");
|
|||
|
return;
|
|||
|
}
|
|||
|
/* Ok, on a une s<>lection et on l'affiche. */
|
|||
|
|
|||
|
g_print("La s<>lection est ");
|
|||
|
|
|||
|
/* On r<>cup<75>re l'item dans la liste doublement cha<68>n<EFBFBD>e
|
|||
|
* puis on interroge la donn<6E>e associ<63>e 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");
|
|||
|
}
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
<sect1>Widget item de liste
|
|||
|
<p>
|
|||
|
Le widget <em/GtkListItem/ sert de container pour contenir un fils,
|
|||
|
lui fournissant des fonctions de s<>lection/d<>s<EFBFBD>selection exactement
|
|||
|
comme le widget GtkList les demande pour ses fils.
|
|||
|
|
|||
|
Un <em/GtkListItem/ a sa propre fen<65>tre pour recevoir les <20>v<EFBFBD>nements et a
|
|||
|
sa propre couleur de fond, habituellement blanche.
|
|||
|
|
|||
|
Comme il est directement d<>riv<69> d'un <em/GtkItem/, il peut <20>tre trait<69>
|
|||
|
comme tel en utilisant la macro GTK_ITEM(ListItem), reportez-vous <20> la
|
|||
|
section sur le widget GtkItem pour plus de d<>tail sur celui-ci.
|
|||
|
Habituellement, un <em/GtkListItem/ contient juste un label pour
|
|||
|
identifier, par exemple, un nom de fichier dans un <em/GtkList/ -- la
|
|||
|
fonction appropri<72>e <em/gtk_list_item_new_with_label()/ est donc
|
|||
|
fournie. Le m<>me effet peut <20>tre obtenu en cr<63>ant un <em/GtkLabel/ <20>
|
|||
|
part, en configurant son alignement avec <em/xalign/=0 et
|
|||
|
<em/yalign/=0.5 suivi d'un ajout ult<6C>rieur au <em/GtkListItem/.
|
|||
|
|
|||
|
Tout comme on n'est pas forc<72> d'ajouter un <em/GtkLabel/ <20> un
|
|||
|
<em/GtkListItem/, on peut aussi ajouter un <em/GtkVBox/ ou un
|
|||
|
<em/GtkArrow/ etc. <20> un <em/GtkListItem/.
|
|||
|
|
|||
|
<sect1>Signaux
|
|||
|
<p>
|
|||
|
Un <em/GtkListItem/ ne cr<63>e pas de nouveaux signaux par lui-m<>me, mais
|
|||
|
h<EFBFBD>rite de ceux d'un <em/GtkItem/. Voir <em/GtkItem::/, pour plus
|
|||
|
d'informations.
|
|||
|
|
|||
|
|
|||
|
<sect1>Fonctions
|
|||
|
<p>
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
guint gtk_list_item_get_type (void)
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Retourne l'identificateur du type <20> GtkListItem <20>.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
GtkWidget* gtk_list_item_new (void)
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Cr<EFBFBD>ation d'un objet <em/GtkListItem/. Le nouveau widget est retourn<72>
|
|||
|
sous la forme d'un pointeur vers un objet <em/GtkWidget/. NULL est
|
|||
|
retourn<EFBFBD> en cas d'erreur.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
GtkWidget* gtk_list_item_new_with_label (gchar *LABEL)
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Cr<EFBFBD>ation d'un objet <em/GtkListItem/ ayant un simple <em/GtkLabel/
|
|||
|
comme seul fils. Le nouveau widget est retourn<72> sous la forme d'un
|
|||
|
pointeur vers un objet <em/GtkWidget/. NULL est retourn<72> en cas
|
|||
|
d'erreur.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_list_item_select (GtkListItem *LIST_ITEM)
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Cette fonction est surtout un emballage de <em/gtk_item_select
|
|||
|
(GTK_ITEM (list_item))/ qui <20>mettra le signal <em/GtkItem::select/.
|
|||
|
Voir <em/GtkItem::/, pour plus d'informations.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_list_item_deselect (GtkListItem *LIST_ITEM)
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Cette fonction est surtout un emballage de <em/gtk_item_deselect
|
|||
|
(GTK_ITEM (list_item))/ qui <20>mettra le signal
|
|||
|
<em/GtkItem::deselect/. Voir <em/GtkItem::/, pour plus d'informations.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
GtkListItem* GTK_LIST_ITEM (gpointer OBJ)
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Convertit un pointeur g<>n<EFBFBD>rique en <em/GtkListItem*/. Voir
|
|||
|
<em/Standard Macros::/ pour plus d'informations.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
GtkListItemClass* GTK_LIST_ITEM_CLASS (gpointer CLASS)
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Convertit un pointeur g<>n<EFBFBD>rique en <em/GtkListItemClass*/. Voir
|
|||
|
<em/Standard Macros::/ pour plus d'informations.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
gint GTK_IS_LIST_ITEM (gpointer OBJ)
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
D<EFBFBD>termine si un pointeur g<>n<EFBFBD>rique se r<>f<EFBFBD>re <20> un objet
|
|||
|
<em/GtkListItem/. Voir <em/Standard Macros::/ pour plus
|
|||
|
d'informations.
|
|||
|
|
|||
|
<sect1>Exemple
|
|||
|
<p>
|
|||
|
L'exemple des GtkList couvre aussi l'utilisation des
|
|||
|
GtkListItem. <20>tudiez-le attentivement.
|
|||
|
|
|||
|
|
|||
|
<sect>Widgets s<>lections de fichiers
|
|||
|
<p>
|
|||
|
Le widget s<>lection de fichier est un moyen simple et rapide pour
|
|||
|
afficher un fichier dans une bo<62>te de dialogue. Il est complet, avec
|
|||
|
des boutons Ok, Annuler et Aide. C'est un bon moyen de raccourcir les
|
|||
|
temps de programmation.
|
|||
|
|
|||
|
Pour cr<63>er une bo<62>te de s<>lection de fichier, on utilise :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
GtkWidget* gtk_file_selection_new (gchar *title);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Pour configurer le nom de fichier, par exemple pour aller dans un
|
|||
|
r<EFBFBD>pertoire pr<70>cis ou donner un nom de fichier par d<>faut, on utilise
|
|||
|
la fonction :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_file_selection_set_filename (GtkFileSelection *filesel, gchar *filename);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Pour r<>cup<75>rer le texte que l'utilisateur a entr<74>, ou sur lequel il a
|
|||
|
cliqu<EFBFBD>, on utilisera la fonction :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
gchar* gtk_file_selection_get_filename (GtkFileSelection *filesel);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Des pointeurs permettent d'acc<63>der aux widgets contenus dans la widget
|
|||
|
de s<>lection de fichiers. Ce sont :
|
|||
|
|
|||
|
<itemize>
|
|||
|
<item>dir_list
|
|||
|
<item>file_list
|
|||
|
<item>selection_entry
|
|||
|
<item>selection_text
|
|||
|
<item>main_vbox
|
|||
|
<item>ok_button
|
|||
|
<item>cancel_button
|
|||
|
<item>help_button
|
|||
|
</itemize>
|
|||
|
|
|||
|
Le plus souvent, on utilise les pointeurs <em/ok_button, cancel_button/, et
|
|||
|
<em/help_button/ pour pr<70>ciser leurs utilisations.
|
|||
|
|
|||
|
Voici un exemple emprunt<6E> <20> <em/testgtk.c/ et modifi<66> pour fonctionner
|
|||
|
tout seul. Comme vous le verrez, il n'y a pas grand chose <20> faire pour
|
|||
|
cr<EFBFBD>er un wigdget de s<>lection de fichier. Cependant, dans cet exemple,
|
|||
|
si le bouton Aide appara<72>t <20> l'<27>cran, il ne fait rien car aucun signal
|
|||
|
ne lui est attach<63>.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
#include <gtk/gtk.h>
|
|||
|
|
|||
|
/* R<>cup<75>re le nom de fichier s<>lectionn<6E> et l'affiche sur la console. */
|
|||
|
|
|||
|
void file_ok_sel (GtkWidget *w, GtkFileSelection *fs)
|
|||
|
{
|
|||
|
g_print ("%s\n", gtk_file_selection_get_filename (GTK_FILE_SELECTION (fs)));
|
|||
|
}
|
|||
|
|
|||
|
void destroy (GtkWidget *widget, gpointer *data)
|
|||
|
{
|
|||
|
gtk_main_quit ();
|
|||
|
}
|
|||
|
|
|||
|
int main (int argc, char *argv[])
|
|||
|
{
|
|||
|
GtkWidget *filew;
|
|||
|
|
|||
|
gtk_init (&argc, &argv);
|
|||
|
|
|||
|
/* Cr<43>ation d'un widget de s<>lection de fichier. */
|
|||
|
|
|||
|
filew = gtk_file_selection_new ("File selection");
|
|||
|
|
|||
|
gtk_signal_connect (GTK_OBJECT (filew), "destroy",
|
|||
|
(GtkSignalFunc) destroy, &filew);
|
|||
|
|
|||
|
/* Connexion de ok_button <20> 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 d<>truire 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 d<>faut. */
|
|||
|
|
|||
|
gtk_file_selection_set_filename (GTK_FILE_SELECTION(filew),
|
|||
|
"penguin.png");
|
|||
|
|
|||
|
gtk_widget_show(filew);
|
|||
|
gtk_main ();
|
|||
|
return 0;
|
|||
|
}
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
<sect>Widgets Menu
|
|||
|
<p>
|
|||
|
Il y a deux fa<66>ons de cr<63>er des menus, la facile et la compliqu<71>e. Les
|
|||
|
deux ont leur utilit<69>, mais on peut g<>n<EFBFBD>ralement utiliser l'<em/usine
|
|||
|
<EFBFBD> menus/ (c'est la m<>thode facile...). La m<>thode <20> compliqu<71>e <20>
|
|||
|
consiste <20> cr<63>er tous les menus en utilisant directement les
|
|||
|
appels. La m<>thode facile consiste <20> utiliser les appels
|
|||
|
<em/gtk_menu_factory/. C'est beaucoup plus simple, mais chaque
|
|||
|
approche a ses avantages et inconv<6E>nients.
|
|||
|
|
|||
|
L'usine <20> menus est beaucoup plus facile <20> utiliser, elle facilite
|
|||
|
aussi l'ajout d'autres menus. Par contre, <20>crire quelques fonctions
|
|||
|
permettant de cr<63>er des menus en utilisant la m<>thode manuelle peut
|
|||
|
<EFBFBD>tre le d<>but d'un long chemin avant une quelconque utilisation. Avec
|
|||
|
l'usine <20> menus, il n'est pas possible d'ajouter des images ou des <20> /
|
|||
|
<EFBFBD> aux menus.
|
|||
|
<p>
|
|||
|
<sect1>Cr<43>ation manuelle de menus
|
|||
|
<p>
|
|||
|
Selon la tradition p<>dagogique, nous commencerons par le plus compliqu<71> <tt/:)/
|
|||
|
<p>
|
|||
|
Regardons les fonctions utilis<69>es pour cr<63>er les menus. La premi<6D>re sert <20> cr<63>er un nouveau menu.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
GtkWidget *gtk_menu_bar_new()
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Cette fonction cr<63>e une nouvelle barre de menu. On utilise la fonction
|
|||
|
<em/gtk_container_add/ pour la placer dans une fen<65>tre, ou les
|
|||
|
fonctions <em/box_pack/ pour la placer dans une bo<62>te - la m<>me que
|
|||
|
pour les boutons.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
GtkWidget *gtk_menu_new();
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Cette fonction retourne un pointeur vers un nouveau menu, il n'est
|
|||
|
jamais montr<74> (avec <em/gtk_widget_show/), il ne fait que contenir les
|
|||
|
items du menu. Ceci deviendra plus clair lorsque nous <20>tudierons
|
|||
|
l'exemple ci-dessous.
|
|||
|
<p>
|
|||
|
Les deux appels suivants servent <20> cr<63>er des items de menu qui seront
|
|||
|
plac<EFBFBD>s dans le menu.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
GtkWidget *gtk_menu_item_new()
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
et
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
GtkWidget *gtk_menu_item_new_with_label(const char *label)
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Ces appels servent <20> cr<63>er les menus qui doivent <20>tre affich<63>s. On
|
|||
|
doit bien faire la diff<66>rence entre un <20> menu <20> qui est cr<63><72> avec
|
|||
|
<em/gtk_menu_new()/ et un <20> item de menu <20> cr<63><72> avec les fonctions
|
|||
|
<em/gtk_menu_item_new()/. L'item de menu sera un v<>ritable bouton avec
|
|||
|
une action associ<63>e alors qu'un menu sera un container contenant les
|
|||
|
items.
|
|||
|
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
gtk_menu_item_append()
|
|||
|
|
|||
|
gtk_menu_item_set_submenu()
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Les fonctions <em/gtk_menu_new_with_label()/ et <em/gtk_menu_new()/
|
|||
|
sont telles que vous les attendiez apr<70>s avoir <20>tudi<64> les
|
|||
|
boutons. L'une cr<63>e un nouvel item de menu contenant d<>j<EFBFBD> un label, et
|
|||
|
l'autre ne fait que cr<63>er un item de menu vide.
|
|||
|
<p>
|
|||
|
Voici les <20>tapes pour cr<63>er une barre de menu avec des menus attach<63>s :
|
|||
|
<itemize>
|
|||
|
<item>Cr<43>er un nouveau menu avec <em/gtk_menu_new()/ <item>Cr<43>er un
|
|||
|
item de menu avec <em/gtk_menu_item_new()/. Ce sera la racine du
|
|||
|
menu, le texte apparaissant ici sera aussi sur la barre de menu.
|
|||
|
<item>Utiliser plusieurs appels <20> <em/gtk_menu_item_new()/ pour
|
|||
|
chaque item que l'on d<>sire dans le menu. Utiliser
|
|||
|
<em/gtk_menu_item_append()/ pour placer chacun de ces items les uns
|
|||
|
apr<EFBFBD>s les autres. Cela cr<63>e une liste d'items de menu.
|
|||
|
<item>Utiliser <em/gtk_menu_item_set_submenu()/ pour attacher les
|
|||
|
items de menus nouvellement cr<63><72>s <20> l'item de menu racine (celui cr<63><72>
|
|||
|
<EFBFBD> la seconde <20>tape).
|
|||
|
<item>Cr<43>er une nouvelle barre de menu avec
|
|||
|
<em/gtk_menu_bar_new()/. Cette <20>tape ne doit <20>tre faite qu'une fois
|
|||
|
lorsque l'on cr<63>e une s<>rie de menu sur une seule barre de menus.
|
|||
|
<item>Utiliser <em/gtk_menu_bar_append()/ pour placer le menu racine
|
|||
|
dans la barre de menu.
|
|||
|
</itemize>
|
|||
|
<p>
|
|||
|
La cr<63>ation d'un menu surgissant est presque identique. La diff<66>rence
|
|||
|
est que le menu n'est pas post<73> <20> automatiquement <20> par une barre de
|
|||
|
menu, mais explicitement en appelant la fonction <em/gtk_menu_popup()/
|
|||
|
par un <20>v<EFBFBD>nement <20> bouton press<73> <20>.
|
|||
|
|
|||
|
Suivez ces <20>tapes
|
|||
|
<itemize>
|
|||
|
<item>Cr<43>er une fonction de gestion d'<27>v<EFBFBD>nement. Elle doit avoir le prototype
|
|||
|
<tscreen>
|
|||
|
static gint handler(GtkWidget *widget, GdkEvent *event);
|
|||
|
</tscreen>
|
|||
|
et elle utilisera l'<27>v<EFBFBD>nement <em/event/ pour savoir o<> faire surgir
|
|||
|
le menu.
|
|||
|
<item>Ce gestionnaire, si l'<27>v<EFBFBD>nement est un appui sur un
|
|||
|
bouton souris, traite <em/event/ comme un <20>v<EFBFBD>nement bouton (ce qu'il
|
|||
|
est) et l'utilise, de la fa<66>on indiqu<71>e dans le code d'exemple, pour
|
|||
|
passer l'information <20> <em/gtk_menu_popup()/.
|
|||
|
|
|||
|
<item> Lier ce gestionnaire <20> un widget avec :
|
|||
|
<tscreen>
|
|||
|
gtk_signal_connect_object(GTK_OBJECT(widget), "event",
|
|||
|
GTK_SIGNAL_FUNC (handler), GTK_OBJECT(menu));
|
|||
|
</tscreen>
|
|||
|
o<EFBFBD> <em/widget/ est le widget auquel vous le liez, <em/handler/ est
|
|||
|
le gestionnaire, et <em/menu/ est un menu cr<63><72> avec
|
|||
|
<em/gtk_menu_new()/. Cela peut <20>tre un menu qui est aussi post<73> par
|
|||
|
une barre de menu, comme le montre l'exemple.
|
|||
|
</itemize>
|
|||
|
<p>
|
|||
|
<sect1>Exemple de menu manuel
|
|||
|
<p>
|
|||
|
<tscreen><verb>
|
|||
|
|
|||
|
#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);
|
|||
|
|
|||
|
/* Cr<43>ation d'un fen<65>tre */
|
|||
|
|
|||
|
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 <20> menu racine <20> de l'application. */
|
|||
|
|
|||
|
menu = gtk_menu_new();
|
|||
|
|
|||
|
/* Voici le menu racine dont le label sera le nom du menu affich<63> sur la barre
|
|||
|
* de menu. Il n'a pas de gestionnaire de signal attach<63> car il ne fait
|
|||
|
* qu'afficher le reste du menu lorsqu'il est press<73>. */
|
|||
|
|
|||
|
root_menu = gtk_menu_item_new_with_label("Menu racine");
|
|||
|
|
|||
|
gtk_widget_show(root_menu);
|
|||
|
|
|||
|
/* Puis, on cr<63>e une petite boucle cr<63>ant trois entr<74>es pour <20> menu test <20>
|
|||
|
* Notez l'appel <20> gtk_menu_append(). Ici, on ajoute une liste d'items <20>
|
|||
|
* 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);
|
|||
|
|
|||
|
/* Cr<43>ation 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'int<6E>ressant lorsque l'item est
|
|||
|
* s<>lectionn<6E>. */
|
|||
|
|
|||
|
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 sp<73>cifi<66> que nous voulons que notre nouveau <20> menu <20>
|
|||
|
* soit le menu du <20> menu racine <20>. */
|
|||
|
|
|||
|
gtk_menu_item_set_submenu(GTK_MENU_ITEM (root_menu), menu);
|
|||
|
|
|||
|
/* Cr<43>ation 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);
|
|||
|
|
|||
|
/* Cr<43>ation d'une barre de menus pour contenir les menus. Puis, on
|
|||
|
* l'ajoute <20> notre fen<65>tre principale. */
|
|||
|
|
|||
|
menu_bar = gtk_menu_bar_new();
|
|||
|
gtk_box_pack_start(GTK_BOX(vbox), menu_bar, FALSE, FALSE, 2);
|
|||
|
gtk_widget_show(menu_bar);
|
|||
|
|
|||
|
/* Cr<43>ation 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 <20> la barre de menu --
|
|||
|
* c'est l'item de menu racine sur lequel je me suis d<>cha<68>n<EFBFBD> ;-) */
|
|||
|
|
|||
|
gtk_menu_bar_append(GTK_MENU_BAR (menu_bar), root_menu);
|
|||
|
|
|||
|
/* Affichage de la fen<65>tre. */
|
|||
|
|
|||
|
gtk_widget_show(window);
|
|||
|
|
|||
|
gtk_main ();
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/* On r<>pond <20> un appui sur le bouton en postant un nouveau menu pass<73> comme
|
|||
|
* un widget.
|
|||
|
*
|
|||
|
* On remarque que le param<61>tre "widget" est le menu <20> poster, PAS le bouton
|
|||
|
* qui a <20>t<EFBFBD> press<73>. */
|
|||
|
|
|||
|
|
|||
|
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 <20> l'appelant que l'on a g<>r<EFBFBD> cet <20>v<EFBFBD>nement. */
|
|||
|
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
/* On indique <20> l'appelant que l'on n'a pas g<>r<EFBFBD> cet <20>v<EFBFBD>nement. */
|
|||
|
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* Affiche une cha<68>ne lorsqu'un item de menu est choisi. */
|
|||
|
|
|||
|
static void menuitem_response (GtkWidget *widget, gchar *string)
|
|||
|
{
|
|||
|
printf("%s\n", string);
|
|||
|
}
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Vous pouvez aussi configurer un item de menu pour qu'il ne soit pas
|
|||
|
s<EFBFBD>lectionnable et, en utilisant une table de raccourcis clavier, lier
|
|||
|
des touches aux fonctions du menu.
|
|||
|
<p>
|
|||
|
<sect1>Utilisation de GtkMenuFactory
|
|||
|
<p>
|
|||
|
Maintenant que nous avons explor<6F> la voie difficile, nous allons voir
|
|||
|
l'utilisation des appels <em/gtk_menu_factory./
|
|||
|
<p>
|
|||
|
<sect1>Exemple d'usine <20> menu
|
|||
|
<p>
|
|||
|
Voici un exemple utilisant l'usine <20> menu de GTK. Le premier
|
|||
|
fichier est <em/menus.h/. Nous s<>parerons <em/menus.c/ et <em/main.c/ <20>
|
|||
|
cause des variables globales utilis<69>es dans le fichier <em/menus.c/.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
#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__ */
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
Voici le fichier <em/menus.c/ :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
|
|||
|
#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<69>e pour cr<63>er les menus. Le premier champ
|
|||
|
* est la cha<68>ne de d<>finition du menu. Le second, la touche de raccourci
|
|||
|
* utilis<69>e pour acc<63>der <20> cette fonction du menu avec le clavier.
|
|||
|
* Le troisi<73>me est la fonction de rappel <20> utiliser lorsque l'item de menu
|
|||
|
* est choisi (par la touche de raccourci ou avec la souris). Le dernier
|
|||
|
* <20>l<EFBFBD>ment est la donn<6E>e <20> passer <20> 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'<27>l<EFBFBD>ments de menu_item */
|
|||
|
|
|||
|
static int nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
|
|||
|
|
|||
|
static int initialize = TRUE;
|
|||
|
static GtkMenuFactory *factory = NULL;
|
|||
|
static GtkMenuFactory *subfactory[1];
|
|||
|
static GHashTable *entry_ht = NULL;
|
|||
|
|
|||
|
void get_main_menu(GtkWidget ** menubar, GtkAcceleratorTable ** table)
|
|||
|
{
|
|||
|
if (initialize)
|
|||
|
menus_init();
|
|||
|
|
|||
|
if (menubar)
|
|||
|
*menubar = subfactory[0]->widget;
|
|||
|
if (table)
|
|||
|
*table = subfactory[0]->table;
|
|||
|
}
|
|||
|
|
|||
|
void menus_init(void)
|
|||
|
{
|
|||
|
if (initialize) {
|
|||
|
initialize = FALSE;
|
|||
|
|
|||
|
factory = gtk_menu_factory_new(GTK_MENU_FACTORY_MENU_BAR);
|
|||
|
subfactory[0] = gtk_menu_factory_new(GTK_MENU_FACTORY_MENU_BAR);
|
|||
|
|
|||
|
gtk_menu_factory_add_subfactory(factory, subfactory[0], "<Main>");
|
|||
|
menus_create(menu_items, nmenu_items);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void menus_create(GtkMenuEntry * entries, int nmenu_entries)
|
|||
|
{
|
|||
|
char *accelerator;
|
|||
|
int i;
|
|||
|
|
|||
|
if (initialize)
|
|||
|
menus_init();
|
|||
|
|
|||
|
if (entry_ht)
|
|||
|
for (i = 0; i < nmenu_entries; i++) {
|
|||
|
accelerator = g_hash_table_lookup(entry_ht, entries[i].path);
|
|||
|
if (accelerator) {
|
|||
|
if (accelerator[0] == '\0')
|
|||
|
entries[i].accelerator = NULL;
|
|||
|
else
|
|||
|
entries[i].accelerator = accelerator;
|
|||
|
}
|
|||
|
}
|
|||
|
gtk_menu_factory_add_entries(factory, entries, nmenu_entries);
|
|||
|
|
|||
|
for (i = 0; i < nmenu_entries; i++)
|
|||
|
if (entries[i].widget) {
|
|||
|
gtk_signal_connect(GTK_OBJECT(entries[i].widget), "install_accelerator",
|
|||
|
(GtkSignalFunc) menus_install_accel,
|
|||
|
entries[i].path);
|
|||
|
gtk_signal_connect(GTK_OBJECT(entries[i].widget), "remove_accelerator",
|
|||
|
(GtkSignalFunc) menus_remove_accel,
|
|||
|
entries[i].path);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
static gint menus_install_accel(GtkWidget * widget, gchar * signal_name, gchar key, gchar modifiers, gchar * path)
|
|||
|
{
|
|||
|
char accel[64];
|
|||
|
char *t1, t2[2];
|
|||
|
|
|||
|
accel[0] = '\0';
|
|||
|
if (modifiers & GDK_CONTROL_MASK)
|
|||
|
strcat(accel, "<control>");
|
|||
|
if (modifiers & GDK_SHIFT_MASK)
|
|||
|
strcat(accel, "<shift>");
|
|||
|
if (modifiers & GDK_MOD1_MASK)
|
|||
|
strcat(accel, "<alt>");
|
|||
|
|
|||
|
t2[0] = key;
|
|||
|
t2[1] = '\0';
|
|||
|
strcat(accel, t2);
|
|||
|
|
|||
|
if (entry_ht) {
|
|||
|
t1 = g_hash_table_lookup(entry_ht, path);
|
|||
|
g_free(t1);
|
|||
|
} else
|
|||
|
entry_ht = g_hash_table_new(g_string_hash, g_string_equal);
|
|||
|
|
|||
|
g_hash_table_insert(entry_ht, path, g_strdup(accel));
|
|||
|
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
static void menus_remove_accel(GtkWidget * widget, gchar * signal_name, gchar * path)
|
|||
|
{
|
|||
|
char *t;
|
|||
|
|
|||
|
if (entry_ht) {
|
|||
|
t = g_hash_table_lookup(entry_ht, path);
|
|||
|
g_free(t);
|
|||
|
|
|||
|
g_hash_table_insert(entry_ht, path, g_strdup(""));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void menus_set_sensitive(char *path, int sensitive)
|
|||
|
{
|
|||
|
GtkMenuPath *menu_path;
|
|||
|
|
|||
|
if (initialize)
|
|||
|
menus_init();
|
|||
|
|
|||
|
menu_path = gtk_menu_factory_find(factory, path);
|
|||
|
if (menu_path)
|
|||
|
gtk_widget_set_sensitive(menu_path->widget, sensitive);
|
|||
|
else
|
|||
|
g_warning("Impossible de configurer la sensitivit<69> d'un menu qui n'existe pas : %s", path);
|
|||
|
}
|
|||
|
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
Voici <em/main.h/ :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
#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__ */
|
|||
|
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
Et, enfin, <em/main.c/ :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
#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 <20> 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 d<>monstration du fonctionnement des fonctions de rappel
|
|||
|
* lorsqu'on utilise l'usine <20> menus. Souvent, on met tous les rappels
|
|||
|
* des menus dans un fichier s<>par<61>, ce qui assure une meilleure
|
|||
|
* organisation. */
|
|||
|
|
|||
|
void file_quit_cmd_callback (GtkWidget *widget, gpointer data)
|
|||
|
{
|
|||
|
g_print ("%s\n", (char *) data);
|
|||
|
gtk_exit(0);
|
|||
|
}
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
Un <em/makefile/ pour que cela soit plus facile <20> compiler :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
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 *~
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
Pour l'instant, il n'y a que cet exemple. Une explication et de nombreux commentaires seront int<6E>gr<67>s plus tard.
|
|||
|
|
|||
|
|
|||
|
<sect>Widgets non document<6E>s
|
|||
|
<p>
|
|||
|
On a besoin de leurs auteurs! :). Participez <20> notre didacticiel.
|
|||
|
|
|||
|
Si vous devez utiliser un de ces widgets non document<6E>s, je vous recommande fortement de consulter leurs fichiers en-t<>tes respectifs dans la distribution GTK. Les noms de fonctions du GTK sont tr<74>s parlantes. Lorsque vous avez compris comment les choses fonctionnent, il n'est pas difficile de savoir comment utiliser un widget <20> partir des d<>clarations de ses fonctions. Cela, avec quelques exemples de codes pris ailleurs, devrait ne pas poser de probl<62>me.
|
|||
|
|
|||
|
Lorsque vous avez compris toutes les fonctions d'un nouveau widget non document<6E>, pensez <20> <20>crire un didacticiel pour que les autres puissent b<>n<EFBFBD>ficier du temps que vous y avez pass<73>.
|
|||
|
|
|||
|
<sect1>Entr<74>es de texte
|
|||
|
<p>
|
|||
|
|
|||
|
<sect1>S<>lections de couleurs
|
|||
|
<p>
|
|||
|
|
|||
|
<sect1>Contr<74>le d'intervalle
|
|||
|
<p>
|
|||
|
|
|||
|
<sect1>R<>gles
|
|||
|
<p>
|
|||
|
|
|||
|
<sect1>Bo<42>tes de texte
|
|||
|
<p>
|
|||
|
|
|||
|
<sect1>Pr<50>visualisation
|
|||
|
<p>
|
|||
|
(Ceci peut devoir <20>tre r<><72>crit pour suivre le style du reste de ce
|
|||
|
didacticiel).
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
|
|||
|
Les pr<70>visualisateurs servent <20> plusieurs choses dans GIMP/GTK. La
|
|||
|
plus importante est celle-ci : les images de haute qualit<69> peuvent
|
|||
|
occuper des dizaines de mega-octets en m<>moire - facilement ! Toute
|
|||
|
op<EFBFBD>ration sur une image aussi grosse implique un temps de traitement
|
|||
|
<EFBFBD>lev<EFBFBD>. Si cela vous prend 5 <20> 10 essais (i.e. 10 <20> 20 <20>tapes puisque
|
|||
|
vous devez recommencer lorsque vous avez fait une erreur) pour choisir
|
|||
|
la bonne modification, cela prendra litt<74>ralement des heures pour
|
|||
|
produire la bonne image - pour peu que vous ne manquiez pas de m<>moire
|
|||
|
avant. Ceux qui on pass<73> des heures dans les chambres noires de
|
|||
|
d<EFBFBD>veloppement couleur connaissent cette sensation. Les
|
|||
|
pr<EFBFBD>visualisations sont notre planche de salut !
|
|||
|
|
|||
|
L'aspect p<>nible de l'attente n'est pas le seul probl<62>me. souvent, il
|
|||
|
est utile de comparer les versions <20> Avant <20> et <20> Apr<70>s <20> c<>te <20> c<>te
|
|||
|
ou, au pire l'une apr<70>s l'autre. Si vous travaillez avec de grosses
|
|||
|
images et des attentes de 10 secondes, l'obtention des versions <20>
|
|||
|
Avant <20> et <20> Apr<70>s <20> est, pour le moins, difficile. Pour des images de
|
|||
|
30Mo (4"x6", 600dpi, 24 bits), la comparaison c<>te <20> c<>te est
|
|||
|
impossible pour la plupart des gens, et la comparaison s<>quentielle
|
|||
|
n'est gu<67>re mieux. Les pr<70>visualisations sont notre planche de salut !
|
|||
|
|
|||
|
Mais il y a plus. Les pr<70>visualisations permettent les
|
|||
|
pr<EFBFBD>-pr<70>visualisations c<>te <20> c<>te. En d'autres termes, vous <20>crivez un
|
|||
|
plug-in (par exemple la simulation filterpack) qui aurait plusieurs
|
|||
|
pr<EFBFBD>visualisations de ce-que-ce-serait-si-vous-faisiez-ceci. Une approche
|
|||
|
comme celle ci agit comme une sorte de palette de pr<70>visualisation et
|
|||
|
est tr<74>s pratique pour les petites modifications. Utilisons les
|
|||
|
pr<EFBFBD>visualisations !
|
|||
|
|
|||
|
Encore plus : pour certains plug-ins une intervention humaine en
|
|||
|
temps r<>el, sp<73>cifique aux images, peut s'av<61>rer n<>cessaire. Dans le
|
|||
|
plug-in SuperNova, par exemple, on demande <20> l'utilisateur d'entrer
|
|||
|
les coordonn<6E>es du centre de la future supernova. La fa<66>on la plus
|
|||
|
simple de faire cela, vraiment, est de pr<70>senter une pr<70>visualisation
|
|||
|
<EFBFBD> l'utilisateur et de lui demander de choisir interactivement le
|
|||
|
point. Utilisons les pr<70>visualisations !
|
|||
|
|
|||
|
Enfin, quelques utilisations diverses : on peut utiliser les
|
|||
|
pr<EFBFBD>visualisations, m<>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 v<>n<EFBFBD>rable 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
|
|||
|
pr<EFBFBD>visualisations pour des petits logos dans vos plug-ins et m<>me pour
|
|||
|
une photo de vous, l'Auteur. Utilisons les pr<70>visualisations !
|
|||
|
|
|||
|
Quand ne pas utiliser les pr<70>visualisations
|
|||
|
|
|||
|
N'utilisez pas les pr<70>visualisations pour les graphes, les trac<61>s,
|
|||
|
etc. GDK est bien plus rapide pour <20>a. N'utilisez les que pour les
|
|||
|
images !
|
|||
|
|
|||
|
Utilisons les pr<70>visualisations !
|
|||
|
|
|||
|
Vous pouvez mettre une pr<70>visualisation dans <20> peu pr<70>s n'importe
|
|||
|
quoi. Dans une vbox, une hbox, un bouton, etc. Mais elles donnent leur
|
|||
|
meilleur d'elles-m<>mes dans des cadres resserr<72>s autour d'elles. Les
|
|||
|
pr<EFBFBD>visualisations n'ont, par elles-m<>mes, aucun contour et semblent
|
|||
|
plates sans eux. (Bien s<>r, si c'est cet aspect que vous
|
|||
|
voulez...). Les cadres serr<72>s fournissent les bordures n<>cessaires.
|
|||
|
|
|||
|
[Image][Image]
|
|||
|
|
|||
|
Les pr<70>visualisations sont, <20> bien des <20>gards, comme tous les autres
|
|||
|
widgets de GTK (avec tout ce que cela implique) sauf qu'il disposent
|
|||
|
d'une fonctionnalit<69> suppl<70>mentaire : ils doivent <20>tre remplis
|
|||
|
avec une image ! Nous traiterons d'abord exclusivement de l'aspect GTK
|
|||
|
des pr<70>visualisations, puis nous verrons comment les remplir.
|
|||
|
|
|||
|
|
|||
|
/* Cr<43>ation d'un widget pr<70>visualisation,
|
|||
|
* 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 pr<70>visualisations 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 pr<70>visualisation de base. Cette fonction retourne le cadre
|
|||
|
<EFBFBD> p<>re <20>, on peut ainsi le placer ailleurs dans notre interface. Bien
|
|||
|
s<EFBFBD>r, on peut passer le cadre <20> p<>re <20> en param<61>tre <20> cette
|
|||
|
fonction. Dans de nombreuses situations, toutefois, le contenu de la
|
|||
|
pr<EFBFBD>visualisation est chang<6E>e continuellement par notre application. En
|
|||
|
ce cas, on peut passer un pointeur vers une pr<70>visualisation <20> la
|
|||
|
fonction <em/create_a_preview()/ et avoir ainsi un contr<74>le sur elle
|
|||
|
plus tard.
|
|||
|
|
|||
|
Un point plus important qui pourra un jour vous faire <20>conomiser
|
|||
|
beaucoup de temps. Quelques fois, il est souhaitable de mettre un
|
|||
|
label <20> votre pr<70>visualisation. Par exemple, on peut nommer la
|
|||
|
pr<EFBFBD>visualisation contenant l'image originale <20> Original <20> et celle
|
|||
|
contenant l'image modifi<66>e <20> Moins Originale <20>. Il peut vous arriver
|
|||
|
de placer la pr<70>visualisation avec le label appropri<72> dans une
|
|||
|
vbox. L'effet inattendu est que si le label est plus large que la
|
|||
|
pr<EFBFBD>visualisation (taille de cette derni<6E>re, taille de la fonte du
|
|||
|
label, etc), le cadre s'<27>largit et ne convient plus <20> la
|
|||
|
pr<EFBFBD>visualisation. Le m<>me probl<62>me se passera probablement dans
|
|||
|
d'autres situations aussi.
|
|||
|
|
|||
|
[Image]
|
|||
|
|
|||
|
La solution consiste <20> placer la pr<70>visualisation et le label dans une
|
|||
|
table de 2x2 en les attachant avec les param<61>tres suivants (c'est
|
|||
|
l'une des possibilit<69>s, bien s<>r. La cl<63> consiste <20> ne pas mettre
|
|||
|
GTK_FILL dans le second attachement)<29>nbsp;:
|
|||
|
|
|||
|
gtk_table_attach(GTK_TABLE(table),label,0,1,0,1,
|
|||
|
0,
|
|||
|
GTK_EXPAND|GTK_FILL,
|
|||
|
0,0);
|
|||
|
gtk_table_attach(GTK_TABLE(table),frame,0,1,1,2,
|
|||
|
GTK_EXPAND,
|
|||
|
GTK_EXPAND,
|
|||
|
0,0);
|
|||
|
|
|||
|
|
|||
|
Et voici le r<>sultat :
|
|||
|
|
|||
|
[Image]
|
|||
|
|
|||
|
Divers
|
|||
|
|
|||
|
Rendre une pr<70>visualisation cliquable se fait tr<74>s facilement en la pla<6C>ant dans un bouton. Cela ajoute aussi une bordure agr<67>able autour de la pr<70>visualisation et vous n'avez m<>me pas besoin de la mettre dans un cadre. Voir le plug-in Filter Pack Simulation comme exemple.
|
|||
|
|
|||
|
Remplir une pr<70>visualisation
|
|||
|
|
|||
|
Afin de nous familiariser avec les bases de ce remplissage, cr<63>ons 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; /* Coordonn<6E>es */
|
|||
|
double r, alpha, x, y;
|
|||
|
|
|||
|
if (preview==NULL) return; /* J'ajoute g<>n<EFBFBD>ralement ceci quand je */
|
|||
|
/* veux <20>viter des plantages stupides */
|
|||
|
/* Vous devez vous assurer que tout a */
|
|||
|
/* <20>t<EFBFBD> correctement initialis<69> ! */
|
|||
|
|
|||
|
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);
|
|||
|
|
|||
|
/* Ins<6E>re "row" dans "preview" en partant du point de */
|
|||
|
/* coordonn<6E>es (0,j) premi<6D>re colonne, j_i<5F>me ligne allant de SIZE */
|
|||
|
/* pixels vers la droite */
|
|||
|
}
|
|||
|
|
|||
|
free(row); /* on r<>cup<75>re un peu d'espace */
|
|||
|
gtk_widget_draw(preview,NULL); /* qu'est-ce que <20>a fait ? */
|
|||
|
gdk_flush(); /* et <20>a ? */
|
|||
|
}
|
|||
|
|
|||
|
Ceux qui n'utilisent pas GIMP en ont suffisamment vu pour faire
|
|||
|
d<EFBFBD>j<EFBFBD> beaucoup de choses. Pour ceux qui l'utilisent, j'ai quelques
|
|||
|
pr<EFBFBD>cisions <20> ajouter.
|
|||
|
|
|||
|
Pr<EFBFBD>visualisation d'image
|
|||
|
|
|||
|
Il est pratique de conserver une version r<>duite de l'image ayant
|
|||
|
juste assez de pixels pour remplir la pr<70>visualisation. Ceci est
|
|||
|
possible en choisissant chaque <20>ni<6E>me pixel o<> n est le ratio de la
|
|||
|
taille de l'image par rapport <20> la taille de la visualisation. Toutes
|
|||
|
les op<6F>rations suivantes (y compris le remplissage des
|
|||
|
pr<EFBFBD>visualisations) sont alors r<>alis<69>es seulement sur le nombre r<>duit
|
|||
|
de pixels. Ce qui suit est mon implantation de la r<>duction d'image
|
|||
|
(Gardez <20> l'esprit que je n'ai que quelques notions de base en C !).
|
|||
|
|
|||
|
(ATTENTION : CODE NON TEST<53> !!!)
|
|||
|
|
|||
|
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 r<>duit l'image <20> la taille de pr<70>visualisation choisie */
|
|||
|
/* La taille de la pr<70>visualisation est d<>termin<69>e par LongerSize, i.e. */
|
|||
|
/* la plus grande des deux dimensions. Ne fonctionne qu'avec des images */
|
|||
|
/* RGB ! */
|
|||
|
|
|||
|
gint RH, RW; /* Hauteur et Largeur r<>duites */
|
|||
|
gint width, height; /* Largeur et Hauteur de la surface <20> r<>duire */
|
|||
|
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 enti<74>re */
|
|||
|
|
|||
|
gimp_drawable_mask_bounds (drawable->id, &x1, &y1, &x2, &y2);
|
|||
|
width = x2-x1;
|
|||
|
height = y2-y1;
|
|||
|
/* S'il y a une SELECTION, on r<>cup<75>re ses fronti<74>res ! */
|
|||
|
|
|||
|
if (width != drawable->width && height != drawable->height)
|
|||
|
NoSelectionMade=FALSE;
|
|||
|
/* On v<>rifie si l'utilisateur a rendu une s<>lection active */
|
|||
|
/* Ceci sera important plus tard, lorsqu'on cr<63>era un masque r<>duit */
|
|||
|
|
|||
|
/* Si on veut pr<70>visualiser l'image enti<74>re, supprimer ce qui suit ! */
|
|||
|
/* Bien s<>r, s'il n'y a pas de s<>lection, cela n'a aucun effet ! */
|
|||
|
if (Selection==ENTIRE_IMAGE) {
|
|||
|
x1=0;
|
|||
|
x2=drawable->width;
|
|||
|
y1=0;
|
|||
|
y2=drawable->height;
|
|||
|
}
|
|||
|
|
|||
|
/* Si on veut pr<70>visualiser une s<>lection avec une surface qui l'entoure, */
|
|||
|
/* on doit l'agrandir un petit peu. Consid<69>rez <20>a comme une devinette. */
|
|||
|
|
|||
|
if (Selection==SELECTION_IN_CONTEXT) {
|
|||
|
x1=MAX(0, x1-width/2.0);
|
|||
|
x2=MIN(drawable->width, x2+width/2.0);
|
|||
|
y1=MAX(0, y1-height/2.0);
|
|||
|
y2=MIN(drawable->height, y2+height/2.0);
|
|||
|
}
|
|||
|
|
|||
|
/* Calcul de la largeur et de la hauteur de la surface <20> r<>duire. */
|
|||
|
|
|||
|
width = x2-x1;
|
|||
|
height = y2-y1;
|
|||
|
|
|||
|
/* Les lignes ci-dessous d<>terminent la dimension qui sera le cot<6F> */
|
|||
|
/* le plus long. Cette id<69>e est emprunt<6E>e au plug-in Supernova. */
|
|||
|
/* Je soup<75>onne que j'aurais pu y penser moi-m<>me, mais la v<>rit<69> */
|
|||
|
/* doit <20>tre dite. Le plagiat pue ! */
|
|||
|
|
|||
|
if (width>height) {
|
|||
|
RW=LongerSize;
|
|||
|
RH=(float) height * (float) LongerSize/ (float) width;
|
|||
|
}
|
|||
|
else {
|
|||
|
RH=LongerSize;
|
|||
|
RW=(float)width * (float) LongerSize/ (float) height;
|
|||
|
}
|
|||
|
|
|||
|
/* L'image enti<74>re est r<>duite dans une cha<68>ne ! */
|
|||
|
|
|||
|
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);
|
|||
|
|
|||
|
/* R<>servation 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 s<>lection = chaque point est compl<70>tement s<>lectionn<6E> ! */
|
|||
|
|
|||
|
if (NoSelectionMade)
|
|||
|
tempmask[i*RW+j]=255;
|
|||
|
else
|
|||
|
tempmask[i*RW+j]=src_mask_row[whichcol];
|
|||
|
|
|||
|
/* Ajout de la ligne <20> la longue cha<68>ne 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 <20> 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 pr<70>visualisation qui utilise le m<>me type
|
|||
|
<em/ReducedImage/ ! On remarque qu'elle utilise une fausse
|
|||
|
transparence (au moyen de <em/fake_transparancy/ qui est d<>fini 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 pr<70>visualisation<6F>nbsp;:
|
|||
|
|
|||
|
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<69>e */
|
|||
|
void gtk_preview_uninit (void);
|
|||
|
/* Aucune id<69>e */
|
|||
|
GtkWidget* gtk_preview_new (GtkPreviewType type);
|
|||
|
/* D<>crite ci-dessous */
|
|||
|
void gtk_preview_size (GtkPreview *preview,
|
|||
|
gint width,
|
|||
|
gint height);
|
|||
|
/* Permet de changer la taille d'une pr<70>visualisation existante */
|
|||
|
/* Apparamment, il y a un bug dans GTK qui rend ce traitement */
|
|||
|
/* hasardeux. Une m<>thode pour corriger ce probl<62>me consiste <20> */
|
|||
|
/* changer manuellement la taille de la fen<65>tre contenant la */
|
|||
|
/* pr<70>visualisation apr<70>s avoir chang<6E> la taille de la */
|
|||
|
/* pr<70>visualisation. */
|
|||
|
|
|||
|
void gtk_preview_put (GtkPreview *preview,
|
|||
|
GdkWindow *window,
|
|||
|
GdkGC *gc,
|
|||
|
gint srcx,
|
|||
|
gint srcy,
|
|||
|
gint destx,
|
|||
|
gint desty,
|
|||
|
gint width,
|
|||
|
gint height);
|
|||
|
/* Aucune id<69>e */
|
|||
|
|
|||
|
void gtk_preview_put_row (GtkPreview *preview,
|
|||
|
guchar *src,
|
|||
|
guchar *dest,
|
|||
|
gint x,
|
|||
|
gint y,
|
|||
|
gint w);
|
|||
|
/* Aucune id<69>e */
|
|||
|
|
|||
|
void gtk_preview_draw_row (GtkPreview *preview,
|
|||
|
guchar *data,
|
|||
|
gint x,
|
|||
|
gint y,
|
|||
|
gint w);
|
|||
|
/* D<>crite dans le texte */
|
|||
|
|
|||
|
void gtk_preview_set_expand (GtkPreview *preview,
|
|||
|
gint expand);
|
|||
|
/* Aucune id<69>e */
|
|||
|
|
|||
|
/* Aucune piste pour celles qui suivent mais devrait <20>tre */
|
|||
|
/* un standard pour la plupart des widgets. */
|
|||
|
|
|||
|
void gtk_preview_set_gamma (double gamma);
|
|||
|
void gtk_preview_set_color_cube (guint nred_shades,
|
|||
|
guint ngreen_shades,
|
|||
|
guint nblue_shades,
|
|||
|
guint ngray_shades);
|
|||
|
void gtk_preview_set_install_cmap (gint install_cmap);
|
|||
|
void gtk_preview_set_reserved (gint nreserved);
|
|||
|
GdkVisual* gtk_preview_get_visual (void);
|
|||
|
GdkColormap* gtk_preview_get_cmap (void);
|
|||
|
GtkPreviewInfo* gtk_preview_get_info (void);
|
|||
|
|
|||
|
That's all, folks!
|
|||
|
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
|
|||
|
<sect1>Courbes
|
|||
|
<p>
|
|||
|
|
|||
|
|
|||
|
<sect>Widget EventBox <label id="sec_The_EventBox_Widget">
|
|||
|
<p>
|
|||
|
Il n'est disponible que dans <em/gtk+970916.tar.gz/ et les
|
|||
|
distributions ult<6C>rieures. <p> Certains widgets GTK n'ont pas de
|
|||
|
fen<EFBFBD>tre X associ<63>e, il se dessinent donc sur leurs parents. <20> cause de
|
|||
|
cela, ils ne peuvent recevoir d'<27>v<EFBFBD>nements 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, <em/EventBox/ est fait pour vous.
|
|||
|
|
|||
|
Au premier abord, le widget <em/EventBox/ peut appara<72>tre comme
|
|||
|
totalement d<>nu<6E> d'int<6E>r<EFBFBD>t. Il ne dessine rien <20> l'<27>cran et ne r<>pond
|
|||
|
<EFBFBD> aucun <20>venement. Cependant, il joue un r<>le - il fournit une fen<65>tre
|
|||
|
X pour son widget fils. Ceci est important car de nombreux widgets GTK
|
|||
|
n'ont pas de fen<65>tre X associ<63>e. Ne pas avoir de fen<65>tre permet
|
|||
|
d'<27>conomiser de la m<>moire mais a aussi quelques inconv<6E>nients. Un
|
|||
|
widget sans fen<65>tre ne peut recevoir d'<27>v<EFBFBD>nement, et ne r<>alise aucune
|
|||
|
mise en place de ce qu'il contient. Bien que le nom <20> EventBox <20>
|
|||
|
insiste sur la fonction de gestion d'<27>v<EFBFBD>nement, le widget peut aussi
|
|||
|
<EFBFBD>tre utilis<69> pour la mise en place (et plus... voir l'exemple
|
|||
|
ci-dessous).
|
|||
|
|
|||
|
<p>
|
|||
|
Pour cr<63>er un widget EventBox, on utilise :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
GtkWidget* gtk_event_box_new (void);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
<p>
|
|||
|
Un widget fils peut alors <20>tre ajout<75> <20> cet <em/EventBox/ :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
gtk_container_add (GTK_CONTAINER(event_box), widget);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
<p>
|
|||
|
L'exemple suivant montre l'utilisation d'un <em/EventBox/ - un label
|
|||
|
est cr<63><72> et mis en place sur une petite bo<62>te, et configur<75> pour qu'un
|
|||
|
clic souris sur le label provoque la fin du programme.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
#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);
|
|||
|
|
|||
|
/* Cr<43>ation d'un EventBox et ajout de celui-ci dans la fen<65>tre. */
|
|||
|
|
|||
|
event_box = gtk_event_box_new ();
|
|||
|
gtk_container_add (GTK_CONTAINER(window), event_box);
|
|||
|
gtk_widget_show (event_box);
|
|||
|
|
|||
|
/* Cr<43>ation 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<72>. */
|
|||
|
|
|||
|
gtk_widget_set_usize (label, 110, 20);
|
|||
|
|
|||
|
/* Attachement d'une action <20> 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 fen<65>tre 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;
|
|||
|
}
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
<sect>Configuration des attributs de widget<label
|
|||
|
id="sec_setting_widget_attributes">
|
|||
|
<p>
|
|||
|
Cette section d<>crit les fonctions op<6F>rant sur les widgets. Elles
|
|||
|
peuvent <20>tre utilis<69>es pour configurer le style, l'espacement, la
|
|||
|
taille, etc.
|
|||
|
|
|||
|
(Je devrais peut <20>tre faire une section uniquement consacr<63>e aux
|
|||
|
raccourcis clavier.)
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
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);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
|
|||
|
|
|||
|
<sect>Temporisations, fonctions d'E/S et d'attente<label id="sec_timeouts">
|
|||
|
<p>
|
|||
|
<sect1>Temporisations
|
|||
|
<p>
|
|||
|
Vous pouvez vous demander comment faire pour que GTK fasse quelque
|
|||
|
chose d'utile lorsqu'il est dans <em/gtk_main/. En fait, on a
|
|||
|
plusieurs options. L'utilisation des fonctions suivantes permet de
|
|||
|
cr<EFBFBD>er une temporisation qui sera appel<65>e tous les
|
|||
|
<em/interval/ millisecondes.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
gint gtk_timeout_add (guint32 interval,
|
|||
|
GtkFunction function,
|
|||
|
gpointer data);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Le premier param<61>tre est le nombre de millisecondes entre les appels <20>
|
|||
|
notre fonction. Le deuxi<78>me est la fonction <20> appeler et le troisi<73>me
|
|||
|
est la donn<6E>e pass<73>e <20> cette fonction de rappel. La valeur retourn<72>e
|
|||
|
est un <20> marqueur <20> de type entier qui pourra <20>tre utilis<69> pour
|
|||
|
arr<EFBFBD>ter la temporisation en appelant :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_timeout_remove (gint tag);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
On peut aussi stopper la fonction de temporisation en faisant
|
|||
|
retourner z<>ro ou FALSE <20> notre fonction de rappel. <20>videmment, cela
|
|||
|
veut dire que si vous voulez que votre fonction continue <20> <20>tre
|
|||
|
appel<EFBFBD>e, elle doit retourner une valeur non nulle, ou TRUE.
|
|||
|
|
|||
|
La d<>claration de votre fonction de rappel doit ressembler <20> <20>a :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
gint timeout_callback (gpointer data);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
<sect1>Surveillance des E/S
|
|||
|
<p>
|
|||
|
Une autre caract<63>ristique int<6E>ressante du GTK est la possibilit<69> de
|
|||
|
v<EFBFBD>rifier les donn<6E>es d'un descripteur de fichier (celles retourn<72>es
|
|||
|
par <em/open/(2) ou <em/socket/(2)). C'est particuli<6C>rement pratique pour les
|
|||
|
applications r<>seau. La fonction suivante permet cette
|
|||
|
v<EFBFBD>rification :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
gint gdk_input_add (gint source,
|
|||
|
GdkInputCondition condition,
|
|||
|
GdkInputFunction function,
|
|||
|
gpointer data);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Le premier param<61>tre est le descripteur de fichier que l'on veut
|
|||
|
<EFBFBD>tudier, le second sp<73>cifie ce qu'on veut que le GDK recherche. Cela
|
|||
|
peut <20>tre :
|
|||
|
<p>
|
|||
|
GDK_INPUT_READ - Appel <em/function/ lorsqu'il y a une donn<6E>e pr<70>te <20>
|
|||
|
<EFBFBD>tre lue dans le descripteur de fichier.
|
|||
|
<p>
|
|||
|
GDK_INPUT_WRITE - Appel de <em/function/ lorsque le descripteur de
|
|||
|
fichier est pr<70>t pour une <20>criture.
|
|||
|
<p>
|
|||
|
Je suis s<>r que vous vous doutez, maintenant, que le troisi<73>me
|
|||
|
param<EFBFBD>tre est la fonction que l'on veut appeler lorsque les conditions
|
|||
|
ci-dessus sont satisfaites. Le dernier param<61>tre est la donn<6E>e <20>
|
|||
|
passer <20> cette fonction.
|
|||
|
<p>
|
|||
|
La valeur retourn<72>e est un marqueur qui pourra <20>tre utilis<69> pour dire
|
|||
|
au GDK de cesser de surveiller ce descripteur <20> l'aide de la fonction :
|
|||
|
<p>
|
|||
|
<tscreen><verb>
|
|||
|
void gdk_input_remove (gint tag);
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
La fonction de rappel doit <20>tre d<>clar<61>e de la fa<66>on suivante :
|
|||
|
<p>
|
|||
|
<tscreen><verb>
|
|||
|
void input_callback (gpointer data, gint source,
|
|||
|
GdkInputCondition condition);
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
|
|||
|
<sect1>Fonctions d'attente
|
|||
|
<p>
|
|||
|
Que se passe-t'il si vous avez une fonction qui doit <20>tre appel<65>e
|
|||
|
lorsque rien d'autre ne se passe ? On utilise la fonction suivante qui force
|
|||
|
GTK <20> appeler <em/function/ lorsqu'on est en phase d'inaction ;
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
gint gtk_idle_add (GtkFunction function,
|
|||
|
gpointer data);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_idle_remove (gint tag);
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
Je n'expliquerai pas la signification des param<61>tres car ils
|
|||
|
ressemblent beaucoup <20> ceux d<>j<EFBFBD> vus ci-dessus. La fonction point<6E>e
|
|||
|
par le premier param<61>tre de <em/gtk_idle_add()/ sera appel<65>e <20> chaque
|
|||
|
occasion. Comme pour les autres, retourner FALSE emp<6D>chera la fonction
|
|||
|
d'attente d'<27>tre appel<65>e.
|
|||
|
|
|||
|
<sect>Gestion des s<>lections
|
|||
|
|
|||
|
<sect1>Introduction
|
|||
|
<p>
|
|||
|
Un type de communication inter-processus g<>r<EFBFBD>e par GTK est les
|
|||
|
<em>s<>lections</em>. Une s<>lection identifie un morceau de donn<6E>es,
|
|||
|
par exemple une portion de texte s<>lectionn<6E>e par l'utilisateur avec
|
|||
|
la souris. Seule une application sur un <20>cran (le <em/propri<72>taire/)
|
|||
|
peut poss<73>der une s<>lection particuli<6C>re <20> un moment donn<6E>, ainsi
|
|||
|
lorsqu'une s<>lection est r<>clam<61>e par une application, le propri<72>taire
|
|||
|
pr<EFBFBD>c<EFBFBD>dent doit indiquer <20> l'utilisateur que la s<>lection a <20>t<EFBFBD>
|
|||
|
abandonn<EFBFBD>e. Les autres applications peuvent demander le contenu d'une
|
|||
|
s<EFBFBD>lection sous diff<66>rentes formes appel<65>es <em/cibles/. Il peut y
|
|||
|
avoir un nombre quelconque de s<>lections, mais la plupart des
|
|||
|
applications X n'en g<>rent qu'une, la <em/s<>lection primaire/.
|
|||
|
|
|||
|
<p>
|
|||
|
Dans la plupart des cas, une application GTK n'a pas besoin de g<>rer elle-m<>me
|
|||
|
les s<>lections. Les widgets standards, comme le widget Entr<74>e de texte,
|
|||
|
poss<EFBFBD>dent d<>j<EFBFBD> la capacit<69> de r<>clamer la s<>lection lorsqu'il le faut (par
|
|||
|
exemple, lorsque l'utilisateur glisse au dessus d'un texte) et de r<>cup<75>rer le
|
|||
|
contenu de la s<>lection d<>tenue par un autre widget ou une autre application
|
|||
|
(par exemple, lorsque l'utilisateur clique avec le deuxi<78>me bouton de la
|
|||
|
souris). Cependant, il peut il y avoir des cas dans lesquels vous voulez donner
|
|||
|
aux autres widgets la possibilit<69> de fournir la s<>lection, ou vous d<>sirez
|
|||
|
r<EFBFBD>cup<EFBFBD>rer des cibles non support<72>es par d<>faut.
|
|||
|
|
|||
|
<p>
|
|||
|
Un concept fondamental dans la compr<70>hension du fonctionnement des
|
|||
|
s<EFBFBD>lections est celui d'<em/atome/. Un atome est un entier qui d<>finit
|
|||
|
de fa<66>on unique une cha<68>ne (sur un affichage particulier). Certains
|
|||
|
atomes sont pr<70>d<EFBFBD>finis par le serveur X et, dans certains cas, des
|
|||
|
constantes d<>finies dans <em/gtk.h/ correspondent <20> ces atomes. Par
|
|||
|
exemple, la constante GDK_PRIMARY_SELECTION correspond <20> la cha<68>ne
|
|||
|
"PRIMARY". Dans d'autres cas, on doit utiliser les fonctions
|
|||
|
<em/gdk_atom_intern()/, pour obtenir l'atome correspondant <20> une
|
|||
|
cha<EFBFBD>ne, et <em/gdk_atom_name()/, pour obtenir le nom d'un atome. Les
|
|||
|
s<EFBFBD>lections et les cibles sont identifi<66>s par des atomes.
|
|||
|
|
|||
|
<sect1>R<>cup<75>ration de la s<>lection
|
|||
|
<p>
|
|||
|
La r<>cup<75>ration de la s<>lection est un processus asynchrone. Pour d<>marrer le processus, on appelle :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
gint gtk_selection_convert (GtkWidget *widget,
|
|||
|
GdkAtom selection,
|
|||
|
GdkAtom target,
|
|||
|
guint32 time)
|
|||
|
</verb</tscreen>
|
|||
|
|
|||
|
Cela <em>convertit</em> la s<>lection dans la forme sp<73>cifi<66>e par
|
|||
|
<em/target/. Si tout est possible, le param<61>tre <em/time/ sera le
|
|||
|
moment de l'<27>v<EFBFBD>nement qui a d<>clench<63> la s<>lection. Ceci aide <20>
|
|||
|
s'assurer que les <20>v<EFBFBD>nements arrivent dans l'ordre o<> l'utilisateur
|
|||
|
les a demand<6E>. Cependant, si cela n'est pas possible (par exemple,
|
|||
|
lorsque la conversion a <20>t<EFBFBD> d<>clench<63>e par un signal "clicked"), alors
|
|||
|
on peut utiliser la macro GDK_CURRENT_TIME.
|
|||
|
|
|||
|
<p>
|
|||
|
Quand le propri<72>taire de la s<>lection r<>pond <20> la requ<71>te, un signal
|
|||
|
"selection_received" est envoy<6F> <20> notre application. Le gestionnaire
|
|||
|
de ce signal re<72>oit un pointeur vers une structure
|
|||
|
<tt/GtkSelectionData/ d<>finie ainsi :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
struct _GtkSelectionData
|
|||
|
{
|
|||
|
GdkAtom selection;
|
|||
|
GdkAtom target;
|
|||
|
GdkAtom type;
|
|||
|
gint format;
|
|||
|
guchar *data;
|
|||
|
gint length;
|
|||
|
};
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
<em/selection/ et <em/target/ sont les valeurs que l'on a donn<6E> dans
|
|||
|
notre appel <em/gtk_selection_convert()/. <em/type/ est un atome qui
|
|||
|
identifie le type de donn<6E>es retourn<72> par le propri<72>taire de la
|
|||
|
s<EFBFBD>lection. Quelques valeurs possibles sont : "STRING", une cha<68>ne
|
|||
|
de caract<63>res latin-1, "ATOM", une s<>rie d'atomes, "INTEGER", un
|
|||
|
entier, etc. La plupart des cibles ne peuvent retourner qu'un
|
|||
|
type. <em/format/ donne la longueur des unit<69>s (les caract<63>res, par
|
|||
|
exemple) en bits. Habituellement, on ne se pr<70>occupe pas de cela
|
|||
|
lorsqu'on re<72>oit des donn<6E>es. <em/data/ est un pointeur vers la donn<6E>e
|
|||
|
retourn<EFBFBD>e et <em/length/ donne la longueur en octets de la donn<6E>e
|
|||
|
retourn<EFBFBD>e. Si <em/length/ est n<>gative, cela indique qu'une erreur est
|
|||
|
survenue et que la s<>lection ne peut <20>tre r<>cup<75>r<EFBFBD>e. Ceci peut arriver
|
|||
|
si aucune application n'est propri<72>taire de la s<>lection, ou si vous
|
|||
|
avez demand<6E> une cible que l'application ne sait pas g<>rer. Le tampon
|
|||
|
est garanti d'<27>tre un octet plus long que <em/length/ ; l'octet
|
|||
|
suppl<EFBFBD>mentaire sera toujours z<>ro, et il n'est donc pas n<>cessaire de
|
|||
|
faire une copie de cha<68>ne simplement pour qu'elle soit termin<69>e par
|
|||
|
z<EFBFBD>ro (comme doivent l'<27>tre toutes les cha<68>nes C).
|
|||
|
|
|||
|
<p>
|
|||
|
Dans l'exemple qui suit, on r<>cup<75>re la cible sp<73>ciale "TARGETS", qui
|
|||
|
est une liste de toutes les cibles en lesquelles la s<>lection peut
|
|||
|
<EFBFBD>tre convertie.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
#include <gtk/gtk.h>
|
|||
|
|
|||
|
void selection_received (GtkWidget *widget,
|
|||
|
GtkSelectionData *selection_data,
|
|||
|
gpointer data);
|
|||
|
|
|||
|
/* Gestionnaire de signal invoqu<71> lorsque l'utilisateur clique sur
|
|||
|
* le bouton <20> Obtenir les cibles <20>. */
|
|||
|
|
|||
|
void get_targets (GtkWidget *widget, gpointer data)
|
|||
|
{
|
|||
|
static GdkAtom targets_atom = GDK_NONE;
|
|||
|
|
|||
|
/* Obtention de l'atome correspondant <20> la cha<68>ne "TARGETS" */
|
|||
|
|
|||
|
if (targets_atom == GDK_NONE)
|
|||
|
targets_atom = gdk_atom_intern ("TARGETS", FALSE);
|
|||
|
|
|||
|
/* Demande de la cible "TARGETS" pour la s<>lection primaire */
|
|||
|
|
|||
|
gtk_selection_convert (widget, GDK_SELECTION_PRIMARY, targets_atom,
|
|||
|
GDK_CURRENT_TIME);
|
|||
|
}
|
|||
|
|
|||
|
/* Gestionnaire de signal appel<65> quand le propri<72>taire des s<>lections
|
|||
|
* retourne la donn<6E>e. */
|
|||
|
|
|||
|
void selection_received (GtkWidget *widget, GtkSelectionData *selection_data,
|
|||
|
gpointer data)
|
|||
|
{
|
|||
|
GdkAtom *atoms;
|
|||
|
GList *item_list;
|
|||
|
int i;
|
|||
|
|
|||
|
/* **** IMPORTANT **** On v<>rifie si la r<>cup<75>ration s'est bien pass<73>e. */
|
|||
|
|
|||
|
if (selection_data->length < 0)
|
|||
|
{
|
|||
|
g_print ("Selection retrieval failed\n");
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
/* On s'assure que l'on a obtenu la donn<6E>e sous la forme attendue. */
|
|||
|
|
|||
|
if (selection_data->type != GDK_SELECTION_TYPE_ATOM)
|
|||
|
{
|
|||
|
g_print ("La s<>lection \"TARGETS\" n'a pas <20>t<EFBFBD> retourn<72>e sous la forme d'atomes !\n");
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
/* Affichage des atomes re<72>us. */
|
|||
|
|
|||
|
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);
|
|||
|
|
|||
|
/* Cr<43>ation de la fen<65>tre de l'application. */
|
|||
|
|
|||
|
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|||
|
gtk_window_set_title (GTK_WINDOW (window), "S<>lections");
|
|||
|
gtk_container_border_width (GTK_CONTAINER (window), 10);
|
|||
|
|
|||
|
gtk_signal_connect (GTK_OBJECT (window), "destroy",
|
|||
|
GTK_SIGNAL_FUNC (gtk_exit), NULL);
|
|||
|
|
|||
|
/* Cr<43>ation 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;
|
|||
|
}
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
<sect1>Fournir la s<>lection
|
|||
|
<p>
|
|||
|
|
|||
|
Fournir la s<>lection est un peu plus compliqu<71>. On doit enregistrer
|
|||
|
les gestionnaires qui seront appel<65>s lorsque notre s<>lection est
|
|||
|
demand<EFBFBD>e. Pour chaque paire s<>lection/cible que l'on g<>rera, on fera
|
|||
|
un appel <20> :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_selection_add_handler (GtkWidget *widget,
|
|||
|
GdkAtom selection,
|
|||
|
GdkAtom target,
|
|||
|
GtkSelectionFunction function,
|
|||
|
GtkRemoveFunction remove_func,
|
|||
|
gpointer data);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
<em/widget/, <em/selection/ et <em/target/ identifient les requ<71>tes
|
|||
|
que ce gestionnaire g<>rera. S'il ne vaut pas NULL, <em/remove_func/
|
|||
|
sera appel<65> lorsque le gestionnaire de signal est supprim<69>. Ceci est
|
|||
|
utile, par exemple, pour des langages interpr<70>t<EFBFBD>s qui doivent garder
|
|||
|
une trace du nombre de r<>f<EFBFBD>rences <20> <em/data/.
|
|||
|
|
|||
|
<p>
|
|||
|
La fonction de rappel <em/function/ doit avoir la signature suivante :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
typedef void (*GtkSelectionFunction) (GtkWidget *widget,
|
|||
|
GtkSelectionData *selection_data,
|
|||
|
gpointer data);
|
|||
|
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Le <em/GtkSelectionData/ est le m<>me qu'au dessus, mais, cette fois,
|
|||
|
nous sommes responsables de l'initialisation de ses champs <em/type/,
|
|||
|
<em/format/, <em/data/, et <em/length/. (Le champ <em/format/ est
|
|||
|
important ici - le serveur X l'utilise pour savoir si la donn<6E>e doit
|
|||
|
<EFBFBD>tre <20>chang<6E>e par octet ou non. Habituellement, ce sera 8 (un
|
|||
|
caract<EFBFBD>re), ou 32 (un entier)). Cette initialisation est faite en
|
|||
|
utilisant l'appel :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_selection_data_set (GtkSelectionData *selection_data,
|
|||
|
GdkAtom type,
|
|||
|
gint format,
|
|||
|
guchar *data,
|
|||
|
gint length);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Cette fonction s'occupe de faire une copie correcte des donn<6E>es afin
|
|||
|
que l'on n'ait pas <20> se soucier du reste. (On ne doit pas remplir ces
|
|||
|
champs <20> la main).
|
|||
|
|
|||
|
<p>
|
|||
|
Lorsque cela est demand<6E> par l'utilisateur, on r<>clame la possession
|
|||
|
de la s<>lection en appelant :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
gint gtk_selection_owner_set (GtkWidget *widget,
|
|||
|
GdkAtom selection,
|
|||
|
guint32 time);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Si une autre application r<>clame la possession de la s<>lection, on
|
|||
|
recevra un "selection_clear_event".
|
|||
|
|
|||
|
Comme exemple de fourniture de s<>lection, l'exemple suivant ajoute une
|
|||
|
fonctionnalit<EFBFBD> de s<>lection <20> un bouton commutateur. Lorsque ce bouton
|
|||
|
est appuy<75>, le programme r<>clame la s<>lection primaire. La seule cible
|
|||
|
support<EFBFBD>e (<28> part certaines cibles fournies par GTK lui-m<>me, comme
|
|||
|
<EFBFBD> TARGETS <20>) est <20> STRING <20>. Lorsque celle-ci est demand<6E>e, on
|
|||
|
retourne une repr<70>sentation de l'heure sous forme de cha<68>ne.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
#include <gtk/gtk.h>
|
|||
|
#include <time.h>
|
|||
|
|
|||
|
/* Fonction de rappel appel<65>e lorsque l'utilisateur commute la s<>lection. */
|
|||
|
|
|||
|
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 s<>lection <20>choue, on remet le bouton en position sortie. */
|
|||
|
|
|||
|
if (!*have_selection)
|
|||
|
gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON(widget), FALSE);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
if (*have_selection)
|
|||
|
{
|
|||
|
/* Avant de nettoyer la selection en mettant son propri<72>taire <20> NULL,
|
|||
|
* on v<>rifie que nous sommes bien son propri<72>taire 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<65>e lorsqu'une autre application demande la s<>lection. */
|
|||
|
|
|||
|
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 s<>lection. */
|
|||
|
|
|||
|
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 cha<68>ne, 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);
|
|||
|
|
|||
|
/* Cr<43>ation de la fen<65>tre 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);
|
|||
|
|
|||
|
/* Cr<43>ation d'un bouton commutateur pour qu'il agisse comme une s<>lection. */
|
|||
|
|
|||
|
selection_button = gtk_toggle_button_new_with_label ("Demande de s<>lection");
|
|||
|
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;
|
|||
|
}
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
|
|||
|
|
|||
|
<sect>glib<label id="sec_glib">
|
|||
|
<p>
|
|||
|
La <em/glib/ fournit de nombreuses fonctions et d<>finitions utiles,
|
|||
|
pr<EFBFBD>tes <20> <20>tre utilis<69>es lorsqu'on cr<63>e des applications GDK et GTK. Je
|
|||
|
les <20>num<75>rerais toutes avec une br<62>ve explication. Beaucoup sont des
|
|||
|
r<EFBFBD>pliques des fonctions standards de la <em/libc/, et je ne les
|
|||
|
d<EFBFBD>taillerais donc pas trop. Ceci doit surtout servir de r<>f<EFBFBD>rence afin
|
|||
|
de savoir ce qui est disponible pour <20>tre utilis<69>.
|
|||
|
|
|||
|
<sect1>D<>finitions
|
|||
|
<p>
|
|||
|
Les d<>finitions pour les bornes de la plupart des types standards sont :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
G_MINFLOAT
|
|||
|
G_MAXFLOAT
|
|||
|
G_MINDOUBLE
|
|||
|
G_MAXDOUBLE
|
|||
|
G_MINSHORT
|
|||
|
G_MAXSHORT
|
|||
|
G_MININT
|
|||
|
G_MAXINT
|
|||
|
G_MINLONG
|
|||
|
G_MAXLONG
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Voici aussi les red<65>finitions de types. Celles qui ne sont pas
|
|||
|
sp<EFBFBD>cifi<EFBFBD>es sont configur<75>es dynamiquement selon l'architecture. <20>vitez
|
|||
|
surtout de compter sur la taille d'un pointeur si vous voulez un
|
|||
|
programme portable ! Un pointeur sur un Alpha fait 8 octets, mais il
|
|||
|
en fait 4 sur un Intel.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
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
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
<sect1>Listes doublement cha<68>n<EFBFBD>es
|
|||
|
<p>
|
|||
|
Les fonctions suivantes servent <20> cr<63>er, g<>rer et d<>truire des listes
|
|||
|
doublement cha<68>n<EFBFBD>es. Je suppose que vous savez ce qu'est une liste
|
|||
|
cha<EFBFBD>n<EFBFBD>e car leur explication n'entre pas dans le cadre de ce
|
|||
|
document. Bien s<>r, il n'y a pas besoin de les conna<6E>tre pour une
|
|||
|
utilisation g<>n<EFBFBD>rale de GTK, mais c'est bien de savoir comment elles
|
|||
|
fonctionnent.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
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);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
|
|||
|
<sect1>Listes simplement cha<68>n<EFBFBD>es
|
|||
|
<p>
|
|||
|
La plupart des fonctions pour les listes simplement cha<68>n<EFBFBD>es
|
|||
|
ci-dessous sont identiques <20> celles vues plus haut. Voici une liste
|
|||
|
compl<EFBFBD>te :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
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);
|
|||
|
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
<sect1>Gestion de la m<>moire
|
|||
|
<p>
|
|||
|
<tscreen><verb>
|
|||
|
gpointer g_malloc (gulong size);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Remplace <em/malloc()/. On n'a pas besoin de v<>rifier la valeur de
|
|||
|
retour car cela est fait pour nous dans cette fonction.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
gpointer g_malloc0 (gulong size);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Identique <20> la pr<70>c<EFBFBD>dente, mais initialise la m<>moire <20> z<>ro avant de
|
|||
|
retourner un pointeur vers la zone r<>serv<72>e.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
gpointer g_realloc (gpointer mem,
|
|||
|
gulong size);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
R<EFBFBD>alloue <em/size/ octets de m<>moire <20> partir de <em/mem/. <20>videmment,
|
|||
|
la m<>moire doit avoir <20>t<EFBFBD> allou<6F>e auparavant.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void g_free (gpointer mem);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Lib<EFBFBD>re la m<>moire. Facile.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void g_mem_profile (void);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Produit un profil de la m<>moire utilis<69>e, mais requiert l'ajout de
|
|||
|
<em/#define MEM_PROFILE/ au d<>but de <em>glib/gmem.c</em>,
|
|||
|
de refaire un <em/make/ et un <em/make install/.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void g_mem_check (gpointer mem);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
V<EFBFBD>rifie qu'un emplacement m<>moire est valide. N<>cessite que l'on
|
|||
|
ajoute <em/#define MEM_CHECK/ au d<>but de <em/gmem.c/ que l'on refasse
|
|||
|
un <em/make/ et un <em/make install/.
|
|||
|
|
|||
|
<sect1>Timers
|
|||
|
<p>
|
|||
|
Fonctions des timers...
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
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);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
<sect1>Gestion des cha<68>nes
|
|||
|
<p>
|
|||
|
Un ensemble complet de fonction de gestion des cha<68>nes. Elles semblent
|
|||
|
toutes tr<74>s int<6E>ressantes et sont s<>rement meilleures, <20> bien des
|
|||
|
<EFBFBD>gards, que les fonctions C standards, mais elle n<>cessitent de la
|
|||
|
documentation.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
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,
|
|||
|
...);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
<sect1>Utilitaires et fonctions d'erreurs
|
|||
|
<p>
|
|||
|
<tscreen><verb>
|
|||
|
gchar* g_strdup (const gchar *str);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Remplace la fonction <em/strdup/. Elle copie le contenu de la cha<68>ne
|
|||
|
d'origine dans la m<>moire venant d'<27>tre allou<6F>e et retourne un
|
|||
|
pointeur sur cette zone.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
gchar* g_strerror (gint errnum);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Je recommande de l'utiliser pour tous les messages d'erreur. Elle est
|
|||
|
beaucoup plus propre et plus portable que <em/perror()/ ou les
|
|||
|
autres. La sortie est habituellement de la forme :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
nom du programme:fonction qui a <20>chou<6F>:fichier ou autre descripteur:strerror
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Voici un exemple d'appel utilis<69> dans le programme <20> Bonjour tout le monde ! <20> :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
g_print("bonjour_monde:open:%s:%s\n", filename, g_strerror(errno));
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void g_error (gchar *format, ...);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Affiche un message d'erreur. Le format est comme <em/printf/, mais il
|
|||
|
ajoute <20> ** ERROR **: <20> au d<>but du message et sort du programme. <20>
|
|||
|
n'utiliser que pour les erreurs fatales.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void g_warning (gchar *format, ...);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Comme au dessus, mais ajoute <20> ** WARNING **: <20>, et ne termine pas le
|
|||
|
programme.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void g_message (gchar *format, ...);
|
|||
|
</verb></tscreen>
|
|||
|
Affiche <20> message: <20> avant la cha<68>ne pass<73>e en param<61>tre.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void g_print (gchar *format, ...);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Remplace <em/printf()/.
|
|||
|
|
|||
|
Enfin la derni<6E>re fonction :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
gchar* g_strsignal (gint signum);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Affiche le nom du signal syst<73>me Unix correspondant au num<75>ro de
|
|||
|
signal. Utile pour les fonctions g<>n<EFBFBD>riques de gestion de signaux.
|
|||
|
|
|||
|
Tout ce qui est ci-dessus est plus ou moins vol<6F> <20> <em/glib.h/. Si
|
|||
|
quelqu'un s'occupe de documenter une fonction, qu'il m'envoit un
|
|||
|
courrier !
|
|||
|
|
|||
|
|
|||
|
<sect>Fichiers rc de GTK
|
|||
|
<p>
|
|||
|
GTK a sa propre m<>thode pour g<>rer les configurations par d<>faut des
|
|||
|
applications, en utilisant des fichiers <tt/rc/. Ceux-ci peuvent <20>tre
|
|||
|
utilis<EFBFBD>s pour configurer les couleurs de presque tous les widgets, et
|
|||
|
pour mettre des pixmaps sur le fond de certains widgets.
|
|||
|
|
|||
|
<sect1>Fonctions pour les fichiers rc
|
|||
|
<p>
|
|||
|
Au d<>marrage de votre application, ajoutez un appel <20> :
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_rc_parse (char *filename);
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
en lui passant le nom de votre fichier rc. Ceci forcera GTK <20> analyser
|
|||
|
ce fichier et <20> utiliser les configurations de styles pour les types
|
|||
|
de widgets qui y sont d<>finis.
|
|||
|
<p>
|
|||
|
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 <20> :
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_widget_set_name (GtkWidget *widget,
|
|||
|
gchar *name);
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
En lui passant comme premier param<61>tre le widget que vous avez cr<63><72>,
|
|||
|
et le nom que vous voulez lui donner comme second param<61>tre. Ceci vous
|
|||
|
permettra de changer les attributs de ce widget par son nom dans le
|
|||
|
fichier rc.
|
|||
|
<p>
|
|||
|
Si vous utilisez un appel comme celui-ci :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
button = gtk_button_new_with_label ("Bouton Sp<53>cial");
|
|||
|
gtk_widget_set_name (button, "bouton special");
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
Ce bouton s'appelle <20> bouton special <20> et peut <20>tre acc<63>d<EFBFBD> par son nom
|
|||
|
dans le fichier rc en tant que <20> bouton special.GtkButton <20>. [<---
|
|||
|
V<EFBFBD>rifiez !]
|
|||
|
<p>
|
|||
|
Le fichier rc ci-dessous configure les propri<72>t<EFBFBD>s de la fen<65>tre
|
|||
|
principale et fait h<>riter tous les fils de celle-ci du style d<>crit
|
|||
|
par <20> bouton_principal <20>. Le code utilis<69> dans l'application
|
|||
|
est :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|||
|
gtk_widget_set_name (window, "fenetre principale");
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
Et le style est d<>fini dans le fichier rc avec :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
widget "fenetre principale.*GtkButton*" style "bouton_principal"
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
Ce qui configure tous les widgets <em/GtkButton/ de <20> fen<65>tre
|
|||
|
principale <20> avec le style <20> bouton_principal <20> d<>fini dans le fichier
|
|||
|
rc.
|
|||
|
<p>
|
|||
|
Ainsi que vous pouvez le voir, il s'agit d'un syst<73>me puissant et
|
|||
|
flexible. Utilisez votre imagination pour en tirer le meilleur.
|
|||
|
|
|||
|
<sect1>Format des fichiers rc de GTK
|
|||
|
<p>
|
|||
|
Le format du fichier GTK est illustr<74> dans l'exemple suivant. Il
|
|||
|
s'agit du fichier <em/testgtkrc/ de la distribution GTK mais j'ai
|
|||
|
ajout<EFBFBD> quelques commentaires et autres choses. Vous pouvez inclure
|
|||
|
cette explication <20> votre application pour permettre <20> l'utilisateur
|
|||
|
de r<>gler finement son application.
|
|||
|
<p>
|
|||
|
Il y a plusieurs directives pour changer les attributs d'un widget.
|
|||
|
<itemize>
|
|||
|
<item>fg - configure la couleur de premier plan d'un widget.
|
|||
|
<item>bg - configure la couleur d'arri<72>re plan d'un widget.
|
|||
|
<item>bg_pixmap - configure l'arri<72>re plan d'un widget avec un pixmap.
|
|||
|
<item>font - configure la fonte <20> utiliser pour un widget.
|
|||
|
</itemize>
|
|||
|
<p>
|
|||
|
De plus, un widget peut se trouver dans diff<66>rents <20>tats et l'on peut
|
|||
|
configurer des couleurs, pixmaps et fontes diff<66>rentes pour chacun
|
|||
|
d'eux. Ces <20>tats sont :
|
|||
|
<itemize>
|
|||
|
<item>NORMAL - L'<27>tat normal d'un widget, sans la souris au dessus de
|
|||
|
lui, non press<73>, etc.
|
|||
|
<item>PRELIGHT - Lorsque la souris se trouve au dessus du widget, les
|
|||
|
couleurs d<>finies pour cet <20>tat sont actives.
|
|||
|
<item>ACTIVE - Lorsque le widget est press<73> ou cliqu<71>, il devient
|
|||
|
actif et les attributs associ<63>s <20> cet <20>tat sont appliqu<71>s.
|
|||
|
<item>INSENSITIVE - Quand un widget est configur<75> pour <20>tre
|
|||
|
insensible et qu'il ne peut <20>tre activ<69>, il prend ces attributs.
|
|||
|
<item>SELECTED - Lorsqu'un objet est choisi, il prend ces attributs.
|
|||
|
</itemize>
|
|||
|
<p>
|
|||
|
Lorsqu'on utilise les mots-cl<63>s <20> <em/fg/ <20> et <20> <em/bg/ <20> pour
|
|||
|
configurer les couleurs des widgets, le format est :
|
|||
|
|
|||
|
<tscreen><verb> fg[<STATE>] = { Red, Green, Blue } </verb></tscreen>
|
|||
|
<p>
|
|||
|
O<EFBFBD> STATE est l'un des <20>tats vus plus haut (PRELIGHT, ACTIVE etc), et
|
|||
|
o<EFBFBD> <em/Red/, <em/Green/ et <em/Blue/ sont des valeurs comprises entre
|
|||
|
0 et 1.0. { 1.0, 1.0, 1.0 } repr<70>sente la couleur blanche. Ces
|
|||
|
valeurs doivent <20>tre de type r<>el ou elles seront consid<69>r<EFBFBD>es comme
|
|||
|
valant 0, ainsi un simple <20> 1 <20> ne marchera pas, il faut mettre <20> 1.0
|
|||
|
<EFBFBD>. Un <20> 0 <20> simple convient car ce n'est pas un probl<62>me s'il n'est
|
|||
|
pas reconnu puisque toutes les valeurs non reconnues sont mises <20> 0.
|
|||
|
<p>
|
|||
|
<em/bg_pixmap/ est tr<74>s similaire, sauf que les couleurs sont
|
|||
|
remplac<EFBFBD>es par un nom de fichier.
|
|||
|
|
|||
|
<em/pixmap_path/ est une liste de chemins s<>par<61>s par des <20> : <20>. Ces
|
|||
|
chemins seront parcourus pour chaque pixmap que l'on sp<73>cifie.
|
|||
|
|
|||
|
<p>
|
|||
|
La directive <em/font/ est simplement :
|
|||
|
<tscreen><verb>
|
|||
|
font = "<font name>"
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
O<EFBFBD> la seule partie difficile est d'arriver <20> comprendre la cha<68>ne
|
|||
|
contenant le nom de la fonte. L'utilisation de <em/xfontsel/ ou d'un
|
|||
|
autre utilitaire semblable peut aider.
|
|||
|
<p>
|
|||
|
<EFBFBD> <em/widget_class/ <20> configure le style d'une classe de widgets. Ces
|
|||
|
classes sont list<73>es dans la section sur la hi<68>rarchie des widgets.
|
|||
|
<p>
|
|||
|
La directive <20> <em/widget/ <20> configure un ensemble sp<73>cifique de
|
|||
|
widgets selon un style donn<6E>, annulant tout style de configuration
|
|||
|
pour la classe de widget donn<6E>e. Ces widgets sont enregistr<74>s dans
|
|||
|
l'application en utilisant l'appel <em/gtk_widget_set_name()/. Ceci
|
|||
|
vous permet de sp<73>cifier les attributs d'un widget, widget par widget,
|
|||
|
au lieu de configurer les attributs d'une classe enti<74>re de
|
|||
|
widgets. Je vous demande instamment de documenter tous ces widgets
|
|||
|
sp<EFBFBD>ciaux pour que les utilisateurs puisse les adapter <20> leurs besoins.
|
|||
|
<p>
|
|||
|
Lorsque le mot-cl<63> <20> <em/parent/ <20> est utilis<69> comme attribut, le
|
|||
|
widget prendra les attributs de son parent dans l'application.
|
|||
|
<p>
|
|||
|
Lorsqu'on d<>finit un style, on peut assigner les attributs d'un style
|
|||
|
d<EFBFBD>j<EFBFBD> d<>fini <20> ce nouveau style.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
style "bouton_principal" = "button"
|
|||
|
{
|
|||
|
font = "-adobe-helvetica-medium-r-normal--*-100-*-*-*-*-*-*"
|
|||
|
bg[PRELIGHT] = { 0.75, 0, 0 }
|
|||
|
}
|
|||
|
</verb></tscreen>
|
|||
|
<p>
|
|||
|
Cet exemple prend le style "button" et cr<63>e un nouveau style
|
|||
|
"bouton_principal"en changeant simplement la fonte et la couleur de
|
|||
|
fond pour l'<27>tat PRELIGHT.
|
|||
|
<p>
|
|||
|
Bien s<>r, un bon nombre de ces attributs ne s'applique pas <20> tous les
|
|||
|
widgets. C'est une question de bon sens. Tout ce qui peut s'appliquer
|
|||
|
s'applique.
|
|||
|
|
|||
|
<sect1>Exemple de fichier rc
|
|||
|
<p>
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
# 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 <20>tats possibles. Remarquez que certains ne s'appliquent
|
|||
|
# pas <20> certains widgets.
|
|||
|
#
|
|||
|
# NORMAL - L'<27>tat normal d'un widget, sans la souris au dessus de lui,
|
|||
|
# non press<73>, etc.
|
|||
|
#
|
|||
|
# PRELIGHT - Lorsque la souris se trouve au dessus du widget, les couleurs
|
|||
|
# d<>finies pour cet <20>tat sont actives.
|
|||
|
#
|
|||
|
# ACTIVE - Lorsque le widget est press<73> ou cliqu<71>, il devient actif et les
|
|||
|
# attributs associ<63>s <20> cet <20>tat sont appliqu<71>s.
|
|||
|
#
|
|||
|
# INSENSITIVE - Quand un widget est configur<75> pour <20>tre insensible, et qu'il
|
|||
|
# ne peut <20>tre activ<69>, il prend ces attributs.
|
|||
|
#
|
|||
|
# SELECTED - Lorsqu'un objet est choisi, il prend ces attributs.
|
|||
|
#
|
|||
|
# Avec ces <20>tats, on peut configurer les attributs des widgets dans chacun
|
|||
|
# de ces <20>tats en utilisant les directives suivantes.
|
|||
|
#
|
|||
|
# fg - configure la couleur de premier plan d'un widget.
|
|||
|
# bg - configure la couleur d'arri<72>re plan d'un widget.
|
|||
|
# bg_pixmap - configure l'arri<72>re plan d'un widget avec un pixmap.
|
|||
|
# font - configure la fonte <20> utiliser pour un widget.
|
|||
|
|
|||
|
# Configuration d'un style appel<65> "button". Le nom n'est pas important
|
|||
|
# car il est assign<67> aux widgets r<>els <20> la fin du fichier.
|
|||
|
|
|||
|
style "window"
|
|||
|
{
|
|||
|
#Configure l'espace autour de la fen<65>tre avec le pixmap sp<73>cifi<66>.
|
|||
|
#bg_pixmap[<STATE>] = "<pixmap filename>"
|
|||
|
bg_pixmap[NORMAL] = "warning.xpm"
|
|||
|
}
|
|||
|
|
|||
|
style "scale"
|
|||
|
{
|
|||
|
#Configure la couleur de premier plan (celle de la fonte) <20> rouge
|
|||
|
#lorsqu'on est dans l'<27>tat "NORMAL".
|
|||
|
|
|||
|
fg[NORMAL] = { 1.0, 0, 0 }
|
|||
|
|
|||
|
#Configure le pixmap d'arri<72>re plan de ce widget <20> celui de son parent.
|
|||
|
bg_pixmap[NORMAL] = "<parent>"
|
|||
|
}
|
|||
|
|
|||
|
style "button"
|
|||
|
{
|
|||
|
# Voici tous les <20>tats possibles pour un bouton. Le seul qui ne peut
|
|||
|
# s'appliquer est l'<27>tat SELECTED.
|
|||
|
|
|||
|
fg[PRELIGHT] = { 0, 1.0, 1.0 }
|
|||
|
bg[PRELIGHT] = { 0, 0, 1.0 }
|
|||
|
bg[ACTIVE] = { 1.0, 0, 0 }
|
|||
|
fg[ACTIVE] = { 0, 1.0, 0 }
|
|||
|
bg[NORMAL] = { 1.0, 1.0, 0 }
|
|||
|
fg[NORMAL] = { .99, 0, .99 }
|
|||
|
bg[INSENSITIVE] = { 1.0, 1.0, 1.0 }
|
|||
|
fg[INSENSITIVE] = { 1.0, 0, 1.0 }
|
|||
|
}
|
|||
|
|
|||
|
# Dans cet exemple, on h<>rite des attributs du style "button" puis on
|
|||
|
# <20>crase la fonte et la couleur de fond pour cr<63>er 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 <20> celui de son widget
|
|||
|
# parent (comme d<>fini 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 d<>finis
|
|||
|
# plus haut.
|
|||
|
# Les types de widget sont list<73>s dans la hi<68>rarchie des classes, mais
|
|||
|
# peut probablement <20>tre list<73>e dans ce document pour que l'utilisateur
|
|||
|
# puisse s'y r<>f<EFBFBD>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 <20>tre document<6E> pour en tirer profit.
|
|||
|
widget "main window.*GtkButton*" style "main_button"
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
<sect><3E>criture de vos propres widgets
|
|||
|
<p>
|
|||
|
<sect1>Vue d'ensemble
|
|||
|
<p>
|
|||
|
Bien que la distribution GTK fournisse de nombreux types de widgets
|
|||
|
qui devraient couvrir la plupart des besoins de base, il peut arriver
|
|||
|
un moment o<> vous aurez besoin de cr<63>er votre propre type de
|
|||
|
widget. Comme GTK utilise l'h<>ritage de widget de fa<66>on intensive et
|
|||
|
qu'il y a d<>j<EFBFBD> un widget ressemblant <20> celui que vous voulez, il est
|
|||
|
souvent possible de cr<63>er un nouveau type de widget en seulement
|
|||
|
quelques lignes de code. Mais, avant de travailler sur un nouveau
|
|||
|
widget, il faut v<>rifier d'abord que quelqu'un ne l'a pas d<>j<EFBFBD>
|
|||
|
<EFBFBD>crit. Ceci <20>viter la duplication des efforts et maintient au minimum
|
|||
|
le nombre de widgets, ce qui permet de garder la coh<6F>rence du code et
|
|||
|
de l'interface des diff<66>rentes applications. Un effet de bord est que,
|
|||
|
lorsque l'on a cr<63><72> un nouveau widget, il faut l'annoncer afin que les
|
|||
|
autres puissent en b<>n<EFBFBD>ficier. Le meilleur endroit pour faire cela
|
|||
|
est, sans doute, la <tt>gtk-list</tt>.
|
|||
|
|
|||
|
<sect1>Anatomie d'un widget
|
|||
|
|
|||
|
<p>
|
|||
|
Afin de cr<63>er un nouveau widget, il importe de comprendre comment
|
|||
|
fonctionnent les objets GTK. Cette section ne se veut <20>tre qu'un
|
|||
|
rapide survol. Consultez la documentation de r<>f<EFBFBD>rence pour plus de
|
|||
|
d<EFBFBD>tails.
|
|||
|
|
|||
|
<p>
|
|||
|
Les widgets sont implant<6E>s selon une m<>thode orient<6E>e
|
|||
|
objet. Cependant, ils sont <20>crits en C standard. Ceci am<61>liore
|
|||
|
beaucoup la portabilit<69> et la stabilit<69>, par contre cela signifie que
|
|||
|
celui qui <20>crit des widgets doit faire attention <20> certains d<>tails
|
|||
|
d'implantation. Les informations communes <20> toutes les instances d'une
|
|||
|
classe de widget (tous les widgets boutons, par exemple) sont stock<63>es
|
|||
|
dans la <em/structure de la classe/. Il n'y en a qu'une copie dans
|
|||
|
laquelle sont stock<63>es les informations sur les signaux de la
|
|||
|
classe (fonctionnement identique aux fonctions virtuelles en C). Pour
|
|||
|
permettre l'h<>ritage, le premier champ de la structure de classe doit
|
|||
|
<EFBFBD>tre une copie de la structure de classe du p<>re. La d<>claration de la
|
|||
|
structure de classe de <em/GtkButton/ ressemble <20> ceci :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
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);
|
|||
|
};
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
<p>
|
|||
|
Lorsqu'un bouton est trait<69> comme un container (par exemple, lorsqu'il
|
|||
|
change de taille), sa structure de classe peut <20>tre convertie en
|
|||
|
<em/GtkContainerClass/ et les champs ad<61>quats utilis<69>s pour g<>rer les
|
|||
|
signaux.
|
|||
|
|
|||
|
<p>
|
|||
|
Il y a aussi une structure pour chaque widget cr<63><72> sur une base
|
|||
|
d'instance. Cette structure a des champs pour stocker les informations
|
|||
|
qui sont diff<66>rentes pour chaque instance du widget. Nous l'appelerons
|
|||
|
<em/structure d'objet/. Pour la classe <em/Button/, elle ressemble
|
|||
|
<EFBFBD> :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
struct _GtkButton
|
|||
|
{
|
|||
|
GtkContainer container;
|
|||
|
|
|||
|
GtkWidget *child;
|
|||
|
|
|||
|
guint in_button : 1;
|
|||
|
guint button_down : 1;
|
|||
|
};
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
<p>
|
|||
|
Notez que, comme pour la structure de classe, le premier champ est la
|
|||
|
structure d'objet de la classe parente, cette structure peut donc <20>tre
|
|||
|
convertie dans la structure d'objet de la classe parente si besoin
|
|||
|
est.
|
|||
|
|
|||
|
<sect1> Cr<43>ation d'un widget compos<6F>
|
|||
|
|
|||
|
<sect2> Introduction
|
|||
|
|
|||
|
<p>
|
|||
|
Un type de widget qui peut <20>tre int<6E>ressant <20> cr<63>er est un widget qui
|
|||
|
est simplement un agr<67>gat d'autres widgets GTK. Ce type de widget ne
|
|||
|
fait rien qui ne pourrait <20>tre fait sans cr<63>er de nouveaux widgets,
|
|||
|
mais offre une m<>thode pratique pour empaqueter les <20>l<EFBFBD>ments d'une
|
|||
|
interface utilisateur afin de la r<>utiliser facilement. Les widgets
|
|||
|
<em/FileSelection/ et <em/ColorSelection/ de la distribution standard
|
|||
|
sont des exemples de ce type de widget.
|
|||
|
|
|||
|
<p>
|
|||
|
L'exemple de widget que nous allons cr<63>er dans cette section cr<63>era un
|
|||
|
widget <em/Tictactoe/, un tableau de 3x3 boutons commutateurs qui
|
|||
|
d<EFBFBD>clenche un signal lorsque tous les boutons d'une ligne, d'une
|
|||
|
colonne, ou d'une diagonale sont press<73>s.
|
|||
|
|
|||
|
<sect2> Choix d'une classe parent
|
|||
|
|
|||
|
<p>
|
|||
|
La classe parent d'un widget compos<6F> est, typiquement, la classe
|
|||
|
container contenant tous les <20>l<EFBFBD>ments du widget compos<6F>. Par exemple,
|
|||
|
la classe parent du widget <em/FileSelection/ est la classe
|
|||
|
<em/Dialog/. Comme nos boutons seront mis sous la forme d'un tableau,
|
|||
|
il semble naturel d'utiliser la classe <em/GtkTable/ comme
|
|||
|
parent. Malheureusement, cela ne peut marcher. La cr<63>ation d'un widget
|
|||
|
est divis<69>e en deux fonctions -- <em/WIDGETNAME_new()/ que
|
|||
|
l'utilisateur appelle, et <em/WIDGETNAME_init()/ qui r<>alise le
|
|||
|
travail d'initialisation du widget ind<6E>pendamment des param<61>tre pass<73>s
|
|||
|
<EFBFBD> la fonction <tt/_new()/. Les widgets fils n'appellent que la
|
|||
|
fonction <em/_init/ de leur widget parent. Mais cette division du
|
|||
|
travail ne fonctionne pas bien avec les tableaux qui, lorsqu'ils sont
|
|||
|
cr<EFBFBD><EFBFBD>s, ont besoin de conna<6E>tre leue nombre de lignes et de
|
|||
|
colonnes. Sauf <20> dupliquer la plupart des fonctionnalit<69>s de
|
|||
|
<em/gtk_table_new()/ dans notre widget <em/Tictactoe/, nous ferions
|
|||
|
mieux d'<27>viter de le d<>river de <em/GtkTable/. Pour cette raison, nous
|
|||
|
la d<>riverons plut<75>t de <em/GtkVBox/ et nous placerons notre table
|
|||
|
dans la VBox.
|
|||
|
|
|||
|
<sect2> The header file
|
|||
|
|
|||
|
<p>
|
|||
|
Chaque classe de widget poss<73>de un fichier en-t<>te qui d<>clare les
|
|||
|
structures d'objet et de classe pour ce widget, en plus de fonctions
|
|||
|
publiques. Quelques caract<63>ristiques m<>ritent d'<27>tre indiqu<71>es. Afin
|
|||
|
d'<27>viter des d<>finitions multiples, on enveloppe le fichier en-t<>te
|
|||
|
avec :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
#ifndef __TICTACTOE_H__
|
|||
|
#define __TICTACTOE_H__
|
|||
|
.
|
|||
|
.
|
|||
|
.
|
|||
|
#endif /* __TICTACTOE_H__ */
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Et, pour faire plaisir aux programmes C++ qui inclueront ce fichier, on l'enveloppe aussi dans :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
#ifdef __cplusplus
|
|||
|
extern "C" {
|
|||
|
#endif /* __cplusplus */
|
|||
|
.
|
|||
|
.
|
|||
|
.
|
|||
|
#ifdef __cplusplus
|
|||
|
}
|
|||
|
#endif /* __cplusplus */
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
En plus des fonctions et structures, nous d<>clarons trois macros
|
|||
|
standard, <tt/TICTACTOE(obj)/, <tt/TICTACTOE_CLASS(class)/, et
|
|||
|
<tt/IS_TICTACTOE(obj)/, qui, respectivement, convertissent un pointeur
|
|||
|
en un pointeur vers une structure d'objet ou de classe, et v<>rifient
|
|||
|
si un objet est un widget Tictactoe.
|
|||
|
|
|||
|
<p>
|
|||
|
Voici le fichier en-t<>te complet :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
|
|||
|
#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__ */
|
|||
|
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
<sect2>La fonction <tt/_get_type()/
|
|||
|
|
|||
|
<p>
|
|||
|
Continuons maintenant avec l'implantation de notre widget. La fonction
|
|||
|
centrale pour chaque widget est <em/WIDGETNAME_get_type()/. Cette
|
|||
|
fonction, lorsqu'elle est appel<65>e pour la premi<6D>re fois, informe le
|
|||
|
GTK de la classe et r<>cup<75>re un ID permettant d'identifier celle-ci de
|
|||
|
fa<EFBFBD>on unique. Lors des appels suivants, elle ne fait que retourner cet
|
|||
|
ID.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
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;
|
|||
|
}
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
<p>
|
|||
|
La structure <em/GtkTypeInfo/ est d<>finie de la fa<66>on suivante :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
struct _GtkTypeInfo
|
|||
|
{
|
|||
|
gchar *type_name;
|
|||
|
guint object_size;
|
|||
|
guint class_size;
|
|||
|
GtkClassInitFunc class_init_func;
|
|||
|
GtkObjectInitFunc object_init_func;
|
|||
|
GtkArgFunc arg_func;
|
|||
|
};
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
<p>
|
|||
|
Les champs de cette structure s'expliquent d'eux-m<>mes. Nous
|
|||
|
ignorerons le champ <em/arg_func/ ici : il a un r<>le important
|
|||
|
permettant aux options des widgets d'<27>tre correctement initialis<69>es <20>
|
|||
|
partir des langages interpr<70>t<EFBFBD>s, mais cette fonctionnalit<69> est encore
|
|||
|
tr<EFBFBD>s peu implant<6E>e. Lorsque GTK dispose d'une copie correctement
|
|||
|
remplie de cette structure, il sait comment cr<63>er des objets d'un type
|
|||
|
particulier de widget.
|
|||
|
|
|||
|
<sect2>La fonction <em/_class_init()/
|
|||
|
|
|||
|
<p>
|
|||
|
La fonction <em/WIDGETNAME_class_init()/ initialise les champs de la
|
|||
|
structure de classe du widget et configure tous les signaux de cette
|
|||
|
classe. Pour notre widget Tictactoe, cet appel est :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
|
|||
|
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;
|
|||
|
}
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
<p>
|
|||
|
Notre widget n'a qu'un signal : "tictactoe", invoqu<71> lorsqu'une
|
|||
|
ligne, une colonne ou une diagonale est compl<70>tement remplie. Tous les
|
|||
|
widgets compos<6F>s n'ont pas besoin de signaux. Si vous lisez ceci pour
|
|||
|
la premi<6D>re fois, vous pouvez passer directement <20> la section suivante
|
|||
|
car les choses vont se compliquer un peu
|
|||
|
|
|||
|
La fonction :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
gint gtk_signal_new (gchar *name,
|
|||
|
GtkSignalRunType run_type,
|
|||
|
gint object_type,
|
|||
|
gint function_offset,
|
|||
|
GtkSignalMarshaller marshaller,
|
|||
|
GtkArgType return_val,
|
|||
|
gint nparams,
|
|||
|
...);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
cr<EFBFBD>e un nouveau signal. Les param<61>tres sont :
|
|||
|
|
|||
|
<itemize>
|
|||
|
<item> <em/name/ : Le nom du signal signal.
|
|||
|
<item> <em/run_type/ : Indique si le gestionnaire par d<>faut doit <20>tre
|
|||
|
lanc<EFBFBD> avant ou apr<70>s le gestionnaire de l'utilisateur. Le plus
|
|||
|
souvent, ce sera <tt/GTK_RUN_FIRST/, ou <tt/GTK_RUN_LAST/, bien qu'il
|
|||
|
y ait d'autres possibilit<69>s.
|
|||
|
|
|||
|
<item> <em/object_type/ : L'ID de l'objet auquel s'applique ce signal
|
|||
|
(il s'appliquera aussi au descendants de l'objet).
|
|||
|
|
|||
|
<item> <em/function_offset/ : L'offset d'un pointeur vers le
|
|||
|
gestionnaire par d<>faut dans la structure de classe.
|
|||
|
|
|||
|
<item> <em/marshaller/ : Fonction utilis<69>e pour invoquer le
|
|||
|
gestionnaire de signal. Pour les gestionnaires de signaux n'ayant pas
|
|||
|
d'autres param<61>tres que l'objet <20>metteur et les donn<6E>es utilisateur,
|
|||
|
on peut utiliser la fonction pr<70>d<EFBFBD>finie
|
|||
|
<em/gtk_signal_default_marshaller()/.
|
|||
|
|
|||
|
<item> <em/return_val/ : Type de la valeur retourn<72>e.
|
|||
|
|
|||
|
<item> <em/nparams/ : Nombre de param<61>tres du gestionnaire de signal
|
|||
|
(autres que les deux par d<>faut mentionn<6E>s plus haut).
|
|||
|
|
|||
|
<item> <em/.../ : Types des param<61>tres.
|
|||
|
</itemize>
|
|||
|
|
|||
|
Lorsque l'on sp<73>cifie les types, on utilise l'<27>num<75>ration
|
|||
|
<em/GtkArgType/ :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
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;
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
<p>
|
|||
|
<em/gtk_signal_new()/ retourne un identificateur entier pour le
|
|||
|
signal, que l'on stocke dans le tableau <em/tictactoe_signals/, indic<69>
|
|||
|
par une <20>num<75>ration (conventionnellement, les <20>l<EFBFBD>ments de
|
|||
|
l'<27>num<75>ration sont le nom du signal, en majuscules, mais, ici, il y
|
|||
|
aurait un conflit avec la macro <tt/TICTACTOE()/, nous l'appellerons
|
|||
|
donc <tt/TICTACTOE_SIGNAL/ <20> la place.
|
|||
|
|
|||
|
Apr<EFBFBD>s avoir cr<63><72> nos signaux, nous devons demander <20> GTK d'associer
|
|||
|
ceux-ci <20> la classe Tictactoe. Ceci est fait en appelant
|
|||
|
<em/gtk_object_class_add_signals()/. Puis nous configurons le pointeur
|
|||
|
qui pointe sur le gestionnaire par d<>faut du signal "tictactoe" <20>
|
|||
|
NULL, pour indiquer qu'il n'y a pas d'action par d<>faut.
|
|||
|
|
|||
|
<sect2>La fonction <em/_init()/
|
|||
|
<p>
|
|||
|
|
|||
|
Chaque classe de widget a aussi besoin d'une fonction pour initialiser
|
|||
|
la structure d'objet. Habituellement, cette fonction a le r<>le, plut<75>t
|
|||
|
limit<EFBFBD>, d'initialiser les champs de la structure avec des valeurs par
|
|||
|
d<EFBFBD>faut. Cependant, pour les widgets compos<6F>s, cette fonction cr<63>e
|
|||
|
aussi les widgets composants.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
|
|||
|
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]);
|
|||
|
}
|
|||
|
}
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
<sect2> Et le reste...
|
|||
|
<p>
|
|||
|
|
|||
|
Il reste une fonction que chaque widget (sauf pour les types widget de
|
|||
|
base, comme <em/GtkBin/, qui ne peuvent <20>tre instanci<63>s) <20> besoin
|
|||
|
d'avoir -- celle que l'utilisateur appelle pour cr<63>er un objet de ce
|
|||
|
type. Elle est conventionnellement appel<65>e <em/WIDGETNAME_new()/. Pour
|
|||
|
certains widgets, par pour ceux de Tictactoe, cette fonction prend des
|
|||
|
param<EFBFBD>tres et r<>alise certaines initialisations d<>pendantes des
|
|||
|
param<EFBFBD>tres. Les deux autres fonctions sont sp<73>cifiques au widget
|
|||
|
Tictactoe.
|
|||
|
|
|||
|
<p>
|
|||
|
<em/tictactoe_clear()/ est une fonction publique qui remet tous les
|
|||
|
boutons du widget en position rel<65>ch<63>e. Notez l'utilisation de
|
|||
|
<em/gtk_signal_handler_block_by_data()/ pour emp<6D>cher notre
|
|||
|
gestionnaire de signaux des boutons commutateurs d'<27>tre d<>clench<63> sans
|
|||
|
besoin.
|
|||
|
|
|||
|
<p>
|
|||
|
<em/tictactoe_toggle()/ est le gestionnaire de signal invoqu<71>
|
|||
|
lorsqu'on clique sur un bouton. Il v<>rifie s'il y a des combinaisons
|
|||
|
gagnantes concernant le bouton qui vient d'<27>tre commut<75> et, si c'est
|
|||
|
le cas, <20>met le signal "tictactoe".
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
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;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
<p>
|
|||
|
|
|||
|
Enfin, un exemple de programme utilisant notre widget Tictactoe
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
#include <gtk/gtk.h>
|
|||
|
#include "tictactoe.h"
|
|||
|
|
|||
|
/* Invoqu<71> lorsqu'une ligne, une colonne ou une diagonale est compl<70>te */
|
|||
|
|
|||
|
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);
|
|||
|
|
|||
|
/* Cr<43>ation 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;
|
|||
|
}
|
|||
|
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
<sect1> Cr<43>ation d'un widget <20> partir de z<>ro
|
|||
|
|
|||
|
<sect2> Introduction
|
|||
|
|
|||
|
<p>
|
|||
|
|
|||
|
Dans cette section, nous en apprendrons plus sur la fa<66>on dont les
|
|||
|
widgets s'affichent eux-m<>mes <20> l'<27>cran et comment ils interagissent
|
|||
|
avec les <20>v<EFBFBD>nements. Comme exemple, nous cr<63>erons un widget d'appel
|
|||
|
t<EFBFBD>lephonique interactif avec un pointeur que l'utilisateur pourra
|
|||
|
d<EFBFBD>placer pour initialiser la valeur.
|
|||
|
|
|||
|
<sect2>Afficher un widget <20> l'<27>cran
|
|||
|
<p>
|
|||
|
Il y a plusieurs <20>tapes mises en jeu lors de l'affichage. Lorsque le widget est
|
|||
|
cr<EFBFBD><EFBFBD> par l'appel <em/WIDGETNAME_new()/, plusieurs autres fonctions
|
|||
|
suppl<EFBFBD>mentaires sont requises.
|
|||
|
|
|||
|
<itemize>
|
|||
|
<item> <em/WIDGETNAME_realize()/ s'occupe de cr<63>er une fen<65>tre X pour le
|
|||
|
widget, s'il en a une.
|
|||
|
<item> <em/WIDGETNAME_map()/ est invoqu<71>e apr<70>s l'appel de
|
|||
|
<em/gtk_widget_show()/. Elle s'assure que le widget est bien trac<61> <20> l'<27>cran
|
|||
|
(<em/mapp<70>/). Dans le cas d'une classe container, elle doit aussi appeler des
|
|||
|
fonctions <em/map()/> pour chaque widget fils.
|
|||
|
<item> <em/WIDGETNAME_draw()/ est invoqu<71>e lorsque <em/gtk_widget_draw()/ est
|
|||
|
appel<EFBFBD> pour le widget ou l'un de ces anc<6E>tres. Elle r<>alise les v<>ritables
|
|||
|
appels aux fonctions de dessin pour tracer le widget <20> l'<27>cran. Pour les
|
|||
|
widgets containers, cette fonction doit appeler <em/gtk_widget_draw()/ pour ses
|
|||
|
widgets fils.
|
|||
|
<item> <em/WIDGETNAME_expose()/ est un gestionnaire pour les <20>v<EFBFBD>nements
|
|||
|
d'exposition du widget. Il r<>alise les appels n<>cessaires aux fonctions de
|
|||
|
dessin pour tracer la partie expos<6F>e <20> l'<27>cran. Pour les widgets containers,
|
|||
|
cette fonction doit g<>n<EFBFBD>rer les <20>v<EFBFBD>nements d'exposition pour ses widgets
|
|||
|
enfants n'ayant pas leurs propres fen<65>tres (s'ils ont leurs propres fen<65>tres, X
|
|||
|
g<EFBFBD>n<EFBFBD>rera les <20>v<EFBFBD>nements d'exposition n<>cessaires).
|
|||
|
</itemize>
|
|||
|
|
|||
|
<p>
|
|||
|
Vous avez pu noter que les deux derni<6E>res fonctions sont assez similaires --
|
|||
|
chacune se charge de tracer le widget <20> l'<27>cran. En fait, de nombreux types de
|
|||
|
widgets ne se pr<70>occupent pas vraiment de la diff<66>rence entre les deux. La
|
|||
|
fonction <em/draw()/ par d<>faut de la classe widget g<>n<EFBFBD>re simplement un
|
|||
|
<EFBFBD>v<EFBFBD>nement d'exposition synth<74>tique pour la zone <20> redessiner. Cependant,
|
|||
|
certains types de widgets peuvent <20>conomiser du travail en faisant la
|
|||
|
diff<EFBFBD>rence entre les deux fonctions. Par exemple, si un widget a plusieurs
|
|||
|
fen<EFBFBD>tres X et puisque les <20>v<EFBFBD>nements d'exposition identifient la fen<65>tre
|
|||
|
expos<EFBFBD>e, il peut redessiner seulement la fen<65>tre concern<72>e, ce qui n'est pas
|
|||
|
possible avec des appels <20> <em/draw()/.
|
|||
|
|
|||
|
<p>
|
|||
|
Les widgets container, m<>me s'ils ne se soucient pas eux-m<>mes de la
|
|||
|
diff<EFBFBD>rence, ne peuvent pas utiliser simplement la fonction <em/draw()/ car
|
|||
|
leurs widgets enfants tiennent compte de cette diff<66>rence. Cependant, ce serait
|
|||
|
du gaspillage de dupliquer le code de trac<61> pour les deux
|
|||
|
fonctions. Conventionnellement, de tels widgets poss<73>dent une fonction nomm<6D>e
|
|||
|
<em/WIDGETNAME_paint()/ qui r<>alise le v<>ritable travail de trac<61> du widget et
|
|||
|
qui est appel<65>e par les fonctions <tt/draw()/ et <tt/expose()/.
|
|||
|
|
|||
|
<p>
|
|||
|
Dans notre exemple, comme le widget d'appel n'est pas un widget container et
|
|||
|
n'a qu'une fen<65>tre, nous pouvons utiliser l'approche la plus simple :
|
|||
|
utiliser la fonction <em/draw()/ par d<>faut et n'implanter que la fonction
|
|||
|
<em/expose()/.
|
|||
|
|
|||
|
<sect2>Origines du widget Dial
|
|||
|
|
|||
|
<p>
|
|||
|
Exactement comme les animaux terrestres ne sont que des variantes des premiers
|
|||
|
amphibiens qui ramp<6D>rent hors de la boue, les widgets GTK sont des variantes
|
|||
|
d'autres widgets, d<>j<EFBFBD> <20>crits. Ainsi, bien que cette section s'appelle <20> cr<63>er
|
|||
|
un widget <20> partir de z<>ro <20>, le widget Dial commence r<>ellement avec le code
|
|||
|
source du widget Range. Celui-ci a <20>t<EFBFBD> pris comme point de d<>part car ce serait
|
|||
|
bien que notre Dial ait la m<>me interface que les widgets Scale qui ne sont que
|
|||
|
des descendants sp<73>cialis<69>s du widget Range. Par cons<6E>quent, bien que le code
|
|||
|
source soit pr<70>sent<6E> ci-dessous sous une forme achev<65>e, cela n'implique pas
|
|||
|
qu'il a <20>t<EFBFBD> <20>crit <em/deus ex machina/. De plus, si vous ne savez pas comment
|
|||
|
fonctionnent les widgets Scale du point de vue du programmeur de l'application,
|
|||
|
il est pr<70>f<EFBFBD>rable de les <20>tudier avant de continuer.
|
|||
|
|
|||
|
<sect2>Les bases
|
|||
|
|
|||
|
<p>
|
|||
|
Un petite partie de notre widget devrait ressembler au widget Tictactoe. Nous
|
|||
|
avons d'abord le fichier en-t<>te :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
/* 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 <20> jour
|
|||
|
(GTK_UPDATE_[CONTINUOUS/DELAYED/DISCONTINUOUS]) */
|
|||
|
|
|||
|
guint policy : 2;
|
|||
|
|
|||
|
/* Le bouton qui est press<73>, 0 si aucun */
|
|||
|
guint8 button;
|
|||
|
|
|||
|
/* Dimensions des composants de dial */
|
|||
|
gint radius;
|
|||
|
gint pointer_width;
|
|||
|
|
|||
|
/* ID du timer de mise <20> jour, 0 si aucun */
|
|||
|
guint32 timer;
|
|||
|
|
|||
|
/* Angle courant*/
|
|||
|
gfloat angle;
|
|||
|
|
|||
|
/* Anciennes valeurs d'ajustement stock<63>es. On sait donc quand quelque
|
|||
|
chose change */
|
|||
|
gfloat old_value;
|
|||
|
gfloat old_lower;
|
|||
|
gfloat old_upper;
|
|||
|
|
|||
|
/* L'objet ajustment qui stocke les donn<6E>es 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__ */
|
|||
|
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Comme il y a plus de choses <20> faire avec ce widget par rapport <20> l'autre, nous
|
|||
|
avons plus de champs dans la structure de donn<6E>es, mais <20> part <20>a, les choses
|
|||
|
sont plut<75>t similaires.
|
|||
|
|
|||
|
<p>
|
|||
|
|
|||
|
Puis, apr<70>s avoir inclus les fichiers en-t<>te et d<>clar<61> quelques constantes,
|
|||
|
nous devons fournir quelques fonctions pour donner des informations sur le
|
|||
|
widget et pour l'initialiser :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
#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
|
|||
|
|
|||
|
/* D<>clararations des prototypes */
|
|||
|
|
|||
|
[ omis pour gagner de la place ]
|
|||
|
|
|||
|
/* Donn<6E>es 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);
|
|||
|
}
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Notez que cette fonction <em/init()/ fait moins de choses que pour le widget
|
|||
|
Tictactoe car ce n'est pas un widget compos<6F> et que la fonction <em/new()/ en
|
|||
|
fait plus car elle a maintenant un param<61>tre. Notez aussi que lorsque nous
|
|||
|
stockons un pointeur vers l'objet Adjustement, nous incr<63>mentons son nombre de
|
|||
|
r<EFBFBD>f<EFBFBD>rences (et nous le d<>cr<63>mentons lorsque nous ne l'utilisons plus) afin que
|
|||
|
GTK puisse savoir quand il pourra <20>tre d<>truit sans danger.
|
|||
|
|
|||
|
<p>
|
|||
|
Il y a aussi quelques fonctions pour manipuler les options du widget :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
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);
|
|||
|
}
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
<sect2> <em/gtk_dial_realize()/
|
|||
|
|
|||
|
<p>
|
|||
|
Nous arrivons maintenant <20> quelques nouveaux types de fonctions. D'abord, nous
|
|||
|
avons une fonction qui r<>alise la cr<63>ation de la fen<65>tre X. Notez que l'on
|
|||
|
passe un masque <20> la fonction <em/gdk_window_new()/ pour sp<73>cifier quels sont
|
|||
|
les champs de la structure GdkWindowAttr qui contiennent des donn<6E>es (les
|
|||
|
autres recevront des valeurs par d<>faut). Notez aussi la fa<66>on dont est cr<63><72> le
|
|||
|
masque d'<27>v<EFBFBD>nement du widget. On appelle <em/gtk_widget_get_events()/ pour
|
|||
|
r<EFBFBD>cup<EFBFBD>rer le masque d'<27>v<EFBFBD>nement que l'utilisateur a sp<73>cifi<66> pour ce widget
|
|||
|
(avec <em/gtk_widget_set_events()/) et ajouter les <20>v<EFBFBD>nements qui nous
|
|||
|
int<EFBFBD>ressent.
|
|||
|
|
|||
|
<p>
|
|||
|
Apr<EFBFBD>s avoir cr<63><72> la fen<65>tre, nous configurons son style et son fond et mettons
|
|||
|
un pointeur vers le widget dans le champ user de la GdkWindow. Cette derni<6E>re
|
|||
|
<EFBFBD>tape permet <20> GTK de distribuer les <20>v<EFBFBD>nements pour cette fen<65>tre au widget
|
|||
|
correct.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
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);
|
|||
|
}
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
<sect2>N<>gotiation de la taille
|
|||
|
|
|||
|
<p>
|
|||
|
Avant le premier affichage de la fen<65>tre contenant un widget et <20> chaque fois
|
|||
|
que la forme de la fen<65>tre change, GTK demande <20> chaque widget fils la taille
|
|||
|
qu'il d<>sire avoir. Cette requ<71>te est g<>r<EFBFBD>e par la fonction
|
|||
|
<em/gtk_dial_size_request()/. Comme notre widget n'est pas un widget container,
|
|||
|
et n'a pas de contraintes r<>elles sur sa taille, nous ne faisons que retourner
|
|||
|
une valeur raisonnable par d<>faut.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
static void
|
|||
|
gtk_dial_size_request (GtkWidget *widget,
|
|||
|
GtkRequisition *requisition)
|
|||
|
{
|
|||
|
requisition->width = DIAL_DEFAULT_SIZE;
|
|||
|
requisition->height = DIAL_DEFAULT_SIZE;
|
|||
|
}
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
<p>
|
|||
|
Lorsque tous les widgets on demand<6E> une taille id<69>ale, le forme de la fen<65>tre
|
|||
|
est calcul<75>e et chaque widget fils est averti de sa taille. Habituellement, ce
|
|||
|
sera autant que la taille requise, mais si, par exemple, l'utilisateur a
|
|||
|
redimensionn<EFBFBD> la fen<65>tre, cette taille peut occasionnellement <20>tre plus petite
|
|||
|
que la taille requise. La notification de la taille est g<>r<EFBFBD>e par la fonction
|
|||
|
<em/gtk_dial_size_allocate()/. Notez qu'en m<>me temps qu'elle calcule les
|
|||
|
tailles de certains composants pour une utilisation future, cette routine fait
|
|||
|
aussi le travail de base consistant <20> d<>placer les widgets X Window dans leur
|
|||
|
nouvelles positions et tailles.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
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;
|
|||
|
}
|
|||
|
}
|
|||
|
</verb></tscreen>.
|
|||
|
|
|||
|
<sect2> <em/gtk_dial_expose()/
|
|||
|
|
|||
|
<p>
|
|||
|
Comme cela est mentionn<6E> plus haut, tout le dessin de ce widget est r<>alis<69>
|
|||
|
dans le gestionnaire pour les <20>v<EFBFBD>nements d'exposition. Il n'y a pas grand chose
|
|||
|
de plus <20> dire l<> dessus, sauf constater l'utilisation de la fonction
|
|||
|
<em/gtk_draw_polygon/ pour dessiner le pointeur avec une forme en trois
|
|||
|
dimensions selon les couleurs stock<63>es dans le style du widget.
|
|||
|
style.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
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;
|
|||
|
}
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
<sect2>Gestion des <20>v<EFBFBD>nements
|
|||
|
|
|||
|
<p>
|
|||
|
Le reste du code du widget g<>re diff<66>rents types d'<27>v<EFBFBD>nements et n'est pas
|
|||
|
trop diff<66>rent de ce que l'on trouve dans la plupart des applications GTK. Deux
|
|||
|
types d'<27>v<EFBFBD>nements peuvent survenir -- l'utilisateur peut cliquer sur le widget
|
|||
|
avec la souris et faire glisser pour d<>placer le pointeur, ou bien la valeur de
|
|||
|
l'objet Adjustment peut changer <20> cause d'une circonstance ext<78>rieure.
|
|||
|
|
|||
|
<p>
|
|||
|
Lorsque l'utilisateur clique sur le widget, on v<>rifie si le clic s'est bien
|
|||
|
pass<EFBFBD> pr<70>s du pointeur et si c'est le cas, on stocke alors le bouton avec
|
|||
|
lequel l'utilisateur a cliqu<71> dans le champ <em/button/ de la structure du
|
|||
|
widget et on r<>cup<75>re tous les <20>v<EFBFBD>nements souris avec un appel <20>
|
|||
|
<em/gtk_grab_add()/. Un d<>placement ult<6C>rieur de la souris provoque le recalcul
|
|||
|
de la valeur de contr<74>le (par la fonction <em/gtk_dial_update_mouse/). Selon la
|
|||
|
politique qui a <20>t<EFBFBD> choisie, les <20>v<EFBFBD>nements "value_changed" sont, soit g<>n<EFBFBD>r<EFBFBD>s
|
|||
|
instantan<EFBFBD>ment (<em/GTK_UPDATE_CONTINUOUS/), apr<70>s un d<>lai ajout<75> au timer
|
|||
|
avec <em/gtk_timeout_add()/ (<em/GTK_UPDATE_DELAYED/), ou seulement lorsque le
|
|||
|
bouton est rel<65>ch<63> (<em/GTK_UPDATE_DISCONTINUOUS/).
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
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);
|
|||
|
|
|||
|
/* D<>termine si le bouton press<73> est dans la r<>gion du pointeur.
|
|||
|
On fait cela en calculant les distances parall<6C>le et perpendiculaire
|
|||
|
du point o<> la souris a <20>t<EFBFBD> press<73>e par rapport <20> 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);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
<p>
|
|||
|
Les changements de l'Adjustement par des moyens ext<78>rieurs sont communiqu<71>s <20>
|
|||
|
notre widget par les signaux "changed" et "value_changed". Les gestionnaires
|
|||
|
pour ces fonctions appellent <em/gtk_dial_update()/ pour valider les
|
|||
|
param<EFBFBD>tres, calculer le nouvel angle du pointeur et redessiner le widget (en
|
|||
|
appelant <em/gtk_widget_draw()/).
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
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;
|
|||
|
}
|
|||
|
}
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
<sect2>Am<41>liorations possibles
|
|||
|
|
|||
|
<p>
|
|||
|
|
|||
|
Le widget Dial d<>crit jusqu'<27> maintenant ex<65>cute <20> peu pr<70>s 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-t<>tes et <20> la pr<70>paration. Cependant, certaines am<61>liorations
|
|||
|
peuvent <20>tre apport<72>es <20> ce widget :
|
|||
|
|
|||
|
<itemize>
|
|||
|
<item> Si vous testez ce widget, vous vous apercevrez qu'il y a un peu de
|
|||
|
scintillement lorsque le pointeur est d<>plac<61>. Ceci est d<> au fait que le
|
|||
|
widget entier est effac<61>, puis redessin<69> <20> chaque mouvement du
|
|||
|
pointeur. Souvent, la meilleure fa<66>on de g<>rer ce probl<62>me est de dessiner sur
|
|||
|
un pixmap non affich<63>, puis de copier le r<>sultat final sur l'<27>cran en une
|
|||
|
seule <20>tape (le widget <em/ProgressBar/ se dessine de cette fa<66>on).
|
|||
|
|
|||
|
<item> L'utilisateur devrait pouvoir utiliser les fl<66>ches du curseur vers le
|
|||
|
haut et vers le bas pour incr<63>menter et d<>cr<63>menter la valeur.
|
|||
|
|
|||
|
<item> 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 <em/Button/ pour cela, nous voudrions aussi que les
|
|||
|
boutons s'auto-r<>p<EFBFBD>tent lorsqu'ils sont maintenus appuy<75>s, comme font les
|
|||
|
fl<EFBFBD>ches d'une barre de d<>filement. La majeure partie du code pour implanter ce
|
|||
|
type de comportement peut se trouver dans le widget <em/GtkRange/.
|
|||
|
|
|||
|
<item> Le widget Dial pourrait <20>tre fait dans un widget container avec un seul
|
|||
|
widget fils positionn<6E>e en bas, entre les boutons mentionn<6E>s
|
|||
|
ci-dessus. L'utilisateur pourrait alors ajouter au choix, un widget label ou
|
|||
|
entr<EFBFBD>e pour afficher la valeur courante de l'appel.
|
|||
|
|
|||
|
</itemize>
|
|||
|
|
|||
|
<sect1>En savoir plus
|
|||
|
|
|||
|
<p>
|
|||
|
Seule une petite partie des nombreux d<>tails de la cr<63>ation des widgets a pu
|
|||
|
<EFBFBD>tre d<>crite. Si vous d<>sirez <20>crire vos propres widgets, la meilleure source
|
|||
|
d'exemples est le source de GTK lui-m<>me. Posez-vous quelques questions sur les
|
|||
|
widgets que vous voulez <20>crire : est-ce un widget container ? poss<73>de-t-il
|
|||
|
sa propre fen<65>tre ? est-ce une modification d'un widget existant ? Puis,
|
|||
|
trouvez un widget identique et commencez <20> faire les modifications. Bonne
|
|||
|
chance !
|
|||
|
|
|||
|
<sect>Scribble, un programme simple de dessin
|
|||
|
|
|||
|
<sect1>Pr<50>sentation
|
|||
|
|
|||
|
<p>
|
|||
|
Dans cette section, nous construirons un programme simple de dessin. Ce
|
|||
|
faisant, nous examinerons comment g<>rer les <20>v<EFBFBD>nements souris, comment dessiner
|
|||
|
dans une fen<65>tre, et comment mieux dessiner en utilisant un pixmap en arri<72>re
|
|||
|
plan. Apr<70>s avoir cr<63><72> ce programme, nous l'<27>tendrons en lui ajoutant le
|
|||
|
support des p<>riph<70>riques <em/Xinput/, comme les tables de trac<61>. GTK dispose
|
|||
|
de routines de support qui facilitent beaucoup l'obtention des informations
|
|||
|
<EFBFBD>tendues (comme la pression et l'inclinaison du stylet) <20> partir de tels
|
|||
|
p<EFBFBD>riph<EFBFBD>riques.
|
|||
|
|
|||
|
<sect1>Gestion d'<27>v<EFBFBD>nement
|
|||
|
|
|||
|
<p>
|
|||
|
Les signaux GTK que nous avons d<>j<EFBFBD> vus concernent les actions de haut niveau,
|
|||
|
comme la s<>lection d'un choix d'un menu. Cependant, il est quelques fois utile
|
|||
|
de conna<6E>tre les cas de bas niveau, comme le d<>placement de la souris, ou la
|
|||
|
pression d'une touche. Il existe aussi des signaux GTK correspondant <20> ces
|
|||
|
<em/<2F>v<EFBFBD>nements/ bas niveau. Les gestionnaires de ces signaux ont un param<61>tre
|
|||
|
suppl<EFBFBD>mentaire qui est un pointeur vers une structure contenant des
|
|||
|
informations sur l'<27>v<EFBFBD>nement. Par exemple, les gestionnaires des <20>v<EFBFBD>nements de
|
|||
|
d<EFBFBD>placement recoivent un param<61>tre vers une structure <em/GdkEventMotion/ qui
|
|||
|
ressemble (en partie) <20> ceci :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
struct _GdkEventMotion
|
|||
|
{
|
|||
|
GdkEventType type;
|
|||
|
GdkWindow *window;
|
|||
|
guint32 time;
|
|||
|
gdouble x;
|
|||
|
gdouble y;
|
|||
|
...
|
|||
|
guint state;
|
|||
|
...
|
|||
|
};
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
<em/type/ sera initialis<69> avec le type de l'<27>v<EFBFBD>nement, ici
|
|||
|
<em/GDK_MOTION_NOTIFY/, <em/window/ est la fen<65>tre dans laquelle l'<27>v<EFBFBD>nement
|
|||
|
est survenu. <em/x/ et <em/y/ donnent les coordonn<6E>es de l'<27>v<EFBFBD>nement et
|
|||
|
<em/state/ sp<73>cifie l'<27>tat du modificateur lorsque l'<27>v<EFBFBD>nement s'est produit
|
|||
|
(c'est-<2D>-dire quelles sont les touches de modification et les boutons souris
|
|||
|
qui ont <20>t<EFBFBD> press<73>s). Il s'agit d'un OU bit <20> bit de l'une des valeurs
|
|||
|
suivantes :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
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
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
<p>
|
|||
|
Comme pour les autres signaux, on appelle <em/gtk_signal_connect()/ pour
|
|||
|
d<EFBFBD>terminer ce qui se passe lorsqu'un <20>v<EFBFBD>nement survient. Mais nous devons aussi
|
|||
|
faire en sorte que GTK sache de quels <20>v<EFBFBD>nements nous voulons <20>tre
|
|||
|
avertis. Pour ce faire, on appelle la fonction :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_widget_set_events (GtkWidget *widget,
|
|||
|
gint events);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Le deuxi<78>me champ sp<73>cifie les <20>v<EFBFBD>nements qui nous int<6E>ressent. Il s'agit d'un
|
|||
|
OU bit <20> bit de constantes qui indiquent diff<66>rent types d'<27>v<EFBFBD>nements. Pour
|
|||
|
r<EFBFBD>f<EFBFBD>rence ult<6C>rieure, les types d'<27>v<EFBFBD>nements sont :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
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
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Il y a quelques points subtils qui doivent <20>tre observ<72>s lorsqu'on appelle
|
|||
|
<em/gtk_widget_set_events()/. D'abord, elle doit <20>tre appel<65>e avant que la
|
|||
|
fen<EFBFBD>tre X d'un widget GTK soit cr<63><72>e. En pratique, cela signifie que l'on doit
|
|||
|
l'appeler imm<6D>diatement apr<70>s avoir cr<63><72> le widget. Ensuite, le widget doit
|
|||
|
avoir une fen<65>tre X associ<63>e. Pour des raisons d'efficacit<69>, de nombreux types
|
|||
|
de widgets n'ont pas de fen<65>tre propre, mais se dessinent dans la fen<65>tre de
|
|||
|
leur parent. Ces widgets sont :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
GtkAlignment
|
|||
|
GtkArrow
|
|||
|
GtkBin
|
|||
|
GtkBox
|
|||
|
GtkImage
|
|||
|
GtkItem
|
|||
|
GtkLabel
|
|||
|
GtkPaned
|
|||
|
GtkPixmap
|
|||
|
GtkScrolledWindow
|
|||
|
GtkSeparator
|
|||
|
GtkTable
|
|||
|
GtkViewport
|
|||
|
GtkAspectFrame
|
|||
|
GtkFrame
|
|||
|
GtkVPaned
|
|||
|
GtkHPaned
|
|||
|
GtkVBox
|
|||
|
GtkHBox
|
|||
|
GtkVSeparator
|
|||
|
GtkHSeparator
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Pour capturer les <20>v<EFBFBD>nements pour ces widgets, on doit utiliser un widget
|
|||
|
<em/EventBox/. Voir la section sur <ref id="sec_The_EventBox_Widget" name="Le
|
|||
|
widget EventBox"> pour plus de d<>tails.
|
|||
|
|
|||
|
<p>
|
|||
|
Pour notre programme de dessin, on veut savoir quand le bouton de la souris est
|
|||
|
press<EFBFBD> et quand la souris est d<>plac<61>e, nous indiquons donc
|
|||
|
<em/GDK_POINTER_MOTION_MASK/ et <em/GDK_BUTTON_PRESS_MASK/. On veut aussi
|
|||
|
savoir quand il est n<>cessaire de redessiner notre fen<65>tre, on indique donc
|
|||
|
<em/GDK_EXPOSURE_MASK/. Bien que nous voulions <20>tre avertis via un <20>v<EFBFBD>nement
|
|||
|
<em/Configure/ lorsque la taille de notre fen<65>tre change, on n'a pas besoin de
|
|||
|
pr<EFBFBD>ciser le flag <em/GDK_STRUCTURE_MASK/ correspondant car il est
|
|||
|
automatiquement sp<73>cifi<66> pour chaque fen<65>tre.
|
|||
|
|
|||
|
<p>
|
|||
|
Il arrive cependant qu'il puisse y avoir un probl<62>me en indiquant seulement
|
|||
|
<em/GDK_POINTER_MOTION_MASK/. Cela fera que le serveur ajoutera un nouvel
|
|||
|
<EFBFBD>v<EFBFBD>nement de d<>placement <20> la file des <20>v<EFBFBD>nements <20> chaque fois que
|
|||
|
l'utilisateur d<>place la souris. Si cela nous prend 0,1 seconde pour g<>rer un
|
|||
|
<EFBFBD>v<EFBFBD>nement de d<>placement, si le serveur X n'ajoute un nouvel <20>v<EFBFBD>nement de
|
|||
|
d<EFBFBD>placement dans la queue que toutes les 0,05 secondes, nous serons vite <20> la
|
|||
|
tra<EFBFBD>ne de l'utilisateur. Si l'utilisateur dessine pendant 5 secondes, cela nous
|
|||
|
prendra 5 secondes de plus pour le traiter apr<70>s qu'il ait rel<65>ch<63> le bouton de
|
|||
|
la souris ! Ce que l'on voudrait, c'est ne r<>cup<75>rer qu'un <20>v<EFBFBD>nement de
|
|||
|
d<EFBFBD>placement pour chaque <20>v<EFBFBD>nement que l'on traite. Pour cela, il faut pr<70>ciser
|
|||
|
<em/GDK_POINTER_MOTION_HINT_MASK/.
|
|||
|
|
|||
|
<p>
|
|||
|
Avec <em/GDK_POINTER_MOTION_HINT_MASK/, le serveur nous envoit un <20>v<EFBFBD>nement de
|
|||
|
d<EFBFBD>placement la premi<6D>re fois que la pointeur se d<>place apr<70>s <20>tre entr<74> dans
|
|||
|
la fen<65>tre, ou apr<70>s un <20>v<EFBFBD>nement d'appui ou de rel<65>chement d'un bouton. Les
|
|||
|
<EFBFBD>v<EFBFBD>nements de d<>placement suivants seront supprim<69>s jusqu'<27> ce que l'on demande
|
|||
|
explicitement la position du pointeur en utilisant la fonction :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
GdkWindow* gdk_window_get_pointer (GdkWindow *window,
|
|||
|
gint *x,
|
|||
|
gint *y,
|
|||
|
GdkModifierType *mask);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
(Il existe une autre fonction, <em/gtk_widget_get_pointer()/ qui poss<73>de une
|
|||
|
interface simple, mais n'est pas tr<74>s utile car elle ne fait que r<>cup<75>rer la
|
|||
|
position de la souris et ne se pr<70>occupe pas de savoir si les boutons sont
|
|||
|
press<EFBFBD>s).
|
|||
|
|
|||
|
<p>
|
|||
|
Le code pour configurer les <20>v<EFBFBD>nements pour notre fen<65>tre ressemble alors
|
|||
|
<EFBFBD> :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
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);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
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<74>s simples :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
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;
|
|||
|
}
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
|
|||
|
<sect1>Le widget DrawingArea et le dessin
|
|||
|
|
|||
|
<p>
|
|||
|
Revenons au processus de dessin sur l'<27>cran. Le widget que l'on utilise pour
|
|||
|
ceci est le widget <em/DrawingArea/. Un tel widget est essentiellement une
|
|||
|
fen<EFBFBD>tre 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 cr<63><72>e avec l'appel :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
GtkWidget* gtk_drawing_area_new (void);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Une taille par d<>faut peut <20>tre donn<6E>e au widget par l'appel :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_drawing_area_size (GtkDrawingArea *darea,
|
|||
|
gint width,
|
|||
|
gint height);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Cette taille par d<>faut peu <20>tre surcharg<72>e en appelant
|
|||
|
<em/gtk_widget_set_usize()/ et celle-ci, <20> son tour, peut <20>tre surcharg<72>e si
|
|||
|
l'utilisateur modifie manuellement la taille de la fen<65>tre contenant la zone de
|
|||
|
dessin.
|
|||
|
|
|||
|
<p>
|
|||
|
Il faut noter que lorsque l'on cr<63>e un widget <em/DrawingArea/, nous sommes
|
|||
|
<em>compl<70>tement</em> responsable du dessin du contenu. Si notre fen<65>tre est
|
|||
|
cach<EFBFBD>e puis red<65>couverte, nous recevrons un <20>v<EFBFBD>nement d'exposition et devrons
|
|||
|
redessiner ce qui a <20>t<EFBFBD> cach<63> auparavant.
|
|||
|
|
|||
|
<p>
|
|||
|
Devoir se rappeler tout ce qui a <20>t<EFBFBD> dessin<69> <20> l'<27>cran pour pouvoir
|
|||
|
correctement le redessiner peut s'av<61>rer, c'est le moins que l'on puisse dire,
|
|||
|
p<EFBFBD>nible. De plus, cela peut <20>tre visible si des portions de la fen<65>tre sont
|
|||
|
effac<EFBFBD>es puis redessin<69>es <20>tape par <20>tape. La solution <20> ce probl<62>me est
|
|||
|
d'utiliser un <em/pixmap d'arri<72>re-plan/ qui n'est pas sur l'<27>cran. Au lieu de
|
|||
|
dessiner directement <20> l'<27>cran, on dessine sur une image stock<63>e dans la
|
|||
|
m<EFBFBD>moire du serveur et qui n'est pas affich<63>e, puis, lorsque l'image change ou
|
|||
|
lorsque de nouvelles portions de l'image sont affich<63>es, on copie les parties
|
|||
|
ad<EFBFBD>quates sur l'<27>cran.
|
|||
|
|
|||
|
<p>
|
|||
|
Pour cr<63>er un pixmap m<>moire, on appelle la fonction :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
GdkPixmap* gdk_pixmap_new (GdkWindow *window,
|
|||
|
gint width,
|
|||
|
gint height,
|
|||
|
gint depth);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Le param<61>tre <em/windows/ indique une fen<65>tre GTK de laquelle ce pixmap tire
|
|||
|
certaines de ses propri<72>t<EFBFBD>s. <em/width/ et <em/height/ pr<70>cisent la taille du
|
|||
|
pixmap. <em/depth/ pr<70>cise la <em/profondeur de couleur/, c'est-<2D>-dire le
|
|||
|
nombre de bits par pixel, de la nouvelle fen<65>tre. Si cette profondeur vaut
|
|||
|
<em/-1/, elle correspondra <20> celle de <em/window/.
|
|||
|
|
|||
|
<p>
|
|||
|
Nous cr<63>ons le pixmap dans notre gestionnaire "configure_event". Cet <20>v<EFBFBD>nement
|
|||
|
est g<>n<EFBFBD>r<EFBFBD> <20> chaque fois que la fen<65>tre change de taille, y compris lorsqu'elle
|
|||
|
initialement cr<63><72>e.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
/* Pixmap d'arri<72>re-plan pour la zone de dessin */
|
|||
|
static GdkPixmap *pixmap = NULL;
|
|||
|
|
|||
|
/* Cr<43>ation d'un nouveau pixmap d'arri<72>re-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;
|
|||
|
}
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
L'appel <20> <em/gdk_draw_rectangle()/ remet le pixmap <20> blanc. Nous en dirons un
|
|||
|
peu plus dans un moment.
|
|||
|
|
|||
|
<p>
|
|||
|
Notre gestionnaire d'<27>v<EFBFBD>nement d'exposition copie alors simplement la partie
|
|||
|
concern<EFBFBD>es du pixmap sur l'<27>cran (on d<>termine la zone qu'il faut redessiner en
|
|||
|
utilisant le champ <em/event->area/ de l'<27>v<EFBFBD>nement d'exposition) :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
/* Remplit l'<27>cran <20> partir du pixmap d'arri<72>re-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;
|
|||
|
}
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Nous avons vu comment garder l'<27>cran <20> jour avec notre pixmap, mais comment
|
|||
|
dessine-t-on r<>ellement ce que l'on veut dans le pixmap ? Il existe un grand
|
|||
|
nombre d'appels dans la biblioth<74>que GDK de GTK pour dessiner sur des
|
|||
|
<em>dessinables</em>. Un dessinable est simplement quelque chose sur lequel on
|
|||
|
peut dessiner. Cela peut <20>tre une fen<65>tre, un pixmap, ou un bitmap (une image
|
|||
|
en noir et blanc). Nous avons d<>j<EFBFBD> vu plus haut deux de ces appels,
|
|||
|
<em/gdk_draw_rectangle()/ et <em/gdk_draw_pixmap()/. La liste compl<70>te
|
|||
|
est :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
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 ()
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Consultez la documentation de r<>f<EFBFBD>rence ou le fichier en-t<>te
|
|||
|
<em/<gdk/gdk.h>/ pour plus de d<>tails sur ces fonctions. Celles-ci
|
|||
|
partagent toutes les m<>mes deux param<61>tres. Le premier est le dessinable sur
|
|||
|
lequel dessiner, le second est un <em/contexte graphique/ (GC).
|
|||
|
|
|||
|
<p>
|
|||
|
Un contexte graphique encapsule l'information sur des choses comme les couleurs
|
|||
|
de premier et d'arri<72>re plan et la largeur de ligne. GDK poss<73>de un ensemble
|
|||
|
complet de fonctions pour cr<63>er et manipuler les contextes graphiques, mais,
|
|||
|
pour simplifier, nous n'utiliserons que les contextes graphiques
|
|||
|
pr<EFBFBD>d<EFBFBD>finis. Chaque widget a un style associ<63> (qui peut <20>tre modifi<66> dans un
|
|||
|
fichier gtkrc, voir la section sur les fichiers rc de GTK). Celui-ci, entre
|
|||
|
autres choses, stocke plusieurs contextes graphiques. Quelques exemples d'acc<63>s
|
|||
|
<EFBFBD> ces contextes sont :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
widget->style->white_gc
|
|||
|
widget->style->black_gc
|
|||
|
widget->style->fg_gc[GTK_STATE_NORMAL]
|
|||
|
widget->style->bg_gc[GTK_WIDGET_STATE(widget)]
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Les champs <em/fg_gc, bg_gc, dark_gc/ et <em/light_gc/ sont index<65>s par un
|
|||
|
param<EFBFBD>tre de type <em/GtkStateType/ qui peut prendre les valeurs :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
GTK_STATE_NORMAL,
|
|||
|
GTK_STATE_ACTIVE,
|
|||
|
GTK_STATE_PRELIGHT,
|
|||
|
GTK_STATE_SELECTED,
|
|||
|
GTK_STATE_INSENSITIVE
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Par exemple, pour <em/GTK_STATE_SELECTED/, la couleur de premier plan par
|
|||
|
d<EFBFBD>faut est blanc et la couleur d'arri<72>re plan par d<>faut est bleu fonc<6E>.
|
|||
|
|
|||
|
<p>
|
|||
|
Notre fonction <em/draw_brush()/, qui r<>alise le dessin <20> l'<27>cran est alors :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
/* Dessine un rectangle <20> l'<27>cran */
|
|||
|
static void
|
|||
|
draw_brush (GtkWidget *widget, gdouble x, gdouble y)
|
|||
|
{
|
|||
|
GdkRectangle update_rect;
|
|||
|
|
|||
|
update_rect.x = x - 5;
|
|||
|
update_rect.y = y - 5;
|
|||
|
update_rect.width = 10;
|
|||
|
update_rect.height = 10;
|
|||
|
gdk_draw_rectangle (pixmap,
|
|||
|
widget->style->black_gc,
|
|||
|
TRUE,
|
|||
|
update_rect.x, update_rect.y,
|
|||
|
update_rect.width, update_rect.height);
|
|||
|
gtk_widget_draw (widget, &update_rect);
|
|||
|
}
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Apr<EFBFBD>s avoir dessin<69> le rectangle repr<70>sentant la brosse sur le pixmap, nous
|
|||
|
appelons la fonction :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void gtk_widget_draw (GtkWidget *widget,
|
|||
|
GdkRectangle *area);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
qui indique <20> X que la zone donn<6E>e par le param<61>tre <em/area/ a besoin d'<27>tre
|
|||
|
mise <20> jour. X g<>n<EFBFBD>rera <20>ventuellement un <20>v<EFBFBD>nement d'exposition (en combinant
|
|||
|
peut-<2D>tre les zones pass<73>s dans plusieurs appels <20> <em/gtk_widget_draw()/) ce
|
|||
|
qui forcera notre gestionnaire d'<27>v<EFBFBD>nement d'exposition <20> copier les parties
|
|||
|
ad<EFBFBD>quates <20> l'<27>cran.
|
|||
|
|
|||
|
<p>
|
|||
|
Nous avons maintenant couvert enti<74>rement le programme de dessin, sauf quelques
|
|||
|
d<EFBFBD>tails banals comme la cr<63>ation de la fen<65>tre principale. Le code source
|
|||
|
complet est disponible <20> l'endroit o<> vous avez obtenu ce didacticiel.
|
|||
|
|
|||
|
<sect1>Ajouter le support XInput
|
|||
|
|
|||
|
<p>
|
|||
|
Il est maintenant possible d'acheter des p<>riph<70>riques bon march<63>, comme les
|
|||
|
tablettes graphiques qui permettent d'exprimer beaucoup plus facilement son
|
|||
|
talent qu'avec une souris. La fa<66>on la plus simple pour utiliser de tels
|
|||
|
p<EFBFBD>riph<EFBFBD>riques est simplement de le faire comme un remplacement de la souris,
|
|||
|
mais cela ne tire pas partie des nombreux avantages de ces p<>riph<70>riques,
|
|||
|
comme :
|
|||
|
|
|||
|
<itemize>
|
|||
|
<item> Sensibilit<69> <20> la pression ;
|
|||
|
<item> rapport d'inclinaison ;
|
|||
|
<item> positionnement au dessous du pixel ;
|
|||
|
<item> entr<74>es multiples (par exemple, un stylet avec pointe et gomme).
|
|||
|
</itemize>
|
|||
|
|
|||
|
Pour des informations sur l'extension XInput, voir <url
|
|||
|
url="http://www.msc.cornell.edu/~otaylor/xinput/XInput-HOWTO.html"
|
|||
|
name="XInput-HOWTO">.
|
|||
|
|
|||
|
<p>
|
|||
|
Si l'on examine la d<>finition compl<70>te de, par exemple, la structure
|
|||
|
<em/GdkEventMotion/, on voit qu'elle poss<73>de des champs pour supporter des
|
|||
|
informations <20>tendues sur les p<>riph<70>riques.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
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;
|
|||
|
};
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
<em/pressure/ indique la pression comme un nombre r<>el compris entre 0 et 1.
|
|||
|
<em/xtilt/ et <em/ytilt/ peuvent prendre des valeurs entre -1 et 1,
|
|||
|
correspondant au degr<67> d'inclinaison dans chaque direction, <em/source/ et
|
|||
|
<em/deviceid/ pr<70>cisent de deux fa<66>ons diff<66>rentes le p<>riph<70>rique pour lequel
|
|||
|
l'<27>v<EFBFBD>nement est survenus. <em/source/ donne une simple information sur le type
|
|||
|
du p<>riph<70>rique. Il peut prendre l'une des valeurs suivantes :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
GDK_SOURCE_MOUSE
|
|||
|
GDK_SOURCE_PEN
|
|||
|
GDK_SOURCE_ERASER
|
|||
|
GDK_SOURCE_CURSOR
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
<tt/deviceid/ pr<70>cise un ID num<75>rique unique pour le p<>riph<70>rique. Il peut <20>tre
|
|||
|
utilis<EFBFBD> pour trouver des informations suppl<70>mentaires sur le p<>riph<70>rique en
|
|||
|
utilisant l'appel <em/gdk_input_list_devices()/ (voir ci-dessous). La valeur
|
|||
|
sp<EFBFBD>ciale <em/GDK_CORE_POINTER/ sert <20> d<>signer le p<>riph<70>rique de pointage
|
|||
|
principal (habituellement la souris).
|
|||
|
|
|||
|
<sect2>Valider l'information suppl<70>mentaire sur un p<>riph<70>rique
|
|||
|
|
|||
|
<p>
|
|||
|
Pour indiquer <20> GTK que l'on d<>sire obtenir des informations suppl<70>mentaires
|
|||
|
sur le p<>riph<70>rique, on a simplement besoin d'ajouter une ligne <20> nos
|
|||
|
programmes.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
gtk_widget_set_extension_events (drawing_area, GDK_EXTENSION_EVENTS_CURSOR);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
En donnant la valeur <em/GDK_EXTENSION_EVENTS_CURSOR/, on indique que nous
|
|||
|
d<EFBFBD>sirons les <20>v<EFBFBD>nements d'extension, mais seulement si l'on ne doit pas
|
|||
|
dessiner notre propre curseur. Voir la section <ref
|
|||
|
id="sec_Further_Sophistications" name="Sophistications suppl<70>mentaires">
|
|||
|
ci-dessous pour des plus de d<>tails sur le dessin du curseur. Nous pourrions
|
|||
|
aussi donner les valeurs <em/GDK_EXTENSION_EVENTS_ALL/ si nous voulons dessiner
|
|||
|
notre propre curseur, ou <tt/GDK_EXTENSION_EVENTS_NONE/ pour revenir <20> la
|
|||
|
situation par d<>faut.
|
|||
|
|
|||
|
<p>
|
|||
|
Toutefois, nous ne sommes pas compl<70>tement <20> la fin de l'histoire. Par d<>faut,
|
|||
|
aucun p<>riph<70>rique d'extension n'est autoris<69>. Nous avons besoin d'un m<>canisme
|
|||
|
pour que les utilisateurs puissent autoriser et configurer leur extensions. GTK
|
|||
|
dispose du widget <em/InputDialog/ pour automatiser cette t<>che. La proc<6F>dure
|
|||
|
suivante g<>re un widget InputDialog. Elle cr<63>e le dialogue s'il n'est pas
|
|||
|
pr<EFBFBD>sent et le place au premier plan sinon.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
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);
|
|||
|
}
|
|||
|
}
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
(vous pouvez remarquer la fa<66>on dont nous g<>rons ce dialogue. En le connectant
|
|||
|
au signal "destroy", nous nous assurons que nous ne garderons pas un pointeur
|
|||
|
sur le dialogue apr<70>s l'avoir d<>truit -- cela pourrait provoquer une erreur de
|
|||
|
segmentation).
|
|||
|
|
|||
|
|
|||
|
<p>
|
|||
|
InputDialog a deux boutons "Close" et "Save", qui n'ont pas d'actions qui leur
|
|||
|
sont assign<67>es par d<>faut. Dans la fonction ci-dessus, nous associons <20> "Close"
|
|||
|
le masquage du dialogue et nous cachons le bouton "Save" car nous n'implantons
|
|||
|
pas la sauvegarde des options XInput dans ce programme.
|
|||
|
|
|||
|
<sect2>Utiliser l'information suppl<70>mentaire d'un p<>riph<70>rique
|
|||
|
|
|||
|
<p>
|
|||
|
Lorsque l'on a valid<69> le p<>riph<70>rique, on peut simplement utiliser
|
|||
|
l'information suppl<70>mentaire des champs des structures d'<27>v<EFBFBD>nement. En fait, il
|
|||
|
est toujours prident d'utiliser ces informations car ces champs auront des
|
|||
|
valeurs par d<>faut judicieuses m<>me lorsque les <20>v<EFBFBD>nements suppl<70>mentaires ne
|
|||
|
sont pas autoris<69>s.
|
|||
|
|
|||
|
<p>
|
|||
|
La seule modification consiste <20> appeler <em/gdk_input_window_get_pointer()/ au
|
|||
|
lieu de <em/gdk_window_get_pointer/. Cela est n<>cessaire car
|
|||
|
<em/gdk_window_get_pointer/ ne retourne pas l'information suppl<70>mentaire.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
void gdk_input_window_get_pointer (GdkWindow *window,
|
|||
|
guint32 deviceid,
|
|||
|
gdouble *x,
|
|||
|
gdouble *y,
|
|||
|
gdouble *pressure,
|
|||
|
gdouble *xtilt,
|
|||
|
gdouble *ytilt,
|
|||
|
GdkModifierType *mask);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Lorsque l'on appelle cette fonction, on doit pr<70>ciser l'ID du p<>riph<70>rique
|
|||
|
ainsi que la fen<65>tre. Habituellement, on aura obtenu cet ID par le champ
|
|||
|
<em/deviceid/ d'une structure d'<27>v<EFBFBD>nement. Cette fonction retournera une valeur
|
|||
|
coh<EFBFBD>rente lorsque les <20>v<EFBFBD>nements ne sont pas autoris<69>s (dans ce cas,
|
|||
|
<em/event->deviceid/ aura la valeur <em/GDK_CORE_POINTER/).
|
|||
|
|
|||
|
La structure de base des gestionnaires d'<27>v<EFBFBD>nements de d<>placement et de bouton
|
|||
|
press<EFBFBD> ne change donc pas trop -- il faut juste ajouter le code permettant de
|
|||
|
traiter l'information suppl<70>mentaire.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
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;
|
|||
|
}
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
On doit aussi faire quelquechose de cette nouvelle information. Notre nouvelle
|
|||
|
fonction <em/draw_brush()/ dessine avec une couleur diff<66>rente pour chaque
|
|||
|
<em/event->source/ et change la taille du pinceau selon la pression.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
/* Dessine un rectangle <20> l'<27>cran, la taille d<>pend de la pression,
|
|||
|
et la couleur d<>pend du type de p<>riph<70>rique */
|
|||
|
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);
|
|||
|
}
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
<sect2>En savoir plus sur un p<>riph<70>rique
|
|||
|
|
|||
|
<p>
|
|||
|
Notre programme affichera le nom du p<>riph<70>rique qui a g<>n<EFBFBD>r<EFBFBD> chaque appui de
|
|||
|
bouton. Pour trouver le nom d'un p<>riph<70>rique, nous appelons la fonction :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
GList *gdk_input_list_devices (void);
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
qui retourne une GList (un type de liste cha<68>n<EFBFBD>e de la biblioth<74>que glib) de
|
|||
|
structures GdkDeviceInfo. La structure GdkDeviceInfo est d<>finie de la fa<66>on
|
|||
|
suivante :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
struct _GdkDeviceInfo
|
|||
|
{
|
|||
|
guint32 deviceid;
|
|||
|
gchar *name;
|
|||
|
GdkInputSource source;
|
|||
|
GdkInputMode mode;
|
|||
|
gint has_cursor;
|
|||
|
gint num_axes;
|
|||
|
GdkAxisUse *axes;
|
|||
|
gint num_keys;
|
|||
|
GdkDeviceKey *keys;
|
|||
|
};
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
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 int<6E>resse est <em/name/ qui est, tout simplement, le nom que X donne
|
|||
|
au p<>riph<70>rique. L'autre champ, qui n'est pas une information de configuration,
|
|||
|
est <em/has_cursor/. Si <em/has_cursor/ est faux, on doit dessiner notre propre
|
|||
|
curseur, mais puisque nous avons pr<70>cis<69> <em/GDK_EXTENSION_EVENTS_CURSOR/, nous
|
|||
|
n'avons pas <20> nous en pr<70>occuper.
|
|||
|
|
|||
|
<p>
|
|||
|
Notre fonction <em/print_button_press()/ ne fait parcourir la liste retourn<72>e
|
|||
|
jusqu'<27> trouver une correspondance, puis affiche le nom du p<>riph<70>rique.
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
static void
|
|||
|
print_button_press (guint32 deviceid)
|
|||
|
{
|
|||
|
GList *tmp_list;
|
|||
|
|
|||
|
/* gdk_input_list_devices retourne une liste interne, nous ne devons donc
|
|||
|
pas la lib<69>rer apr<70>s */
|
|||
|
tmp_list = gdk_input_list_devices();
|
|||
|
|
|||
|
while (tmp_list)
|
|||
|
{
|
|||
|
GdkDeviceInfo *info = (GdkDeviceInfo *)tmp_list->data;
|
|||
|
|
|||
|
if (info->deviceid == deviceid)
|
|||
|
{
|
|||
|
printf("Bouton press<73> sur le p<>riph<70>rique '%s'\n", info->name);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
tmp_list = tmp_list->next;
|
|||
|
}
|
|||
|
}
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
Ceci termine les modifications de notre programme <20> XInputize <20>. Comme pour la
|
|||
|
premi<EFBFBD>re version, le code complet est disponible <20> l'endroit o<> vous avez
|
|||
|
obtenu ce didacticiel.
|
|||
|
|
|||
|
<sect2>Sophistications suppl<70>mentaires<label id="sec_Further_Sophistications">
|
|||
|
|
|||
|
<p>
|
|||
|
Bien que notre programme supporte maintenant XInput, il y manque des
|
|||
|
caract<EFBFBD>ristiques que l'on souhaite trouver dans les applications
|
|||
|
compl<EFBFBD>tes. D'abord, l'utilisateur ne veut probablement pas avoir <20> configurer
|
|||
|
ses p<>riph<70>riques <20> chaque fois qu'il lance le programme et nous devons donc
|
|||
|
lui permettre de sauvegarder la configuration du p<>riph<70>rique. Ceci est r<>alis<69>
|
|||
|
en parcourant ce que retourne <em/gdk_input_list_devices()/ et en <20>crivant la
|
|||
|
configuration dans un fichier.
|
|||
|
|
|||
|
<p>
|
|||
|
Pour restaurer un <20>tat au prochain d<>marrage du programme, GDK dispose de
|
|||
|
fonctions pour changer la configuration des p<>riph<70>riques :
|
|||
|
|
|||
|
<tscreen><verb>
|
|||
|
gdk_input_set_extension_events()
|
|||
|
gdk_input_set_source()
|
|||
|
gdk_input_set_mode()
|
|||
|
gdk_input_set_axes()
|
|||
|
gdk_input_set_key()
|
|||
|
</verb></tscreen>
|
|||
|
|
|||
|
(La liste retourn<72>e par <em/gdk_input_list_devices()/ ne doit pas <20>tre modifi<66>e
|
|||
|
directement). Un exemple est donn<6E> dans le programme de dessin <em/gsumi/
|
|||
|
(disponible <20> l'adresse <htmlurl
|
|||
|
url="http://www.msc.cornell.edu/~otaylor/gsumi/"
|
|||
|
name="http://www.msc.cornell.edu/~otaylor/gsumi/">). De plus, ce serait
|
|||
|
pratique d'avoir une m<>thode standard pour faire cela pour toutes les
|
|||
|
applications. Ceci appartient probablement <20> un niveau l<>g<EFBFBD>rement sup<75>rieur <20>
|
|||
|
GTK, peut-<2D>tre dans la biblioth<74>que GNOME.
|
|||
|
|
|||
|
<p>
|
|||
|
Une autre grosse omission que nous avons mentionn<6E>e plus haut est l'absence de
|
|||
|
dessin du curseur. Les plates-formes autres qu'XFree86 n'autorisent pas encore
|
|||
|
l'utilisation simultan<61>e d'un p<>riph<70>rique comme pointeur de base et comme
|
|||
|
pointeur d'une application. Lisez le <url
|
|||
|
url="http://www.msc.cornell.edu/~otaylor/xinput/XInput-HOWTO.html"
|
|||
|
name="XInput-HOWTO"> pour plus d'informations l<>-dessus. Ceci signifie que les
|
|||
|
applications qui veulent atteindre le plus de monde possible doivent dessiner
|
|||
|
leur propre curseur.
|
|||
|
|
|||
|
<p>
|
|||
|
Une application qui dessine son propre curseur doit faire deux choses :
|
|||
|
d<EFBFBD>terminer si le p<>riph<70>rique courant a besoin ou non d'un curseur dessin<69> et
|
|||
|
d<EFBFBD>terminer si le p<>riph<70>rique courant est <20> proximit<69> (si celui-ci est une
|
|||
|
tablette de dessin, il est pratique de faire dispara<72>tre le curseur lorsque le
|
|||
|
stylet est en dehors de la tablette. Lorsque le p<>riph<70>rique voit le stylet, on
|
|||
|
dit qu'il est <20> <20> proximit<69> <20>). La premi<6D>re v<>rification est faite en
|
|||
|
recherchant dans la liste des p<>riph<70>riques, comme nous l'avons fait pour
|
|||
|
obtenir le nom du p<>riph<70>rique. La deuxi<78>me est r<>alis<69>e en choisissant des
|
|||
|
<EFBFBD>v<EFBFBD>nements "proximity_out". Une exemple de dessin d'un curseur personnel est
|
|||
|
donn<EFBFBD> dans le programme <em/testinput/ de la distribution GTK.
|
|||
|
|
|||
|
|
|||
|
<sect>Conseils pour l'<27>criture d'applications GTK
|
|||
|
<p>
|
|||
|
Cette section est simplement un regroupement de lignes de conduites g<>n<EFBFBD>rales
|
|||
|
et sages, ainsi que d'astuces pour cr<63>er des applications GTK correctes. Elle
|
|||
|
est totalement inutile pour l'instant car il ne s'agit que d'une phrase :)
|
|||
|
|
|||
|
Utilisez <em/autoconf/ et <em/automake/ de GNU ! Ce sont vos amis :) J'ai en
|
|||
|
projet de les pr<70>senter bri<72>vement ici.
|
|||
|
|
|||
|
<sect>Contributions
|
|||
|
|
|||
|
<p>
|
|||
|
Ce document, comme beaucoup d'autres beaux logiciels, a <20>t<EFBFBD> cr<63><72> (NdT : et
|
|||
|
traduit) par des
|
|||
|
volontaires b<>n<EFBFBD>voles. Si vous vous y connaissez sur certains aspects de GTK
|
|||
|
qui ne sont pas encore document<6E>s, soyez gentils de contribuer <20> ce document.
|
|||
|
|
|||
|
<p>
|
|||
|
Si vous d<>cidez de contribuer, envoyez-moi (Ian Main) votre texte <20>
|
|||
|
<tt><htmlurl url="mailto:slow@intergate.bc.ca"
|
|||
|
name="slow@intergate.bc.ca"></tt>. La totalit<69> de ce document est libre et
|
|||
|
tout ajout que vous pourriez y faire doit l'<27>tre <20>galement. Ainsi, tout le
|
|||
|
monde peut utiliser n'importe quelle partie de vos exemples dans les
|
|||
|
programmes, les copies de ce document peuvent <20>tre distribu<62>es <20> volont<6E>, etc.
|
|||
|
<p>
|
|||
|
Merci.
|
|||
|
|
|||
|
|
|||
|
<sect>Remerciements
|
|||
|
<p>
|
|||
|
Je voudrai remercier les personnes suivantes pour leurs contributions <20> ce texte :
|
|||
|
|
|||
|
<itemize>
|
|||
|
<item>Bawer Dagdeviren, <tt><htmlurl url="mailto:chamele0n@geocities.com"
|
|||
|
name="chamele0n@geocities.com"></tt> pour le didacticiel sur les menus.
|
|||
|
|
|||
|
<item>Raph Levien, <tt><htmlurl url="mailto:raph@acm.org"
|
|||
|
name="raph@acm.org"></tt>
|
|||
|
pour <em/bonjour tout le monde/ <20> la GTK, le placement des widgets et pour sa
|
|||
|
sagesse. Il a aussi g<>n<EFBFBD>reusement donn<6E> un abri <20> ce didacticiel.
|
|||
|
|
|||
|
<item>Peter Mattis, <tt><htmlurl url="mailto:petm@xcf.berkeley.edu"
|
|||
|
name="petm@xcf.berkeley.edu"></tt> pour le programme GTK le plus simple et pour
|
|||
|
sa capacit<69> <20> le faire :)
|
|||
|
|
|||
|
<item>Werner Koch <tt><htmlurl url="mailto:werner.koch@guug.de"
|
|||
|
name="werner.koch@guug.de"></tt> pour la conversion du texte original en SGML,
|
|||
|
et pour la hi<68>rarchie des classes de widget.
|
|||
|
|
|||
|
<item>Mark Crichton <tt><htmlurl url="mailto:crichton@expert.cc.purdue.edu"
|
|||
|
name="crichton@expert.cc.purdue.edu"></tt> pour le code de l'usine <20> menus et
|
|||
|
pour le didacticiel de placement des tables.
|
|||
|
|
|||
|
<item>Owen Taylor <tt><htmlurl url="mailto:owt1@cornell.edu"
|
|||
|
name="owt1@cornell.edu"></tt> pour la section sur le widget EventBox (et le
|
|||
|
patch de la distribution). Il est aussi responsable des sections sur l'<27>criture
|
|||
|
de vos propres widgets GTK et de l'application exemple. Merci beaucoup <20> Owen
|
|||
|
pour toute son aide !
|
|||
|
|
|||
|
<item>Mark VanderBoom <tt><htmlurl url="mailto:mvboom42@calvin.edu"
|
|||
|
name="mvboom42@calvin.edu"></tt> pour son merveilleux travail sur les widgets
|
|||
|
Notebook, Progress Bar, Dialog et File selection. Merci beaucoup, Mark ! Ton
|
|||
|
aide a <20>t<EFBFBD> tr<74>s pr<70>cieuse.
|
|||
|
|
|||
|
<item>Tim Janik <tt><htmlurl url="mailto:timj@psynet.net"
|
|||
|
name="timj@psynet.net"></tt> pour son beau travail sur le widget Lists.
|
|||
|
Merci Tim :)
|
|||
|
|
|||
|
<item>Rajat Datta <tt><htmlurl url="mailto:rajat@ix.netcom.com"
|
|||
|
name="rajat@ix.netcom.com"</tt> pour son excellent travail sur le didacticiel
|
|||
|
sur les Pixmaps.
|
|||
|
|
|||
|
<item>Michael K. Johnson <tt><htmlurl url="mailto:johnsonm@redhat.com"
|
|||
|
name="johnsonm@redhat.com"></tt> pour ses infos et le code pour les menus.
|
|||
|
|
|||
|
</itemize>
|
|||
|
<p>
|
|||
|
Et <20> tous ceux d'entre vous qui ont comment<6E> et aid<69> <20> am<61>liorer ce document.
|
|||
|
<p>
|
|||
|
Merci.
|
|||
|
|
|||
|
<sect>Copyright
|
|||
|
<p>
|
|||
|
Ce didacticiel est Copyright (C) 1997 Ian Main
|
|||
|
<p>
|
|||
|
Ce programme est un logiciel libre ; vous pouvez le redistribuer et/ou le
|
|||
|
modifier sous les termes de la licence publique g<>n<EFBFBD>rale GNU telle qu'elle est
|
|||
|
publi<EFBFBD>e par la Free Software Foundation ; soit la version 2 de la licence, ou
|
|||
|
(comme vous voulez) toute version ult<6C>rieure.
|
|||
|
<p>
|
|||
|
Ce programme est distribu<62> dans l'espoir qu'il sera utile, mais SANS AUCUNE
|
|||
|
GARANTIE ; m<>me sans la garantie de COMMERCIALIT<49> ou d'AD<41>QUATION A UN BUT
|
|||
|
PARTICULIER. Voir la licence publique g<>n<EFBFBD>rale GNU pour plus de d<>tails.
|
|||
|
<p>
|
|||
|
Vous devriez avoir re<72>u une copie de la licence publique g<>n<EFBFBD>rale GNU avec ce
|
|||
|
programme ; si ce n'est pas le cas, <20>crivez <20> la Free Software
|
|||
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|||
|
|
|||
|
<sect1>Notes du traducteur
|
|||
|
Ce document a <20>t<EFBFBD> adapt<70> en Fran<61>ais par <url url="mailto:jaco@dotcom.fr"
|
|||
|
name="<22>ric Jacoboni">. Toute remarque sur cette adaptation sera la bienvenue.
|
|||
|
<p>
|
|||
|
Merci <20> <url url="mailto:kheops@linux-kheops.com" name="Jo<4A>l Bernier"> pour
|
|||
|
avoir initi<74> cette adaptation, et <20> <url url="mailto:vincent@debian.org"
|
|||
|
name="Vincent Renardias"> pour sa relecture.
|
|||
|
</article>
|