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