17.2. Retrieving the selection

Retrieving the selection is an asynchronous process. To start the process, you call:

gboolean gtk_selection_convert( GtkWidget *widget, 
                                GdkAtom    selection, 
                                GdkAtom    target,
                                guint32    time );

This converts the selection into the form specified by target. If at all possible, the time field should be the time from the event that triggered the selection. This helps make sure that events occur in the order that the user requested them. However, if it is not available (for instance, if the conversion was triggered by a "clicked" signal), then you can use the constant GDK_CURRENT_TIME.

When the selection owner responds to the request, a "selection_received" signal is sent to your application. The handler for this signal receives a pointer to a GtkSelectionData structure, which is defined as:

struct _GtkSelectionData
{
  GdkAtom selection;
  GdkAtom target;
  GdkAtom type;
  gint    format;
  guchar *data;
  gint    length;
};

selection and target are the values you gave in your gtk_selection_convert() call. type is an atom that identifies the type of data returned by the selection owner. Some possible values are "STRING", a string of latin-1 characters, "ATOM", a series of atoms, "INTEGER", an integer, etc. Most targets can only return one type. format gives the length of the units (for instance characters) in bits. Usually, you don't care about this when receiving data. data is a pointer to the returned data, and length gives the length of the returned data, in bytes. If length is negative, then an error occurred and the selection could not be retrieved. This might happen if no application owned the selection, or if you requested a target that the application didn't support. The buffer is actually guaranteed to be one byte longer than length; the extra byte will always be zero, so it isn't necessary to make a copy of strings just to nul-terminate them.

In the following example, we retrieve the special target "TARGETS", which is a list of all targets into which the selection can be converted.


#include <stdlib.h>
#include <gtk/gtk.h>

static void selection_received( GtkWidget        *widget, 
                                GtkSelectionData *selection_data, 
                                gpointer          data );

/* Signal handler invoked when user clicks on the "Get Targets" button */
static void get_targets( GtkWidget *widget,
                         gpointer data )
{
  static GdkAtom targets_atom = GDK_NONE;
  GtkWidget *window = (GtkWidget *)data;	

  /* Get the atom corresponding to the string "TARGETS" */
  if (targets_atom == GDK_NONE)
    targets_atom = gdk_atom_intern ("TARGETS", FALSE);

  /* And request the "TARGETS" target for the primary selection */
  gtk_selection_convert (window, GDK_SELECTION_PRIMARY, targets_atom,
			 GDK_CURRENT_TIME);
}

/* Signal handler called when the selections owner returns the data */
static void selection_received( GtkWidget        *widget,
                                GtkSelectionData *selection_data, 
                                gpointer          data )
{
  GdkAtom *atoms;
  GList *item_list;
  int i;

  /* **** IMPORTANT **** Check to see if retrieval succeeded  */
  if (selection_data->length < 0)
    {
      g_print ("Selection retrieval failed\n");
      return;
    }
  /* Make sure we got the data in the expected form */
  if (selection_data->type != GDK_SELECTION_TYPE_ATOM)
    {
      g_print ("Selection \"TARGETS\" was not returned as atoms!\n");
      return;
    }
  
  /* Print out the atoms we received */
  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 ("(bad atom)\n");
    }

  return;
}

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

  /* Create the toplevel window */

  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title (GTK_WINDOW (window), "Event Box");
  gtk_container_set_border_width (GTK_CONTAINER (window), 10);

  g_signal_connect (G_OBJECT (window), "destroy",
	            G_CALLBACK (exit), NULL);

  /* Create a button the user can click to get targets */

  button = gtk_button_new_with_label ("Get Targets");
  gtk_container_add (GTK_CONTAINER (window), button);

  g_signal_connect (G_OBJECT (button), "clicked",
		    G_CALLBACK (get_targets), (gpointer) window);
  g_signal_connect (G_OBJECT (window), "selection_received",
		    G_CALLBACK (selection_received), NULL);

  gtk_widget_show (button);
  gtk_widget_show (window);
  
  gtk_main ();
  
  return 0;
}