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