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