]> Pileus Git - ~andy/gtk/blob - gtk/gtkselection.c
700494ff2c77ad10e196acf564a335d56d9af5e7
[~andy/gtk] / gtk / gtkselection.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /* This file implements most of the work of the ICCM selection protocol.
21  * The code was written after an intensive study of the equivalent part
22  * of John Ousterhout's Tk toolkit, and does many things in much the 
23  * same way.
24  *
25  * The one thing in the ICCM that isn't fully supported here (or in Tk)
26  * is side effects targets. For these to be handled properly, MULTIPLE
27  * targets need to be done in the order specified. This cannot be
28  * guaranteed with the way we do things, since if we are doing INCR
29  * transfers, the order will depend on the timing of the requestor.
30  *
31  * By Owen Taylor <owt1@cornell.edu>          8/16/97
32  */
33
34 /* Terminology note: when not otherwise specified, the term "incr" below
35  * refers to the _sending_ part of the INCR protocol. The receiving
36  * portion is referred to just as "retrieval". (Terminology borrowed
37  * from Tk, because there is no good opposite to "retrieval" in English.
38  * "send" can't be made into a noun gracefully and we're already using
39  * "emission" for something else ....)
40  */
41
42 /* The MOTIF entry widget seems to ask for the TARGETS target, then
43    (regardless of the reply) ask for the TEXT target. It's slightly
44    possible though that it somehow thinks we are responding negatively
45    to the TARGETS request, though I don't really think so ... */
46
47 /*
48  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
49  * file for a list of people on the GTK+ Team.  See the ChangeLog
50  * files for a list of changes.  These files are distributed with
51  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
52  */
53
54 #include <stdarg.h>
55 #include <string.h>
56 #include "gdk.h"
57
58 #include "gtkmain.h"
59 #include "gtkselection.h"
60
61 /* #define DEBUG_SELECTION */
62
63 /* Maximum size of a sent chunk, in bytes. Also the default size of
64    our buffers */
65 #ifdef GDK_WINDOWING_WIN32
66 /* No chunks on Win32 */
67 #define GTK_SELECTION_MAX_SIZE G_MAXINT
68 #else
69 #define GTK_SELECTION_MAX_SIZE 4000
70 #endif
71
72 #define IDLE_ABORT_TIME 300
73
74 enum {
75   INCR,
76   MULTIPLE,
77   TARGETS,
78   TIMESTAMP,
79   LAST_ATOM
80 };
81
82 typedef struct _GtkSelectionInfo GtkSelectionInfo;
83 typedef struct _GtkIncrConversion GtkIncrConversion;
84 typedef struct _GtkIncrInfo GtkIncrInfo;
85 typedef struct _GtkRetrievalInfo GtkRetrievalInfo;
86
87 struct _GtkSelectionInfo
88 {
89   GdkAtom        selection;
90   GtkWidget     *widget;        /* widget that owns selection */
91   guint32        time;          /* time used to acquire selection */
92   GdkDisplay    *display;       /* needed in gtk_selection_remove_all */    
93 };
94
95 struct _GtkIncrConversion 
96 {
97   GdkAtom           target;     /* Requested target */
98   GdkAtom           property;   /* Property to store in */
99   GtkSelectionData  data;       /* The data being supplied */
100   gint              offset;     /* Current offset in sent selection.
101                                  *  -1 => All done
102                                  *  -2 => Only the final (empty) portion
103                                  *        left to send */
104 };
105
106 struct _GtkIncrInfo
107 {
108   GdkWindow *requestor;         /* Requestor window - we create a GdkWindow
109                                    so we can receive events */
110   GdkAtom    selection;         /* Selection we're sending */
111   
112   GtkIncrConversion *conversions; /* Information about requested conversions -
113                                    * With MULTIPLE requests (benighted 1980's
114                                    * hardware idea), there can be more than
115                                    * one */
116   gint num_conversions;
117   gint num_incrs;               /* number of remaining INCR style transactions */
118   guint32 idle_time;
119 };
120
121
122 struct _GtkRetrievalInfo
123 {
124   GtkWidget *widget;
125   GdkAtom selection;            /* Selection being retrieved. */
126   GdkAtom target;               /* Form of selection that we requested */
127   guint32 idle_time;            /* Number of seconds since we last heard
128                                    from selection owner */
129   guchar   *buffer;             /* Buffer in which to accumulate results */
130   gint     offset;              /* Current offset in buffer, -1 indicates
131                                    not yet started */
132   guint32 notify_time;          /* Timestamp from SelectionNotify */
133 };
134
135 /* Local Functions */
136 static void gtk_selection_init              (void);
137 static gint gtk_selection_incr_timeout      (GtkIncrInfo      *info);
138 static gint gtk_selection_retrieval_timeout (GtkRetrievalInfo *info);
139 static void gtk_selection_retrieval_report  (GtkRetrievalInfo *info,
140                                              GdkAtom           type,
141                                              gint              format,
142                                              guchar           *buffer,
143                                              gint              length,
144                                              guint32           time);
145 static void gtk_selection_invoke_handler    (GtkWidget        *widget,
146                                              GtkSelectionData *data,
147                                              guint             time);
148 static void gtk_selection_default_handler   (GtkWidget        *widget,
149                                              GtkSelectionData *data);
150 static int  gtk_selection_bytes_per_item    (gint              format);
151
152 /* Local Data */
153 static gint initialize = TRUE;
154 static GList *current_retrievals = NULL;
155 static GList *current_incrs = NULL;
156 static GList *current_selections = NULL;
157
158 static GdkAtom gtk_selection_atoms[LAST_ATOM];
159 static const char *gtk_selection_handler_key = "gtk-selection-handlers";
160
161 /****************
162  * Target Lists *
163  ****************/
164
165 /*
166  * Target lists
167  */
168
169 GtkTargetList *
170 gtk_target_list_new (const GtkTargetEntry *targets,
171                      guint                 ntargets)
172 {
173   GtkTargetList *result = g_new (GtkTargetList, 1);
174   result->list = NULL;
175   result->ref_count = 1;
176
177   if (targets)
178     gtk_target_list_add_table (result, targets, ntargets);
179   
180   return result;
181 }
182
183 void               
184 gtk_target_list_ref (GtkTargetList *list)
185 {
186   g_return_if_fail (list != NULL);
187
188   list->ref_count++;
189 }
190
191 void               
192 gtk_target_list_unref (GtkTargetList *list)
193 {
194   g_return_if_fail (list != NULL);
195   g_return_if_fail (list->ref_count > 0);
196
197   list->ref_count--;
198   if (list->ref_count == 0)
199     {
200       GList *tmp_list = list->list;
201       while (tmp_list)
202         {
203           GtkTargetPair *pair = tmp_list->data;
204           g_free (pair);
205
206           tmp_list = tmp_list->next;
207         }
208       
209       g_list_free (list->list);
210       g_free (list);
211     }
212 }
213
214 void 
215 gtk_target_list_add (GtkTargetList *list,
216                      GdkAtom            target,
217                      guint              flags,
218                      guint              info)
219 {
220   GtkTargetPair *pair;
221
222   g_return_if_fail (list != NULL);
223   
224   pair = g_new (GtkTargetPair, 1);
225   pair->target = target;
226   pair->flags = flags;
227   pair->info = info;
228
229   list->list = g_list_append (list->list, pair);
230 }
231
232 void               
233 gtk_target_list_add_table (GtkTargetList        *list,
234                            const GtkTargetEntry *targets,
235                            guint                 ntargets)
236 {
237   gint i;
238
239   for (i=ntargets-1; i >= 0; i--)
240     {
241       GtkTargetPair *pair = g_new (GtkTargetPair, 1);
242       pair->target = gdk_atom_intern (targets[i].target, FALSE);
243       pair->flags = targets[i].flags;
244       pair->info = targets[i].info;
245       
246       list->list = g_list_prepend (list->list, pair);
247     }
248 }
249
250 void 
251 gtk_target_list_remove (GtkTargetList *list,
252                         GdkAtom            target)
253 {
254   GList *tmp_list;
255
256   g_return_if_fail (list != NULL);
257
258   tmp_list = list->list;
259   while (tmp_list)
260     {
261       GtkTargetPair *pair = tmp_list->data;
262       
263       if (pair->target == target)
264         {
265           g_free (pair);
266
267           list->list = g_list_remove_link (list->list, tmp_list);
268           g_list_free_1 (tmp_list);
269
270           return;
271         }
272       
273       tmp_list = tmp_list->next;
274     }
275 }
276
277 gboolean
278 gtk_target_list_find (GtkTargetList *list,
279                       GdkAtom        target,
280                       guint         *info)
281 {
282   GList *tmp_list = list->list;
283   while (tmp_list)
284     {
285       GtkTargetPair *pair = tmp_list->data;
286
287       if (pair->target == target)
288         {
289           *info = pair->info;
290           return TRUE;
291         }
292       tmp_list = tmp_list->next;
293     }
294
295   return FALSE;
296 }
297
298 /**
299  * gtk_selection_owner_set_for_display:
300  * @display: the #Gdkdisplay where the selection is set 
301  * @widget: new selection owner (a #GdkWidget), or %NULL.
302  * @selection: an interned atom representing the selection to claim.
303  * @time_: timestamp with which to claim the selection
304  *
305  * Claim ownership of a given selection for a particular widget, or,
306  * if @widget is %NULL, release ownership of the selection.
307  *
308  * Return value: TRUE if the operation succeeded 
309  * 
310  * Since: 2.2
311  */
312 gboolean
313 gtk_selection_owner_set_for_display (GdkDisplay   *display,
314                                      GtkWidget    *widget,
315                                      GdkAtom       selection,
316                                      guint32       time)
317 {
318   GList *tmp_list;
319   GtkWidget *old_owner;
320   GtkSelectionInfo *selection_info = NULL;
321   GdkWindow *window;
322
323   g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
324   g_return_val_if_fail (widget == NULL || GTK_WIDGET_REALIZED (widget), FALSE);
325   g_return_val_if_fail (widget == NULL || gtk_widget_get_display (widget) == display, FALSE);
326   
327   if (widget == NULL)
328     window = NULL;
329   else
330     window = widget->window;
331
332   tmp_list = current_selections;
333   while (tmp_list)
334     {
335       if (((GtkSelectionInfo *)tmp_list->data)->selection == selection)
336         {
337           selection_info = tmp_list->data;
338           break;
339         }
340       
341       tmp_list = tmp_list->next;
342     }
343   
344   if (gdk_selection_owner_set_for_display (display, window, selection, time, TRUE))
345     {
346       old_owner = NULL;
347       
348       if (widget == NULL)
349         {
350           if (selection_info)
351             {
352               old_owner = selection_info->widget;
353               current_selections = g_list_remove_link (current_selections,
354                                                        tmp_list);
355               g_list_free (tmp_list);
356               g_free (selection_info);
357             }
358         }
359       else
360         {
361           if (selection_info == NULL)
362             {
363               selection_info = g_new (GtkSelectionInfo, 1);
364               selection_info->selection = selection;
365               selection_info->widget = widget;
366               selection_info->time = time;
367               selection_info->display = display;
368               current_selections = g_list_prepend (current_selections,
369                                                    selection_info);
370             }
371           else
372             {
373               old_owner = selection_info->widget;
374               selection_info->widget = widget;
375               selection_info->time = time;
376               selection_info->display = display;
377             }
378         }
379       /* If another widget in the application lost the selection,
380        *  send it a GDK_SELECTION_CLEAR event.
381        */
382       if (old_owner && old_owner != widget)
383         {
384           GdkEvent *event = gdk_event_new (GDK_SELECTION_CLEAR);
385           
386           event->selection.window = g_object_ref (old_owner->window);
387           event->selection.selection = selection;
388           event->selection.time = time;
389           
390           gtk_widget_event (old_owner, event);
391
392           gdk_event_free (event);
393         }
394       return TRUE;
395     }
396   else
397     return FALSE;
398 }
399
400 /**
401  * gtk_selection_owner_set:
402  * @widget:  a #GtkWidget, or %NULL.
403  * @selection:  an interned atom representing the selection to claim
404  * @time_: timestamp with which to claim the selection
405  * 
406  * Claims ownership of a given selection for a particular widget,
407  * or, if @widget is %NULL, release ownership of the selection.
408  * 
409  * Return value: %TRUE if the operation succeeded
410  **/
411 gboolean
412 gtk_selection_owner_set (GtkWidget *widget,
413                          GdkAtom    selection,
414                          guint32    time)
415 {
416   GdkDisplay *display;
417   
418   g_return_val_if_fail (widget == NULL || GTK_WIDGET_REALIZED (widget), FALSE);
419
420   if (widget)
421     display = gtk_widget_get_display (widget);
422   else
423     {
424       GTK_NOTE (MULTIHEAD,
425                 g_warning ("gtk_selection_owner_set (NULL,...) is not multihead safe"));
426                  
427       display = gdk_display_get_default ();
428     }
429   
430   return gtk_selection_owner_set_for_display (display, widget,
431                                               selection, time);
432 }
433
434 /*************************************************************
435  * gtk_selection_add_target
436  *     Add specified target to list of supported targets
437  *
438  *   arguments:
439  *     widget:     The widget for which this target applies
440  *     selection:
441  *     target:
442  *     info:       guint to pass to to the selection_get signal 
443  *
444  *   results:
445  *************************************************************/
446
447 typedef struct _GtkSelectionTargetList GtkSelectionTargetList;
448
449 struct _GtkSelectionTargetList {
450   GdkAtom selection;
451   GtkTargetList *list;
452 };
453
454 static GtkTargetList *
455 gtk_selection_target_list_get (GtkWidget    *widget,
456                                GdkAtom       selection)
457 {
458   GtkSelectionTargetList *sellist;
459   GList *tmp_list;
460   GList *lists;
461
462   lists = g_object_get_data (G_OBJECT (widget), gtk_selection_handler_key);
463   
464   tmp_list = lists;
465   while (tmp_list)
466     {
467       sellist = tmp_list->data;
468       if (sellist->selection == selection)
469         return sellist->list;
470       tmp_list = tmp_list->next;
471     }
472
473   sellist = g_new (GtkSelectionTargetList, 1);
474   sellist->selection = selection;
475   sellist->list = gtk_target_list_new (NULL, 0);
476
477   lists = g_list_prepend (lists, sellist);
478   g_object_set_data (G_OBJECT (widget), gtk_selection_handler_key, lists);
479
480   return sellist->list;
481 }
482
483 static void
484 gtk_selection_target_list_remove (GtkWidget    *widget)
485 {
486   GtkSelectionTargetList *sellist;
487   GList *tmp_list;
488   GList *lists;
489
490   lists = g_object_get_data (G_OBJECT (widget), gtk_selection_handler_key);
491   
492   tmp_list = lists;
493   while (tmp_list)
494     {
495       sellist = tmp_list->data;
496
497       gtk_target_list_unref (sellist->list);
498
499       g_free (sellist);
500       tmp_list = tmp_list->next;
501     }
502
503   g_list_free (lists);
504   g_object_set_data (G_OBJECT (widget), gtk_selection_handler_key, NULL);
505 }
506
507 /**
508  * gtk_selection_clear_targets:
509  * @widget:    a #GtkWidget
510  * @selection: an atom representing a selection
511  *
512  * Remove all targets registered for the given selection for the
513  * widget.
514  **/
515 void 
516 gtk_selection_clear_targets (GtkWidget *widget,
517                              GdkAtom    selection)
518 {
519   GtkSelectionTargetList *sellist;
520   GList *tmp_list;
521   GList *lists;
522
523   lists = g_object_get_data (G_OBJECT (widget), gtk_selection_handler_key);
524   
525   tmp_list = lists;
526   while (tmp_list)
527     {
528       sellist = tmp_list->data;
529       if (sellist->selection == selection)
530         {
531           lists = g_list_delete_link (lists, tmp_list);
532           gtk_target_list_unref (sellist->list);
533           g_free (sellist);
534
535           break;
536         }
537       
538       tmp_list = tmp_list->next;
539     }
540   
541   g_object_set_data (G_OBJECT (widget), gtk_selection_handler_key, lists);
542 }
543
544 void 
545 gtk_selection_add_target (GtkWidget         *widget, 
546                           GdkAtom            selection,
547                           GdkAtom            target,
548                           guint              info)
549 {
550   GtkTargetList *list;
551
552   g_return_if_fail (widget != NULL);
553
554   list = gtk_selection_target_list_get (widget, selection);
555   gtk_target_list_add (list, target, 0, info);
556 }
557
558 void 
559 gtk_selection_add_targets (GtkWidget            *widget, 
560                            GdkAtom               selection,
561                            const GtkTargetEntry *targets,
562                            guint                 ntargets)
563 {
564   GtkTargetList *list;
565   
566   g_return_if_fail (widget != NULL);
567   g_return_if_fail (targets != NULL);
568   
569   list = gtk_selection_target_list_get (widget, selection);
570   gtk_target_list_add_table (list, targets, ntargets);
571 }
572
573
574 /*************************************************************
575  * gtk_selection_remove_all:
576  *     Removes all handlers and unsets ownership of all 
577  *     selections for a widget. Called when widget is being
578  *     destroyed
579  *     
580  *   arguments:
581  *     widget:    The widget
582  *   results:
583  *************************************************************/
584
585 void
586 gtk_selection_remove_all (GtkWidget *widget)
587 {
588   GList *tmp_list;
589   GList *next;
590   GtkSelectionInfo *selection_info;
591   
592   /* Remove pending requests/incrs for this widget */
593   
594   tmp_list = current_retrievals;
595   while (tmp_list)
596     {
597       next = tmp_list->next;
598       if (((GtkRetrievalInfo *)tmp_list->data)->widget == widget)
599         {
600           current_retrievals = g_list_remove_link (current_retrievals, 
601                                                    tmp_list);
602           /* structure will be freed in timeout */
603           g_list_free (tmp_list);
604         }
605       tmp_list = next;
606     }
607   
608   /* Disclaim ownership of any selections */
609   
610   tmp_list = current_selections;
611   while (tmp_list)
612     {
613       next = tmp_list->next;
614       selection_info = (GtkSelectionInfo *)tmp_list->data;
615       
616       if (selection_info->widget == widget)
617         {       
618           gdk_selection_owner_set_for_display (selection_info->display,
619                                                NULL, 
620                                                selection_info->selection,
621                                                GDK_CURRENT_TIME, FALSE);
622           current_selections = g_list_remove_link (current_selections,
623                                                    tmp_list);
624           g_list_free (tmp_list);
625           g_free (selection_info);
626         }
627       
628       tmp_list = next;
629     }
630
631   /* Remove all selection lists */
632   gtk_selection_target_list_remove (widget);
633 }
634
635 /*************************************************************
636  * gtk_selection_convert:
637  *     Request the contents of a selection. When received, 
638  *     a "selection_received" signal will be generated.
639  *
640  *   arguments:
641  *     widget:     The widget which acts as requestor
642  *     selection:  Which selection to get
643  *     target:     Form of information desired (e.g., STRING)
644  *     time:       Time of request (usually of triggering event)
645  *                 In emergency, you could use GDK_CURRENT_TIME
646  *
647  *   results:
648  *     TRUE if requested succeeded. FALSE if we could not process
649  *     request. (e.g., there was already a request in process for
650  *     this widget). 
651  *************************************************************/
652
653 gboolean
654 gtk_selection_convert (GtkWidget *widget, 
655                        GdkAtom    selection, 
656                        GdkAtom    target,
657                        guint32    time)
658 {
659   GtkRetrievalInfo *info;
660   GList *tmp_list;
661   GdkWindow *owner_window;
662   GdkDisplay *display;
663   
664   g_return_val_if_fail (widget != NULL, FALSE);
665   
666   if (initialize)
667     gtk_selection_init ();
668   
669   if (!GTK_WIDGET_REALIZED (widget))
670     gtk_widget_realize (widget);
671   
672   /* Check to see if there are already any retrievals in progress for
673      this widget. If we changed GDK to use the selection for the 
674      window property in which to store the retrieved information, then
675      we could support multiple retrievals for different selections.
676      This might be useful for DND. */
677   
678   tmp_list = current_retrievals;
679   while (tmp_list)
680     {
681       info = (GtkRetrievalInfo *)tmp_list->data;
682       if (info->widget == widget)
683         return FALSE;
684       tmp_list = tmp_list->next;
685     }
686   
687   info = g_new (GtkRetrievalInfo, 1);
688   
689   info->widget = widget;
690   info->selection = selection;
691   info->target = target;
692   info->idle_time = 0;
693   info->buffer = NULL;
694   info->offset = -1;
695   
696   /* Check if this process has current owner. If so, call handler
697      procedure directly to avoid deadlocks with INCR. */
698
699   display = gtk_widget_get_display (widget);
700   owner_window = gdk_selection_owner_get_for_display (display, selection);
701   
702   if (owner_window != NULL)
703     {
704       GtkWidget *owner_widget;
705       GtkSelectionData selection_data;
706       
707       selection_data.selection = selection;
708       selection_data.target = target;
709       selection_data.data = NULL;
710       selection_data.length = -1;
711       selection_data.display = display;
712       
713       gdk_window_get_user_data (owner_window, (gpointer *)&owner_widget);
714       
715       if (owner_widget != NULL)
716         {
717           gtk_selection_invoke_handler (owner_widget, 
718                                         &selection_data,
719                                         time);
720           
721           gtk_selection_retrieval_report (info,
722                                           selection_data.type, 
723                                           selection_data.format,
724                                           selection_data.data,
725                                           selection_data.length,
726                                           time);
727           
728           g_free (selection_data.data);
729           
730           g_free (info);
731           return TRUE;
732         }
733     }
734   
735   /* Otherwise, we need to go through X */
736   
737   current_retrievals = g_list_append (current_retrievals, info);
738   gdk_selection_convert (widget->window, selection, target, time);
739   gtk_timeout_add (1000, (GtkFunction) gtk_selection_retrieval_timeout, info);
740   
741   return TRUE;
742 }
743
744 /*************************************************************
745  * gtk_selection_data_set:
746  *     Store new data into a GtkSelectionData object. Should
747  *     _only_ by called from a selection handler callback.
748  *     Null terminates the stored data.
749  *   arguments:
750  *     type:    the type of selection data
751  *     format:  format (number of bits in a unit)
752  *     data:    pointer to the data (will be copied)
753  *     length:  length of the data
754  *   results:
755  *************************************************************/
756
757 void 
758 gtk_selection_data_set (GtkSelectionData *selection_data,
759                         GdkAtom           type,
760                         gint              format,
761                         const guchar     *data,
762                         gint              length)
763 {
764   if (selection_data->data)
765     g_free (selection_data->data);
766   
767   selection_data->type = type;
768   selection_data->format = format;
769   
770   if (data)
771     {
772       selection_data->data = g_new (guchar, length+1);
773       memcpy (selection_data->data, data, length);
774       selection_data->data[length] = 0;
775     }
776   else
777     {
778       g_return_if_fail (length <= 0);
779       
780       if (length < 0)
781         selection_data->data = NULL;
782       else
783         selection_data->data = g_strdup("");
784     }
785   
786   selection_data->length = length;
787 }
788
789 static GdkAtom utf8_atom;
790 static GdkAtom text_atom;
791 static GdkAtom ctext_atom;
792
793 static void 
794 init_atoms (void)
795 {
796   if (!utf8_atom)
797     {
798       utf8_atom = gdk_atom_intern ("UTF8_STRING", FALSE);
799       text_atom = gdk_atom_intern ("TEXT", FALSE);
800       ctext_atom = gdk_atom_intern ("COMPOUND_TEXT", FALSE);
801     }
802 }
803
804 /**
805  * gtk_selection_data_set_text:
806  * @selection_data: a #GtkSelectionData
807  * @str: a UTF-8 string
808  * @len: the length of @str, or -1 if @str is nul-terminated.
809  * 
810  * Sets the contents of the selection from a UTF-8 encoded string.
811  * The string is converted to the form determined by
812  * @selection_data->target.
813  * 
814  * Return value: %TRUE if the selection was successfully set,
815  *   otherwise %FALSE.
816  **/
817 gboolean
818 gtk_selection_data_set_text (GtkSelectionData     *selection_data,
819                              const gchar          *str,
820                              gint                  len)
821 {
822   gboolean result = FALSE;
823   
824   if (len < 0)
825     len = strlen (str);
826   
827   init_atoms ();
828
829   if (selection_data->target == utf8_atom)
830     {
831       gtk_selection_data_set (selection_data,
832                               utf8_atom,
833                               8, (guchar *)str, len);
834       result = TRUE;
835     }
836   else if (selection_data->target == GDK_TARGET_STRING)
837     {
838       gchar *tmp = g_strndup (str, len);
839       gchar *latin1 = gdk_utf8_to_string_target (tmp);
840       g_free (tmp);
841
842       if (latin1)
843         {
844           gtk_selection_data_set (selection_data,
845                                   GDK_SELECTION_TYPE_STRING,
846                                   8, latin1, strlen (latin1));
847           g_free (latin1);
848           
849           result = TRUE;
850         }
851
852     }
853   else if (selection_data->target == ctext_atom ||
854            selection_data->target == text_atom)
855     {
856       gchar *tmp;
857       guchar *text;
858       GdkAtom encoding;
859       gint format;
860       gint new_length;
861
862       tmp = g_strndup (str, len);
863       if (gdk_utf8_to_compound_text_for_display (selection_data->display, tmp,
864                                                  &encoding, &format, &text, &new_length))
865         {
866           gtk_selection_data_set (selection_data, encoding, format, text, new_length);
867           gdk_free_compound_text (text);
868
869           result = TRUE;
870         }
871
872       g_free (tmp);
873     }
874   
875   return result;
876 }
877
878 /**
879  * gtk_selection_data_get_text:
880  * @selection_data: a #GtkSelectionData
881  * 
882  * Gets the contents of the selection data as a UTF-8 string.
883  * 
884  * Return value: if the selection data contained a recognized
885  *   text type and it could be converted to UTF-8, a newly allocated
886  *   string containing the converted text, otherwise %NULL.
887  *   If the result is non-%NULL it must be freed with g_free().
888  **/
889 guchar *
890 gtk_selection_data_get_text (GtkSelectionData *selection_data)
891 {
892   guchar *result = NULL;
893
894   init_atoms ();
895   
896   if (selection_data->length >= 0 &&
897       (selection_data->type == GDK_TARGET_STRING ||
898        selection_data->type == ctext_atom ||
899        selection_data->type == utf8_atom))
900     {
901       gchar **list;
902       gint i;
903       gint count = gdk_text_property_to_utf8_list_for_display (selection_data->display,
904                                                                selection_data->type,
905                                                                selection_data->format, 
906                                                                selection_data->data,
907                                                                selection_data->length,
908                                                                &list);
909       if (count > 0)
910         result = list[0];
911
912       for (i = 1; i < count; i++)
913         g_free (list[i]);
914       g_free (list);
915     }
916
917   return result;
918 }
919
920 /**
921  * gtk_selection_data_get_targets:
922  * @selection_data: a #GtkSelectionData object
923  * @targets: location to store an array of targets. The result
924  *           stored here must be freed with g_free().
925  * @n_atoms: location to store number of items in @targets.
926  * 
927  * Gets the contents of @selection_data as an array of targets.
928  * This can be used to interpret the results of getting
929  * the standard TARGETS target that is always supplied for
930  * any selection.
931  * 
932  * Return value: %TRUE if @selection_data contains a valid
933  *    array of targets, otherwise %FALSE.
934  **/
935 gboolean
936 gtk_selection_data_get_targets (GtkSelectionData  *selection_data,
937                                 GdkAtom          **targets,
938                                 gint              *n_atoms)
939 {
940   if (selection_data->length >= 0 &&
941       selection_data->format == 32 &&
942       selection_data->type == GDK_SELECTION_TYPE_ATOM)
943     {
944       if (targets)
945         *targets = g_memdup (selection_data->data, selection_data->length);
946       if (n_atoms)
947         *n_atoms = selection_data->length / sizeof (GdkAtom);
948
949       return TRUE;
950     }
951   else
952     {
953       if (targets)
954         *targets = NULL;
955       if (n_atoms)
956         *n_atoms = -1;
957
958       return FALSE;
959     }
960 }
961
962 /**
963  * gtk_selection_data_targets_include_text:
964  * @selection_data: a #GtkSelectionData object
965  * 
966  * Given a #GtkSelectionData object holding a list of targets,
967  * determines if any of the targets in @targets can be used to
968  * provide text.
969  * 
970  * Return value: %TRUE if @selection_data holds a list of targets,
971  *   and a suitable target for text is included, otherwise %FALSE.
972  **/
973 gboolean
974 gtk_selection_data_targets_include_text (GtkSelectionData *selection_data)
975 {
976   GdkAtom *targets;
977   gint n_targets;
978   gint i;
979   gboolean result = FALSE;
980
981   if (gtk_selection_data_get_targets (selection_data, &targets, &n_targets))
982     {
983       for (i=0; i < n_targets; i++)
984         {
985           if (targets[i] == gdk_atom_intern ("STRING", FALSE) ||
986               targets[i] == gdk_atom_intern ("TEXT", FALSE) ||
987               targets[i] == gdk_atom_intern ("COMPOUND_TEXT", FALSE) ||
988               targets[i] == gdk_atom_intern ("UTF8_STRING", FALSE))
989             result = TRUE;
990         }
991
992       g_free (targets);
993     }
994
995   return result;
996 }
997           
998 /*************************************************************
999  * gtk_selection_init:
1000  *     Initialize local variables
1001  *   arguments:
1002  *     
1003  *   results:
1004  *************************************************************/
1005
1006 static void
1007 gtk_selection_init (void)
1008 {
1009   gtk_selection_atoms[INCR] = gdk_atom_intern ("INCR", FALSE);
1010   gtk_selection_atoms[MULTIPLE] = gdk_atom_intern ("MULTIPLE", FALSE);
1011   gtk_selection_atoms[TIMESTAMP] = gdk_atom_intern ("TIMESTAMP", FALSE);
1012   gtk_selection_atoms[TARGETS] = gdk_atom_intern ("TARGETS", FALSE);
1013
1014   initialize = FALSE;
1015 }
1016
1017 /**
1018  * gtk_selection_clear:
1019  * @widget: a #GtkWidget
1020  * @event: the event
1021  * 
1022  * The default handler for the GtkWidget::selection_clear_event
1023  * signal. Instead of calling this function, chain up from
1024  * your selection_clear_event handler. Calling this function
1025  * from any other context is illegal. This function will
1026  * be deprecated in future versions of GTK+.
1027  * 
1028  * Return value: %TRUE if the event was handled, otherwise false
1029  * 
1030  * Since: 2.2
1031  **/
1032 gboolean
1033 gtk_selection_clear (GtkWidget         *widget,
1034                      GdkEventSelection *event)
1035 {
1036   /* Note that we filter clear events in gdkselection-x11.c, so
1037    * that we only will get here if the clear event actually
1038    * represents a change that we didn't do ourself.
1039    */
1040   GList *tmp_list;
1041   GtkSelectionInfo *selection_info = NULL;
1042   
1043   tmp_list = current_selections;
1044   while (tmp_list)
1045     {
1046       selection_info = (GtkSelectionInfo *)tmp_list->data;
1047       
1048       if ((selection_info->selection == event->selection) &&
1049           (selection_info->widget == widget))
1050         break;
1051       
1052       tmp_list = tmp_list->next;
1053     }
1054   
1055   if (tmp_list)
1056     {
1057       current_selections = g_list_remove_link (current_selections, tmp_list);
1058       g_list_free (tmp_list);
1059       g_free (selection_info);
1060     }
1061   
1062   return TRUE;
1063 }
1064
1065
1066 /*************************************************************
1067  * _gtk_selection_request:
1068  *     Handler for "selection_request_event" 
1069  *   arguments:
1070  *     widget:
1071  *     event:
1072  *   results:
1073  *************************************************************/
1074
1075 gboolean
1076 _gtk_selection_request (GtkWidget *widget,
1077                         GdkEventSelection *event)
1078 {
1079   GtkIncrInfo *info;
1080   GList *tmp_list;
1081   guchar *mult_atoms;
1082   int i;
1083   
1084   if (initialize)
1085     gtk_selection_init ();
1086   
1087   /* Check if we own selection */
1088   
1089   tmp_list = current_selections;
1090   while (tmp_list)
1091     {
1092       GtkSelectionInfo *selection_info = (GtkSelectionInfo *)tmp_list->data;
1093       
1094       if ((selection_info->selection == event->selection) &&
1095           (selection_info->widget == widget))
1096         break;
1097       
1098       tmp_list = tmp_list->next;
1099     }
1100   
1101   if (tmp_list == NULL)
1102     return FALSE;
1103   
1104   info = g_new (GtkIncrInfo, 1);
1105
1106   g_object_ref (widget);
1107   
1108   info->selection = event->selection;
1109   info->num_incrs = 0;
1110   
1111   /* Create GdkWindow structure for the requestor */
1112   
1113   info->requestor = gdk_window_lookup_for_display (gtk_widget_get_display (widget),
1114                                                    event->requestor);
1115   if (!info->requestor)
1116     info->requestor = gdk_window_foreign_new_for_display (gtk_widget_get_display (widget),
1117                                                           event->requestor);
1118   
1119   /* Determine conversions we need to perform */
1120   
1121   if (event->target == gtk_selection_atoms[MULTIPLE])
1122     {
1123       GdkAtom  type;
1124       gint     format;
1125       gint     length;
1126       
1127       mult_atoms = NULL;
1128       
1129       gdk_error_trap_push ();
1130       if (!gdk_property_get (info->requestor, event->property, 0, /* AnyPropertyType */
1131                              0, GTK_SELECTION_MAX_SIZE, FALSE,
1132                              &type, &format, &length, &mult_atoms))
1133         {
1134           gdk_selection_send_notify_for_display (gtk_widget_get_display (widget),
1135                                                  event->requestor, 
1136                                                  event->selection,
1137                                                  event->target, 
1138                                                  GDK_NONE, 
1139                                                  event->time);
1140           g_free (mult_atoms);
1141           g_free (info);
1142           return TRUE;
1143         }
1144       gdk_error_trap_pop ();
1145       
1146       info->num_conversions = length / (2*sizeof (GdkAtom));
1147       info->conversions = g_new (GtkIncrConversion, info->num_conversions);
1148       
1149       for (i=0; i<info->num_conversions; i++)
1150         {
1151           info->conversions[i].target = ((GdkAtom *)mult_atoms)[2*i];
1152           info->conversions[i].property = ((GdkAtom *)mult_atoms)[2*i+1];
1153         }
1154     }
1155   else                          /* only a single conversion */
1156     {
1157       info->conversions = g_new (GtkIncrConversion, 1);
1158       info->num_conversions = 1;
1159       info->conversions[0].target = event->target;
1160       info->conversions[0].property = event->property;
1161       mult_atoms = (guchar *)info->conversions;
1162     }
1163   
1164   /* Loop through conversions and determine which of these are big
1165      enough to require doing them via INCR */
1166   for (i=0; i<info->num_conversions; i++)
1167     {
1168       GtkSelectionData data;
1169       glong items;
1170       
1171       data.selection = event->selection;
1172       data.target = info->conversions[i].target;
1173       data.data = NULL;
1174       data.length = -1;
1175       data.display = gtk_widget_get_display (widget);
1176       
1177 #ifdef DEBUG_SELECTION
1178       g_message ("Selection %ld, target %ld (%s) requested by 0x%x (property = %ld)",
1179                  event->selection, info->conversions[i].target,
1180                  gdk_atom_name (info->conversions[i].target),
1181                  event->requestor, event->property);
1182 #endif
1183       
1184       gtk_selection_invoke_handler (widget, &data, event->time);
1185       
1186       if (data.length < 0)
1187         {
1188           ((GdkAtom *)mult_atoms)[2*i+1] = GDK_NONE;
1189           info->conversions[i].property = GDK_NONE;
1190           continue;
1191         }
1192       
1193       g_return_val_if_fail ((data.format >= 8) && (data.format % 8 == 0), FALSE);
1194       
1195       items = data.length / gtk_selection_bytes_per_item (data.format);
1196       
1197       if (data.length > GTK_SELECTION_MAX_SIZE)
1198         {
1199           /* Sending via INCR */
1200           
1201           info->conversions[i].offset = 0;
1202           info->conversions[i].data = data;
1203           info->num_incrs++;
1204           
1205           gdk_property_change (info->requestor, 
1206                                info->conversions[i].property,
1207                                gtk_selection_atoms[INCR],
1208                                32,
1209                                GDK_PROP_MODE_REPLACE,
1210                                (guchar *)&items, 1);
1211         }
1212       else
1213         {
1214           info->conversions[i].offset = -1;
1215           
1216           gdk_property_change (info->requestor, 
1217                                info->conversions[i].property,
1218                                data.type,
1219                                data.format,
1220                                GDK_PROP_MODE_REPLACE,
1221                                data.data, items);
1222           
1223           g_free (data.data);
1224         }
1225     }
1226   
1227   /* If we have some INCR's, we need to send the rest of the data in
1228      a callback */
1229   
1230   if (info->num_incrs > 0)
1231     {
1232       /* FIXME: this could be dangerous if window doesn't still
1233          exist */
1234       
1235 #ifdef DEBUG_SELECTION
1236       g_message ("Starting INCR...");
1237 #endif
1238       
1239       gdk_window_set_events (info->requestor,
1240                              gdk_window_get_events (info->requestor) |
1241                              GDK_PROPERTY_CHANGE_MASK);
1242       current_incrs = g_list_append (current_incrs, info);
1243       gtk_timeout_add (1000, (GtkFunction)gtk_selection_incr_timeout, info);
1244     }
1245   
1246   /* If it was a MULTIPLE request, set the property to indicate which
1247      conversions succeeded */
1248   if (event->target == gtk_selection_atoms[MULTIPLE])
1249     {
1250       gdk_property_change (info->requestor, event->property,
1251                            gdk_atom_intern ("ATOM_PAIR", FALSE), 32, 
1252                            GDK_PROP_MODE_REPLACE,
1253                            mult_atoms, 2*info->num_conversions);
1254       g_free (mult_atoms);
1255     }
1256
1257   if (info->num_conversions == 1 &&
1258       info->conversions[0].property == GDK_NONE)
1259     {
1260       /* Reject the entire conversion */
1261       gdk_selection_send_notify_for_display (gtk_widget_get_display (widget),
1262                                              event->requestor, 
1263                                              event->selection, 
1264                                              event->target, 
1265                                              GDK_NONE, 
1266                                              event->time);
1267     }
1268   else
1269     {
1270       gdk_selection_send_notify_for_display (gtk_widget_get_display (widget),
1271                                              event->requestor, 
1272                                              event->selection,
1273                                              event->target,
1274                                              event->property, 
1275                                              event->time);
1276     }
1277
1278   if (info->num_incrs == 0)
1279     {
1280       g_free (info->conversions);
1281       g_free (info);
1282     }
1283
1284   g_object_unref (widget);
1285   
1286   return TRUE;
1287 }
1288
1289 /*************************************************************
1290  * _gtk_selection_incr_event:
1291  *     Called whenever an PropertyNotify event occurs for an 
1292  *     GdkWindow with user_data == NULL. These will be notifications
1293  *     that a window we are sending the selection to via the
1294  *     INCR protocol has deleted a property and is ready for
1295  *     more data.
1296  *
1297  *   arguments:
1298  *     window:  the requestor window
1299  *     event:   the property event structure
1300  *
1301  *   results:
1302  *************************************************************/
1303
1304 gboolean
1305 _gtk_selection_incr_event (GdkWindow       *window,
1306                            GdkEventProperty *event)
1307 {
1308   GList *tmp_list;
1309   GtkIncrInfo *info = NULL;
1310   gint num_bytes;
1311   guchar *buffer;
1312   
1313   int i;
1314   
1315   if (event->state != GDK_PROPERTY_DELETE)
1316     return FALSE;
1317   
1318 #ifdef DEBUG_SELECTION
1319   g_message ("PropertyDelete, property %ld", event->atom);
1320 #endif
1321   
1322   /* Now find the appropriate ongoing INCR */
1323   tmp_list = current_incrs;
1324   while (tmp_list)
1325     {
1326       info = (GtkIncrInfo *)tmp_list->data;
1327       if (info->requestor == event->window)
1328         break;
1329       
1330       tmp_list = tmp_list->next;
1331     }
1332   
1333   if (tmp_list == NULL)
1334     return FALSE;
1335   
1336   /* Find out which target this is for */
1337   for (i=0; i<info->num_conversions; i++)
1338     {
1339       if (info->conversions[i].property == event->atom &&
1340           info->conversions[i].offset != -1)
1341         {
1342           int bytes_per_item;
1343           
1344           info->idle_time = 0;
1345           
1346           if (info->conversions[i].offset == -2) /* only the last 0-length
1347                                                     piece*/
1348             {
1349               num_bytes = 0;
1350               buffer = NULL;
1351             }
1352           else
1353             {
1354               num_bytes = info->conversions[i].data.length -
1355                 info->conversions[i].offset;
1356               buffer = info->conversions[i].data.data + 
1357                 info->conversions[i].offset;
1358               
1359               if (num_bytes > GTK_SELECTION_MAX_SIZE)
1360                 {
1361                   num_bytes = GTK_SELECTION_MAX_SIZE;
1362                   info->conversions[i].offset += GTK_SELECTION_MAX_SIZE;
1363                 }
1364               else
1365                 info->conversions[i].offset = -2;
1366             }
1367 #ifdef DEBUG_SELECTION
1368           g_message ("INCR: put %d bytes (offset = %d) into window 0x%lx , property %ld",
1369                      num_bytes, info->conversions[i].offset, 
1370                      GDK_WINDOW_XWINDOW(info->requestor), event->atom);
1371 #endif
1372
1373           bytes_per_item = gtk_selection_bytes_per_item (info->conversions[i].data.format);
1374           gdk_property_change (info->requestor, event->atom,
1375                                info->conversions[i].data.type,
1376                                info->conversions[i].data.format,
1377                                GDK_PROP_MODE_REPLACE,
1378                                buffer,
1379                                num_bytes / bytes_per_item);
1380           
1381           if (info->conversions[i].offset == -2)
1382             {
1383               g_free (info->conversions[i].data.data);
1384               info->conversions[i].data.data = NULL;
1385             }
1386           
1387           if (num_bytes == 0)
1388             {
1389               info->num_incrs--;
1390               info->conversions[i].offset = -1;
1391             }
1392         }
1393       break;
1394     }
1395   
1396   /* Check if we're finished with all the targets */
1397   
1398   if (info->num_incrs == 0)
1399     {
1400       current_incrs = g_list_remove_link (current_incrs, tmp_list);
1401       g_list_free (tmp_list);
1402       /* Let the timeout free it */
1403     }
1404   
1405   return TRUE;
1406 }
1407
1408 /*************************************************************
1409  * gtk_selection_incr_timeout:
1410  *     Timeout callback for the sending portion of the INCR
1411  *     protocol
1412  *   arguments:
1413  *     info:    Information about this incr
1414  *   results:
1415  *************************************************************/
1416
1417 static gint
1418 gtk_selection_incr_timeout (GtkIncrInfo *info)
1419 {
1420   GList *tmp_list;
1421   gboolean retval;
1422
1423   GDK_THREADS_ENTER ();
1424   
1425   /* Determine if retrieval has finished by checking if it still in
1426      list of pending retrievals */
1427   
1428   tmp_list = current_incrs;
1429   while (tmp_list)
1430     {
1431       if (info == (GtkIncrInfo *)tmp_list->data)
1432         break;
1433       tmp_list = tmp_list->next;
1434     }
1435   
1436   /* If retrieval is finished */
1437   if (!tmp_list || info->idle_time >= IDLE_ABORT_TIME)
1438     {
1439       if (tmp_list && info->idle_time >= IDLE_ABORT_TIME)
1440         {
1441           current_incrs = g_list_remove_link (current_incrs, tmp_list);
1442           g_list_free (tmp_list);
1443         }
1444       
1445       g_free (info->conversions);
1446       /* FIXME: we should check if requestor window is still in use,
1447          and if not, remove it? */
1448       
1449       g_free (info);
1450       
1451       retval =  FALSE;          /* remove timeout */
1452     }
1453   else
1454     {
1455       info->idle_time++;
1456       
1457       retval = TRUE;            /* timeout will happen again */
1458     }
1459   
1460   GDK_THREADS_LEAVE ();
1461
1462   return retval;
1463 }
1464
1465 /*************************************************************
1466  * _gtk_selection_notify:
1467  *     Handler for "selection_notify_event" signals on windows
1468  *     where a retrieval is currently in process. The selection
1469  *     owner has responded to our conversion request.
1470  *   arguments:
1471  *     widget:          Widget getting signal
1472  *     event:           Selection event structure
1473  *     info:            Information about this retrieval
1474  *   results:
1475  *     was event handled?
1476  *************************************************************/
1477
1478 gboolean
1479 _gtk_selection_notify (GtkWidget               *widget,
1480                        GdkEventSelection *event)
1481 {
1482   GList *tmp_list;
1483   GtkRetrievalInfo *info = NULL;
1484   guchar  *buffer = NULL;
1485   gint length;
1486   GdkAtom type;
1487   gint    format;
1488   
1489 #ifdef DEBUG_SELECTION
1490   g_message ("Initial receipt of selection %ld, target %ld (property = %ld)",
1491              event->selection, event->target, event->property);
1492 #endif
1493   
1494   tmp_list = current_retrievals;
1495   while (tmp_list)
1496     {
1497       info = (GtkRetrievalInfo *)tmp_list->data;
1498       if (info->widget == widget && info->selection == event->selection)
1499         break;
1500       tmp_list = tmp_list->next;
1501     }
1502   
1503   if (!tmp_list)                /* no retrieval in progress */
1504     return FALSE;
1505
1506   if (event->property != GDK_NONE)
1507     length = gdk_selection_property_get (widget->window, &buffer, 
1508                                          &type, &format);
1509   else
1510     length = 0; /* silence gcc */
1511   
1512   if (event->property == GDK_NONE || buffer == NULL)
1513     {
1514       current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1515       g_list_free (tmp_list);
1516       /* structure will be freed in timeout */
1517       gtk_selection_retrieval_report (info,
1518                                       GDK_NONE, 0, NULL, -1, event->time);
1519       
1520       return TRUE;
1521     }
1522   
1523   if (type == gtk_selection_atoms[INCR])
1524     {
1525       /* The remainder of the selection will come through PropertyNotify
1526          events */
1527
1528       info->notify_time = event->time;
1529       info->idle_time = 0;
1530       info->offset = 0;         /* Mark as OK to proceed */
1531       gdk_window_set_events (widget->window,
1532                              gdk_window_get_events (widget->window)
1533                              | GDK_PROPERTY_CHANGE_MASK);
1534     }
1535   else
1536     {
1537       /* We don't delete the info structure - that will happen in timeout */
1538       current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1539       g_list_free (tmp_list);
1540       
1541       info->offset = length;
1542       gtk_selection_retrieval_report (info,
1543                                       type, format, 
1544                                       buffer, length, event->time);
1545     }
1546   
1547   gdk_property_delete (widget->window, event->property);
1548   
1549   g_free (buffer);
1550   
1551   return TRUE;
1552 }
1553
1554 /*************************************************************
1555  * _gtk_selection_property_notify:
1556  *     Handler for "property_notify_event" signals on windows
1557  *     where a retrieval is currently in process. The selection
1558  *     owner has added more data.
1559  *   arguments:
1560  *     widget:          Widget getting signal
1561  *     event:           Property event structure
1562  *     info:            Information about this retrieval
1563  *   results:
1564  *     was event handled?
1565  *************************************************************/
1566
1567 gboolean
1568 _gtk_selection_property_notify (GtkWidget       *widget,
1569                                 GdkEventProperty *event)
1570 {
1571   GList *tmp_list;
1572   GtkRetrievalInfo *info = NULL;
1573   guchar *new_buffer;
1574   int length;
1575   GdkAtom type;
1576   gint    format;
1577   
1578   g_return_val_if_fail (widget != NULL, FALSE);
1579   g_return_val_if_fail (event != NULL, FALSE);
1580
1581 #if defined(GDK_WINDOWING_WIN32) || defined(GDK_WINDOWING_X11)
1582   if ((event->state != GDK_PROPERTY_NEW_VALUE) ||  /* property was deleted */
1583       (event->atom != gdk_atom_intern ("GDK_SELECTION", FALSE))) /* not the right property */
1584 #endif
1585     return FALSE;
1586   
1587 #ifdef DEBUG_SELECTION
1588   g_message ("PropertyNewValue, property %ld",
1589              event->atom);
1590 #endif
1591   
1592   tmp_list = current_retrievals;
1593   while (tmp_list)
1594     {
1595       info = (GtkRetrievalInfo *)tmp_list->data;
1596       if (info->widget == widget)
1597         break;
1598       tmp_list = tmp_list->next;
1599     }
1600   
1601   if (!tmp_list)                /* No retrieval in progress */
1602     return FALSE;
1603   
1604   if (info->offset < 0)         /* We haven't got the SelectionNotify
1605                                    for this retrieval yet */
1606     return FALSE;
1607   
1608   info->idle_time = 0;
1609   
1610   length = gdk_selection_property_get (widget->window, &new_buffer, 
1611                                        &type, &format);
1612   gdk_property_delete (widget->window, event->atom);
1613   
1614   /* We could do a lot better efficiency-wise by paying attention to
1615      what length was sent in the initial INCR transaction, instead of
1616      doing memory allocation at every step. But its only guaranteed to
1617      be a _lower bound_ (pretty useless!) */
1618   
1619   if (length == 0 || type == GDK_NONE)          /* final zero length portion */
1620     {
1621       /* Info structure will be freed in timeout */
1622       current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1623       g_list_free (tmp_list);
1624       gtk_selection_retrieval_report (info,
1625                                       type, format, 
1626                                       (type == GDK_NONE) ?  NULL : info->buffer,
1627                                       (type == GDK_NONE) ?  -1 : info->offset,
1628                                       info->notify_time);
1629     }
1630   else                          /* append on newly arrived data */
1631     {
1632       if (!info->buffer)
1633         {
1634 #ifdef DEBUG_SELECTION
1635           g_message ("Start - Adding %d bytes at offset 0",
1636                      length);
1637 #endif
1638           info->buffer = new_buffer;
1639           info->offset = length;
1640         }
1641       else
1642         {
1643           
1644 #ifdef DEBUG_SELECTION
1645           g_message ("Appending %d bytes at offset %d",
1646                      length,info->offset);
1647 #endif
1648           /* We copy length+1 bytes to preserve guaranteed null termination */
1649           info->buffer = g_realloc (info->buffer, info->offset+length+1);
1650           memcpy (info->buffer + info->offset, new_buffer, length+1);
1651           info->offset += length;
1652           g_free (new_buffer);
1653         }
1654     }
1655   
1656   return TRUE;
1657 }
1658
1659 /*************************************************************
1660  * gtk_selection_retrieval_timeout:
1661  *     Timeout callback while receiving a selection.
1662  *   arguments:
1663  *     info:    Information about this retrieval
1664  *   results:
1665  *************************************************************/
1666
1667 static gint
1668 gtk_selection_retrieval_timeout (GtkRetrievalInfo *info)
1669 {
1670   GList *tmp_list;
1671   gboolean retval;
1672
1673   GDK_THREADS_ENTER ();
1674   
1675   /* Determine if retrieval has finished by checking if it still in
1676      list of pending retrievals */
1677   
1678   tmp_list = current_retrievals;
1679   while (tmp_list)
1680     {
1681       if (info == (GtkRetrievalInfo *)tmp_list->data)
1682         break;
1683       tmp_list = tmp_list->next;
1684     }
1685   
1686   /* If retrieval is finished */
1687   if (!tmp_list || info->idle_time >= IDLE_ABORT_TIME)
1688     {
1689       if (tmp_list && info->idle_time >= IDLE_ABORT_TIME)
1690         {
1691           current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1692           g_list_free (tmp_list);
1693           gtk_selection_retrieval_report (info, GDK_NONE, 0, NULL, -1, GDK_CURRENT_TIME);
1694         }
1695       
1696       g_free (info->buffer);
1697       g_free (info);
1698       
1699       retval =  FALSE;          /* remove timeout */
1700     }
1701   else
1702     {
1703       info->idle_time++;
1704       
1705       retval =  TRUE;           /* timeout will happen again */
1706     }
1707
1708   GDK_THREADS_LEAVE ();
1709
1710   return retval;
1711 }
1712
1713 /*************************************************************
1714  * gtk_selection_retrieval_report:
1715  *     Emits a "selection_received" signal.
1716  *   arguments:
1717  *     info:      information about the retrieval that completed
1718  *     buffer:    buffer containing data (NULL => errror)
1719  *     time:      timestamp for data in buffer
1720  *   results:
1721  *************************************************************/
1722
1723 static void
1724 gtk_selection_retrieval_report (GtkRetrievalInfo *info,
1725                                 GdkAtom type, gint format, 
1726                                 guchar *buffer, gint length,
1727                                 guint32 time)
1728 {
1729   GtkSelectionData data;
1730   
1731   data.selection = info->selection;
1732   data.target = info->target;
1733   data.type = type;
1734   data.format = format;
1735   
1736   data.length = length;
1737   data.data = buffer;
1738   data.display = gtk_widget_get_display (info->widget);
1739   
1740   g_signal_emit_by_name (info->widget,
1741                          "selection_received", 
1742                          &data, time);
1743 }
1744
1745 /*************************************************************
1746  * gtk_selection_invoke_handler:
1747  *     Finds and invokes handler for specified
1748  *     widget/selection/target combination, calls 
1749  *     gtk_selection_default_handler if none exists.
1750  *
1751  *   arguments:
1752  *     widget:      selection owner
1753  *     data:        selection data [INOUT]
1754  *     time:        time from requeset
1755  *     
1756  *   results:
1757  *     Number of bytes written to buffer, -1 if error
1758  *************************************************************/
1759
1760 static void
1761 gtk_selection_invoke_handler (GtkWidget        *widget,
1762                               GtkSelectionData *data,
1763                               guint             time)
1764 {
1765   GtkTargetList *target_list;
1766   guint info;
1767   
1768
1769   g_return_if_fail (widget != NULL);
1770
1771   target_list = gtk_selection_target_list_get (widget, data->selection);
1772   if (target_list && 
1773       gtk_target_list_find (target_list, data->target, &info))
1774     {
1775       g_signal_emit_by_name (widget,
1776                              "selection_get",
1777                              data,
1778                              info, time);
1779     }
1780   else
1781     gtk_selection_default_handler (widget, data);
1782 }
1783
1784 /*************************************************************
1785  * gtk_selection_default_handler:
1786  *     Handles some default targets that exist for any widget
1787  *     If it can't fit results into buffer, returns -1. This
1788  *     won't happen in any conceivable case, since it would
1789  *     require 1000 selection targets!
1790  *
1791  *   arguments:
1792  *     widget:      selection owner
1793  *     data:        selection data [INOUT]
1794  *
1795  *************************************************************/
1796
1797 static void
1798 gtk_selection_default_handler (GtkWidget        *widget,
1799                                GtkSelectionData *data)
1800 {
1801   if (data->target == gtk_selection_atoms[TIMESTAMP])
1802     {
1803       /* Time which was used to obtain selection */
1804       GList *tmp_list;
1805       GtkSelectionInfo *selection_info;
1806       
1807       tmp_list = current_selections;
1808       while (tmp_list)
1809         {
1810           selection_info = (GtkSelectionInfo *)tmp_list->data;
1811           if ((selection_info->widget == widget) &&
1812               (selection_info->selection == data->selection))
1813             {
1814               gulong time = selection_info->time;
1815
1816               gtk_selection_data_set (data,
1817                                       GDK_SELECTION_TYPE_INTEGER,
1818                                       32,
1819                                       (guchar *)&time,
1820                                       sizeof (time));
1821               return;
1822             }
1823           
1824           tmp_list = tmp_list->next;
1825         }
1826       
1827       data->length = -1;
1828     }
1829   else if (data->target == gtk_selection_atoms[TARGETS])
1830     {
1831       /* List of all targets supported for this widget/selection pair */
1832       GdkAtom *p;
1833       guint count;
1834       GList *tmp_list;
1835       GtkTargetList *target_list;
1836       GtkTargetPair *pair;
1837       
1838       target_list = gtk_selection_target_list_get (widget,
1839                                                    data->selection);
1840       count = g_list_length (target_list->list) + 3;
1841       
1842       data->type = GDK_SELECTION_TYPE_ATOM;
1843       data->format = 32;
1844       data->length = count * sizeof (GdkAtom);
1845       
1846       p = g_new (GdkAtom, count);
1847       data->data = (guchar *)p;
1848       
1849       *p++ = gtk_selection_atoms[TIMESTAMP];
1850       *p++ = gtk_selection_atoms[TARGETS];
1851       *p++ = gtk_selection_atoms[MULTIPLE];
1852       
1853       tmp_list = target_list->list;
1854       while (tmp_list)
1855         {
1856           pair = (GtkTargetPair *)tmp_list->data;
1857           *p++ = pair->target;
1858           
1859           tmp_list = tmp_list->next;
1860         }
1861     }
1862   else
1863     {
1864       data->length = -1;
1865     }
1866 }
1867
1868
1869 GtkSelectionData*
1870 gtk_selection_data_copy (GtkSelectionData *selection_data)
1871 {
1872   GtkSelectionData *new_data;
1873   
1874   g_return_val_if_fail (selection_data != NULL, NULL);
1875   
1876   new_data = g_new (GtkSelectionData, 1);
1877   *new_data = *selection_data;
1878
1879   if (selection_data->data)
1880     {
1881       new_data->data = g_malloc (selection_data->length + 1);
1882       memcpy (new_data->data, selection_data->data, selection_data->length + 1);
1883     }
1884   
1885   return new_data;
1886 }
1887
1888 void
1889 gtk_selection_data_free (GtkSelectionData *data)
1890 {
1891   g_return_if_fail (data != NULL);
1892   
1893   if (data->data)
1894     g_free (data->data);
1895   
1896   g_free (data);
1897 }
1898
1899 GType
1900 gtk_selection_data_get_type (void)
1901 {
1902   static GType our_type = 0;
1903   
1904   if (our_type == 0)
1905     our_type = g_boxed_type_register_static ("GtkSelectionData",
1906                                              (GBoxedCopyFunc) gtk_selection_data_copy,
1907                                              (GBoxedFreeFunc) gtk_selection_data_free);
1908
1909   return our_type;
1910 }
1911
1912 static int 
1913 gtk_selection_bytes_per_item (gint format)
1914 {
1915   switch (format)
1916     {
1917     case 8:
1918       return sizeof (char);
1919       break;
1920     case 16:
1921       return sizeof (short);
1922       break;
1923     case 32:
1924       return sizeof (long);
1925       break;
1926     default:
1927       g_assert_not_reached();
1928     }
1929   return 0;
1930 }