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