]> Pileus Git - ~andy/gtk/blob - gtk/gtkselection.c
removed unused var 64-bit cast point to const data Made const-correct.
[~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 (const 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                            const 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                            const 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   gboolean retval;
1093
1094   GDK_THREADS_ENTER ();
1095   
1096   /* Determine if retrieval has finished by checking if it still in
1097      list of pending retrievals */
1098   
1099   tmp_list = current_incrs;
1100   while (tmp_list)
1101     {
1102       if (info == (GtkIncrInfo *)tmp_list->data)
1103         break;
1104       tmp_list = tmp_list->next;
1105     }
1106   
1107   /* If retrieval is finished */
1108   if (!tmp_list || info->idle_time >= 5)
1109     {
1110       if (tmp_list && info->idle_time >= 5)
1111         {
1112           current_incrs = g_list_remove_link (current_incrs, tmp_list);
1113           g_list_free (tmp_list);
1114         }
1115       
1116       g_free (info->conversions);
1117       /* FIXME: we should check if requestor window is still in use,
1118          and if not, remove it? */
1119       
1120       g_free (info);
1121       
1122       retval =  FALSE;          /* remove timeout */
1123     }
1124   else
1125     {
1126       info->idle_time++;
1127       
1128       retval = TRUE;            /* timeout will happen again */
1129     }
1130   
1131   GDK_THREADS_LEAVE ();
1132
1133   return retval;
1134 }
1135
1136 /*************************************************************
1137  * gtk_selection_notify:
1138  *     Handler for "selection_notify_event" signals on windows
1139  *     where a retrieval is currently in process. The selection
1140  *     owner has responded to our conversion request.
1141  *   arguments:
1142  *     widget:          Widget getting signal
1143  *     event:           Selection event structure
1144  *     info:            Information about this retrieval
1145  *   results:
1146  *     was event handled?
1147  *************************************************************/
1148
1149 gint
1150 gtk_selection_notify (GtkWidget        *widget,
1151                       GdkEventSelection *event)
1152 {
1153   GList *tmp_list;
1154   GtkRetrievalInfo *info;
1155   guchar  *buffer;
1156   gint length;
1157   GdkAtom type;
1158   gint    format;
1159   
1160 #ifdef DEBUG_SELECTION
1161   g_message ("Initial receipt of selection %ld, target %ld (property = %ld)",
1162              event->selection, event->target, event->property);
1163 #endif
1164   
1165   tmp_list = current_retrievals;
1166   while (tmp_list)
1167     {
1168       info = (GtkRetrievalInfo *)tmp_list->data;
1169       if (info->widget == widget && info->selection == event->selection)
1170         break;
1171       tmp_list = tmp_list->next;
1172     }
1173   
1174   if (!tmp_list)                /* no retrieval in progress */
1175     return FALSE;
1176   
1177   if (event->property == GDK_NONE)
1178     {
1179       current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1180       g_list_free (tmp_list);
1181       /* structure will be freed in timeout */
1182       gtk_selection_retrieval_report (info,
1183                                       GDK_NONE, 0, NULL, -1, event->time);
1184       
1185       return TRUE;
1186     }
1187   
1188   length = gdk_selection_property_get (widget->window, &buffer, 
1189                                        &type, &format);
1190   
1191   if (type == gtk_selection_atoms[INCR])
1192     {
1193       /* The remainder of the selection will come through PropertyNotify
1194          events */
1195
1196       info->notify_time = event->time;
1197       info->idle_time = 0;
1198       info->offset = 0;         /* Mark as OK to proceed */
1199       gdk_window_set_events (widget->window,
1200                              gdk_window_get_events (widget->window)
1201                              | GDK_PROPERTY_CHANGE_MASK);
1202     }
1203   else
1204     {
1205       /* We don't delete the info structure - that will happen in timeout */
1206       current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1207       g_list_free (tmp_list);
1208       
1209       info->offset = length;
1210       gtk_selection_retrieval_report (info,
1211                                       type, format, 
1212                                       buffer, length, event->time);
1213     }
1214   
1215   gdk_property_delete (widget->window, event->property);
1216   
1217   g_free (buffer);
1218   
1219   return TRUE;
1220 }
1221
1222 /*************************************************************
1223  * gtk_selection_property_notify:
1224  *     Handler for "property_notify_event" signals on windows
1225  *     where a retrieval is currently in process. The selection
1226  *     owner has added more data.
1227  *   arguments:
1228  *     widget:          Widget getting signal
1229  *     event:           Property event structure
1230  *     info:            Information about this retrieval
1231  *   results:
1232  *     was event handled?
1233  *************************************************************/
1234
1235 gint
1236 gtk_selection_property_notify (GtkWidget        *widget,
1237                                GdkEventProperty *event)
1238 {
1239   GList *tmp_list;
1240   GtkRetrievalInfo *info;
1241   guchar *new_buffer;
1242   int length;
1243   GdkAtom type;
1244   gint    format;
1245   
1246   if ((event->state != GDK_PROPERTY_NEW_VALUE) ||  /* property was deleted */
1247       (event->atom != gdk_selection_property)) /* not the right property */
1248     return FALSE;
1249   
1250 #ifdef DEBUG_SELECTION
1251   g_message ("PropertyNewValue, property %ld",
1252              event->atom);
1253 #endif
1254   
1255   tmp_list = current_retrievals;
1256   while (tmp_list)
1257     {
1258       info = (GtkRetrievalInfo *)tmp_list->data;
1259       if (info->widget == widget)
1260         break;
1261       tmp_list = tmp_list->next;
1262     }
1263   
1264   if (!tmp_list)                /* No retrieval in progress */
1265     return FALSE;
1266   
1267   if (info->offset < 0)         /* We haven't got the SelectionNotify
1268                                    for this retrieval yet */
1269     return FALSE;
1270   
1271   info->idle_time = 0;
1272   
1273   length = gdk_selection_property_get (widget->window, &new_buffer, 
1274                                        &type, &format);
1275   gdk_property_delete (widget->window, event->atom);
1276   
1277   /* We could do a lot better efficiency-wise by paying attention to
1278      what length was sent in the initial INCR transaction, instead of
1279      doing memory allocation at every step. But its only guaranteed to
1280      be a _lower bound_ (pretty useless!) */
1281   
1282   if (length == 0 || type == GDK_NONE)          /* final zero length portion */
1283     {
1284       /* Info structure will be freed in timeout */
1285       current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1286       g_list_free (tmp_list);
1287       gtk_selection_retrieval_report (info,
1288                                       type, format, 
1289                                       (type == GDK_NONE) ?  NULL : info->buffer,
1290                                       (type == GDK_NONE) ?  -1 : info->offset,
1291                                       info->notify_time);
1292     }
1293   else                          /* append on newly arrived data */
1294     {
1295       if (!info->buffer)
1296         {
1297 #ifdef DEBUG_SELECTION
1298           g_message ("Start - Adding %d bytes at offset 0",
1299                      length);
1300 #endif
1301           info->buffer = new_buffer;
1302           info->offset = length;
1303         }
1304       else
1305         {
1306           
1307 #ifdef DEBUG_SELECTION
1308           g_message ("Appending %d bytes at offset %d",
1309                      length,info->offset);
1310 #endif
1311           /* We copy length+1 bytes to preserve guaranteed null termination */
1312           info->buffer = g_realloc (info->buffer, info->offset+length+1);
1313           memcpy (info->buffer + info->offset, new_buffer, length+1);
1314           info->offset += length;
1315           g_free (new_buffer);
1316         }
1317     }
1318   
1319   return TRUE;
1320 }
1321
1322 /*************************************************************
1323  * gtk_selection_retrieval_timeout:
1324  *     Timeout callback while receiving a selection.
1325  *   arguments:
1326  *     info:    Information about this retrieval
1327  *   results:
1328  *************************************************************/
1329
1330 static gint
1331 gtk_selection_retrieval_timeout (GtkRetrievalInfo *info)
1332 {
1333   GList *tmp_list;
1334   gboolean retval;
1335
1336   GDK_THREADS_ENTER ();
1337   
1338   /* Determine if retrieval has finished by checking if it still in
1339      list of pending retrievals */
1340   
1341   tmp_list = current_retrievals;
1342   while (tmp_list)
1343     {
1344       if (info == (GtkRetrievalInfo *)tmp_list->data)
1345         break;
1346       tmp_list = tmp_list->next;
1347     }
1348   
1349   /* If retrieval is finished */
1350   if (!tmp_list || info->idle_time >= 5)
1351     {
1352       if (tmp_list && info->idle_time >= 5)
1353         {
1354           current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1355           g_list_free (tmp_list);
1356           gtk_selection_retrieval_report (info, GDK_NONE, 0, NULL, -1, GDK_CURRENT_TIME);
1357         }
1358       
1359       g_free (info->buffer);
1360       g_free (info);
1361       
1362       retval =  FALSE;          /* remove timeout */
1363     }
1364   else
1365     {
1366       info->idle_time++;
1367       
1368       retval =  TRUE;           /* timeout will happen again */
1369     }
1370
1371   GDK_THREADS_LEAVE ();
1372
1373   return retval;
1374 }
1375
1376 /*************************************************************
1377  * gtk_selection_retrieval_report:
1378  *     Emits a "selection_received" signal.
1379  *   arguments:
1380  *     info:      information about the retrieval that completed
1381  *     buffer:    buffer containing data (NULL => errror)
1382  *     time:      timestamp for data in buffer
1383  *   results:
1384  *************************************************************/
1385
1386 static void
1387 gtk_selection_retrieval_report (GtkRetrievalInfo *info,
1388                                 GdkAtom type, gint format, 
1389                                 guchar *buffer, gint length,
1390                                 guint32 time)
1391 {
1392   GtkSelectionData data;
1393   
1394   data.selection = info->selection;
1395   data.target = info->target;
1396   data.type = type;
1397   data.format = format;
1398   
1399   data.length = length;
1400   data.data = buffer;
1401   
1402   gtk_signal_emit_by_name (GTK_OBJECT(info->widget),
1403                            "selection_received", 
1404                            &data, time);
1405 }
1406
1407 /*************************************************************
1408  * gtk_selection_invoke_handler:
1409  *     Finds and invokes handler for specified
1410  *     widget/selection/target combination, calls 
1411  *     gtk_selection_default_handler if none exists.
1412  *
1413  *   arguments:
1414  *     widget:      selection owner
1415  *     data:        selection data [INOUT]
1416  *     time:        time from requeset
1417  *     
1418  *   results:
1419  *     Number of bytes written to buffer, -1 if error
1420  *************************************************************/
1421
1422 static void
1423 gtk_selection_invoke_handler (GtkWidget        *widget,
1424                               GtkSelectionData *data,
1425                               guint             time)
1426 {
1427   GtkTargetList *target_list;
1428   guint info;
1429   
1430
1431   g_return_if_fail (widget != NULL);
1432
1433   target_list = gtk_selection_target_list_get (widget, data->selection);
1434   if (target_list && 
1435       gtk_target_list_find (target_list, data->target, &info))
1436     {
1437       gtk_signal_emit_by_name (GTK_OBJECT (widget), 
1438                                "selection_get",
1439                                data,
1440                                info, time);
1441     }
1442   else
1443     gtk_selection_default_handler (widget, data);
1444 }
1445
1446 /*************************************************************
1447  * gtk_selection_default_handler:
1448  *     Handles some default targets that exist for any widget
1449  *     If it can't fit results into buffer, returns -1. This
1450  *     won't happen in any conceivable case, since it would
1451  *     require 1000 selection targets!
1452  *
1453  *   arguments:
1454  *     widget:      selection owner
1455  *     data:        selection data [INOUT]
1456  *
1457  *************************************************************/
1458
1459 static void
1460 gtk_selection_default_handler (GtkWidget        *widget,
1461                                GtkSelectionData *data)
1462 {
1463   if (data->target == gtk_selection_atoms[TIMESTAMP])
1464     {
1465       /* Time which was used to obtain selection */
1466       GList *tmp_list;
1467       GtkSelectionInfo *selection_info;
1468       
1469       tmp_list = current_selections;
1470       while (tmp_list)
1471         {
1472           selection_info = (GtkSelectionInfo *)tmp_list->data;
1473           if ((selection_info->widget == widget) &&
1474               (selection_info->selection == data->selection))
1475             {
1476               gtk_selection_data_set (data,
1477                                       GDK_SELECTION_TYPE_INTEGER,
1478                                       sizeof (guint32)*8,
1479                                       (guchar *)&selection_info->time,
1480                                       sizeof (guint32));
1481               return;
1482             }
1483           
1484           tmp_list = tmp_list->next;
1485         }
1486       
1487       data->length = -1;
1488     }
1489   else if (data->target == gtk_selection_atoms[TARGETS])
1490     {
1491       /* List of all targets supported for this widget/selection pair */
1492       GdkAtom *p;
1493       gint count;
1494       GList *tmp_list;
1495       GtkTargetList *target_list;
1496       GtkTargetPair *pair;
1497       
1498       count = 3;
1499       target_list = gtk_selection_target_list_get (widget,
1500                                                    data->selection);
1501       tmp_list = target_list->list;
1502       while (tmp_list)
1503         {
1504           count++;
1505           tmp_list = tmp_list->next;
1506         }
1507       
1508       data->type = GDK_SELECTION_TYPE_ATOM;
1509       data->format = 8*sizeof (GdkAtom);
1510       data->length = count*sizeof (GdkAtom);
1511       
1512       p = g_new (GdkAtom, count);
1513       data->data = (guchar *)p;
1514       
1515       *p++ = gtk_selection_atoms[TIMESTAMP];
1516       *p++ = gtk_selection_atoms[TARGETS];
1517       *p++ = gtk_selection_atoms[MULTIPLE];
1518       
1519       tmp_list = target_list->list;
1520       while (tmp_list)
1521         {
1522           pair = (GtkTargetPair *)tmp_list->data;
1523           *p++ = pair->target;
1524           
1525           tmp_list = tmp_list->next;
1526         }
1527     }
1528   else
1529     {
1530       data->length = -1;
1531     }
1532 }
1533
1534
1535 GtkSelectioData*
1536 gtk_selection_data_copy (GtkSelectionData *data)
1537 {
1538   GtkSelectionData *new_data;
1539   
1540   g_return_val_if_fail (data != NULL, NULL);
1541   
1542   new_data = g_new (GtkSelectionData, 1);
1543   *new_data = *data;
1544   
1545   return new_data;
1546 }
1547
1548 void
1549 gtk_selection_data_free (GtkSelectionData *data)
1550 {
1551   g_return_if_fail (data != NULL);
1552   
1553   g_free (data);
1554 }