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