]> Pileus Git - ~andy/gtk/blob - gtk/gtkselection.c
7314e3f22f11550ac4e2712160042bc4ea3faf57
[~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 #ifdef GDK_WINDOWING_X11
62 #include "x11/gdkx.h"
63 #endif
64
65 /* #define DEBUG_SELECTION */
66
67 /* Maximum size of a sent chunk, in bytes. Also the default size of
68    our buffers */
69 #ifdef GDK_WINDOWING_WIN32
70 /* No chunks on Win32 */
71 #define GTK_SELECTION_MAX_SIZE G_MAXINT
72 #else
73 #define GTK_SELECTION_MAX_SIZE 4000
74 #endif
75
76 #define IDLE_ABORT_TIME 300
77
78 enum {
79   INCR,
80   MULTIPLE,
81   TARGETS,
82   TIMESTAMP,
83   LAST_ATOM
84 };
85
86 typedef struct _GtkSelectionInfo GtkSelectionInfo;
87 typedef struct _GtkIncrConversion GtkIncrConversion;
88 typedef struct _GtkIncrInfo GtkIncrInfo;
89 typedef struct _GtkRetrievalInfo GtkRetrievalInfo;
90
91 struct _GtkSelectionInfo
92 {
93   GdkAtom        selection;
94   GtkWidget     *widget;        /* widget that owns selection */
95   guint32        time;          /* time used to acquire selection */
96   GdkDisplay    *display;       /* needed in gtk_selection_remove_all */    
97 };
98
99 struct _GtkIncrConversion 
100 {
101   GdkAtom           target;     /* Requested target */
102   GdkAtom           property;   /* Property to store in */
103   GtkSelectionData  data;       /* The data being supplied */
104   gint              offset;     /* Current offset in sent selection.
105                                  *  -1 => All done
106                                  *  -2 => Only the final (empty) portion
107                                  *        left to send */
108 };
109
110 struct _GtkIncrInfo
111 {
112   GdkWindow *requestor;         /* Requestor window - we create a GdkWindow
113                                    so we can receive events */
114   GdkAtom    selection;         /* Selection we're sending */
115   
116   GtkIncrConversion *conversions; /* Information about requested conversions -
117                                    * With MULTIPLE requests (benighted 1980's
118                                    * hardware idea), there can be more than
119                                    * one */
120   gint num_conversions;
121   gint num_incrs;               /* number of remaining INCR style transactions */
122   guint32 idle_time;
123 };
124
125
126 struct _GtkRetrievalInfo
127 {
128   GtkWidget *widget;
129   GdkAtom selection;            /* Selection being retrieved. */
130   GdkAtom target;               /* Form of selection that we requested */
131   guint32 idle_time;            /* Number of seconds since we last heard
132                                    from selection owner */
133   guchar   *buffer;             /* Buffer in which to accumulate results */
134   gint     offset;              /* Current offset in buffer, -1 indicates
135                                    not yet started */
136   guint32 notify_time;          /* Timestamp from SelectionNotify */
137 };
138
139 /* Local Functions */
140 static void gtk_selection_init              (void);
141 static gint gtk_selection_incr_timeout      (GtkIncrInfo      *info);
142 static gint gtk_selection_retrieval_timeout (GtkRetrievalInfo *info);
143 static void gtk_selection_retrieval_report  (GtkRetrievalInfo *info,
144                                              GdkAtom           type,
145                                              gint              format,
146                                              guchar           *buffer,
147                                              gint              length,
148                                              guint32           time);
149 static void gtk_selection_invoke_handler    (GtkWidget        *widget,
150                                              GtkSelectionData *data,
151                                              guint             time);
152 static void gtk_selection_default_handler   (GtkWidget        *widget,
153                                              GtkSelectionData *data);
154 static int  gtk_selection_bytes_per_item    (gint              format);
155
156 /* Local Data */
157 static gint initialize = TRUE;
158 static GList *current_retrievals = NULL;
159 static GList *current_incrs = NULL;
160 static GList *current_selections = NULL;
161
162 static GdkAtom gtk_selection_atoms[LAST_ATOM];
163 static const char gtk_selection_handler_key[] = "gtk-selection-handlers";
164
165 /****************
166  * Target Lists *
167  ****************/
168
169 /*
170  * Target lists
171  */
172
173 GtkTargetList *
174 gtk_target_list_new (const GtkTargetEntry *targets,
175                      guint                 ntargets)
176 {
177   GtkTargetList *result = g_new (GtkTargetList, 1);
178   result->list = NULL;
179   result->ref_count = 1;
180
181   if (targets)
182     gtk_target_list_add_table (result, targets, ntargets);
183   
184   return result;
185 }
186
187 void               
188 gtk_target_list_ref (GtkTargetList *list)
189 {
190   g_return_if_fail (list != NULL);
191
192   list->ref_count++;
193 }
194
195 void               
196 gtk_target_list_unref (GtkTargetList *list)
197 {
198   g_return_if_fail (list != NULL);
199   g_return_if_fail (list->ref_count > 0);
200
201   list->ref_count--;
202   if (list->ref_count == 0)
203     {
204       GList *tmp_list = list->list;
205       while (tmp_list)
206         {
207           GtkTargetPair *pair = tmp_list->data;
208           g_free (pair);
209
210           tmp_list = tmp_list->next;
211         }
212       
213       g_list_free (list->list);
214       g_free (list);
215     }
216 }
217
218 void 
219 gtk_target_list_add (GtkTargetList *list,
220                      GdkAtom            target,
221                      guint              flags,
222                      guint              info)
223 {
224   GtkTargetPair *pair;
225
226   g_return_if_fail (list != NULL);
227   
228   pair = g_new (GtkTargetPair, 1);
229   pair->target = target;
230   pair->flags = flags;
231   pair->info = info;
232
233   list->list = g_list_append (list->list, pair);
234 }
235
236 void               
237 gtk_target_list_add_table (GtkTargetList        *list,
238                            const GtkTargetEntry *targets,
239                            guint                 ntargets)
240 {
241   gint i;
242
243   for (i=ntargets-1; i >= 0; i--)
244     {
245       GtkTargetPair *pair = g_new (GtkTargetPair, 1);
246       pair->target = gdk_atom_intern (targets[i].target, FALSE);
247       pair->flags = targets[i].flags;
248       pair->info = targets[i].info;
249       
250       list->list = g_list_prepend (list->list, pair);
251     }
252 }
253
254 void 
255 gtk_target_list_remove (GtkTargetList *list,
256                         GdkAtom            target)
257 {
258   GList *tmp_list;
259
260   g_return_if_fail (list != NULL);
261
262   tmp_list = list->list;
263   while (tmp_list)
264     {
265       GtkTargetPair *pair = tmp_list->data;
266       
267       if (pair->target == target)
268         {
269           g_free (pair);
270
271           list->list = g_list_remove_link (list->list, tmp_list);
272           g_list_free_1 (tmp_list);
273
274           return;
275         }
276       
277       tmp_list = tmp_list->next;
278     }
279 }
280
281 gboolean
282 gtk_target_list_find (GtkTargetList *list,
283                       GdkAtom        target,
284                       guint         *info)
285 {
286   GList *tmp_list = list->list;
287   while (tmp_list)
288     {
289       GtkTargetPair *pair = tmp_list->data;
290
291       if (pair->target == target)
292         {
293           *info = pair->info;
294           return TRUE;
295         }
296       tmp_list = tmp_list->next;
297     }
298
299   return FALSE;
300 }
301
302 /**
303  * gtk_selection_owner_set_for_display:
304  * @display: the #Gdkdisplay where the selection is set 
305  * @widget: new selection owner (a #GdkWidget), or %NULL.
306  * @selection: an interned atom representing the selection to claim.
307  * @time_: timestamp with which to claim the selection
308  *
309  * Claim ownership of a given selection for a particular widget, or,
310  * if @widget is %NULL, release ownership of the selection.
311  *
312  * Return value: TRUE if the operation succeeded 
313  * 
314  * Since: 2.2
315  */
316 gboolean
317 gtk_selection_owner_set_for_display (GdkDisplay   *display,
318                                      GtkWidget    *widget,
319                                      GdkAtom       selection,
320                                      guint32       time)
321 {
322   GList *tmp_list;
323   GtkWidget *old_owner;
324   GtkSelectionInfo *selection_info = NULL;
325   GdkWindow *window;
326
327   g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
328   g_return_val_if_fail (selection != GDK_NONE, FALSE);
329   g_return_val_if_fail (widget == NULL || GTK_WIDGET_REALIZED (widget), FALSE);
330   g_return_val_if_fail (widget == NULL || gtk_widget_get_display (widget) == display, FALSE);
331   
332   if (widget == NULL)
333     window = NULL;
334   else
335     window = widget->window;
336
337   tmp_list = current_selections;
338   while (tmp_list)
339     {
340       if (((GtkSelectionInfo *)tmp_list->data)->selection == selection)
341         {
342           selection_info = tmp_list->data;
343           break;
344         }
345       
346       tmp_list = tmp_list->next;
347     }
348   
349   if (gdk_selection_owner_set_for_display (display, window, selection, time, TRUE))
350     {
351       old_owner = NULL;
352       
353       if (widget == NULL)
354         {
355           if (selection_info)
356             {
357               old_owner = selection_info->widget;
358               current_selections = g_list_remove_link (current_selections,
359                                                        tmp_list);
360               g_list_free (tmp_list);
361               g_free (selection_info);
362             }
363         }
364       else
365         {
366           if (selection_info == NULL)
367             {
368               selection_info = g_new (GtkSelectionInfo, 1);
369               selection_info->selection = selection;
370               selection_info->widget = widget;
371               selection_info->time = time;
372               selection_info->display = display;
373               current_selections = g_list_prepend (current_selections,
374                                                    selection_info);
375             }
376           else
377             {
378               old_owner = selection_info->widget;
379               selection_info->widget = widget;
380               selection_info->time = time;
381               selection_info->display = display;
382             }
383         }
384       /* If another widget in the application lost the selection,
385        *  send it a GDK_SELECTION_CLEAR event.
386        */
387       if (old_owner && old_owner != widget)
388         {
389           GdkEvent *event = gdk_event_new (GDK_SELECTION_CLEAR);
390           
391           event->selection.window = g_object_ref (old_owner->window);
392           event->selection.selection = selection;
393           event->selection.time = time;
394           
395           gtk_widget_event (old_owner, event);
396
397           gdk_event_free (event);
398         }
399       return TRUE;
400     }
401   else
402     return FALSE;
403 }
404
405 /**
406  * gtk_selection_owner_set:
407  * @widget:  a #GtkWidget, or %NULL.
408  * @selection:  an interned atom representing the selection to claim
409  * @time_: timestamp with which to claim the selection
410  * 
411  * Claims ownership of a given selection for a particular widget,
412  * or, if @widget is %NULL, release ownership of the selection.
413  * 
414  * Return value: %TRUE if the operation succeeded
415  **/
416 gboolean
417 gtk_selection_owner_set (GtkWidget *widget,
418                          GdkAtom    selection,
419                          guint32    time)
420 {
421   GdkDisplay *display;
422   
423   g_return_val_if_fail (widget == NULL || GTK_WIDGET_REALIZED (widget), FALSE);
424   g_return_val_if_fail (selection != GDK_NONE, FALSE);
425
426   if (widget)
427     display = gtk_widget_get_display (widget);
428   else
429     {
430       GTK_NOTE (MULTIHEAD,
431                 g_warning ("gtk_selection_owner_set (NULL,...) is not multihead safe"));
432                  
433       display = gdk_display_get_default ();
434     }
435   
436   return gtk_selection_owner_set_for_display (display, widget,
437                                               selection, time);
438 }
439
440 /*************************************************************
441  * gtk_selection_add_target
442  *     Add specified target to list of supported targets
443  *
444  *   arguments:
445  *     widget:     The widget for which this target applies
446  *     selection:
447  *     target:
448  *     info:       guint to pass to to the selection_get signal 
449  *
450  *   results:
451  *************************************************************/
452
453 typedef struct _GtkSelectionTargetList GtkSelectionTargetList;
454
455 struct _GtkSelectionTargetList {
456   GdkAtom selection;
457   GtkTargetList *list;
458 };
459
460 static GtkTargetList *
461 gtk_selection_target_list_get (GtkWidget    *widget,
462                                GdkAtom       selection)
463 {
464   GtkSelectionTargetList *sellist;
465   GList *tmp_list;
466   GList *lists;
467
468   lists = g_object_get_data (G_OBJECT (widget), gtk_selection_handler_key);
469   
470   tmp_list = lists;
471   while (tmp_list)
472     {
473       sellist = tmp_list->data;
474       if (sellist->selection == selection)
475         return sellist->list;
476       tmp_list = tmp_list->next;
477     }
478
479   sellist = g_new (GtkSelectionTargetList, 1);
480   sellist->selection = selection;
481   sellist->list = gtk_target_list_new (NULL, 0);
482
483   lists = g_list_prepend (lists, sellist);
484   g_object_set_data (G_OBJECT (widget), gtk_selection_handler_key, lists);
485
486   return sellist->list;
487 }
488
489 static void
490 gtk_selection_target_list_remove (GtkWidget    *widget)
491 {
492   GtkSelectionTargetList *sellist;
493   GList *tmp_list;
494   GList *lists;
495
496   lists = g_object_get_data (G_OBJECT (widget), gtk_selection_handler_key);
497   
498   tmp_list = lists;
499   while (tmp_list)
500     {
501       sellist = tmp_list->data;
502
503       gtk_target_list_unref (sellist->list);
504
505       g_free (sellist);
506       tmp_list = tmp_list->next;
507     }
508
509   g_list_free (lists);
510   g_object_set_data (G_OBJECT (widget), gtk_selection_handler_key, NULL);
511 }
512
513 /**
514  * gtk_selection_clear_targets:
515  * @widget:    a #GtkWidget
516  * @selection: an atom representing a selection
517  *
518  * Remove all targets registered for the given selection for the
519  * widget.
520  **/
521 void 
522 gtk_selection_clear_targets (GtkWidget *widget,
523                              GdkAtom    selection)
524 {
525   GtkSelectionTargetList *sellist;
526   GList *tmp_list;
527   GList *lists;
528
529   g_return_if_fail (GTK_IS_WIDGET (widget));
530   g_return_if_fail (selection != GDK_NONE);
531
532   lists = g_object_get_data (G_OBJECT (widget), gtk_selection_handler_key);
533   
534   tmp_list = lists;
535   while (tmp_list)
536     {
537       sellist = tmp_list->data;
538       if (sellist->selection == selection)
539         {
540           lists = g_list_delete_link (lists, tmp_list);
541           gtk_target_list_unref (sellist->list);
542           g_free (sellist);
543
544           break;
545         }
546       
547       tmp_list = tmp_list->next;
548     }
549   
550   g_object_set_data (G_OBJECT (widget), gtk_selection_handler_key, lists);
551 }
552
553 void 
554 gtk_selection_add_target (GtkWidget         *widget, 
555                           GdkAtom            selection,
556                           GdkAtom            target,
557                           guint              info)
558 {
559   GtkTargetList *list;
560
561   g_return_if_fail (GTK_IS_WIDGET (widget));
562   g_return_if_fail (selection != GDK_NONE);
563
564   list = gtk_selection_target_list_get (widget, selection);
565   gtk_target_list_add (list, target, 0, info);
566 }
567
568 void 
569 gtk_selection_add_targets (GtkWidget            *widget, 
570                            GdkAtom               selection,
571                            const GtkTargetEntry *targets,
572                            guint                 ntargets)
573 {
574   GtkTargetList *list;
575
576   g_return_if_fail (GTK_IS_WIDGET (widget));
577   g_return_if_fail (selection != GDK_NONE);
578   g_return_if_fail (targets != NULL);
579   
580   list = gtk_selection_target_list_get (widget, selection);
581   gtk_target_list_add_table (list, targets, ntargets);
582 }
583
584
585 /*************************************************************
586  * gtk_selection_remove_all:
587  *     Removes all handlers and unsets ownership of all 
588  *     selections for a widget. Called when widget is being
589  *     destroyed
590  *     
591  *   arguments:
592  *     widget:    The widget
593  *   results:
594  *************************************************************/
595
596 void
597 gtk_selection_remove_all (GtkWidget *widget)
598 {
599   GList *tmp_list;
600   GList *next;
601   GtkSelectionInfo *selection_info;
602   
603   /* Remove pending requests/incrs for this widget */
604   
605   tmp_list = current_retrievals;
606   while (tmp_list)
607     {
608       next = tmp_list->next;
609       if (((GtkRetrievalInfo *)tmp_list->data)->widget == widget)
610         {
611           current_retrievals = g_list_remove_link (current_retrievals, 
612                                                    tmp_list);
613           /* structure will be freed in timeout */
614           g_list_free (tmp_list);
615         }
616       tmp_list = next;
617     }
618   
619   /* Disclaim ownership of any selections */
620   
621   tmp_list = current_selections;
622   while (tmp_list)
623     {
624       next = tmp_list->next;
625       selection_info = (GtkSelectionInfo *)tmp_list->data;
626       
627       if (selection_info->widget == widget)
628         {       
629           gdk_selection_owner_set_for_display (selection_info->display,
630                                                NULL, 
631                                                selection_info->selection,
632                                                GDK_CURRENT_TIME, FALSE);
633           current_selections = g_list_remove_link (current_selections,
634                                                    tmp_list);
635           g_list_free (tmp_list);
636           g_free (selection_info);
637         }
638       
639       tmp_list = next;
640     }
641
642   /* Remove all selection lists */
643   gtk_selection_target_list_remove (widget);
644 }
645
646 /*************************************************************
647  * gtk_selection_convert:
648  *     Request the contents of a selection. When received, 
649  *     a "selection_received" signal will be generated.
650  *
651  *   arguments:
652  *     widget:     The widget which acts as requestor
653  *     selection:  Which selection to get
654  *     target:     Form of information desired (e.g., STRING)
655  *     time:       Time of request (usually of triggering event)
656  *                 In emergency, you could use GDK_CURRENT_TIME
657  *
658  *   results:
659  *     TRUE if requested succeeded. FALSE if we could not process
660  *     request. (e.g., there was already a request in process for
661  *     this widget). 
662  *************************************************************/
663
664 gboolean
665 gtk_selection_convert (GtkWidget *widget, 
666                        GdkAtom    selection, 
667                        GdkAtom    target,
668                        guint32    time)
669 {
670   GtkRetrievalInfo *info;
671   GList *tmp_list;
672   GdkWindow *owner_window;
673   GdkDisplay *display;
674   
675   g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
676   g_return_val_if_fail (selection != GDK_NONE, FALSE);
677   
678   if (initialize)
679     gtk_selection_init ();
680   
681   if (!GTK_WIDGET_REALIZED (widget))
682     gtk_widget_realize (widget);
683   
684   /* Check to see if there are already any retrievals in progress for
685      this widget. If we changed GDK to use the selection for the 
686      window property in which to store the retrieved information, then
687      we could support multiple retrievals for different selections.
688      This might be useful for DND. */
689   
690   tmp_list = current_retrievals;
691   while (tmp_list)
692     {
693       info = (GtkRetrievalInfo *)tmp_list->data;
694       if (info->widget == widget)
695         return FALSE;
696       tmp_list = tmp_list->next;
697     }
698   
699   info = g_new (GtkRetrievalInfo, 1);
700   
701   info->widget = widget;
702   info->selection = selection;
703   info->target = target;
704   info->idle_time = 0;
705   info->buffer = NULL;
706   info->offset = -1;
707   
708   /* Check if this process has current owner. If so, call handler
709      procedure directly to avoid deadlocks with INCR. */
710
711   display = gtk_widget_get_display (widget);
712   owner_window = gdk_selection_owner_get_for_display (display, selection);
713   
714   if (owner_window != NULL)
715     {
716       GtkWidget *owner_widget;
717       GtkSelectionData selection_data;
718       
719       selection_data.selection = selection;
720       selection_data.target = target;
721       selection_data.data = NULL;
722       selection_data.length = -1;
723       selection_data.display = display;
724       
725       gdk_window_get_user_data (owner_window, (gpointer *)&owner_widget);
726       
727       if (owner_widget != NULL)
728         {
729           gtk_selection_invoke_handler (owner_widget, 
730                                         &selection_data,
731                                         time);
732           
733           gtk_selection_retrieval_report (info,
734                                           selection_data.type, 
735                                           selection_data.format,
736                                           selection_data.data,
737                                           selection_data.length,
738                                           time);
739           
740           g_free (selection_data.data);
741           
742           g_free (info);
743           return TRUE;
744         }
745     }
746   
747   /* Otherwise, we need to go through X */
748   
749   current_retrievals = g_list_append (current_retrievals, info);
750   gdk_selection_convert (widget->window, selection, target, time);
751   g_timeout_add (1000, (GSourceFunc) gtk_selection_retrieval_timeout, info);
752   
753   return TRUE;
754 }
755
756 /*************************************************************
757  * gtk_selection_data_set:
758  *     Store new data into a GtkSelectionData object. Should
759  *     _only_ by called from a selection handler callback.
760  *     Null terminates the stored data.
761  *   arguments:
762  *     type:    the type of selection data
763  *     format:  format (number of bits in a unit)
764  *     data:    pointer to the data (will be copied)
765  *     length:  length of the data
766  *   results:
767  *************************************************************/
768
769 void 
770 gtk_selection_data_set (GtkSelectionData *selection_data,
771                         GdkAtom           type,
772                         gint              format,
773                         const guchar     *data,
774                         gint              length)
775 {
776   if (selection_data->data)
777     g_free (selection_data->data);
778   
779   selection_data->type = type;
780   selection_data->format = format;
781   
782   if (data)
783     {
784       selection_data->data = g_new (guchar, length+1);
785       memcpy (selection_data->data, data, length);
786       selection_data->data[length] = 0;
787     }
788   else
789     {
790       g_return_if_fail (length <= 0);
791       
792       if (length < 0)
793         selection_data->data = NULL;
794       else
795         selection_data->data = g_strdup("");
796     }
797   
798   selection_data->length = length;
799 }
800
801 static GdkAtom utf8_atom;
802 static GdkAtom text_atom;
803 static GdkAtom ctext_atom;
804
805 static void 
806 init_atoms (void)
807 {
808   if (!utf8_atom)
809     {
810       utf8_atom = gdk_atom_intern ("UTF8_STRING", FALSE);
811       text_atom = gdk_atom_intern ("TEXT", FALSE);
812       ctext_atom = gdk_atom_intern ("COMPOUND_TEXT", FALSE);
813     }
814 }
815
816 static gboolean
817 selection_set_string (GtkSelectionData *selection_data,
818                       const gchar      *str,
819                       gint              len)
820 {
821   gchar *tmp = g_strndup (str, len);
822   gchar *latin1 = gdk_utf8_to_string_target (tmp);
823   g_free (tmp);
824   
825   if (latin1)
826     {
827       gtk_selection_data_set (selection_data,
828                               GDK_SELECTION_TYPE_STRING,
829                               8, latin1, strlen (latin1));
830       g_free (latin1);
831       
832       return TRUE;
833     }
834   else
835     return FALSE;
836 }
837
838 static gboolean
839 selection_set_compound_text (GtkSelectionData *selection_data,
840                              const gchar      *str,
841                              gint              len)
842 {
843   gchar *tmp;
844   guchar *text;
845   GdkAtom encoding;
846   gint format;
847   gint new_length;
848   gboolean result = FALSE;
849   
850   tmp = g_strndup (str, len);
851   if (gdk_utf8_to_compound_text_for_display (selection_data->display, tmp,
852                                              &encoding, &format, &text, &new_length))
853     {
854       gtk_selection_data_set (selection_data, encoding, format, text, new_length);
855       gdk_free_compound_text (text);
856       
857       result = TRUE;
858     }
859
860   g_free (tmp);
861
862   return result;
863 }
864
865 /**
866  * gtk_selection_data_set_text:
867  * @selection_data: a #GtkSelectionData
868  * @str: a UTF-8 string
869  * @len: the length of @str, or -1 if @str is nul-terminated.
870  * 
871  * Sets the contents of the selection from a UTF-8 encoded string.
872  * The string is converted to the form determined by
873  * @selection_data->target.
874  * 
875  * Return value: %TRUE if the selection was successfully set,
876  *   otherwise %FALSE.
877  **/
878 gboolean
879 gtk_selection_data_set_text (GtkSelectionData     *selection_data,
880                              const gchar          *str,
881                              gint                  len)
882 {
883   if (len < 0)
884     len = strlen (str);
885   
886   init_atoms ();
887
888   if (selection_data->target == utf8_atom)
889     {
890       gtk_selection_data_set (selection_data,
891                               utf8_atom,
892                               8, (guchar *)str, len);
893       return TRUE;
894     }
895   else if (selection_data->target == GDK_TARGET_STRING)
896     {
897       return selection_set_string (selection_data, str, len);
898     }
899   else if (selection_data->target == ctext_atom ||
900            selection_data->target == text_atom)
901     {
902       if (selection_set_compound_text (selection_data, str, len))
903         return TRUE;
904       else if (selection_data->target == text_atom)
905         return selection_set_string (selection_data, str, len);
906     }
907
908   return FALSE;
909 }
910
911 /**
912  * gtk_selection_data_get_text:
913  * @selection_data: a #GtkSelectionData
914  * 
915  * Gets the contents of the selection data as a UTF-8 string.
916  * 
917  * Return value: if the selection data contained a recognized
918  *   text type and it could be converted to UTF-8, a newly allocated
919  *   string containing the converted text, otherwise %NULL.
920  *   If the result is non-%NULL it must be freed with g_free().
921  **/
922 guchar *
923 gtk_selection_data_get_text (GtkSelectionData *selection_data)
924 {
925   guchar *result = NULL;
926
927   init_atoms ();
928   
929   if (selection_data->length >= 0 &&
930       (selection_data->type == GDK_TARGET_STRING ||
931        selection_data->type == ctext_atom ||
932        selection_data->type == utf8_atom))
933     {
934       gchar **list;
935       gint i;
936       gint count = gdk_text_property_to_utf8_list_for_display (selection_data->display,
937                                                                selection_data->type,
938                                                                selection_data->format, 
939                                                                selection_data->data,
940                                                                selection_data->length,
941                                                                &list);
942       if (count > 0)
943         result = list[0];
944
945       for (i = 1; i < count; i++)
946         g_free (list[i]);
947       g_free (list);
948     }
949
950   return result;
951 }
952
953 /**
954  * gtk_selection_data_get_targets:
955  * @selection_data: a #GtkSelectionData object
956  * @targets: location to store an array of targets. The result
957  *           stored here must be freed with g_free().
958  * @n_atoms: location to store number of items in @targets.
959  * 
960  * Gets the contents of @selection_data as an array of targets.
961  * This can be used to interpret the results of getting
962  * the standard TARGETS target that is always supplied for
963  * any selection.
964  * 
965  * Return value: %TRUE if @selection_data contains a valid
966  *    array of targets, otherwise %FALSE.
967  **/
968 gboolean
969 gtk_selection_data_get_targets (GtkSelectionData  *selection_data,
970                                 GdkAtom          **targets,
971                                 gint              *n_atoms)
972 {
973   if (selection_data->length >= 0 &&
974       selection_data->format == 32 &&
975       selection_data->type == GDK_SELECTION_TYPE_ATOM)
976     {
977       if (targets)
978         *targets = g_memdup (selection_data->data, selection_data->length);
979       if (n_atoms)
980         *n_atoms = selection_data->length / sizeof (GdkAtom);
981
982       return TRUE;
983     }
984   else
985     {
986       if (targets)
987         *targets = NULL;
988       if (n_atoms)
989         *n_atoms = -1;
990
991       return FALSE;
992     }
993 }
994
995 /**
996  * gtk_selection_data_targets_include_text:
997  * @selection_data: a #GtkSelectionData object
998  * 
999  * Given a #GtkSelectionData object holding a list of targets,
1000  * determines if any of the targets in @targets can be used to
1001  * provide text.
1002  * 
1003  * Return value: %TRUE if @selection_data holds a list of targets,
1004  *   and a suitable target for text is included, otherwise %FALSE.
1005  **/
1006 gboolean
1007 gtk_selection_data_targets_include_text (GtkSelectionData *selection_data)
1008 {
1009   GdkAtom *targets;
1010   gint n_targets;
1011   gint i;
1012   gboolean result = FALSE;
1013
1014   if (gtk_selection_data_get_targets (selection_data, &targets, &n_targets))
1015     {
1016       for (i=0; i < n_targets; i++)
1017         {
1018           if (targets[i] == gdk_atom_intern ("STRING", FALSE) ||
1019               targets[i] == gdk_atom_intern ("TEXT", FALSE) ||
1020               targets[i] == gdk_atom_intern ("COMPOUND_TEXT", FALSE) ||
1021               targets[i] == gdk_atom_intern ("UTF8_STRING", FALSE))
1022             result = TRUE;
1023         }
1024
1025       g_free (targets);
1026     }
1027
1028   return result;
1029 }
1030           
1031 /*************************************************************
1032  * gtk_selection_init:
1033  *     Initialize local variables
1034  *   arguments:
1035  *     
1036  *   results:
1037  *************************************************************/
1038
1039 static void
1040 gtk_selection_init (void)
1041 {
1042   gtk_selection_atoms[INCR] = gdk_atom_intern ("INCR", FALSE);
1043   gtk_selection_atoms[MULTIPLE] = gdk_atom_intern ("MULTIPLE", FALSE);
1044   gtk_selection_atoms[TIMESTAMP] = gdk_atom_intern ("TIMESTAMP", FALSE);
1045   gtk_selection_atoms[TARGETS] = gdk_atom_intern ("TARGETS", FALSE);
1046
1047   initialize = FALSE;
1048 }
1049
1050 /**
1051  * gtk_selection_clear:
1052  * @widget: a #GtkWidget
1053  * @event: the event
1054  * 
1055  * The default handler for the GtkWidget::selection_clear_event
1056  * signal. 
1057  * 
1058  * Return value: %TRUE if the event was handled, otherwise false
1059  * 
1060  * Since: 2.2
1061  *
1062  * Deprecated: Instead of calling this function, chain up from
1063  * your selection_clear_event handler. Calling this function
1064  * from any other context is illegal. 
1065  **/
1066 gboolean
1067 gtk_selection_clear (GtkWidget         *widget,
1068                      GdkEventSelection *event)
1069 {
1070   /* Note that we filter clear events in gdkselection-x11.c, so
1071    * that we only will get here if the clear event actually
1072    * represents a change that we didn't do ourself.
1073    */
1074   GList *tmp_list;
1075   GtkSelectionInfo *selection_info = NULL;
1076   
1077   tmp_list = current_selections;
1078   while (tmp_list)
1079     {
1080       selection_info = (GtkSelectionInfo *)tmp_list->data;
1081       
1082       if ((selection_info->selection == event->selection) &&
1083           (selection_info->widget == widget))
1084         break;
1085       
1086       tmp_list = tmp_list->next;
1087     }
1088   
1089   if (tmp_list)
1090     {
1091       current_selections = g_list_remove_link (current_selections, tmp_list);
1092       g_list_free (tmp_list);
1093       g_free (selection_info);
1094     }
1095   
1096   return TRUE;
1097 }
1098
1099
1100 /*************************************************************
1101  * _gtk_selection_request:
1102  *     Handler for "selection_request_event" 
1103  *   arguments:
1104  *     widget:
1105  *     event:
1106  *   results:
1107  *************************************************************/
1108
1109 gboolean
1110 _gtk_selection_request (GtkWidget *widget,
1111                         GdkEventSelection *event)
1112 {
1113   GdkDisplay *display = gtk_widget_get_display (widget);
1114   GtkIncrInfo *info;
1115   GList *tmp_list;
1116   int i;
1117   
1118   if (initialize)
1119     gtk_selection_init ();
1120   
1121   /* Check if we own selection */
1122   
1123   tmp_list = current_selections;
1124   while (tmp_list)
1125     {
1126       GtkSelectionInfo *selection_info = (GtkSelectionInfo *)tmp_list->data;
1127       
1128       if ((selection_info->selection == event->selection) &&
1129           (selection_info->widget == widget))
1130         break;
1131       
1132       tmp_list = tmp_list->next;
1133     }
1134   
1135   if (tmp_list == NULL)
1136     return FALSE;
1137   
1138   info = g_new (GtkIncrInfo, 1);
1139
1140   g_object_ref (widget);
1141   
1142   info->selection = event->selection;
1143   info->num_incrs = 0;
1144   
1145   /* Create GdkWindow structure for the requestor */
1146   
1147   info->requestor = gdk_window_lookup_for_display (display,
1148                                                    event->requestor);
1149   if (!info->requestor)
1150     info->requestor = gdk_window_foreign_new_for_display (display,
1151                                                           event->requestor);
1152   
1153   /* Determine conversions we need to perform */
1154   
1155   if (event->target == gtk_selection_atoms[MULTIPLE])
1156     {
1157       GdkAtom  type;
1158       guchar  *mult_atoms;
1159       gint     format;
1160       gint     length;
1161       
1162       mult_atoms = NULL;
1163       
1164       gdk_error_trap_push ();
1165       if (!gdk_property_get (info->requestor, event->property, GDK_NONE, /* AnyPropertyType */
1166                              0, GTK_SELECTION_MAX_SIZE, FALSE,
1167                              &type, &format, &length, &mult_atoms))
1168         {
1169           gdk_selection_send_notify_for_display (display,
1170                                                  event->requestor, 
1171                                                  event->selection,
1172                                                  event->target, 
1173                                                  GDK_NONE, 
1174                                                  event->time);
1175           g_free (mult_atoms);
1176           g_free (info);
1177           return TRUE;
1178         }
1179       gdk_error_trap_pop ();
1180
1181       /* This is annoying; the ICCCM doesn't specify the property type
1182        * used for the property contents, so the autoconversion for
1183        * ATOM / ATOM_PAIR in GDK doesn't work properly.
1184        */
1185 #ifdef GDK_WINDOWING_X11
1186       if (type != GDK_SELECTION_TYPE_ATOM &&
1187           type != gdk_atom_intern ("ATOM_PAIR", FALSE))
1188         {
1189           info->num_conversions = length / (2*sizeof (glong));
1190           info->conversions = g_new (GtkIncrConversion, info->num_conversions);
1191           
1192           for (i=0; i<info->num_conversions; i++)
1193             {
1194               info->conversions[i].target = gdk_x11_xatom_to_atom_for_display (display,
1195                                                                                ((glong *)mult_atoms)[2*i]);
1196               info->conversions[i].property = gdk_x11_xatom_to_atom_for_display (display,
1197                                                                                  ((glong *)mult_atoms)[2*i + 1]);
1198             }
1199         }
1200       else
1201 #endif
1202         {
1203           info->num_conversions = length / (2*sizeof (GdkAtom));
1204           info->conversions = g_new (GtkIncrConversion, info->num_conversions);
1205           
1206           for (i=0; i<info->num_conversions; i++)
1207             {
1208               info->conversions[i].target = ((GdkAtom *)mult_atoms)[2*i];
1209               info->conversions[i].property = ((GdkAtom *)mult_atoms)[2*i+1];
1210             }
1211         }
1212     }
1213   else                          /* only a single conversion */
1214     {
1215       info->conversions = g_new (GtkIncrConversion, 1);
1216       info->num_conversions = 1;
1217       info->conversions[0].target = event->target;
1218       info->conversions[0].property = event->property;
1219     }
1220   
1221   /* Loop through conversions and determine which of these are big
1222      enough to require doing them via INCR */
1223   for (i=0; i<info->num_conversions; i++)
1224     {
1225       GtkSelectionData data;
1226       glong items;
1227       
1228       data.selection = event->selection;
1229       data.target = info->conversions[i].target;
1230       data.data = NULL;
1231       data.length = -1;
1232       data.display = gtk_widget_get_display (widget);
1233       
1234 #ifdef DEBUG_SELECTION
1235       g_message ("Selection %ld, target %ld (%s) requested by 0x%x (property = %ld)",
1236                  event->selection, info->conversions[i].target,
1237                  gdk_atom_name (info->conversions[i].target),
1238                  event->requestor, event->property);
1239 #endif
1240       
1241       gtk_selection_invoke_handler (widget, &data, event->time);
1242       
1243       if (data.length < 0)
1244         {
1245           info->conversions[i].property = GDK_NONE;
1246           continue;
1247         }
1248       
1249       g_return_val_if_fail ((data.format >= 8) && (data.format % 8 == 0), FALSE);
1250       
1251       items = data.length / gtk_selection_bytes_per_item (data.format);
1252       
1253       if (data.length > GTK_SELECTION_MAX_SIZE)
1254         {
1255           /* Sending via INCR */
1256           
1257           info->conversions[i].offset = 0;
1258           info->conversions[i].data = data;
1259           info->num_incrs++;
1260           
1261           gdk_property_change (info->requestor, 
1262                                info->conversions[i].property,
1263                                gtk_selection_atoms[INCR],
1264                                32,
1265                                GDK_PROP_MODE_REPLACE,
1266                                (guchar *)&items, 1);
1267         }
1268       else
1269         {
1270           info->conversions[i].offset = -1;
1271           
1272           gdk_property_change (info->requestor, 
1273                                info->conversions[i].property,
1274                                data.type,
1275                                data.format,
1276                                GDK_PROP_MODE_REPLACE,
1277                                data.data, items);
1278           
1279           g_free (data.data);
1280         }
1281     }
1282   
1283   /* If we have some INCR's, we need to send the rest of the data in
1284      a callback */
1285   
1286   if (info->num_incrs > 0)
1287     {
1288       /* FIXME: this could be dangerous if window doesn't still
1289          exist */
1290       
1291 #ifdef DEBUG_SELECTION
1292       g_message ("Starting INCR...");
1293 #endif
1294       
1295       gdk_window_set_events (info->requestor,
1296                              gdk_window_get_events (info->requestor) |
1297                              GDK_PROPERTY_CHANGE_MASK);
1298       current_incrs = g_list_append (current_incrs, info);
1299       g_timeout_add (1000, (GSourceFunc) gtk_selection_incr_timeout, info);
1300     }
1301   
1302   /* If it was a MULTIPLE request, set the property to indicate which
1303      conversions succeeded */
1304   if (event->target == gtk_selection_atoms[MULTIPLE])
1305     {
1306       GdkAtom *mult_atoms = g_new (GdkAtom, 2 * info->num_conversions);
1307       for (i = 0; i < info->num_conversions; i++)
1308         {
1309           mult_atoms[2*i] = info->conversions[i].target;
1310           mult_atoms[2*i+1] = info->conversions[i].property;
1311         }
1312       
1313       gdk_property_change (info->requestor, event->property,
1314                            gdk_atom_intern ("ATOM_PAIR", FALSE), 32, 
1315                            GDK_PROP_MODE_REPLACE,
1316                            (guchar *)mult_atoms, 2*info->num_conversions);
1317       g_free (mult_atoms);
1318     }
1319
1320   if (info->num_conversions == 1 &&
1321       info->conversions[0].property == GDK_NONE)
1322     {
1323       /* Reject the entire conversion */
1324       gdk_selection_send_notify_for_display (gtk_widget_get_display (widget),
1325                                              event->requestor, 
1326                                              event->selection, 
1327                                              event->target, 
1328                                              GDK_NONE, 
1329                                              event->time);
1330     }
1331   else
1332     {
1333       gdk_selection_send_notify_for_display (gtk_widget_get_display (widget),
1334                                              event->requestor, 
1335                                              event->selection,
1336                                              event->target,
1337                                              event->property, 
1338                                              event->time);
1339     }
1340
1341   if (info->num_incrs == 0)
1342     {
1343       g_free (info->conversions);
1344       g_free (info);
1345     }
1346
1347   g_object_unref (widget);
1348   
1349   return TRUE;
1350 }
1351
1352 /*************************************************************
1353  * _gtk_selection_incr_event:
1354  *     Called whenever an PropertyNotify event occurs for an 
1355  *     GdkWindow with user_data == NULL. These will be notifications
1356  *     that a window we are sending the selection to via the
1357  *     INCR protocol has deleted a property and is ready for
1358  *     more data.
1359  *
1360  *   arguments:
1361  *     window:  the requestor window
1362  *     event:   the property event structure
1363  *
1364  *   results:
1365  *************************************************************/
1366
1367 gboolean
1368 _gtk_selection_incr_event (GdkWindow       *window,
1369                            GdkEventProperty *event)
1370 {
1371   GList *tmp_list;
1372   GtkIncrInfo *info = NULL;
1373   gint num_bytes;
1374   guchar *buffer;
1375   
1376   int i;
1377   
1378   if (event->state != GDK_PROPERTY_DELETE)
1379     return FALSE;
1380   
1381 #ifdef DEBUG_SELECTION
1382   g_message ("PropertyDelete, property %ld", event->atom);
1383 #endif
1384   
1385   /* Now find the appropriate ongoing INCR */
1386   tmp_list = current_incrs;
1387   while (tmp_list)
1388     {
1389       info = (GtkIncrInfo *)tmp_list->data;
1390       if (info->requestor == event->window)
1391         break;
1392       
1393       tmp_list = tmp_list->next;
1394     }
1395   
1396   if (tmp_list == NULL)
1397     return FALSE;
1398   
1399   /* Find out which target this is for */
1400   for (i=0; i<info->num_conversions; i++)
1401     {
1402       if (info->conversions[i].property == event->atom &&
1403           info->conversions[i].offset != -1)
1404         {
1405           int bytes_per_item;
1406           
1407           info->idle_time = 0;
1408           
1409           if (info->conversions[i].offset == -2) /* only the last 0-length
1410                                                     piece*/
1411             {
1412               num_bytes = 0;
1413               buffer = NULL;
1414             }
1415           else
1416             {
1417               num_bytes = info->conversions[i].data.length -
1418                 info->conversions[i].offset;
1419               buffer = info->conversions[i].data.data + 
1420                 info->conversions[i].offset;
1421               
1422               if (num_bytes > GTK_SELECTION_MAX_SIZE)
1423                 {
1424                   num_bytes = GTK_SELECTION_MAX_SIZE;
1425                   info->conversions[i].offset += GTK_SELECTION_MAX_SIZE;
1426                 }
1427               else
1428                 info->conversions[i].offset = -2;
1429             }
1430 #ifdef DEBUG_SELECTION
1431           g_message ("INCR: put %d bytes (offset = %d) into window 0x%lx , property %ld",
1432                      num_bytes, info->conversions[i].offset, 
1433                      GDK_WINDOW_XWINDOW(info->requestor), event->atom);
1434 #endif
1435
1436           bytes_per_item = gtk_selection_bytes_per_item (info->conversions[i].data.format);
1437           gdk_property_change (info->requestor, event->atom,
1438                                info->conversions[i].data.type,
1439                                info->conversions[i].data.format,
1440                                GDK_PROP_MODE_REPLACE,
1441                                buffer,
1442                                num_bytes / bytes_per_item);
1443           
1444           if (info->conversions[i].offset == -2)
1445             {
1446               g_free (info->conversions[i].data.data);
1447               info->conversions[i].data.data = NULL;
1448             }
1449           
1450           if (num_bytes == 0)
1451             {
1452               info->num_incrs--;
1453               info->conversions[i].offset = -1;
1454             }
1455         }
1456       break;
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 }