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