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