]> Pileus Git - ~andy/gtk/blob - gtk/gtkselection.c
Assign xtype using gdk_x11_atom_to_xatom_for_display () when type !=
[~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 /**
817  * gtk_selection_data_set_text:
818  * @selection_data: a #GtkSelectionData
819  * @str: a UTF-8 string
820  * @len: the length of @str, or -1 if @str is nul-terminated.
821  * 
822  * Sets the contents of the selection from a UTF-8 encoded string.
823  * The string is converted to the form determined by
824  * @selection_data->target.
825  * 
826  * Return value: %TRUE if the selection was successfully set,
827  *   otherwise %FALSE.
828  **/
829 gboolean
830 gtk_selection_data_set_text (GtkSelectionData     *selection_data,
831                              const gchar          *str,
832                              gint                  len)
833 {
834   gboolean result = FALSE;
835   
836   if (len < 0)
837     len = strlen (str);
838   
839   init_atoms ();
840
841   if (selection_data->target == utf8_atom)
842     {
843       gtk_selection_data_set (selection_data,
844                               utf8_atom,
845                               8, (guchar *)str, len);
846       result = TRUE;
847     }
848   else if (selection_data->target == GDK_TARGET_STRING)
849     {
850       gchar *tmp = g_strndup (str, len);
851       gchar *latin1 = gdk_utf8_to_string_target (tmp);
852       g_free (tmp);
853
854       if (latin1)
855         {
856           gtk_selection_data_set (selection_data,
857                                   GDK_SELECTION_TYPE_STRING,
858                                   8, latin1, strlen (latin1));
859           g_free (latin1);
860           
861           result = TRUE;
862         }
863
864     }
865   else if (selection_data->target == ctext_atom ||
866            selection_data->target == text_atom)
867     {
868       gchar *tmp;
869       guchar *text;
870       GdkAtom encoding;
871       gint format;
872       gint new_length;
873
874       tmp = g_strndup (str, len);
875       if (gdk_utf8_to_compound_text_for_display (selection_data->display, tmp,
876                                                  &encoding, &format, &text, &new_length))
877         {
878           gtk_selection_data_set (selection_data, encoding, format, text, new_length);
879           gdk_free_compound_text (text);
880
881           result = TRUE;
882         }
883
884       g_free (tmp);
885     }
886   
887   return result;
888 }
889
890 /**
891  * gtk_selection_data_get_text:
892  * @selection_data: a #GtkSelectionData
893  * 
894  * Gets the contents of the selection data as a UTF-8 string.
895  * 
896  * Return value: if the selection data contained a recognized
897  *   text type and it could be converted to UTF-8, a newly allocated
898  *   string containing the converted text, otherwise %NULL.
899  *   If the result is non-%NULL it must be freed with g_free().
900  **/
901 guchar *
902 gtk_selection_data_get_text (GtkSelectionData *selection_data)
903 {
904   guchar *result = NULL;
905
906   init_atoms ();
907   
908   if (selection_data->length >= 0 &&
909       (selection_data->type == GDK_TARGET_STRING ||
910        selection_data->type == ctext_atom ||
911        selection_data->type == utf8_atom))
912     {
913       gchar **list;
914       gint i;
915       gint count = gdk_text_property_to_utf8_list_for_display (selection_data->display,
916                                                                selection_data->type,
917                                                                selection_data->format, 
918                                                                selection_data->data,
919                                                                selection_data->length,
920                                                                &list);
921       if (count > 0)
922         result = list[0];
923
924       for (i = 1; i < count; i++)
925         g_free (list[i]);
926       g_free (list);
927     }
928
929   return result;
930 }
931
932 /**
933  * gtk_selection_data_get_targets:
934  * @selection_data: a #GtkSelectionData object
935  * @targets: location to store an array of targets. The result
936  *           stored here must be freed with g_free().
937  * @n_atoms: location to store number of items in @targets.
938  * 
939  * Gets the contents of @selection_data as an array of targets.
940  * This can be used to interpret the results of getting
941  * the standard TARGETS target that is always supplied for
942  * any selection.
943  * 
944  * Return value: %TRUE if @selection_data contains a valid
945  *    array of targets, otherwise %FALSE.
946  **/
947 gboolean
948 gtk_selection_data_get_targets (GtkSelectionData  *selection_data,
949                                 GdkAtom          **targets,
950                                 gint              *n_atoms)
951 {
952   if (selection_data->length >= 0 &&
953       selection_data->format == 32 &&
954       selection_data->type == GDK_SELECTION_TYPE_ATOM)
955     {
956       if (targets)
957         *targets = g_memdup (selection_data->data, selection_data->length);
958       if (n_atoms)
959         *n_atoms = selection_data->length / sizeof (GdkAtom);
960
961       return TRUE;
962     }
963   else
964     {
965       if (targets)
966         *targets = NULL;
967       if (n_atoms)
968         *n_atoms = -1;
969
970       return FALSE;
971     }
972 }
973
974 /**
975  * gtk_selection_data_targets_include_text:
976  * @selection_data: a #GtkSelectionData object
977  * 
978  * Given a #GtkSelectionData object holding a list of targets,
979  * determines if any of the targets in @targets can be used to
980  * provide text.
981  * 
982  * Return value: %TRUE if @selection_data holds a list of targets,
983  *   and a suitable target for text is included, otherwise %FALSE.
984  **/
985 gboolean
986 gtk_selection_data_targets_include_text (GtkSelectionData *selection_data)
987 {
988   GdkAtom *targets;
989   gint n_targets;
990   gint i;
991   gboolean result = FALSE;
992
993   if (gtk_selection_data_get_targets (selection_data, &targets, &n_targets))
994     {
995       for (i=0; i < n_targets; i++)
996         {
997           if (targets[i] == gdk_atom_intern ("STRING", FALSE) ||
998               targets[i] == gdk_atom_intern ("TEXT", FALSE) ||
999               targets[i] == gdk_atom_intern ("COMPOUND_TEXT", FALSE) ||
1000               targets[i] == gdk_atom_intern ("UTF8_STRING", FALSE))
1001             result = TRUE;
1002         }
1003
1004       g_free (targets);
1005     }
1006
1007   return result;
1008 }
1009           
1010 /*************************************************************
1011  * gtk_selection_init:
1012  *     Initialize local variables
1013  *   arguments:
1014  *     
1015  *   results:
1016  *************************************************************/
1017
1018 static void
1019 gtk_selection_init (void)
1020 {
1021   gtk_selection_atoms[INCR] = gdk_atom_intern ("INCR", FALSE);
1022   gtk_selection_atoms[MULTIPLE] = gdk_atom_intern ("MULTIPLE", FALSE);
1023   gtk_selection_atoms[TIMESTAMP] = gdk_atom_intern ("TIMESTAMP", FALSE);
1024   gtk_selection_atoms[TARGETS] = gdk_atom_intern ("TARGETS", FALSE);
1025
1026   initialize = FALSE;
1027 }
1028
1029 /**
1030  * gtk_selection_clear:
1031  * @widget: a #GtkWidget
1032  * @event: the event
1033  * 
1034  * The default handler for the GtkWidget::selection_clear_event
1035  * signal. 
1036  * 
1037  * Return value: %TRUE if the event was handled, otherwise false
1038  * 
1039  * Since: 2.2
1040  *
1041  * Deprecated: Instead of calling this function, chain up from
1042  * your selection_clear_event handler. Calling this function
1043  * from any other context is illegal. 
1044  **/
1045 gboolean
1046 gtk_selection_clear (GtkWidget         *widget,
1047                      GdkEventSelection *event)
1048 {
1049   /* Note that we filter clear events in gdkselection-x11.c, so
1050    * that we only will get here if the clear event actually
1051    * represents a change that we didn't do ourself.
1052    */
1053   GList *tmp_list;
1054   GtkSelectionInfo *selection_info = NULL;
1055   
1056   tmp_list = current_selections;
1057   while (tmp_list)
1058     {
1059       selection_info = (GtkSelectionInfo *)tmp_list->data;
1060       
1061       if ((selection_info->selection == event->selection) &&
1062           (selection_info->widget == widget))
1063         break;
1064       
1065       tmp_list = tmp_list->next;
1066     }
1067   
1068   if (tmp_list)
1069     {
1070       current_selections = g_list_remove_link (current_selections, tmp_list);
1071       g_list_free (tmp_list);
1072       g_free (selection_info);
1073     }
1074   
1075   return TRUE;
1076 }
1077
1078
1079 /*************************************************************
1080  * _gtk_selection_request:
1081  *     Handler for "selection_request_event" 
1082  *   arguments:
1083  *     widget:
1084  *     event:
1085  *   results:
1086  *************************************************************/
1087
1088 gboolean
1089 _gtk_selection_request (GtkWidget *widget,
1090                         GdkEventSelection *event)
1091 {
1092   GdkDisplay *display = gtk_widget_get_display (widget);
1093   GtkIncrInfo *info;
1094   GList *tmp_list;
1095   int i;
1096   
1097   if (initialize)
1098     gtk_selection_init ();
1099   
1100   /* Check if we own selection */
1101   
1102   tmp_list = current_selections;
1103   while (tmp_list)
1104     {
1105       GtkSelectionInfo *selection_info = (GtkSelectionInfo *)tmp_list->data;
1106       
1107       if ((selection_info->selection == event->selection) &&
1108           (selection_info->widget == widget))
1109         break;
1110       
1111       tmp_list = tmp_list->next;
1112     }
1113   
1114   if (tmp_list == NULL)
1115     return FALSE;
1116   
1117   info = g_new (GtkIncrInfo, 1);
1118
1119   g_object_ref (widget);
1120   
1121   info->selection = event->selection;
1122   info->num_incrs = 0;
1123   
1124   /* Create GdkWindow structure for the requestor */
1125   
1126   info->requestor = gdk_window_lookup_for_display (display,
1127                                                    event->requestor);
1128   if (!info->requestor)
1129     info->requestor = gdk_window_foreign_new_for_display (display,
1130                                                           event->requestor);
1131   
1132   /* Determine conversions we need to perform */
1133   
1134   if (event->target == gtk_selection_atoms[MULTIPLE])
1135     {
1136       GdkAtom  type;
1137       guchar  *mult_atoms;
1138       gint     format;
1139       gint     length;
1140       
1141       mult_atoms = NULL;
1142       
1143       gdk_error_trap_push ();
1144       if (!gdk_property_get (info->requestor, event->property, GDK_NONE, /* AnyPropertyType */
1145                              0, GTK_SELECTION_MAX_SIZE, FALSE,
1146                              &type, &format, &length, &mult_atoms))
1147         {
1148           gdk_selection_send_notify_for_display (display,
1149                                                  event->requestor, 
1150                                                  event->selection,
1151                                                  event->target, 
1152                                                  GDK_NONE, 
1153                                                  event->time);
1154           g_free (mult_atoms);
1155           g_free (info);
1156           return TRUE;
1157         }
1158       gdk_error_trap_pop ();
1159
1160       /* This is annoying; the ICCCM doesn't specify the property type
1161        * used for the property contents, so the autoconversion for
1162        * ATOM / ATOM_PAIR in GDK doesn't work properly.
1163        */
1164 #ifdef GDK_WINDOWING_X11
1165       if (type != GDK_SELECTION_TYPE_ATOM &&
1166           type != gdk_atom_intern ("ATOM_PAIR", FALSE))
1167         {
1168           info->num_conversions = length / (2*sizeof (glong));
1169           info->conversions = g_new (GtkIncrConversion, info->num_conversions);
1170           
1171           for (i=0; i<info->num_conversions; i++)
1172             {
1173               info->conversions[i].target = gdk_x11_xatom_to_atom_for_display (display,
1174                                                                                ((glong *)mult_atoms)[2*i]);
1175               info->conversions[i].property = gdk_x11_xatom_to_atom_for_display (display,
1176                                                                                  ((glong *)mult_atoms)[2*i + 1]);
1177             }
1178         }
1179       else
1180 #endif
1181         {
1182           info->num_conversions = length / (2*sizeof (GdkAtom));
1183           info->conversions = g_new (GtkIncrConversion, info->num_conversions);
1184           
1185           for (i=0; i<info->num_conversions; i++)
1186             {
1187               info->conversions[i].target = ((GdkAtom *)mult_atoms)[2*i];
1188               info->conversions[i].property = ((GdkAtom *)mult_atoms)[2*i+1];
1189             }
1190         }
1191     }
1192   else                          /* only a single conversion */
1193     {
1194       info->conversions = g_new (GtkIncrConversion, 1);
1195       info->num_conversions = 1;
1196       info->conversions[0].target = event->target;
1197       info->conversions[0].property = event->property;
1198     }
1199   
1200   /* Loop through conversions and determine which of these are big
1201      enough to require doing them via INCR */
1202   for (i=0; i<info->num_conversions; i++)
1203     {
1204       GtkSelectionData data;
1205       glong items;
1206       
1207       data.selection = event->selection;
1208       data.target = info->conversions[i].target;
1209       data.data = NULL;
1210       data.length = -1;
1211       data.display = gtk_widget_get_display (widget);
1212       
1213 #ifdef DEBUG_SELECTION
1214       g_message ("Selection %ld, target %ld (%s) requested by 0x%x (property = %ld)",
1215                  event->selection, info->conversions[i].target,
1216                  gdk_atom_name (info->conversions[i].target),
1217                  event->requestor, event->property);
1218 #endif
1219       
1220       gtk_selection_invoke_handler (widget, &data, event->time);
1221       
1222       if (data.length < 0)
1223         {
1224           info->conversions[i].property = GDK_NONE;
1225           continue;
1226         }
1227       
1228       g_return_val_if_fail ((data.format >= 8) && (data.format % 8 == 0), FALSE);
1229       
1230       items = data.length / gtk_selection_bytes_per_item (data.format);
1231       
1232       if (data.length > GTK_SELECTION_MAX_SIZE)
1233         {
1234           /* Sending via INCR */
1235           
1236           info->conversions[i].offset = 0;
1237           info->conversions[i].data = data;
1238           info->num_incrs++;
1239           
1240           gdk_property_change (info->requestor, 
1241                                info->conversions[i].property,
1242                                gtk_selection_atoms[INCR],
1243                                32,
1244                                GDK_PROP_MODE_REPLACE,
1245                                (guchar *)&items, 1);
1246         }
1247       else
1248         {
1249           info->conversions[i].offset = -1;
1250           
1251           gdk_property_change (info->requestor, 
1252                                info->conversions[i].property,
1253                                data.type,
1254                                data.format,
1255                                GDK_PROP_MODE_REPLACE,
1256                                data.data, items);
1257           
1258           g_free (data.data);
1259         }
1260     }
1261   
1262   /* If we have some INCR's, we need to send the rest of the data in
1263      a callback */
1264   
1265   if (info->num_incrs > 0)
1266     {
1267       /* FIXME: this could be dangerous if window doesn't still
1268          exist */
1269       
1270 #ifdef DEBUG_SELECTION
1271       g_message ("Starting INCR...");
1272 #endif
1273       
1274       gdk_window_set_events (info->requestor,
1275                              gdk_window_get_events (info->requestor) |
1276                              GDK_PROPERTY_CHANGE_MASK);
1277       current_incrs = g_list_append (current_incrs, info);
1278       g_timeout_add (1000, (GSourceFunc) gtk_selection_incr_timeout, info);
1279     }
1280   
1281   /* If it was a MULTIPLE request, set the property to indicate which
1282      conversions succeeded */
1283   if (event->target == gtk_selection_atoms[MULTIPLE])
1284     {
1285       GdkAtom *mult_atoms = g_new (GdkAtom, 2 * info->num_conversions);
1286       for (i = 0; i < info->num_conversions; i++)
1287         {
1288           mult_atoms[2*i] = info->conversions[i].target;
1289           mult_atoms[2*i+1] = info->conversions[i].property;
1290         }
1291       
1292       gdk_property_change (info->requestor, event->property,
1293                            gdk_atom_intern ("ATOM_PAIR", FALSE), 32, 
1294                            GDK_PROP_MODE_REPLACE,
1295                            (guchar *)mult_atoms, 2*info->num_conversions);
1296       g_free (mult_atoms);
1297     }
1298
1299   if (info->num_conversions == 1 &&
1300       info->conversions[0].property == GDK_NONE)
1301     {
1302       /* Reject the entire conversion */
1303       gdk_selection_send_notify_for_display (gtk_widget_get_display (widget),
1304                                              event->requestor, 
1305                                              event->selection, 
1306                                              event->target, 
1307                                              GDK_NONE, 
1308                                              event->time);
1309     }
1310   else
1311     {
1312       gdk_selection_send_notify_for_display (gtk_widget_get_display (widget),
1313                                              event->requestor, 
1314                                              event->selection,
1315                                              event->target,
1316                                              event->property, 
1317                                              event->time);
1318     }
1319
1320   if (info->num_incrs == 0)
1321     {
1322       g_free (info->conversions);
1323       g_free (info);
1324     }
1325
1326   g_object_unref (widget);
1327   
1328   return TRUE;
1329 }
1330
1331 /*************************************************************
1332  * _gtk_selection_incr_event:
1333  *     Called whenever an PropertyNotify event occurs for an 
1334  *     GdkWindow with user_data == NULL. These will be notifications
1335  *     that a window we are sending the selection to via the
1336  *     INCR protocol has deleted a property and is ready for
1337  *     more data.
1338  *
1339  *   arguments:
1340  *     window:  the requestor window
1341  *     event:   the property event structure
1342  *
1343  *   results:
1344  *************************************************************/
1345
1346 gboolean
1347 _gtk_selection_incr_event (GdkWindow       *window,
1348                            GdkEventProperty *event)
1349 {
1350   GList *tmp_list;
1351   GtkIncrInfo *info = NULL;
1352   gint num_bytes;
1353   guchar *buffer;
1354   
1355   int i;
1356   
1357   if (event->state != GDK_PROPERTY_DELETE)
1358     return FALSE;
1359   
1360 #ifdef DEBUG_SELECTION
1361   g_message ("PropertyDelete, property %ld", event->atom);
1362 #endif
1363   
1364   /* Now find the appropriate ongoing INCR */
1365   tmp_list = current_incrs;
1366   while (tmp_list)
1367     {
1368       info = (GtkIncrInfo *)tmp_list->data;
1369       if (info->requestor == event->window)
1370         break;
1371       
1372       tmp_list = tmp_list->next;
1373     }
1374   
1375   if (tmp_list == NULL)
1376     return FALSE;
1377   
1378   /* Find out which target this is for */
1379   for (i=0; i<info->num_conversions; i++)
1380     {
1381       if (info->conversions[i].property == event->atom &&
1382           info->conversions[i].offset != -1)
1383         {
1384           int bytes_per_item;
1385           
1386           info->idle_time = 0;
1387           
1388           if (info->conversions[i].offset == -2) /* only the last 0-length
1389                                                     piece*/
1390             {
1391               num_bytes = 0;
1392               buffer = NULL;
1393             }
1394           else
1395             {
1396               num_bytes = info->conversions[i].data.length -
1397                 info->conversions[i].offset;
1398               buffer = info->conversions[i].data.data + 
1399                 info->conversions[i].offset;
1400               
1401               if (num_bytes > GTK_SELECTION_MAX_SIZE)
1402                 {
1403                   num_bytes = GTK_SELECTION_MAX_SIZE;
1404                   info->conversions[i].offset += GTK_SELECTION_MAX_SIZE;
1405                 }
1406               else
1407                 info->conversions[i].offset = -2;
1408             }
1409 #ifdef DEBUG_SELECTION
1410           g_message ("INCR: put %d bytes (offset = %d) into window 0x%lx , property %ld",
1411                      num_bytes, info->conversions[i].offset, 
1412                      GDK_WINDOW_XWINDOW(info->requestor), event->atom);
1413 #endif
1414
1415           bytes_per_item = gtk_selection_bytes_per_item (info->conversions[i].data.format);
1416           gdk_property_change (info->requestor, event->atom,
1417                                info->conversions[i].data.type,
1418                                info->conversions[i].data.format,
1419                                GDK_PROP_MODE_REPLACE,
1420                                buffer,
1421                                num_bytes / bytes_per_item);
1422           
1423           if (info->conversions[i].offset == -2)
1424             {
1425               g_free (info->conversions[i].data.data);
1426               info->conversions[i].data.data = NULL;
1427             }
1428           
1429           if (num_bytes == 0)
1430             {
1431               info->num_incrs--;
1432               info->conversions[i].offset = -1;
1433             }
1434         }
1435       break;
1436     }
1437   
1438   /* Check if we're finished with all the targets */
1439   
1440   if (info->num_incrs == 0)
1441     {
1442       current_incrs = g_list_remove_link (current_incrs, tmp_list);
1443       g_list_free (tmp_list);
1444       /* Let the timeout free it */
1445     }
1446   
1447   return TRUE;
1448 }
1449
1450 /*************************************************************
1451  * gtk_selection_incr_timeout:
1452  *     Timeout callback for the sending portion of the INCR
1453  *     protocol
1454  *   arguments:
1455  *     info:    Information about this incr
1456  *   results:
1457  *************************************************************/
1458
1459 static gint
1460 gtk_selection_incr_timeout (GtkIncrInfo *info)
1461 {
1462   GList *tmp_list;
1463   gboolean retval;
1464
1465   GDK_THREADS_ENTER ();
1466   
1467   /* Determine if retrieval has finished by checking if it still in
1468      list of pending retrievals */
1469   
1470   tmp_list = current_incrs;
1471   while (tmp_list)
1472     {
1473       if (info == (GtkIncrInfo *)tmp_list->data)
1474         break;
1475       tmp_list = tmp_list->next;
1476     }
1477   
1478   /* If retrieval is finished */
1479   if (!tmp_list || info->idle_time >= IDLE_ABORT_TIME)
1480     {
1481       if (tmp_list && info->idle_time >= IDLE_ABORT_TIME)
1482         {
1483           current_incrs = g_list_remove_link (current_incrs, tmp_list);
1484           g_list_free (tmp_list);
1485         }
1486       
1487       g_free (info->conversions);
1488       /* FIXME: we should check if requestor window is still in use,
1489          and if not, remove it? */
1490       
1491       g_free (info);
1492       
1493       retval =  FALSE;          /* remove timeout */
1494     }
1495   else
1496     {
1497       info->idle_time++;
1498       
1499       retval = TRUE;            /* timeout will happen again */
1500     }
1501   
1502   GDK_THREADS_LEAVE ();
1503
1504   return retval;
1505 }
1506
1507 /*************************************************************
1508  * _gtk_selection_notify:
1509  *     Handler for "selection_notify_event" signals on windows
1510  *     where a retrieval is currently in process. The selection
1511  *     owner has responded to our conversion request.
1512  *   arguments:
1513  *     widget:          Widget getting signal
1514  *     event:           Selection event structure
1515  *     info:            Information about this retrieval
1516  *   results:
1517  *     was event handled?
1518  *************************************************************/
1519
1520 gboolean
1521 _gtk_selection_notify (GtkWidget               *widget,
1522                        GdkEventSelection *event)
1523 {
1524   GList *tmp_list;
1525   GtkRetrievalInfo *info = NULL;
1526   guchar  *buffer = NULL;
1527   gint length;
1528   GdkAtom type;
1529   gint    format;
1530   
1531 #ifdef DEBUG_SELECTION
1532   g_message ("Initial receipt of selection %ld, target %ld (property = %ld)",
1533              event->selection, event->target, event->property);
1534 #endif
1535   
1536   tmp_list = current_retrievals;
1537   while (tmp_list)
1538     {
1539       info = (GtkRetrievalInfo *)tmp_list->data;
1540       if (info->widget == widget && info->selection == event->selection)
1541         break;
1542       tmp_list = tmp_list->next;
1543     }
1544   
1545   if (!tmp_list)                /* no retrieval in progress */
1546     return FALSE;
1547
1548   if (event->property != GDK_NONE)
1549     length = gdk_selection_property_get (widget->window, &buffer, 
1550                                          &type, &format);
1551   else
1552     length = 0; /* silence gcc */
1553   
1554   if (event->property == GDK_NONE || buffer == NULL)
1555     {
1556       current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1557       g_list_free (tmp_list);
1558       /* structure will be freed in timeout */
1559       gtk_selection_retrieval_report (info,
1560                                       GDK_NONE, 0, NULL, -1, event->time);
1561       
1562       return TRUE;
1563     }
1564   
1565   if (type == gtk_selection_atoms[INCR])
1566     {
1567       /* The remainder of the selection will come through PropertyNotify
1568          events */
1569
1570       info->notify_time = event->time;
1571       info->idle_time = 0;
1572       info->offset = 0;         /* Mark as OK to proceed */
1573       gdk_window_set_events (widget->window,
1574                              gdk_window_get_events (widget->window)
1575                              | GDK_PROPERTY_CHANGE_MASK);
1576     }
1577   else
1578     {
1579       /* We don't delete the info structure - that will happen in timeout */
1580       current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1581       g_list_free (tmp_list);
1582       
1583       info->offset = length;
1584       gtk_selection_retrieval_report (info,
1585                                       type, format, 
1586                                       buffer, length, event->time);
1587     }
1588   
1589   gdk_property_delete (widget->window, event->property);
1590   
1591   g_free (buffer);
1592   
1593   return TRUE;
1594 }
1595
1596 /*************************************************************
1597  * _gtk_selection_property_notify:
1598  *     Handler for "property_notify_event" signals on windows
1599  *     where a retrieval is currently in process. The selection
1600  *     owner has added more data.
1601  *   arguments:
1602  *     widget:          Widget getting signal
1603  *     event:           Property event structure
1604  *     info:            Information about this retrieval
1605  *   results:
1606  *     was event handled?
1607  *************************************************************/
1608
1609 gboolean
1610 _gtk_selection_property_notify (GtkWidget       *widget,
1611                                 GdkEventProperty *event)
1612 {
1613   GList *tmp_list;
1614   GtkRetrievalInfo *info = NULL;
1615   guchar *new_buffer;
1616   int length;
1617   GdkAtom type;
1618   gint    format;
1619   
1620   g_return_val_if_fail (widget != NULL, FALSE);
1621   g_return_val_if_fail (event != NULL, FALSE);
1622
1623 #if defined(GDK_WINDOWING_WIN32) || defined(GDK_WINDOWING_X11)
1624   if ((event->state != GDK_PROPERTY_NEW_VALUE) ||  /* property was deleted */
1625       (event->atom != gdk_atom_intern ("GDK_SELECTION", FALSE))) /* not the right property */
1626 #endif
1627     return FALSE;
1628   
1629 #ifdef DEBUG_SELECTION
1630   g_message ("PropertyNewValue, property %ld",
1631              event->atom);
1632 #endif
1633   
1634   tmp_list = current_retrievals;
1635   while (tmp_list)
1636     {
1637       info = (GtkRetrievalInfo *)tmp_list->data;
1638       if (info->widget == widget)
1639         break;
1640       tmp_list = tmp_list->next;
1641     }
1642   
1643   if (!tmp_list)                /* No retrieval in progress */
1644     return FALSE;
1645   
1646   if (info->offset < 0)         /* We haven't got the SelectionNotify
1647                                    for this retrieval yet */
1648     return FALSE;
1649   
1650   info->idle_time = 0;
1651   
1652   length = gdk_selection_property_get (widget->window, &new_buffer, 
1653                                        &type, &format);
1654   gdk_property_delete (widget->window, event->atom);
1655   
1656   /* We could do a lot better efficiency-wise by paying attention to
1657      what length was sent in the initial INCR transaction, instead of
1658      doing memory allocation at every step. But its only guaranteed to
1659      be a _lower bound_ (pretty useless!) */
1660   
1661   if (length == 0 || type == GDK_NONE)          /* final zero length portion */
1662     {
1663       /* Info structure will be freed in timeout */
1664       current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1665       g_list_free (tmp_list);
1666       gtk_selection_retrieval_report (info,
1667                                       type, format, 
1668                                       (type == GDK_NONE) ?  NULL : info->buffer,
1669                                       (type == GDK_NONE) ?  -1 : info->offset,
1670                                       info->notify_time);
1671     }
1672   else                          /* append on newly arrived data */
1673     {
1674       if (!info->buffer)
1675         {
1676 #ifdef DEBUG_SELECTION
1677           g_message ("Start - Adding %d bytes at offset 0",
1678                      length);
1679 #endif
1680           info->buffer = new_buffer;
1681           info->offset = length;
1682         }
1683       else
1684         {
1685           
1686 #ifdef DEBUG_SELECTION
1687           g_message ("Appending %d bytes at offset %d",
1688                      length,info->offset);
1689 #endif
1690           /* We copy length+1 bytes to preserve guaranteed null termination */
1691           info->buffer = g_realloc (info->buffer, info->offset+length+1);
1692           memcpy (info->buffer + info->offset, new_buffer, length+1);
1693           info->offset += length;
1694           g_free (new_buffer);
1695         }
1696     }
1697   
1698   return TRUE;
1699 }
1700
1701 /*************************************************************
1702  * gtk_selection_retrieval_timeout:
1703  *     Timeout callback while receiving a selection.
1704  *   arguments:
1705  *     info:    Information about this retrieval
1706  *   results:
1707  *************************************************************/
1708
1709 static gint
1710 gtk_selection_retrieval_timeout (GtkRetrievalInfo *info)
1711 {
1712   GList *tmp_list;
1713   gboolean retval;
1714
1715   GDK_THREADS_ENTER ();
1716   
1717   /* Determine if retrieval has finished by checking if it still in
1718      list of pending retrievals */
1719   
1720   tmp_list = current_retrievals;
1721   while (tmp_list)
1722     {
1723       if (info == (GtkRetrievalInfo *)tmp_list->data)
1724         break;
1725       tmp_list = tmp_list->next;
1726     }
1727   
1728   /* If retrieval is finished */
1729   if (!tmp_list || info->idle_time >= IDLE_ABORT_TIME)
1730     {
1731       if (tmp_list && info->idle_time >= IDLE_ABORT_TIME)
1732         {
1733           current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1734           g_list_free (tmp_list);
1735           gtk_selection_retrieval_report (info, GDK_NONE, 0, NULL, -1, GDK_CURRENT_TIME);
1736         }
1737       
1738       g_free (info->buffer);
1739       g_free (info);
1740       
1741       retval =  FALSE;          /* remove timeout */
1742     }
1743   else
1744     {
1745       info->idle_time++;
1746       
1747       retval =  TRUE;           /* timeout will happen again */
1748     }
1749
1750   GDK_THREADS_LEAVE ();
1751
1752   return retval;
1753 }
1754
1755 /*************************************************************
1756  * gtk_selection_retrieval_report:
1757  *     Emits a "selection_received" signal.
1758  *   arguments:
1759  *     info:      information about the retrieval that completed
1760  *     buffer:    buffer containing data (NULL => errror)
1761  *     time:      timestamp for data in buffer
1762  *   results:
1763  *************************************************************/
1764
1765 static void
1766 gtk_selection_retrieval_report (GtkRetrievalInfo *info,
1767                                 GdkAtom type, gint format, 
1768                                 guchar *buffer, gint length,
1769                                 guint32 time)
1770 {
1771   GtkSelectionData data;
1772   
1773   data.selection = info->selection;
1774   data.target = info->target;
1775   data.type = type;
1776   data.format = format;
1777   
1778   data.length = length;
1779   data.data = buffer;
1780   data.display = gtk_widget_get_display (info->widget);
1781   
1782   g_signal_emit_by_name (info->widget,
1783                          "selection_received", 
1784                          &data, time);
1785 }
1786
1787 /*************************************************************
1788  * gtk_selection_invoke_handler:
1789  *     Finds and invokes handler for specified
1790  *     widget/selection/target combination, calls 
1791  *     gtk_selection_default_handler if none exists.
1792  *
1793  *   arguments:
1794  *     widget:      selection owner
1795  *     data:        selection data [INOUT]
1796  *     time:        time from requeset
1797  *     
1798  *   results:
1799  *     Number of bytes written to buffer, -1 if error
1800  *************************************************************/
1801
1802 static void
1803 gtk_selection_invoke_handler (GtkWidget        *widget,
1804                               GtkSelectionData *data,
1805                               guint             time)
1806 {
1807   GtkTargetList *target_list;
1808   guint info;
1809   
1810
1811   g_return_if_fail (widget != NULL);
1812
1813   target_list = gtk_selection_target_list_get (widget, data->selection);
1814   if (target_list && 
1815       gtk_target_list_find (target_list, data->target, &info))
1816     {
1817       g_signal_emit_by_name (widget,
1818                              "selection_get",
1819                              data,
1820                              info, time);
1821     }
1822   else
1823     gtk_selection_default_handler (widget, data);
1824 }
1825
1826 /*************************************************************
1827  * gtk_selection_default_handler:
1828  *     Handles some default targets that exist for any widget
1829  *     If it can't fit results into buffer, returns -1. This
1830  *     won't happen in any conceivable case, since it would
1831  *     require 1000 selection targets!
1832  *
1833  *   arguments:
1834  *     widget:      selection owner
1835  *     data:        selection data [INOUT]
1836  *
1837  *************************************************************/
1838
1839 static void
1840 gtk_selection_default_handler (GtkWidget        *widget,
1841                                GtkSelectionData *data)
1842 {
1843   if (data->target == gtk_selection_atoms[TIMESTAMP])
1844     {
1845       /* Time which was used to obtain selection */
1846       GList *tmp_list;
1847       GtkSelectionInfo *selection_info;
1848       
1849       tmp_list = current_selections;
1850       while (tmp_list)
1851         {
1852           selection_info = (GtkSelectionInfo *)tmp_list->data;
1853           if ((selection_info->widget == widget) &&
1854               (selection_info->selection == data->selection))
1855             {
1856               gulong time = selection_info->time;
1857
1858               gtk_selection_data_set (data,
1859                                       GDK_SELECTION_TYPE_INTEGER,
1860                                       32,
1861                                       (guchar *)&time,
1862                                       sizeof (time));
1863               return;
1864             }
1865           
1866           tmp_list = tmp_list->next;
1867         }
1868       
1869       data->length = -1;
1870     }
1871   else if (data->target == gtk_selection_atoms[TARGETS])
1872     {
1873       /* List of all targets supported for this widget/selection pair */
1874       GdkAtom *p;
1875       guint count;
1876       GList *tmp_list;
1877       GtkTargetList *target_list;
1878       GtkTargetPair *pair;
1879       
1880       target_list = gtk_selection_target_list_get (widget,
1881                                                    data->selection);
1882       count = g_list_length (target_list->list) + 3;
1883       
1884       data->type = GDK_SELECTION_TYPE_ATOM;
1885       data->format = 32;
1886       data->length = count * sizeof (GdkAtom);
1887
1888       /* selection data is always terminated by a trailing \0
1889        */
1890       p = g_malloc (data->length + 1);
1891       data->data = (guchar *)p;
1892       data->data[data->length] = '\0';
1893       
1894       *p++ = gtk_selection_atoms[TIMESTAMP];
1895       *p++ = gtk_selection_atoms[TARGETS];
1896       *p++ = gtk_selection_atoms[MULTIPLE];
1897       
1898       tmp_list = target_list->list;
1899       while (tmp_list)
1900         {
1901           pair = (GtkTargetPair *)tmp_list->data;
1902           *p++ = pair->target;
1903           
1904           tmp_list = tmp_list->next;
1905         }
1906     }
1907   else
1908     {
1909       data->length = -1;
1910     }
1911 }
1912
1913
1914 GtkSelectionData*
1915 gtk_selection_data_copy (GtkSelectionData *selection_data)
1916 {
1917   GtkSelectionData *new_data;
1918   
1919   g_return_val_if_fail (selection_data != NULL, NULL);
1920   
1921   new_data = g_new (GtkSelectionData, 1);
1922   *new_data = *selection_data;
1923
1924   if (selection_data->data)
1925     {
1926       new_data->data = g_malloc (selection_data->length + 1);
1927       memcpy (new_data->data, selection_data->data, selection_data->length + 1);
1928     }
1929   
1930   return new_data;
1931 }
1932
1933 void
1934 gtk_selection_data_free (GtkSelectionData *data)
1935 {
1936   g_return_if_fail (data != NULL);
1937   
1938   if (data->data)
1939     g_free (data->data);
1940   
1941   g_free (data);
1942 }
1943
1944 GType
1945 gtk_selection_data_get_type (void)
1946 {
1947   static GType our_type = 0;
1948   
1949   if (our_type == 0)
1950     our_type = g_boxed_type_register_static ("GtkSelectionData",
1951                                              (GBoxedCopyFunc) gtk_selection_data_copy,
1952                                              (GBoxedFreeFunc) gtk_selection_data_free);
1953
1954   return our_type;
1955 }
1956
1957 static int 
1958 gtk_selection_bytes_per_item (gint format)
1959 {
1960   switch (format)
1961     {
1962     case 8:
1963       return sizeof (char);
1964       break;
1965     case 16:
1966       return sizeof (short);
1967       break;
1968     case 32:
1969       return sizeof (long);
1970       break;
1971     default:
1972       g_assert_not_reached();
1973     }
1974   return 0;
1975 }