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