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 |
---|
22 | instance, a portion of text, selected by the user in some fashion, for |
---|
23 | instance, by dragging with the mouse. Only one application on a |
---|
24 | display (the <EM>owner</EM>) can own a particular selection at one |
---|
25 | time, so when a selection is claimed by one application, the previous |
---|
26 | owner must indicate to the user that selection has been |
---|
27 | relinquished. Other applications can request the contents of a |
---|
28 | selection in different forms, called <EM>targets</EM>. There can be |
---|
29 | any 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 |
---|
32 | selections itself. The standard widgets, such as the Entry widget, |
---|
33 | already have the capability to claim the selection when appropriate |
---|
34 | (e.g., when the user drags over text), and to retrieve the contents of |
---|
35 | the selection owned by another widget or another application (e.g., |
---|
36 | when the user clicks the second mouse button). However, there may be |
---|
37 | cases in which you want to give other widgets the ability to supply |
---|
38 | the selection, or you wish to retrieve targets not supported by |
---|
39 | default. |
---|
40 | <P>A fundamental concept needed to understand selection handling is that |
---|
41 | of the <EM>atom</EM>. An atom is an integer that uniquely identifies a |
---|
42 | string (on a certain display). Certain atoms are predefined by the X |
---|
43 | server, and in some cases there are constants in <CODE>gtk.h</CODE> |
---|
44 | corresponding to these atoms. For instance the constant |
---|
45 | <CODE>GDK_PRIMARY_SELECTION</CODE> corresponds to the string "PRIMARY". |
---|
46 | In other cases, you should use the functions |
---|
47 | <CODE>gdk_atom_intern()</CODE>, to get the atom corresponding to a string, |
---|
48 | and <CODE>gdk_atom_name()</CODE>, to get the name of an atom. Both |
---|
49 | selections 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 |
---|
55 | process, you call: |
---|
56 | <P> |
---|
57 | <BLOCKQUOTE><CODE> |
---|
58 | <PRE> |
---|
59 | gint 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 |
---|
67 | from the event that triggered the selection. This helps make sure that |
---|
68 | events occur in the order that the user requested them. However, if it |
---|
69 | is 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 |
---|
74 | for this signal receives a pointer to a <CODE>GtkSelectionData</CODE> |
---|
75 | structure, which is defined as: |
---|
76 | <P> |
---|
77 | <BLOCKQUOTE><CODE> |
---|
78 | <PRE> |
---|
79 | struct _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 |
---|
92 | identifies the type of data returned by the selection owner. Some |
---|
93 | possible values are "STRING", a string of latin-1 characters, "ATOM", |
---|
94 | a series of atoms, "INTEGER", an integer, etc. Most targets can only |
---|
95 | return one type. <CODE>format</CODE> gives the length of the units (for |
---|
96 | instance characters) in bits. Usually, you don't care about this when |
---|
97 | receiving 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 |
---|
100 | could not be retrieved. This might happen if no application owned the |
---|
101 | selection, or if you requested a target that the application didn't |
---|
102 | support. 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 |
---|
104 | necessary to make a copy of strings just to null terminate them. |
---|
105 | <P>In the following example, we retrieve the special target "TARGETS", |
---|
106 | which is a list of all targets into which the selection can be |
---|
107 | converted. |
---|
108 | <P> |
---|
109 | <BLOCKQUOTE><CODE> |
---|
110 | <PRE> |
---|
111 | /* example-start selection gettargets.c */ |
---|
112 | |
---|
113 | #include <gtk/gtk.h> |
---|
114 | |
---|
115 | void selection_received( GtkWidget *widget, |
---|
116 | GtkSelectionData *selection_data, |
---|
117 | gpointer data ); |
---|
118 | |
---|
119 | /* Signal handler invoked when user clicks on the "Get Targets" button */ |
---|
120 | void 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 */ |
---|
135 | void 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 < 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<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 | |
---|
173 | int main( int argc, |
---|
174 | char *argv[] ) |
---|
175 | { |
---|
176 | GtkWidget *window; |
---|
177 | GtkWidget *button; |
---|
178 | |
---|
179 | gtk_init (&argc, &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 |
---|
215 | handlers that will be called when your selection is requested. For |
---|
216 | each selection/target pair you will handle, you make a call to: |
---|
217 | <P> |
---|
218 | <BLOCKQUOTE><CODE> |
---|
219 | <PRE> |
---|
220 | void 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 |
---|
227 | this handler will manage. When a request for a selection is received, |
---|
228 | the "selection_get" signal will be called. <CODE>info</CODE> can be used as an |
---|
229 | enumerator to identify the specific target within the callback function. |
---|
230 | <P>The callback function has the signature: |
---|
231 | <P> |
---|
232 | <BLOCKQUOTE><CODE> |
---|
233 | <PRE> |
---|
234 | void "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 |
---|
241 | responsible 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 |
---|
243 | important here - the X server uses it to figure out whether the data |
---|
244 | needs to be byte-swapped or not. Usually it will be 8 - <EM>i.e.</EM> a |
---|
245 | character - or 32 - <EM>i.e.</EM> a. integer.) This is done by calling the |
---|
246 | function: |
---|
247 | <P> |
---|
248 | <BLOCKQUOTE><CODE> |
---|
249 | <PRE> |
---|
250 | void 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 |
---|
258 | you don't have to worry about keeping it around. (You should not fill |
---|
259 | in the fields of the GtkSelectionData structure by hand.) |
---|
260 | <P>When prompted by the user, you claim ownership of the selection by |
---|
261 | calling: |
---|
262 | <P> |
---|
263 | <BLOCKQUOTE><CODE> |
---|
264 | <PRE> |
---|
265 | gint 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 |
---|
271 | receive a "selection_clear_event". |
---|
272 | <P>As an example of supplying the selection, the following program adds |
---|
273 | selection functionality to a toggle button. When the toggle button is |
---|
274 | depressed, the program claims the primary selection. The only target |
---|
275 | supported (aside from certain targets like "TARGETS" supplied by GTK |
---|
276 | itself), is the "STRING" target. When this target is requested, a |
---|
277 | string representation of the time is returned. |
---|
278 | <P> |
---|
279 | <BLOCKQUOTE><CODE> |
---|
280 | <PRE> |
---|
281 | /* example-start selection setselection.c */ |
---|
282 | |
---|
283 | #include <gtk/gtk.h> |
---|
284 | #include <time.h> |
---|
285 | |
---|
286 | /* Callback when the user toggles the selection */ |
---|
287 | void 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 */ |
---|
315 | gint 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. */ |
---|
326 | void 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(&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 | |
---|
344 | int 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 (&argc, &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), &have_selection); |
---|
371 | gtk_signal_connect (GTK_OBJECT(selection_button), "selection_clear_event", |
---|
372 | GTK_SIGNAL_FUNC (selection_clear), &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), &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> |
---|