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