Page suivante Page pr�c�dente Table des mati�res

16. Gestion des s�lections

16.1 Introduction

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.

16.2 R�cup�ration de la s�lection

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

16.3 Fournir la s�lection

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(&current_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;
}


Page suivante Page pr�c�dente Table des mati�res