17.3. Supplying the selection

Supplying the selection is a bit more complicated. You must register handlers that will be called when your selection is requested. For each selection/target pair you will handle, you make a call to:

void gtk_selection_add_target( GtkWidget           *widget, 
                               GdkAtom              selection,
                               GdkAtom              target,
                               guint                info );

widget, selection, and target identify the requests this handler will manage. When a request for a selection is received, the "selection_get" signal will be called. info can be used as an enumerator to identify the specific target within the callback function.

The callback function has the signature:

void  "selection_get"( GtkWidget          *widget,
                       GtkSelectionData   *selection_data,
                       guint               info,
                       guint               time );

The GtkSelectionData is the same as above, but this time, we're responsible for filling in the fields type, format, data, and length. (The format field is actually important here - the X server uses it to figure out whether the data needs to be byte-swapped or not. Usually it will be 8 - i.e. a character - or 32 - i.e. an integer.) This is done by calling the function:

void gtk_selection_data_set( GtkSelectionData *selection_data,
                             GdkAtom           type,
                             gint              format,
                             guchar           *data,
                             gint              length );

This function takes care of properly making a copy of the data so that you don't have to worry about keeping it around. (You should not fill in the fields of the GtkSelectionData structure by hand.)

When prompted by the user, you claim ownership of the selection by calling:

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

If another application claims ownership of the selection, you will receive a "selection_clear_event".

As an example of supplying the selection, the following program adds selection functionality to a toggle button. When the toggle button is depressed, the program claims the primary selection. The only target supported (aside from certain targets like "TARGETS" supplied by GTK itself), is the "STRING" target. When this target is requested, a string representation of the time is returned.


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

GtkWidget *selection_button;
GtkWidget *selection_widget;

/* Callback when the user toggles the selection */
static void selection_toggled( GtkWidget *widget,
                               gint      *have_selection )
{
  if (GTK_TOGGLE_BUTTON (widget)->active)
    {
      *have_selection = gtk_selection_owner_set (selection_widget,
						 GDK_SELECTION_PRIMARY,
						 GDK_CURRENT_TIME);
      /* if claiming the selection failed, we return the button to
	 the out state */
      if (!*have_selection)
	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), FALSE);
    }
  else
    {
      if (*have_selection)
	{
	  /* Before clearing the selection by setting the owner to NULL,
	     we check if we are the actual owner */
	  if (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == widget->window)
	    gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY,
				     GDK_CURRENT_TIME);
	  *have_selection = FALSE;
	}
    }
}

/* Called when another application claims the selection */
static gboolean selection_clear( GtkWidget         *widget,
                                 GdkEventSelection *event,
                                 gint              *have_selection )
{
  *have_selection = FALSE;
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (selection_button), FALSE);

  return TRUE;
}

/* Supplies the current time as the selection. */
static void selection_handle( GtkWidget        *widget, 
                              GtkSelectionData *selection_data,
                              guint             info,
                              guint             time_stamp,
                              gpointer          data )
{
  gchar *timestr;
  time_t current_time;

  current_time = time (NULL);
  timestr = asctime (localtime (&current_time)); 
  /* When we return a single string, it should not be null terminated.
     That will be done for us */

  gtk_selection_data_set (selection_data, GDK_SELECTION_TYPE_STRING,
			  8, timestr, strlen (timestr));
}

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

  static int have_selection = FALSE;
  
  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 toggle button to act as the selection */

  selection_widget = gtk_invisible_new ();
  selection_button = gtk_toggle_button_new_with_label ("Claim Selection");
  gtk_container_add (GTK_CONTAINER (window), selection_button);
  gtk_widget_show (selection_button);

  g_signal_connect (G_OBJECT (selection_button), "toggled",
		    G_CALLBACK (selection_toggled), (gpointer) &have_selection);
  g_signal_connect (G_OBJECT (selection_widget), "selection_clear_event",
		    G_CALLBACK (selection_clear), (gpointer) &have_selection);

  gtk_selection_add_target (selection_widget,
			    GDK_SELECTION_PRIMARY,
			    GDK_SELECTION_TYPE_STRING,
		            1);
  g_signal_connect (G_OBJECT (selection_widget), "selection_get",
		    G_CALLBACK (selection_handle), (gpointer) &have_selection);

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