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