]> Pileus Git - ~andy/gtk/blob - gtk/gtkselection.c
Remove g_convert (moved to glib) and now useless utf_to_latin1()
[~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_NANOX)
63 #include "nanox/gdkprivate-nanox.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 gint
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 succesfully 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 gint
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 gint
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)
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 gint
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 gint
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
1367   if (event->property == GDK_NONE || buffer == NULL)
1368     {
1369       current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1370       g_list_free (tmp_list);
1371       /* structure will be freed in timeout */
1372       gtk_selection_retrieval_report (info,
1373                                       GDK_NONE, 0, NULL, -1, event->time);
1374       
1375       return TRUE;
1376     }
1377   
1378   if (type == gtk_selection_atoms[INCR])
1379     {
1380       /* The remainder of the selection will come through PropertyNotify
1381          events */
1382
1383       info->notify_time = event->time;
1384       info->idle_time = 0;
1385       info->offset = 0;         /* Mark as OK to proceed */
1386       gdk_window_set_events (widget->window,
1387                              gdk_window_get_events (widget->window)
1388                              | GDK_PROPERTY_CHANGE_MASK);
1389     }
1390   else
1391     {
1392       /* We don't delete the info structure - that will happen in timeout */
1393       current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1394       g_list_free (tmp_list);
1395       
1396       info->offset = length;
1397       gtk_selection_retrieval_report (info,
1398                                       type, format, 
1399                                       buffer, length, event->time);
1400     }
1401   
1402   gdk_property_delete (widget->window, event->property);
1403   
1404   g_free (buffer);
1405   
1406   return TRUE;
1407 }
1408
1409 /*************************************************************
1410  * gtk_selection_property_notify:
1411  *     Handler for "property_notify_event" signals on windows
1412  *     where a retrieval is currently in process. The selection
1413  *     owner has added more data.
1414  *   arguments:
1415  *     widget:          Widget getting signal
1416  *     event:           Property event structure
1417  *     info:            Information about this retrieval
1418  *   results:
1419  *     was event handled?
1420  *************************************************************/
1421
1422 gint
1423 gtk_selection_property_notify (GtkWidget        *widget,
1424                                GdkEventProperty *event)
1425 {
1426   GList *tmp_list;
1427   GtkRetrievalInfo *info = NULL;
1428   guchar *new_buffer;
1429   int length;
1430   GdkAtom type;
1431   gint    format;
1432   
1433   g_return_val_if_fail (widget != NULL, FALSE);
1434   g_return_val_if_fail (event != NULL, FALSE);
1435
1436 #if defined(GDK_WINDOWING_WIN32) || defined(GDK_WINDOWING_X11)
1437   if ((event->state != GDK_PROPERTY_NEW_VALUE) ||  /* property was deleted */
1438       (event->atom != gdk_selection_property)) /* not the right property */
1439 #endif
1440     return FALSE;
1441   
1442 #ifdef DEBUG_SELECTION
1443   g_message ("PropertyNewValue, property %ld",
1444              event->atom);
1445 #endif
1446   
1447   tmp_list = current_retrievals;
1448   while (tmp_list)
1449     {
1450       info = (GtkRetrievalInfo *)tmp_list->data;
1451       if (info->widget == widget)
1452         break;
1453       tmp_list = tmp_list->next;
1454     }
1455   
1456   if (!tmp_list)                /* No retrieval in progress */
1457     return FALSE;
1458   
1459   if (info->offset < 0)         /* We haven't got the SelectionNotify
1460                                    for this retrieval yet */
1461     return FALSE;
1462   
1463   info->idle_time = 0;
1464   
1465   length = gdk_selection_property_get (widget->window, &new_buffer, 
1466                                        &type, &format);
1467   gdk_property_delete (widget->window, event->atom);
1468   
1469   /* We could do a lot better efficiency-wise by paying attention to
1470      what length was sent in the initial INCR transaction, instead of
1471      doing memory allocation at every step. But its only guaranteed to
1472      be a _lower bound_ (pretty useless!) */
1473   
1474   if (length == 0 || type == GDK_NONE)          /* final zero length portion */
1475     {
1476       /* Info structure will be freed in timeout */
1477       current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1478       g_list_free (tmp_list);
1479       gtk_selection_retrieval_report (info,
1480                                       type, format, 
1481                                       (type == GDK_NONE) ?  NULL : info->buffer,
1482                                       (type == GDK_NONE) ?  -1 : info->offset,
1483                                       info->notify_time);
1484     }
1485   else                          /* append on newly arrived data */
1486     {
1487       if (!info->buffer)
1488         {
1489 #ifdef DEBUG_SELECTION
1490           g_message ("Start - Adding %d bytes at offset 0",
1491                      length);
1492 #endif
1493           info->buffer = new_buffer;
1494           info->offset = length;
1495         }
1496       else
1497         {
1498           
1499 #ifdef DEBUG_SELECTION
1500           g_message ("Appending %d bytes at offset %d",
1501                      length,info->offset);
1502 #endif
1503           /* We copy length+1 bytes to preserve guaranteed null termination */
1504           info->buffer = g_realloc (info->buffer, info->offset+length+1);
1505           memcpy (info->buffer + info->offset, new_buffer, length+1);
1506           info->offset += length;
1507           g_free (new_buffer);
1508         }
1509     }
1510   
1511   return TRUE;
1512 }
1513
1514 /*************************************************************
1515  * gtk_selection_retrieval_timeout:
1516  *     Timeout callback while receiving a selection.
1517  *   arguments:
1518  *     info:    Information about this retrieval
1519  *   results:
1520  *************************************************************/
1521
1522 static gint
1523 gtk_selection_retrieval_timeout (GtkRetrievalInfo *info)
1524 {
1525   GList *tmp_list;
1526   gboolean retval;
1527
1528   GDK_THREADS_ENTER ();
1529   
1530   /* Determine if retrieval has finished by checking if it still in
1531      list of pending retrievals */
1532   
1533   tmp_list = current_retrievals;
1534   while (tmp_list)
1535     {
1536       if (info == (GtkRetrievalInfo *)tmp_list->data)
1537         break;
1538       tmp_list = tmp_list->next;
1539     }
1540   
1541   /* If retrieval is finished */
1542   if (!tmp_list || info->idle_time >= 5)
1543     {
1544       if (tmp_list && info->idle_time >= 5)
1545         {
1546           current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1547           g_list_free (tmp_list);
1548           gtk_selection_retrieval_report (info, GDK_NONE, 0, NULL, -1, GDK_CURRENT_TIME);
1549         }
1550       
1551       g_free (info->buffer);
1552       g_free (info);
1553       
1554       retval =  FALSE;          /* remove timeout */
1555     }
1556   else
1557     {
1558       info->idle_time++;
1559       
1560       retval =  TRUE;           /* timeout will happen again */
1561     }
1562
1563   GDK_THREADS_LEAVE ();
1564
1565   return retval;
1566 }
1567
1568 /*************************************************************
1569  * gtk_selection_retrieval_report:
1570  *     Emits a "selection_received" signal.
1571  *   arguments:
1572  *     info:      information about the retrieval that completed
1573  *     buffer:    buffer containing data (NULL => errror)
1574  *     time:      timestamp for data in buffer
1575  *   results:
1576  *************************************************************/
1577
1578 static void
1579 gtk_selection_retrieval_report (GtkRetrievalInfo *info,
1580                                 GdkAtom type, gint format, 
1581                                 guchar *buffer, gint length,
1582                                 guint32 time)
1583 {
1584   GtkSelectionData data;
1585   
1586   data.selection = info->selection;
1587   data.target = info->target;
1588   data.type = type;
1589   data.format = format;
1590   
1591   data.length = length;
1592   data.data = buffer;
1593   
1594   gtk_signal_emit_by_name (GTK_OBJECT(info->widget),
1595                            "selection_received", 
1596                            &data, time);
1597 }
1598
1599 /*************************************************************
1600  * gtk_selection_invoke_handler:
1601  *     Finds and invokes handler for specified
1602  *     widget/selection/target combination, calls 
1603  *     gtk_selection_default_handler if none exists.
1604  *
1605  *   arguments:
1606  *     widget:      selection owner
1607  *     data:        selection data [INOUT]
1608  *     time:        time from requeset
1609  *     
1610  *   results:
1611  *     Number of bytes written to buffer, -1 if error
1612  *************************************************************/
1613
1614 static void
1615 gtk_selection_invoke_handler (GtkWidget        *widget,
1616                               GtkSelectionData *data,
1617                               guint             time)
1618 {
1619   GtkTargetList *target_list;
1620   guint info;
1621   
1622
1623   g_return_if_fail (widget != NULL);
1624
1625   target_list = gtk_selection_target_list_get (widget, data->selection);
1626   if (target_list && 
1627       gtk_target_list_find (target_list, data->target, &info))
1628     {
1629       gtk_signal_emit_by_name (GTK_OBJECT (widget), 
1630                                "selection_get",
1631                                data,
1632                                info, time);
1633     }
1634   else
1635     gtk_selection_default_handler (widget, data);
1636 }
1637
1638 /*************************************************************
1639  * gtk_selection_default_handler:
1640  *     Handles some default targets that exist for any widget
1641  *     If it can't fit results into buffer, returns -1. This
1642  *     won't happen in any conceivable case, since it would
1643  *     require 1000 selection targets!
1644  *
1645  *   arguments:
1646  *     widget:      selection owner
1647  *     data:        selection data [INOUT]
1648  *
1649  *************************************************************/
1650
1651 static void
1652 gtk_selection_default_handler (GtkWidget        *widget,
1653                                GtkSelectionData *data)
1654 {
1655   if (data->target == gtk_selection_atoms[TIMESTAMP])
1656     {
1657       /* Time which was used to obtain selection */
1658       GList *tmp_list;
1659       GtkSelectionInfo *selection_info;
1660       
1661       tmp_list = current_selections;
1662       while (tmp_list)
1663         {
1664           selection_info = (GtkSelectionInfo *)tmp_list->data;
1665           if ((selection_info->widget == widget) &&
1666               (selection_info->selection == data->selection))
1667             {
1668               gulong time = selection_info->time;
1669
1670               gtk_selection_data_set (data,
1671                                       GDK_SELECTION_TYPE_INTEGER,
1672                                       32,
1673                                       (guchar *)&time,
1674                                       sizeof (time));
1675               return;
1676             }
1677           
1678           tmp_list = tmp_list->next;
1679         }
1680       
1681       data->length = -1;
1682     }
1683   else if (data->target == gtk_selection_atoms[TARGETS])
1684     {
1685       /* List of all targets supported for this widget/selection pair */
1686       GdkAtom *p;
1687       guint count;
1688       GList *tmp_list;
1689       GtkTargetList *target_list;
1690       GtkTargetPair *pair;
1691       
1692       target_list = gtk_selection_target_list_get (widget,
1693                                                    data->selection);
1694       count = g_list_length (target_list->list) + 3;
1695       
1696       data->type = GDK_SELECTION_TYPE_ATOM;
1697       data->format = 32;
1698       data->length = count * sizeof (GdkAtom);
1699       
1700       p = g_new (GdkAtom, count);
1701       data->data = (guchar *)p;
1702       
1703       *p++ = gtk_selection_atoms[TIMESTAMP];
1704       *p++ = gtk_selection_atoms[TARGETS];
1705       *p++ = gtk_selection_atoms[MULTIPLE];
1706       
1707       tmp_list = target_list->list;
1708       while (tmp_list)
1709         {
1710           pair = (GtkTargetPair *)tmp_list->data;
1711           *p++ = pair->target;
1712           
1713           tmp_list = tmp_list->next;
1714         }
1715     }
1716   else
1717     {
1718       data->length = -1;
1719     }
1720 }
1721
1722
1723 GtkSelectioData*
1724 gtk_selection_data_copy (GtkSelectionData *selection_data)
1725 {
1726   GtkSelectionData *new_data;
1727   
1728   g_return_val_if_fail (selection_data != NULL, NULL);
1729   
1730   new_data = g_new (GtkSelectionData, 1);
1731   *new_data = *selection_data;
1732
1733   if (selection_data->data)
1734     {
1735       new_data->data = g_malloc (selection_data->length + 1);
1736       memcpy (new_data->data, selection_data->data, selection_data->length + 1);
1737     }
1738   
1739   return new_data;
1740 }
1741
1742 void
1743 gtk_selection_data_free (GtkSelectionData *data)
1744 {
1745   g_return_if_fail (data != NULL);
1746   
1747   if (data->data)
1748     g_free (data->data);
1749   
1750   g_free (data);
1751 }
1752
1753 static int 
1754 gtk_selection_bytes_per_item (gint format)
1755 {
1756   switch (format)
1757     {
1758     case 8:
1759       return sizeof (char);
1760       break;
1761     case 16:
1762       return sizeof (short);
1763       break;
1764     case 32:
1765       return sizeof (long);
1766       break;
1767     default:
1768       g_assert_not_reached();
1769     }
1770   return 0;
1771 }