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