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