source: trunk/third/gtk/docs/html/gtk_tut-19.html @ 14482

Revision 14482, 14.2 KB checked in by ghudson, 25 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r14481, which included commits to RCS files with non-trunk default branches.
Line 
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
2<HTML>
3<HEAD>
4 <META NAME="GENERATOR" CONTENT="SGML-Tools 1.0.9">
5 <TITLE>GTK v1.2 Tutorial: Managing Selections</TITLE>
6 <LINK HREF="gtk_tut-20.html" REL=next>
7 <LINK HREF="gtk_tut-18.html" REL=previous>
8 <LINK HREF="gtk_tut.html#toc19" REL=contents>
9</HEAD>
10<BODY BGCOLOR="#FFFFFF">
11<A HREF="gtk_tut-20.html">Next</A>
12<A HREF="gtk_tut-18.html">Previous</A>
13<A HREF="gtk_tut.html#toc19">Contents</A>
14<HR NOSHADE>
15<H2><A NAME="s19">19. Managing Selections</A></H2>
16
17<H2><A NAME="ss19.1">19.1 Overview</A>
18</H2>
19
20<P>One type of interprocess communication supported by X and GTK is
21<EM>selections</EM>. A selection identifies a chunk of data, for
22instance, a portion of text, selected by the user in some fashion, for
23instance, by dragging with the mouse. Only one application on a
24display (the <EM>owner</EM>) can own a particular selection at one
25time, so when a selection is claimed by one application, the previous
26owner must indicate to the user that selection has been
27relinquished. Other applications can request the contents of a
28selection in different forms, called <EM>targets</EM>. There can be
29any number of selections, but most X applications only handle one, the
30<EM>primary selection</EM>.
31<P>In most cases, it isn't necessary for a GTK application to deal with
32selections itself. The standard widgets, such as the Entry widget,
33already have the capability to claim the selection when appropriate
34(e.g., when the user drags over text), and to retrieve the contents of
35the selection owned by another widget or another application (e.g.,
36when the user clicks the second mouse button). However, there may be
37cases in which you want to give other widgets the ability to supply
38the selection, or you wish to retrieve targets not supported by
39default.
40<P>A fundamental concept needed to understand selection handling is that
41of the <EM>atom</EM>. An atom is an integer that uniquely identifies a
42string (on a certain display). Certain atoms are predefined by the X
43server, and in some cases there are constants in <CODE>gtk.h</CODE>
44corresponding to these atoms. For instance the constant
45<CODE>GDK_PRIMARY_SELECTION</CODE> corresponds to the string "PRIMARY".
46In other cases, you should use the functions
47<CODE>gdk_atom_intern()</CODE>, to get the atom corresponding to a string,
48and <CODE>gdk_atom_name()</CODE>, to get the name of an atom. Both
49selections and targets are identified by atoms.
50<P>
51<H2><A NAME="ss19.2">19.2 Retrieving the selection</A>
52</H2>
53
54<P>Retrieving the selection is an asynchronous process. To start the
55process, you call:
56<P>
57<BLOCKQUOTE><CODE>
58<PRE>
59gint gtk_selection_convert( GtkWidget *widget,
60                            GdkAtom    selection,
61                            GdkAtom    target,
62                            guint32    time );
63</PRE>
64</CODE></BLOCKQUOTE>
65<P>This <EM>converts</EM> the selection into the form specified by
66<CODE>target</CODE>. If at all possible, the time field should be the time
67from the event that triggered the selection. This helps make sure that
68events occur in the order that the user requested them. However, if it
69is not available (for instance, if the conversion was triggered by a
70"clicked" signal), then you can use the constant
71<CODE>GDK_CURRENT_TIME</CODE>.
72<P>When the selection owner responds to the request, a
73"selection_received" signal is sent to your application. The handler
74for this signal receives a pointer to a <CODE>GtkSelectionData</CODE>
75structure, which is defined as:
76<P>
77<BLOCKQUOTE><CODE>
78<PRE>
79struct _GtkSelectionData
80{
81  GdkAtom selection;
82  GdkAtom target;
83  GdkAtom type;
84  gint    format;
85  guchar *data;
86  gint    length;
87};
88</PRE>
89</CODE></BLOCKQUOTE>
90<P><CODE>selection</CODE> and <CODE>target</CODE> are the values you gave in your
91<CODE>gtk_selection_convert()</CODE> call. <CODE>type</CODE> is an atom that
92identifies the type of data returned by the selection owner. Some
93possible values are "STRING", a string of latin-1 characters, "ATOM",
94a series of atoms, "INTEGER", an integer, etc. Most targets can only
95return one type. <CODE>format</CODE> gives the length of the units (for
96instance characters) in bits. Usually, you don't care about this when
97receiving data. <CODE>data</CODE> is a pointer to the returned data, and
98<CODE>length</CODE> gives the length of the returned data, in bytes. If
99<CODE>length</CODE> is negative, then an error occurred and the selection
100could not be retrieved. This might happen if no application owned the
101selection, or if you requested a target that the application didn't
102support. The buffer is actually guaranteed to be one byte longer than
103<CODE>length</CODE>; the extra byte will always be zero, so it isn't
104necessary to make a copy of strings just to null terminate them.
105<P>In the following example, we retrieve the special target "TARGETS",
106which is a list of all targets into which the selection can be
107converted.
108<P>
109<BLOCKQUOTE><CODE>
110<PRE>
111/* example-start selection gettargets.c */
112
113#include &lt;gtk/gtk.h>
114
115void selection_received( GtkWidget        *widget,
116                         GtkSelectionData *selection_data,
117                         gpointer          data );
118
119/* Signal handler invoked when user clicks on the "Get Targets" button */
120void get_targets( GtkWidget *widget,
121                  gpointer data )
122{
123  static GdkAtom targets_atom = GDK_NONE;
124
125  /* Get the atom corresponding to the string "TARGETS" */
126  if (targets_atom == GDK_NONE)
127    targets_atom = gdk_atom_intern ("TARGETS", FALSE);
128
129  /* And request the "TARGETS" target for the primary selection */
130  gtk_selection_convert (widget, GDK_SELECTION_PRIMARY, targets_atom,
131                         GDK_CURRENT_TIME);
132}
133
134/* Signal handler called when the selections owner returns the data */
135void selection_received( GtkWidget        *widget,
136                         GtkSelectionData *selection_data,
137                         gpointer          data )
138{
139  GdkAtom *atoms;
140  GList *item_list;
141  int i;
142
143  /* **** IMPORTANT **** Check to see if retrieval succeeded  */
144  if (selection_data->length &lt; 0)
145    {
146      g_print ("Selection retrieval failed\n");
147      return;
148    }
149  /* Make sure we got the data in the expected form */
150  if (selection_data->type != GDK_SELECTION_TYPE_ATOM)
151    {
152      g_print ("Selection \"TARGETS\" was not returned as atoms!\n");
153      return;
154    }
155 
156  /* Print out the atoms we received */
157  atoms = (GdkAtom *)selection_data->data;
158
159  item_list = NULL;
160  for (i=0; i&lt;selection_data->length/sizeof(GdkAtom); i++)
161    {
162      char *name;
163      name = gdk_atom_name (atoms[i]);
164      if (name != NULL)
165        g_print ("%s\n",name);
166      else
167        g_print ("(bad atom)\n");
168    }
169
170  return;
171}
172
173int main( int   argc,
174          char *argv[] )
175{
176  GtkWidget *window;
177  GtkWidget *button;
178 
179  gtk_init (&amp;argc, &amp;argv);
180
181  /* Create the toplevel window */
182
183  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
184  gtk_window_set_title (GTK_WINDOW (window), "Event Box");
185  gtk_container_set_border_width (GTK_CONTAINER (window), 10);
186
187  gtk_signal_connect (GTK_OBJECT (window), "destroy",
188                      GTK_SIGNAL_FUNC (gtk_exit), NULL);
189
190  /* Create a button the user can click to get targets */
191
192  button = gtk_button_new_with_label ("Get Targets");
193  gtk_container_add (GTK_CONTAINER (window), button);
194
195  gtk_signal_connect (GTK_OBJECT(button), "clicked",
196                      GTK_SIGNAL_FUNC (get_targets), NULL);
197  gtk_signal_connect (GTK_OBJECT(button), "selection_received",
198                      GTK_SIGNAL_FUNC (selection_received), NULL);
199
200  gtk_widget_show (button);
201  gtk_widget_show (window);
202 
203  gtk_main ();
204 
205  return 0;
206}
207/* example-end */
208</PRE>
209</CODE></BLOCKQUOTE>
210<P>
211<H2><A NAME="ss19.3">19.3 Supplying the selection </A>
212</H2>
213
214<P>Supplying the selection is a bit more complicated. You must register
215handlers that will be called when your selection is requested. For
216each selection/target pair you will handle, you make a call to:
217<P>
218<BLOCKQUOTE><CODE>
219<PRE>
220void gtk_selection_add_target (GtkWidget           *widget,
221                               GdkAtom              selection,
222                               GdkAtom              target,
223                               guint                info);
224</PRE>
225</CODE></BLOCKQUOTE>
226<P><CODE>widget</CODE>, <CODE>selection</CODE>, and <CODE>target</CODE> identify the requests
227this handler will manage. When a request for a selection is received,
228the "selection_get" signal will be called. <CODE>info</CODE> can be used as an
229enumerator to identify the specific target within the callback function.
230<P>The callback function has the signature:
231<P>
232<BLOCKQUOTE><CODE>
233<PRE>
234void  "selection_get" (GtkWidget          *widget,
235                       GtkSelectionData   *selection_data,
236                       guint               info,
237                       guint               time);
238</PRE>
239</CODE></BLOCKQUOTE>
240<P>The GtkSelectionData is the same as above, but this time, we're
241responsible for filling in the fields <CODE>type</CODE>, <CODE>format</CODE>,
242<CODE>data</CODE>, and <CODE>length</CODE>. (The <CODE>format</CODE> field is actually
243important here - the X server uses it to figure out whether the data
244needs to be byte-swapped or not. Usually it will be 8 - <EM>i.e.</EM> a
245character - or 32 - <EM>i.e.</EM> a. integer.) This is done by calling the
246function:
247<P>
248<BLOCKQUOTE><CODE>
249<PRE>
250void gtk_selection_data_set( GtkSelectionData *selection_data,
251                             GdkAtom           type,
252                             gint              format,
253                             guchar           *data,
254                             gint              length );
255</PRE>
256</CODE></BLOCKQUOTE>
257<P>This function takes care of properly making a copy of the data so that
258you don't have to worry about keeping it around. (You should not fill
259in the fields of the GtkSelectionData structure by hand.)
260<P>When prompted by the user, you claim ownership of the selection by
261calling:
262<P>
263<BLOCKQUOTE><CODE>
264<PRE>
265gint gtk_selection_owner_set( GtkWidget *widget,
266                              GdkAtom    selection,
267                              guint32    time );
268</PRE>
269</CODE></BLOCKQUOTE>
270<P>If another application claims ownership of the selection, you will
271receive a "selection_clear_event".
272<P>As an example of supplying the selection, the following program adds
273selection functionality to a toggle button. When the toggle button is
274depressed, the program claims the primary selection. The only target
275supported (aside from certain targets like "TARGETS" supplied by GTK
276itself), is the "STRING" target. When this target is requested, a
277string representation of the time is returned.
278<P>
279<BLOCKQUOTE><CODE>
280<PRE>
281/* example-start selection setselection.c */
282
283#include &lt;gtk/gtk.h>
284#include &lt;time.h>
285
286/* Callback when the user toggles the selection */
287void selection_toggled( GtkWidget *widget,
288                        gint      *have_selection )
289{
290  if (GTK_TOGGLE_BUTTON(widget)->active)
291    {
292      *have_selection = gtk_selection_owner_set (widget,
293                                                 GDK_SELECTION_PRIMARY,
294                                                 GDK_CURRENT_TIME);
295      /* if claiming the selection failed, we return the button to
296         the out state */
297      if (!*have_selection)
298        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(widget), FALSE);
299    }
300  else
301    {
302      if (*have_selection)
303        {
304          /* Before clearing the selection by setting the owner to NULL,
305             we check if we are the actual owner */
306          if (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == widget->window)
307            gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY,
308                                     GDK_CURRENT_TIME);
309          *have_selection = FALSE;
310        }
311    }
312}
313
314/* Called when another application claims the selection */
315gint selection_clear( GtkWidget         *widget,
316                      GdkEventSelection *event,
317                      gint              *have_selection )
318{
319  *have_selection = FALSE;
320  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(widget), FALSE);
321
322  return TRUE;
323}
324
325/* Supplies the current time as the selection. */
326void selection_handle( GtkWidget        *widget,
327                       GtkSelectionData *selection_data,
328                       guint             info,
329                       guint             time_stamp,
330                       gpointer          data )
331{
332  gchar *timestr;
333  time_t current_time;
334
335  current_time = time(NULL);
336  timestr = asctime (localtime(&amp;current_time));
337  /* When we return a single string, it should not be null terminated.
338     That will be done for us */
339
340  gtk_selection_data_set (selection_data, GDK_SELECTION_TYPE_STRING,
341                          8, timestr, strlen(timestr));
342}
343
344int main( int   argc,
345          char *argv[] )
346{
347  GtkWidget *window;
348  GtkWidget *selection_button;
349
350  static int have_selection = FALSE;
351 
352  gtk_init (&amp;argc, &amp;argv);
353
354  /* Create the toplevel window */
355
356  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
357  gtk_window_set_title (GTK_WINDOW (window), "Event Box");
358  gtk_container_set_border_width (GTK_CONTAINER (window), 10);
359
360  gtk_signal_connect (GTK_OBJECT (window), "destroy",
361                      GTK_SIGNAL_FUNC (gtk_exit), NULL);
362
363  /* Create a toggle button to act as the selection */
364
365  selection_button = gtk_toggle_button_new_with_label ("Claim Selection");
366  gtk_container_add (GTK_CONTAINER (window), selection_button);
367  gtk_widget_show (selection_button);
368
369  gtk_signal_connect (GTK_OBJECT(selection_button), "toggled",
370                      GTK_SIGNAL_FUNC (selection_toggled), &amp;have_selection);
371  gtk_signal_connect (GTK_OBJECT(selection_button), "selection_clear_event",
372                      GTK_SIGNAL_FUNC (selection_clear), &amp;have_selection);
373
374  gtk_selection_add_target (selection_button,
375                            GDK_SELECTION_PRIMARY,
376                            GDK_SELECTION_TYPE_STRING,
377                            1);
378  gtk_signal_connect (GTK_OBJECT(selection_button), "selection_get",
379                      GTK_SIGNAL_FUNC (selection_handle), &amp;have_selection);
380
381  gtk_widget_show (selection_button);
382  gtk_widget_show (window);
383 
384  gtk_main ();
385 
386  return 0;
387}
388/* example-end */
389</PRE>
390</CODE></BLOCKQUOTE>
391<P>
392<P>
393<HR NOSHADE>
394<A HREF="gtk_tut-20.html">Next</A>
395<A HREF="gtk_tut-18.html">Previous</A>
396<A HREF="gtk_tut.html#toc19">Contents</A>
397</BODY>
398</HTML>
Note: See TracBrowser for help on using the repository browser.