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