]> Pileus Git - ~andy/gtk/blob - gtk/gtkselection.c
applied patch from Andreas Persenius <ndap@swipnet.se> that updates the
[~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 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 #if defined(GDK_WINDOWING_WIN32) || defined(GDK_WINDOWING_X11)
848   info->requestor = gdk_window_lookup (event->requestor);
849   if (!info->requestor)
850     info->requestor = gdk_window_foreign_new (event->requestor);
851 #else
852   info->requestor = NULL;
853 #endif
854   
855   /* Determine conversions we need to perform */
856   
857   if (event->target == gtk_selection_atoms[MULTIPLE])
858     {
859       GdkAtom  type;
860       gint     format;
861       gint     length;
862       
863       mult_atoms = NULL;
864       
865       gdk_error_trap_push();
866       if (!gdk_property_get (info->requestor, event->property, 0, /* AnyPropertyType */
867                              0, GTK_SELECTION_MAX_SIZE, FALSE,
868                              &type, &format, &length, &mult_atoms))
869         {
870           gdk_selection_send_notify (event->requestor, event->selection,
871                                      event->target, GDK_NONE, event->time);
872           g_free (mult_atoms);
873           g_free (info);
874           return TRUE;
875         }
876       gdk_error_trap_pop();
877       
878       info->num_conversions = length / (2*sizeof (GdkAtom));
879       info->conversions = g_new (GtkIncrConversion, info->num_conversions);
880       
881       for (i=0; i<info->num_conversions; i++)
882         {
883           info->conversions[i].target = ((GdkAtom *)mult_atoms)[2*i];
884           info->conversions[i].property = ((GdkAtom *)mult_atoms)[2*i+1];
885         }
886     }
887   else                          /* only a single conversion */
888     {
889       info->conversions = g_new (GtkIncrConversion, 1);
890       info->num_conversions = 1;
891       info->conversions[0].target = event->target;
892       info->conversions[0].property = event->property;
893       mult_atoms = (guchar *)info->conversions;
894     }
895   
896   /* Loop through conversions and determine which of these are big
897      enough to require doing them via INCR */
898   for (i=0; i<info->num_conversions; i++)
899     {
900       GtkSelectionData data;
901       glong items;
902       
903       data.selection = event->selection;
904       data.target = info->conversions[i].target;
905       data.data = NULL;
906       data.length = -1;
907       
908 #ifdef DEBUG_SELECTION
909       g_message ("Selection %ld, target %ld (%s) requested by 0x%x (property = %ld)",
910                  event->selection, info->conversions[i].target,
911                  gdk_atom_name(info->conversions[i].target),
912                  event->requestor, event->property);
913 #endif
914       
915       gtk_selection_invoke_handler (widget, &data, event->time);
916       
917       if (data.length < 0)
918         {
919           ((GdkAtom *)mult_atoms)[2*i+1] = GDK_NONE;
920           info->conversions[i].property = GDK_NONE;
921           continue;
922         }
923       
924       g_return_val_if_fail ((data.format >= 8) && (data.format % 8 == 0), FALSE);
925       
926       items = data.length / gtk_selection_bytes_per_item (data.format);
927       
928       if (data.length > GTK_SELECTION_MAX_SIZE)
929         {
930           /* Sending via INCR */
931           
932           info->conversions[i].offset = 0;
933           info->conversions[i].data = data;
934           info->num_incrs++;
935           
936           gdk_property_change (info->requestor, 
937                                info->conversions[i].property,
938                                gtk_selection_atoms[INCR],
939                                32,
940                                GDK_PROP_MODE_REPLACE,
941                                (guchar *)&items, 1);
942         }
943       else
944         {
945           info->conversions[i].offset = -1;
946           
947           gdk_property_change (info->requestor, 
948                                info->conversions[i].property,
949                                data.type,
950                                data.format,
951                                GDK_PROP_MODE_REPLACE,
952                                data.data, items);
953           
954           g_free (data.data);
955         }
956     }
957   
958   /* If we have some INCR's, we need to send the rest of the data in
959      a callback */
960   
961   if (info->num_incrs > 0)
962     {
963       /* FIXME: this could be dangerous if window doesn't still
964          exist */
965       
966 #ifdef DEBUG_SELECTION
967       g_message ("Starting INCR...");
968 #endif
969       
970       gdk_window_set_events (info->requestor,
971                              gdk_window_get_events (info->requestor) |
972                              GDK_PROPERTY_CHANGE_MASK);
973       current_incrs = g_list_append (current_incrs, info);
974       gtk_timeout_add (1000, (GtkFunction)gtk_selection_incr_timeout, info);
975     }
976   
977   /* If it was a MULTIPLE request, set the property to indicate which
978      conversions succeeded */
979   if (event->target == gtk_selection_atoms[MULTIPLE])
980     {
981       gdk_property_change (info->requestor, event->property,
982                            GDK_SELECTION_TYPE_ATOM, 32, 
983                            GDK_PROP_MODE_REPLACE,
984                            mult_atoms, 2*info->num_conversions);
985       g_free (mult_atoms);
986     }
987
988   if (info->num_conversions == 1 &&
989       info->conversions[0].property == GDK_NONE)
990     {
991       /* Reject the entire conversion */
992       gdk_selection_send_notify (event->requestor, event->selection, 
993                                  event->target, GDK_NONE, event->time);
994     }
995   else
996     {
997       gdk_selection_send_notify (event->requestor, event->selection, 
998                                  event->target, event->property, event->time);
999     }
1000   
1001   if (info->num_incrs == 0)
1002     {
1003       g_free (info->conversions);
1004       g_free (info);
1005     }
1006   
1007   return TRUE;
1008 }
1009
1010 /*************************************************************
1011  * gtk_selection_incr_event:
1012  *     Called whenever an PropertyNotify event occurs for an 
1013  *     GdkWindow with user_data == NULL. These will be notifications
1014  *     that a window we are sending the selection to via the
1015  *     INCR protocol has deleted a property and is ready for
1016  *     more data.
1017  *
1018  *   arguments:
1019  *     window:  the requestor window
1020  *     event:   the property event structure
1021  *
1022  *   results:
1023  *************************************************************/
1024
1025 gint
1026 gtk_selection_incr_event (GdkWindow        *window,
1027                           GdkEventProperty *event)
1028 {
1029   GList *tmp_list;
1030   GtkIncrInfo *info = NULL;
1031   gint num_bytes;
1032   guchar *buffer;
1033   
1034   int i;
1035   
1036   if (event->state != GDK_PROPERTY_DELETE)
1037     return FALSE;
1038   
1039 #ifdef DEBUG_SELECTION
1040   g_message ("PropertyDelete, property %ld", event->atom);
1041 #endif
1042   
1043   /* Now find the appropriate ongoing INCR */
1044   tmp_list = current_incrs;
1045   while (tmp_list)
1046     {
1047       info = (GtkIncrInfo *)tmp_list->data;
1048       if (info->requestor == event->window)
1049         break;
1050       
1051       tmp_list = tmp_list->next;
1052     }
1053   
1054   if (tmp_list == NULL)
1055     return FALSE;
1056   
1057   /* Find out which target this is for */
1058   for (i=0; i<info->num_conversions; i++)
1059     {
1060       if (info->conversions[i].property == event->atom &&
1061           info->conversions[i].offset != -1)
1062         {
1063           int bytes_per_item;
1064           
1065           info->idle_time = 0;
1066           
1067           if (info->conversions[i].offset == -2) /* only the last 0-length
1068                                                     piece*/
1069             {
1070               num_bytes = 0;
1071               buffer = NULL;
1072             }
1073           else
1074             {
1075               num_bytes = info->conversions[i].data.length -
1076                 info->conversions[i].offset;
1077               buffer = info->conversions[i].data.data + 
1078                 info->conversions[i].offset;
1079               
1080               if (num_bytes > GTK_SELECTION_MAX_SIZE)
1081                 {
1082                   num_bytes = GTK_SELECTION_MAX_SIZE;
1083                   info->conversions[i].offset += GTK_SELECTION_MAX_SIZE;
1084                 }
1085               else
1086                 info->conversions[i].offset = -2;
1087             }
1088 #ifdef DEBUG_SELECTION
1089           g_message ("INCR: put %d bytes (offset = %d) into window 0x%lx , property %ld",
1090                      num_bytes, info->conversions[i].offset, 
1091                      GDK_WINDOW_XWINDOW(info->requestor), event->atom);
1092 #endif
1093
1094           bytes_per_item = gtk_selection_bytes_per_item (info->conversions[i].data.format);
1095           gdk_property_change (info->requestor, event->atom,
1096                                info->conversions[i].data.type,
1097                                info->conversions[i].data.format,
1098                                GDK_PROP_MODE_REPLACE,
1099                                buffer,
1100                                num_bytes / bytes_per_item);
1101           
1102           if (info->conversions[i].offset == -2)
1103             {
1104               g_free (info->conversions[i].data.data);
1105               info->conversions[i].data.data = NULL;
1106             }
1107           
1108           if (num_bytes == 0)
1109             {
1110               info->num_incrs--;
1111               info->conversions[i].offset = -1;
1112             }
1113         }
1114       break;
1115     }
1116   
1117   /* Check if we're finished with all the targets */
1118   
1119   if (info->num_incrs == 0)
1120     {
1121       current_incrs = g_list_remove_link (current_incrs, tmp_list);
1122       g_list_free (tmp_list);
1123       /* Let the timeout free it */
1124     }
1125   
1126   return TRUE;
1127 }
1128
1129 /*************************************************************
1130  * gtk_selection_incr_timeout:
1131  *     Timeout callback for the sending portion of the INCR
1132  *     protocol
1133  *   arguments:
1134  *     info:    Information about this incr
1135  *   results:
1136  *************************************************************/
1137
1138 static gint
1139 gtk_selection_incr_timeout (GtkIncrInfo *info)
1140 {
1141   GList *tmp_list;
1142   gboolean retval;
1143
1144   GDK_THREADS_ENTER ();
1145   
1146   /* Determine if retrieval has finished by checking if it still in
1147      list of pending retrievals */
1148   
1149   tmp_list = current_incrs;
1150   while (tmp_list)
1151     {
1152       if (info == (GtkIncrInfo *)tmp_list->data)
1153         break;
1154       tmp_list = tmp_list->next;
1155     }
1156   
1157   /* If retrieval is finished */
1158   if (!tmp_list || info->idle_time >= 5)
1159     {
1160       if (tmp_list && info->idle_time >= 5)
1161         {
1162           current_incrs = g_list_remove_link (current_incrs, tmp_list);
1163           g_list_free (tmp_list);
1164         }
1165       
1166       g_free (info->conversions);
1167       /* FIXME: we should check if requestor window is still in use,
1168          and if not, remove it? */
1169       
1170       g_free (info);
1171       
1172       retval =  FALSE;          /* remove timeout */
1173     }
1174   else
1175     {
1176       info->idle_time++;
1177       
1178       retval = TRUE;            /* timeout will happen again */
1179     }
1180   
1181   GDK_THREADS_LEAVE ();
1182
1183   return retval;
1184 }
1185
1186 /*************************************************************
1187  * gtk_selection_notify:
1188  *     Handler for "selection_notify_event" signals on windows
1189  *     where a retrieval is currently in process. The selection
1190  *     owner has responded to our conversion request.
1191  *   arguments:
1192  *     widget:          Widget getting signal
1193  *     event:           Selection event structure
1194  *     info:            Information about this retrieval
1195  *   results:
1196  *     was event handled?
1197  *************************************************************/
1198
1199 gint
1200 gtk_selection_notify (GtkWidget        *widget,
1201                       GdkEventSelection *event)
1202 {
1203   GList *tmp_list;
1204   GtkRetrievalInfo *info = NULL;
1205   guchar  *buffer = NULL;
1206   gint length;
1207   GdkAtom type;
1208   gint    format;
1209   
1210 #ifdef DEBUG_SELECTION
1211   g_message ("Initial receipt of selection %ld, target %ld (property = %ld)",
1212              event->selection, event->target, event->property);
1213 #endif
1214   
1215   tmp_list = current_retrievals;
1216   while (tmp_list)
1217     {
1218       info = (GtkRetrievalInfo *)tmp_list->data;
1219       if (info->widget == widget && info->selection == event->selection)
1220         break;
1221       tmp_list = tmp_list->next;
1222     }
1223   
1224   if (!tmp_list)                /* no retrieval in progress */
1225     return FALSE;
1226
1227   if (event->property != GDK_NONE)
1228     length = gdk_selection_property_get (widget->window, &buffer, 
1229                                          &type, &format);
1230
1231   if (event->property == GDK_NONE || buffer == NULL)
1232     {
1233       current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1234       g_list_free (tmp_list);
1235       /* structure will be freed in timeout */
1236       gtk_selection_retrieval_report (info,
1237                                       GDK_NONE, 0, NULL, -1, event->time);
1238       
1239       return TRUE;
1240     }
1241   
1242   if (type == gtk_selection_atoms[INCR])
1243     {
1244       /* The remainder of the selection will come through PropertyNotify
1245          events */
1246
1247       info->notify_time = event->time;
1248       info->idle_time = 0;
1249       info->offset = 0;         /* Mark as OK to proceed */
1250       gdk_window_set_events (widget->window,
1251                              gdk_window_get_events (widget->window)
1252                              | GDK_PROPERTY_CHANGE_MASK);
1253     }
1254   else
1255     {
1256       /* We don't delete the info structure - that will happen in timeout */
1257       current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1258       g_list_free (tmp_list);
1259       
1260       info->offset = length;
1261       gtk_selection_retrieval_report (info,
1262                                       type, format, 
1263                                       buffer, length, event->time);
1264     }
1265   
1266   gdk_property_delete (widget->window, event->property);
1267   
1268   g_free (buffer);
1269   
1270   return TRUE;
1271 }
1272
1273 /*************************************************************
1274  * gtk_selection_property_notify:
1275  *     Handler for "property_notify_event" signals on windows
1276  *     where a retrieval is currently in process. The selection
1277  *     owner has added more data.
1278  *   arguments:
1279  *     widget:          Widget getting signal
1280  *     event:           Property event structure
1281  *     info:            Information about this retrieval
1282  *   results:
1283  *     was event handled?
1284  *************************************************************/
1285
1286 gint
1287 gtk_selection_property_notify (GtkWidget        *widget,
1288                                GdkEventProperty *event)
1289 {
1290   GList *tmp_list;
1291   GtkRetrievalInfo *info = NULL;
1292   guchar *new_buffer;
1293   int length;
1294   GdkAtom type;
1295   gint    format;
1296   
1297   g_return_val_if_fail (widget != NULL, FALSE);
1298   g_return_val_if_fail (event != NULL, FALSE);
1299
1300 #if defined(GDK_WINDOWING_WIN32) || defined(GDK_WINDOWING_X11)
1301   if ((event->state != GDK_PROPERTY_NEW_VALUE) ||  /* property was deleted */
1302       (event->atom != gdk_selection_property)) /* not the right property */
1303 #endif
1304     return FALSE;
1305   
1306 #ifdef DEBUG_SELECTION
1307   g_message ("PropertyNewValue, property %ld",
1308              event->atom);
1309 #endif
1310   
1311   tmp_list = current_retrievals;
1312   while (tmp_list)
1313     {
1314       info = (GtkRetrievalInfo *)tmp_list->data;
1315       if (info->widget == widget)
1316         break;
1317       tmp_list = tmp_list->next;
1318     }
1319   
1320   if (!tmp_list)                /* No retrieval in progress */
1321     return FALSE;
1322   
1323   if (info->offset < 0)         /* We haven't got the SelectionNotify
1324                                    for this retrieval yet */
1325     return FALSE;
1326   
1327   info->idle_time = 0;
1328   
1329   length = gdk_selection_property_get (widget->window, &new_buffer, 
1330                                        &type, &format);
1331   gdk_property_delete (widget->window, event->atom);
1332   
1333   /* We could do a lot better efficiency-wise by paying attention to
1334      what length was sent in the initial INCR transaction, instead of
1335      doing memory allocation at every step. But its only guaranteed to
1336      be a _lower bound_ (pretty useless!) */
1337   
1338   if (length == 0 || type == GDK_NONE)          /* final zero length portion */
1339     {
1340       /* Info structure will be freed in timeout */
1341       current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1342       g_list_free (tmp_list);
1343       gtk_selection_retrieval_report (info,
1344                                       type, format, 
1345                                       (type == GDK_NONE) ?  NULL : info->buffer,
1346                                       (type == GDK_NONE) ?  -1 : info->offset,
1347                                       info->notify_time);
1348     }
1349   else                          /* append on newly arrived data */
1350     {
1351       if (!info->buffer)
1352         {
1353 #ifdef DEBUG_SELECTION
1354           g_message ("Start - Adding %d bytes at offset 0",
1355                      length);
1356 #endif
1357           info->buffer = new_buffer;
1358           info->offset = length;
1359         }
1360       else
1361         {
1362           
1363 #ifdef DEBUG_SELECTION
1364           g_message ("Appending %d bytes at offset %d",
1365                      length,info->offset);
1366 #endif
1367           /* We copy length+1 bytes to preserve guaranteed null termination */
1368           info->buffer = g_realloc (info->buffer, info->offset+length+1);
1369           memcpy (info->buffer + info->offset, new_buffer, length+1);
1370           info->offset += length;
1371           g_free (new_buffer);
1372         }
1373     }
1374   
1375   return TRUE;
1376 }
1377
1378 /*************************************************************
1379  * gtk_selection_retrieval_timeout:
1380  *     Timeout callback while receiving a selection.
1381  *   arguments:
1382  *     info:    Information about this retrieval
1383  *   results:
1384  *************************************************************/
1385
1386 static gint
1387 gtk_selection_retrieval_timeout (GtkRetrievalInfo *info)
1388 {
1389   GList *tmp_list;
1390   gboolean retval;
1391
1392   GDK_THREADS_ENTER ();
1393   
1394   /* Determine if retrieval has finished by checking if it still in
1395      list of pending retrievals */
1396   
1397   tmp_list = current_retrievals;
1398   while (tmp_list)
1399     {
1400       if (info == (GtkRetrievalInfo *)tmp_list->data)
1401         break;
1402       tmp_list = tmp_list->next;
1403     }
1404   
1405   /* If retrieval is finished */
1406   if (!tmp_list || info->idle_time >= 5)
1407     {
1408       if (tmp_list && info->idle_time >= 5)
1409         {
1410           current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1411           g_list_free (tmp_list);
1412           gtk_selection_retrieval_report (info, GDK_NONE, 0, NULL, -1, GDK_CURRENT_TIME);
1413         }
1414       
1415       g_free (info->buffer);
1416       g_free (info);
1417       
1418       retval =  FALSE;          /* remove timeout */
1419     }
1420   else
1421     {
1422       info->idle_time++;
1423       
1424       retval =  TRUE;           /* timeout will happen again */
1425     }
1426
1427   GDK_THREADS_LEAVE ();
1428
1429   return retval;
1430 }
1431
1432 /*************************************************************
1433  * gtk_selection_retrieval_report:
1434  *     Emits a "selection_received" signal.
1435  *   arguments:
1436  *     info:      information about the retrieval that completed
1437  *     buffer:    buffer containing data (NULL => errror)
1438  *     time:      timestamp for data in buffer
1439  *   results:
1440  *************************************************************/
1441
1442 static void
1443 gtk_selection_retrieval_report (GtkRetrievalInfo *info,
1444                                 GdkAtom type, gint format, 
1445                                 guchar *buffer, gint length,
1446                                 guint32 time)
1447 {
1448   GtkSelectionData data;
1449   
1450   data.selection = info->selection;
1451   data.target = info->target;
1452   data.type = type;
1453   data.format = format;
1454   
1455   data.length = length;
1456   data.data = buffer;
1457   
1458   gtk_signal_emit_by_name (GTK_OBJECT(info->widget),
1459                            "selection_received", 
1460                            &data, time);
1461 }
1462
1463 /*************************************************************
1464  * gtk_selection_invoke_handler:
1465  *     Finds and invokes handler for specified
1466  *     widget/selection/target combination, calls 
1467  *     gtk_selection_default_handler if none exists.
1468  *
1469  *   arguments:
1470  *     widget:      selection owner
1471  *     data:        selection data [INOUT]
1472  *     time:        time from requeset
1473  *     
1474  *   results:
1475  *     Number of bytes written to buffer, -1 if error
1476  *************************************************************/
1477
1478 static void
1479 gtk_selection_invoke_handler (GtkWidget        *widget,
1480                               GtkSelectionData *data,
1481                               guint             time)
1482 {
1483   GtkTargetList *target_list;
1484   guint info;
1485   
1486
1487   g_return_if_fail (widget != NULL);
1488
1489   target_list = gtk_selection_target_list_get (widget, data->selection);
1490   if (target_list && 
1491       gtk_target_list_find (target_list, data->target, &info))
1492     {
1493       gtk_signal_emit_by_name (GTK_OBJECT (widget), 
1494                                "selection_get",
1495                                data,
1496                                info, time);
1497     }
1498   else
1499     gtk_selection_default_handler (widget, data);
1500 }
1501
1502 /*************************************************************
1503  * gtk_selection_default_handler:
1504  *     Handles some default targets that exist for any widget
1505  *     If it can't fit results into buffer, returns -1. This
1506  *     won't happen in any conceivable case, since it would
1507  *     require 1000 selection targets!
1508  *
1509  *   arguments:
1510  *     widget:      selection owner
1511  *     data:        selection data [INOUT]
1512  *
1513  *************************************************************/
1514
1515 static void
1516 gtk_selection_default_handler (GtkWidget        *widget,
1517                                GtkSelectionData *data)
1518 {
1519   if (data->target == gtk_selection_atoms[TIMESTAMP])
1520     {
1521       /* Time which was used to obtain selection */
1522       GList *tmp_list;
1523       GtkSelectionInfo *selection_info;
1524       
1525       tmp_list = current_selections;
1526       while (tmp_list)
1527         {
1528           selection_info = (GtkSelectionInfo *)tmp_list->data;
1529           if ((selection_info->widget == widget) &&
1530               (selection_info->selection == data->selection))
1531             {
1532               gulong time = selection_info->time;
1533
1534               gtk_selection_data_set (data,
1535                                       GDK_SELECTION_TYPE_INTEGER,
1536                                       32,
1537                                       (guchar *)&time,
1538                                       sizeof (time));
1539               return;
1540             }
1541           
1542           tmp_list = tmp_list->next;
1543         }
1544       
1545       data->length = -1;
1546     }
1547   else if (data->target == gtk_selection_atoms[TARGETS])
1548     {
1549       /* List of all targets supported for this widget/selection pair */
1550       GdkAtom *p;
1551       guint count;
1552       GList *tmp_list;
1553       GtkTargetList *target_list;
1554       GtkTargetPair *pair;
1555       
1556       target_list = gtk_selection_target_list_get (widget,
1557                                                    data->selection);
1558       count = g_list_length (target_list->list) + 3;
1559       
1560       data->type = GDK_SELECTION_TYPE_ATOM;
1561       data->format = 32;
1562       data->length = count * sizeof (GdkAtom);
1563       
1564       p = g_new (GdkAtom, count);
1565       data->data = (guchar *)p;
1566       
1567       *p++ = gtk_selection_atoms[TIMESTAMP];
1568       *p++ = gtk_selection_atoms[TARGETS];
1569       *p++ = gtk_selection_atoms[MULTIPLE];
1570       
1571       tmp_list = target_list->list;
1572       while (tmp_list)
1573         {
1574           pair = (GtkTargetPair *)tmp_list->data;
1575           *p++ = pair->target;
1576           
1577           tmp_list = tmp_list->next;
1578         }
1579     }
1580   else
1581     {
1582       data->length = -1;
1583     }
1584 }
1585
1586
1587 GtkSelectioData*
1588 gtk_selection_data_copy (GtkSelectionData *data)
1589 {
1590   GtkSelectionData *new_data;
1591   
1592   g_return_val_if_fail (data != NULL, NULL);
1593   
1594   new_data = g_new (GtkSelectionData, 1);
1595   *new_data = *data;
1596   
1597   return new_data;
1598 }
1599
1600 void
1601 gtk_selection_data_free (GtkSelectionData *data)
1602 {
1603   g_return_if_fail (data != NULL);
1604   
1605   g_free (data);
1606 }
1607
1608 static int 
1609 gtk_selection_bytes_per_item (gint format)
1610 {
1611   switch (format)
1612     {
1613     case 8:
1614       return sizeof (char);
1615       break;
1616     case 16:
1617       return sizeof (short);
1618       break;
1619     case 32:
1620       return sizeof (long);
1621       break;
1622     default:
1623       g_assert_not_reached();
1624     }
1625   return 0;
1626 }