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