Un type de communication inter-processus g�r�e par GTK est les s�lections. Une s�lection identifie un morceau de donn�es, par exemple une portion de texte s�lectionn�e par l'utilisateur avec la souris. Seule une application sur un �cran (le propri�taire) peut poss�der une s�lection particuli�re � un moment donn�, ainsi lorsqu'une s�lection est r�clam�e par une application, le propri�taire pr�c�dent doit indiquer � l'utilisateur que la s�lection a �t� abandonn�e. Les autres applications peuvent demander le contenu d'une s�lection sous diff�rentes formes appel�es cibles. Il peut y avoir un nombre quelconque de s�lections, mais la plupart des applications X n'en g�rent qu'une, la s�lection primaire.
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�e de texte, poss�dent d�j� la capacit� 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�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�me bouton de la souris). Cependant, il peut il y avoir des cas dans lesquels vous voulez donner aux autres widgets la possibilit� de fournir la s�lection, ou vous d�sirez r�cup�rer des cibles non support�es par d�faut.
Un concept fondamental dans la compr�hension du fonctionnement des s�lections est celui d'atome. Un atome est un entier qui d�finit de fa�on unique une cha�ne (sur un affichage particulier). Certains atomes sont pr�d�finis par le serveur X et, dans certains cas, des constantes d�finies dans gtk.h correspondent � ces atomes. Par exemple, la constante GDK_PRIMARY_SELECTION correspond � la cha�ne "PRIMARY". Dans d'autres cas, on doit utiliser les fonctions gdk_atom_intern(), pour obtenir l'atome correspondant � une cha�ne, et gdk_atom_name(), pour obtenir le nom d'un atome. Les s�lections et les cibles sont identifi�s par des atomes.
La r�cup�ration de la s�lection est un processus asynchrone. Pour d�marrer le processus, on appelle :
gint gtk_selection_convert (GtkWidget *widget,
GdkAtom selection,
GdkAtom target,
guint32 time)
Cela convertit la s�lection dans la forme sp�cifi�e par target. Si tout est possible, le param�tre time sera le moment de l'�v�nement qui a d�clench� la s�lection. Ceci aide � s'assurer que les �v�nements arrivent dans l'ordre o� l'utilisateur les a demand�. Cependant, si cela n'est pas possible (par exemple, lorsque la conversion a �t� d�clench�e par un signal "clicked"), alors on peut utiliser la macro GDK_CURRENT_TIME.
Quand le propri�taire de la s�lection r�pond � la requ�te, un signal
"selection_received" est envoy� � notre application. Le gestionnaire
de ce signal re�oit un pointeur vers une structure
GtkSelectionData
d�finie ainsi :
struct _GtkSelectionData
{
GdkAtom selection;
GdkAtom target;
GdkAtom type;
gint format;
guchar *data;
gint length;
};
selection et target sont les valeurs que l'on a donn� dans notre appel gtk_selection_convert(). type est un atome qui identifie le type de donn�es retourn� par le propri�taire de la s�lection. Quelques valeurs possibles sont : "STRING", une cha�ne de caract�res latin-1, "ATOM", une s�rie d'atomes, "INTEGER", un entier, etc. La plupart des cibles ne peuvent retourner qu'un type. format donne la longueur des unit�s (les caract�res, par exemple) en bits. Habituellement, on ne se pr�occupe pas de cela lorsqu'on re�oit des donn�es. data est un pointeur vers la donn�e retourn�e et length donne la longueur en octets de la donn�e retourn�e. Si length est n�gative, cela indique qu'une erreur est survenue et que la s�lection ne peut �tre r�cup�r�e. Ceci peut arriver si aucune application n'est propri�taire de la s�lection, ou si vous avez demand� une cible que l'application ne sait pas g�rer. Le tampon est garanti d'�tre un octet plus long que length ; l'octet suppl�mentaire sera toujours z�ro, et il n'est donc pas n�cessaire de faire une copie de cha�ne simplement pour qu'elle soit termin�e par z�ro (comme doivent l'�tre toutes les cha�nes C).
Dans l'exemple qui suit, on r�cup�re la cible sp�ciale "TARGETS", qui est une liste de toutes les cibles en lesquelles la s�lection peut �tre convertie.
#include <gtk/gtk.h>
void selection_received (GtkWidget *widget,
GtkSelectionData *selection_data,
gpointer data);
/* Gestionnaire de signal invoqu� lorsque l'utilisateur clique sur
* le bouton � Obtenir les cibles �. */
void get_targets (GtkWidget *widget, gpointer data)
{
static GdkAtom targets_atom = GDK_NONE;
/* Obtention de l'atome correspondant � la cha�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� quand le propri�taire des s�lections
* retourne la donn�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�ration s'est bien pass�e. */
if (selection_data->length < 0)
{
g_print ("Selection retrieval failed\n");
return;
}
/* On s'assure que l'on a obtenu la donn�e sous la forme attendue. */
if (selection_data->type != GDK_SELECTION_TYPE_ATOM)
{
g_print ("La s�lection \"TARGETS\" n'a pas �t� retourn�e sous la forme d'atomes !\n");
return;
}
/* Affichage des atomes re�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�ation de la fen�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�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;
}
Fournir la s�lection est un peu plus compliqu�. On doit enregistrer les gestionnaires qui seront appel�s lorsque notre s�lection est demand�e. Pour chaque paire s�lection/cible que l'on g�rera, on fera un appel � :
void gtk_selection_add_handler (GtkWidget *widget,
GdkAtom selection,
GdkAtom target,
GtkSelectionFunction function,
GtkRemoveFunction remove_func,
gpointer data);
widget, selection et target identifient les requ�tes que ce gestionnaire g�rera. S'il ne vaut pas NULL, remove_func sera appel� lorsque le gestionnaire de signal est supprim�. Ceci est utile, par exemple, pour des langages interpr�t�s qui doivent garder une trace du nombre de r�f�rences � data.
La fonction de rappel function doit avoir la signature suivante :
typedef void (*GtkSelectionFunction) (GtkWidget *widget,
GtkSelectionData *selection_data,
gpointer data);
Le GtkSelectionData est le m�me qu'au dessus, mais, cette fois, nous sommes responsables de l'initialisation de ses champs type, format, data, et length. (Le champ format est important ici - le serveur X l'utilise pour savoir si la donn�e doit �tre �chang�e par octet ou non. Habituellement, ce sera 8 (un caract�re), ou 32 (un entier)). Cette initialisation est faite en utilisant l'appel :
void gtk_selection_data_set (GtkSelectionData *selection_data,
GdkAtom type,
gint format,
guchar *data,
gint length);
Cette fonction s'occupe de faire une copie correcte des donn�es afin que l'on n'ait pas � se soucier du reste. (On ne doit pas remplir ces champs � la main).
Lorsque cela est demand� par l'utilisateur, on r�clame la possession de la s�lection en appelant :
gint gtk_selection_owner_set (GtkWidget *widget,
GdkAtom selection,
guint32 time);
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� de s�lection � un bouton commutateur. Lorsque ce bouton est appuy�, le programme r�clame la s�lection primaire. La seule cible support�e (� part certaines cibles fournies par GTK lui-m�me, comme � TARGETS �) est � STRING �. Lorsque celle-ci est demand�e, on retourne une repr�sentation de l'heure sous forme de cha�ne.
#include <gtk/gtk.h>
#include <time.h>
/* Fonction de rappel appel�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 �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�taire � NULL,
* on v�rifie que nous sommes bien son propri�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�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(¤t_time));
/* Lorsqu'on retourne une cha�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�ation de la fen�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�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;
}