]> Pileus Git - ~andy/gtk/blob - gdk/x11/gdkselection-x11.c
Inclusion cleanups in sources
[~andy/gtk] / gdk / x11 / gdkselection-x11.c
1 /* GDK - The GIMP Drawing Kit
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 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 #include "config.h"
28
29 #include "gdkselection.h"
30
31 #include "gdkx.h"
32 #include "gdkproperty.h"
33 #include "gdkprivate.h"
34 #include "gdkprivate-x11.h"
35 #include "gdkdisplay-x11.h"
36
37 #include <X11/Xlib.h>
38 #include <X11/Xatom.h>
39 #include <string.h>
40
41
42 typedef struct _OwnerInfo OwnerInfo;
43
44 struct _OwnerInfo
45 {
46   GdkAtom    selection;
47   GdkWindow *owner;
48   gulong     serial;
49 };
50
51 static GSList *owner_list;
52
53 /* When a window is destroyed we check if it is the owner
54  * of any selections. This is somewhat inefficient, but
55  * owner_list is typically short, and it is a low memory,
56  * low code solution
57  */
58 void
59 _gdk_selection_window_destroyed (GdkWindow *window)
60 {
61   GSList *tmp_list = owner_list;
62   while (tmp_list)
63     {
64       OwnerInfo *info = tmp_list->data;
65       tmp_list = tmp_list->next;
66       
67       if (info->owner == window)
68         {
69           owner_list = g_slist_remove (owner_list, info);
70           g_free (info);
71         }
72     }
73 }
74
75 /* We only pass through those SelectionClear events that actually
76  * reflect changes to the selection owner that we didn't make ourself.
77  */
78 gboolean
79 _gdk_selection_filter_clear_event (XSelectionClearEvent *event)
80 {
81   GSList *tmp_list = owner_list;
82   GdkDisplay *display = gdk_x11_lookup_xdisplay (event->display);
83   
84   while (tmp_list)
85     {
86       OwnerInfo *info = tmp_list->data;
87
88       if (gdk_window_get_display (info->owner) == display &&
89           info->selection == gdk_x11_xatom_to_atom_for_display (display, event->selection))
90         {
91           if ((GDK_DRAWABLE_XID (info->owner) == event->window &&
92                event->serial >= info->serial))
93             {
94               owner_list = g_slist_remove (owner_list, info);
95               g_free (info);
96               return TRUE;
97             }
98           else
99             return FALSE;
100         }
101       tmp_list = tmp_list->next;
102     }
103
104   return FALSE;
105 }
106 /**
107  * gdk_selection_owner_set_for_display:
108  * @display: the #GdkDisplay.
109  * @owner: a #GdkWindow or %NULL to indicate that the owner for
110  *         the given should be unset.
111  * @selection: an atom identifying a selection.
112  * @time_: timestamp to use when setting the selection. 
113  *         If this is older than the timestamp given last time the owner was 
114  *         set for the given selection, the request will be ignored.
115  * @send_event: if %TRUE, and the new owner is different from the current
116  *              owner, the current owner will be sent a SelectionClear event.
117  *
118  * Sets the #GdkWindow @owner as the current owner of the selection @selection.
119  * 
120  * Returns: %TRUE if the selection owner was successfully changed to owner,
121  *          otherwise %FALSE. 
122  *
123  * Since: 2.2
124  */
125 gboolean
126 gdk_selection_owner_set_for_display (GdkDisplay *display,
127                                      GdkWindow  *owner,
128                                      GdkAtom     selection,
129                                      guint32     time, 
130                                      gboolean    send_event)
131 {
132   Display *xdisplay;
133   Window xwindow;
134   Atom xselection;
135   GSList *tmp_list;
136   OwnerInfo *info;
137
138   g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
139   g_return_val_if_fail (selection != GDK_NONE, FALSE);
140
141   if (display->closed)
142     return FALSE;
143
144   if (owner) 
145     {
146       if (GDK_WINDOW_DESTROYED (owner) || !GDK_WINDOW_IS_X11 (owner))
147         return FALSE;
148       
149       gdk_window_ensure_native (owner);
150       xdisplay = GDK_WINDOW_XDISPLAY (owner);
151       xwindow = GDK_WINDOW_XID (owner);
152     }
153   else 
154     {
155       xdisplay = GDK_DISPLAY_XDISPLAY (display);
156       xwindow = None;
157     }
158   
159   xselection = gdk_x11_atom_to_xatom_for_display (display, selection);
160
161   tmp_list = owner_list;
162   while (tmp_list)
163     {
164       info = tmp_list->data;
165       if (info->selection == selection) 
166         {
167           owner_list = g_slist_remove (owner_list, info);
168           g_free (info);
169           break;
170         }
171       tmp_list = tmp_list->next;
172     }
173
174   if (owner)
175     {
176       info = g_new (OwnerInfo, 1);
177       info->owner = owner;
178       info->serial = NextRequest (GDK_WINDOW_XDISPLAY (owner));
179       info->selection = selection;
180
181       owner_list = g_slist_prepend (owner_list, info);
182     }
183
184   XSetSelectionOwner (xdisplay, xselection, xwindow, time);
185
186   return (XGetSelectionOwner (xdisplay, xselection) == xwindow);
187 }
188
189 /**
190  * gdk_selection_owner_get_for_display:
191  * @display: a #GdkDisplay.
192  * @selection: an atom indentifying a selection.
193  *
194  * Determine the owner of the given selection.
195  *
196  * Note that the return value may be owned by a different 
197  * process if a foreign window was previously created for that
198  * window, but a new foreign window will never be created by this call. 
199  *
200  * Returns: if there is a selection owner for this window, and it is a 
201  *    window known to the current process, the #GdkWindow that owns the 
202  *    selection, otherwise %NULL.
203  *
204  * Since: 2.2
205  */ 
206 GdkWindow *
207 gdk_selection_owner_get_for_display (GdkDisplay *display,
208                                      GdkAtom     selection)
209 {
210   Window xwindow;
211   
212   g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
213   g_return_val_if_fail (selection != GDK_NONE, NULL);
214   
215   if (display->closed)
216     return NULL;
217   
218   xwindow = XGetSelectionOwner (GDK_DISPLAY_XDISPLAY (display),
219                                 gdk_x11_atom_to_xatom_for_display (display, 
220                                                                    selection));
221   if (xwindow == None)
222     return NULL;
223
224   return gdk_window_lookup_for_display (display, xwindow);
225 }
226
227 void
228 gdk_selection_convert (GdkWindow *requestor,
229                        GdkAtom    selection,
230                        GdkAtom    target,
231                        guint32    time)
232 {
233   GdkDisplay *display;
234
235   g_return_if_fail (selection != GDK_NONE);
236
237   if (GDK_WINDOW_DESTROYED (requestor) || !GDK_WINDOW_IS_X11 (requestor))
238     return;
239
240   gdk_window_ensure_native (requestor);
241   display = GDK_WINDOW_DISPLAY (requestor);
242
243   XConvertSelection (GDK_WINDOW_XDISPLAY (requestor),
244                      gdk_x11_atom_to_xatom_for_display (display, selection),
245                      gdk_x11_atom_to_xatom_for_display (display, target),
246                      gdk_x11_atom_to_xatom_for_display (display, _gdk_selection_property), 
247                      GDK_WINDOW_XID (requestor), time);
248 }
249
250 /**
251  * gdk_selection_property_get:
252  * @requestor: the window on which the data is stored
253  * @data: location to store a pointer to the retrieved data.
254        If the retrieval failed, %NULL we be stored here, otherwise, it
255        will be non-%NULL and the returned data should be freed with g_free()
256        when you are finished using it. The length of the
257        allocated memory is one more than the length
258        of the returned data, and the final byte will always
259        be zero, to ensure nul-termination of strings.
260  * @prop_type: location to store the type of the property.
261  * @prop_format: location to store the format of the property.
262  * 
263  * Retrieves selection data that was stored by the selection
264  * data in response to a call to gdk_selection_convert(). This function
265  * will not be used by applications, who should use the #GtkClipboard
266  * API instead.
267  * 
268  * Return value: the length of the retrieved data.
269  **/
270 gint
271 gdk_selection_property_get (GdkWindow  *requestor,
272                             guchar    **data,
273                             GdkAtom    *ret_type,
274                             gint       *ret_format)
275 {
276   gulong nitems;
277   gulong nbytes;
278   gulong length = 0;            /* Quiet GCC */
279   Atom prop_type;
280   gint prop_format;
281   guchar *t = NULL;
282   GdkDisplay *display; 
283
284   g_return_val_if_fail (requestor != NULL, 0);
285   g_return_val_if_fail (GDK_IS_WINDOW (requestor), 0);
286   g_return_val_if_fail (GDK_WINDOW_IS_X11 (requestor), 0);
287   
288   display = GDK_WINDOW_DISPLAY (requestor);
289
290   if (GDK_WINDOW_DESTROYED (requestor) || !GDK_WINDOW_IS_X11 (requestor))
291     goto err;
292
293   t = NULL;
294
295   /* We can't delete the selection here, because it might be the INCR
296      protocol, in which case the client has to make sure they'll be
297      notified of PropertyChange events _before_ the property is deleted.
298      Otherwise there's no guarantee we'll win the race ... */
299   if (XGetWindowProperty (GDK_DRAWABLE_XDISPLAY (requestor),
300                           GDK_DRAWABLE_XID (requestor),
301                           gdk_x11_atom_to_xatom_for_display (display, _gdk_selection_property),
302                           0, 0x1FFFFFFF /* MAXINT32 / 4 */, False, 
303                           AnyPropertyType, &prop_type, &prop_format,
304                           &nitems, &nbytes, &t) != Success)
305     goto err;
306     
307   if (prop_type != None)
308     {
309       if (ret_type)
310         *ret_type = gdk_x11_xatom_to_atom_for_display (display, prop_type);
311       if (ret_format)
312         *ret_format = prop_format;
313
314       if (prop_type == XA_ATOM ||
315           prop_type == gdk_x11_get_xatom_by_name_for_display (display, "ATOM_PAIR"))
316         {
317           Atom* atoms = (Atom*) t;
318           GdkAtom* atoms_dest;
319           gint num_atom, i;
320
321           if (prop_format != 32)
322             goto err;
323           
324           num_atom = nitems;
325           length = sizeof (GdkAtom) * num_atom + 1;
326
327           if (data)
328             {
329               *data = g_malloc (length);
330               (*data)[length - 1] = '\0';
331               atoms_dest = (GdkAtom *)(*data);
332           
333               for (i=0; i < num_atom; i++)
334                 atoms_dest[i] = gdk_x11_xatom_to_atom_for_display (display, atoms[i]);
335             }
336         }
337       else
338         {
339           switch (prop_format)
340             {
341             case 8:
342               length = nitems;
343               break;
344             case 16:
345               length = sizeof(short) * nitems;
346               break;
347             case 32:
348               length = sizeof(long) * nitems;
349               break;
350             default:
351               g_assert_not_reached ();
352               break;
353             }
354           
355           /* Add on an extra byte to handle null termination.  X guarantees
356              that t will be 1 longer than nitems and null terminated */
357           length += 1;
358
359           if (data)
360             *data = g_memdup (t, length);
361         }
362       
363       if (t)
364         XFree (t);
365       
366       return length - 1;
367     }
368
369  err:
370   if (ret_type)
371     *ret_type = GDK_NONE;
372   if (ret_format)
373     *ret_format = 0;
374   if (data)
375     *data = NULL;
376   
377   return 0;
378 }
379
380 /**
381  * gdk_selection_send_notify_for_display:
382  * @display: the #GdkDisplay where @requestor is realized
383  * @requestor: window to which to deliver response.
384  * @selection: selection that was requested.
385  * @target: target that was selected.
386  * @property: property in which the selection owner stored the data,
387  *            or %GDK_NONE to indicate that the request was rejected.
388  * @time_: timestamp. 
389  *
390  * Send a response to SelectionRequest event.
391  *
392  * Since: 2.2
393  **/
394 void
395 gdk_selection_send_notify_for_display (GdkDisplay       *display,
396                                        GdkNativeWindow  requestor,
397                                        GdkAtom          selection,
398                                        GdkAtom          target,
399                                        GdkAtom          property, 
400                                        guint32          time)
401 {
402   XSelectionEvent xevent;
403   
404   g_return_if_fail (GDK_IS_DISPLAY (display));
405   
406   xevent.type = SelectionNotify;
407   xevent.serial = 0;
408   xevent.send_event = True;
409   xevent.requestor = requestor;
410   xevent.selection = gdk_x11_atom_to_xatom_for_display (display, selection);
411   xevent.target = gdk_x11_atom_to_xatom_for_display (display, target);
412   if (property == GDK_NONE)
413     xevent.property = None;
414   else
415     xevent.property = gdk_x11_atom_to_xatom_for_display (display, property);
416   xevent.time = time;
417
418   _gdk_send_xevent (display, requestor, False, NoEventMask, (XEvent*) & xevent);
419 }
420
421 /**
422  * gdk_text_property_to_text_list_for_display:
423  * @display: The #GdkDisplay where the encoding is defined.
424  * @encoding: an atom representing the encoding. The most 
425  *    common values for this are STRING, or COMPOUND_TEXT. 
426  *    This is value used as the type for the property.
427  * @format: the format of the property.
428  * @text: The text data.
429  * @length: The number of items to transform.
430  * @list: location to store a terminated array of strings in 
431  *    the encoding of the current locale. This array should be 
432  *    freed using gdk_free_text_list().
433  *
434  * Convert a text string from the encoding as it is stored 
435  * in a property into an array of strings in the encoding of
436  * the current locale. (The elements of the array represent the
437  * nul-separated elements of the original text string.)
438  *
439  * Returns: the number of strings stored in list, or 0, 
440  * if the conversion failed. 
441  *
442  * Since: 2.2
443  */
444 gint
445 gdk_text_property_to_text_list_for_display (GdkDisplay   *display,
446                                             GdkAtom       encoding,
447                                             gint          format, 
448                                             const guchar *text,
449                                             gint          length,
450                                             gchar      ***list)
451 {
452   XTextProperty property;
453   gint count = 0;
454   gint res;
455   gchar **local_list;
456   g_return_val_if_fail (GDK_IS_DISPLAY (display), 0);
457
458   if (list)
459     *list = NULL;
460
461   if (display->closed)
462     return 0;
463
464   property.value = (guchar *)text;
465   property.encoding = gdk_x11_atom_to_xatom_for_display (display, encoding);
466   property.format = format;
467   property.nitems = length;
468   res = XmbTextPropertyToTextList (GDK_DISPLAY_XDISPLAY (display), &property, 
469                                    &local_list, &count);
470   if (res == XNoMemory || res == XLocaleNotSupported || res == XConverterNotFound)
471     return 0;
472   else
473     {
474       if (list)
475         *list = local_list;
476       else
477         XFreeStringList (local_list);
478       
479       return count;
480     }
481 }
482
483 void
484 gdk_free_text_list (gchar **list)
485 {
486   g_return_if_fail (list != NULL);
487
488   XFreeStringList (list);
489 }
490
491 static gint
492 make_list (const gchar  *text,
493            gint          length,
494            gboolean      latin1,
495            gchar      ***list)
496 {
497   GSList *strings = NULL;
498   gint n_strings = 0;
499   gint i;
500   const gchar *p = text;
501   const gchar *q;
502   GSList *tmp_list;
503   GError *error = NULL;
504
505   while (p < text + length)
506     {
507       gchar *str;
508       
509       q = p;
510       while (*q && q < text + length)
511         q++;
512
513       if (latin1)
514         {
515           str = g_convert (p, q - p,
516                            "UTF-8", "ISO-8859-1",
517                            NULL, NULL, &error);
518
519           if (!str)
520             {
521               g_warning ("Error converting selection from STRING: %s",
522                          error->message);
523               g_error_free (error);
524             }
525         }
526       else
527         {
528           str = g_strndup (p, q - p);
529           if (!g_utf8_validate (str, -1, NULL))
530             {
531               g_warning ("Error converting selection from UTF8_STRING");
532               g_free (str);
533               str = NULL;
534             }
535         }
536
537       if (str)
538         {
539           strings = g_slist_prepend (strings, str);
540           n_strings++;
541         }
542
543       p = q + 1;
544     }
545
546   if (list)
547     {
548       *list = g_new (gchar *, n_strings + 1);
549       (*list)[n_strings] = NULL;
550     }
551      
552   i = n_strings;
553   tmp_list = strings;
554   while (tmp_list)
555     {
556       if (list)
557         (*list)[--i] = tmp_list->data;
558       else
559         g_free (tmp_list->data);
560       
561       tmp_list = tmp_list->next;
562     }
563   
564   g_slist_free (strings);
565
566   return n_strings;
567 }
568
569 /**
570  * gdk_text_property_to_utf8_list_for_display:
571  * @display:  a #GdkDisplay
572  * @encoding: an atom representing the encoding of the text
573  * @format:   the format of the property
574  * @text:     the text to convert
575  * @length:   the length of @text, in bytes
576  * @list:     location to store the list of strings or %NULL. The
577  *            list should be freed with g_strfreev().
578  * 
579  * Converts a text property in the given encoding to
580  * a list of UTF-8 strings. 
581  * 
582  * Return value: the number of strings in the resulting
583  *               list.
584  *
585  * Since: 2.2
586  **/
587 gint 
588 gdk_text_property_to_utf8_list_for_display (GdkDisplay    *display,
589                                             GdkAtom        encoding,
590                                             gint           format,
591                                             const guchar  *text,
592                                             gint           length,
593                                             gchar       ***list)
594 {
595   g_return_val_if_fail (text != NULL, 0);
596   g_return_val_if_fail (length >= 0, 0);
597   g_return_val_if_fail (GDK_IS_DISPLAY (display), 0);
598   
599   if (encoding == GDK_TARGET_STRING)
600     {
601       return make_list ((gchar *)text, length, TRUE, list);
602     }
603   else if (encoding == gdk_atom_intern_static_string ("UTF8_STRING"))
604     {
605       return make_list ((gchar *)text, length, FALSE, list);
606     }
607   else
608     {
609       gchar **local_list;
610       gint local_count;
611       gint i;
612       const gchar *charset = NULL;
613       gboolean need_conversion = !g_get_charset (&charset);
614       gint count = 0;
615       GError *error = NULL;
616       
617       /* Probably COMPOUND text, we fall back to Xlib routines
618        */
619       local_count = gdk_text_property_to_text_list_for_display (display,
620                                                                 encoding,
621                                                                 format, 
622                                                                 text,
623                                                                 length,
624                                                                 &local_list);
625       if (list)
626         *list = g_new (gchar *, local_count + 1);
627       
628       for (i=0; i<local_count; i++)
629         {
630           /* list contains stuff in our default encoding
631            */
632           if (need_conversion)
633             {
634               gchar *utf = g_convert (local_list[i], -1,
635                                       "UTF-8", charset,
636                                       NULL, NULL, &error);
637               if (utf)
638                 {
639                   if (list)
640                     (*list)[count++] = utf;
641                   else
642                     g_free (utf);
643                 }
644               else
645                 {
646                   g_warning ("Error converting to UTF-8 from '%s': %s",
647                              charset, error->message);
648                   g_error_free (error);
649                   error = NULL;
650                 }
651             }
652           else
653             {
654               if (list)
655                 {
656                   if (g_utf8_validate (local_list[i], -1, NULL))
657                     (*list)[count++] = g_strdup (local_list[i]);
658                   else
659                     g_warning ("Error converting selection");
660                 }
661             }
662         }
663
664       if (local_count)
665         gdk_free_text_list (local_list);
666       
667       if (list)
668         (*list)[count] = NULL;
669
670       return count;
671     }
672 }
673
674 /**
675  * gdk_string_to_compound_text_for_display:
676  * @display:  the #GdkDisplay where the encoding is defined.
677  * @str:      a nul-terminated string.
678  * @encoding: location to store the encoding atom 
679  *            (to be used as the type for the property).
680  * @format:   location to store the format of the property
681  * @ctext:    location to store newly allocated data for the property.
682  * @length:   the length of @text, in bytes
683  * 
684  * Convert a string from the encoding of the current 
685  * locale into a form suitable for storing in a window property.
686  * 
687  * Returns: 0 upon success, non-zero upon failure. 
688  *
689  * Since: 2.2
690  **/
691 gint
692 gdk_string_to_compound_text_for_display (GdkDisplay  *display,
693                                          const gchar *str,
694                                          GdkAtom     *encoding,
695                                          gint        *format,
696                                          guchar     **ctext,
697                                          gint        *length)
698 {
699   gint res;
700   XTextProperty property;
701
702   g_return_val_if_fail (GDK_IS_DISPLAY (display), 0);
703
704   if (display->closed)
705     res = XLocaleNotSupported;
706   else
707     res = XmbTextListToTextProperty (GDK_DISPLAY_XDISPLAY (display), 
708                                      (char **)&str, 1, XCompoundTextStyle,
709                                      &property);
710   if (res != Success)
711     {
712       property.encoding = None;
713       property.format = None;
714       property.value = NULL;
715       property.nitems = 0;
716     }
717
718   if (encoding)
719     *encoding = gdk_x11_xatom_to_atom_for_display (display, property.encoding);
720   if (format)
721     *format = property.format;
722   if (ctext)
723     *ctext = property.value;
724   if (length)
725     *length = property.nitems;
726
727   return res;
728 }
729
730 /* The specifications for COMPOUND_TEXT and STRING specify that C0 and
731  * C1 are not allowed except for \n and \t, however the X conversions
732  * routines for COMPOUND_TEXT only enforce this in one direction,
733  * causing cut-and-paste of \r and \r\n separated text to fail.
734  * This routine strips out all non-allowed C0 and C1 characters
735  * from the input string and also canonicalizes \r, and \r\n to \n
736  */
737 static gchar * 
738 sanitize_utf8 (const gchar *src,
739                gboolean return_latin1)
740 {
741   gint len = strlen (src);
742   GString *result = g_string_sized_new (len);
743   const gchar *p = src;
744
745   while (*p)
746     {
747       if (*p == '\r')
748         {
749           p++;
750           if (*p == '\n')
751             p++;
752
753           g_string_append_c (result, '\n');
754         }
755       else
756         {
757           gunichar ch = g_utf8_get_char (p);
758           
759           if (!((ch < 0x20 && ch != '\t' && ch != '\n') || (ch >= 0x7f && ch < 0xa0)))
760             {
761               if (return_latin1)
762                 {
763                   if (ch <= 0xff)
764                     g_string_append_c (result, ch);
765                   else
766                     g_string_append_printf (result,
767                                             ch < 0x10000 ? "\\u%04x" : "\\U%08x",
768                                             ch);
769                 }
770               else
771                 {
772                   char buf[7];
773                   gint buflen;
774                   
775                   buflen = g_unichar_to_utf8 (ch, buf);
776                   g_string_append_len (result, buf, buflen);
777                 }
778             }
779
780           p = g_utf8_next_char (p);
781         }
782     }
783
784   return g_string_free (result, FALSE);
785 }
786
787 /**
788  * gdk_utf8_to_string_target:
789  * @str: a UTF-8 string
790  * 
791  * Converts an UTF-8 string into the best possible representation
792  * as a STRING. The representation of characters not in STRING
793  * is not specified; it may be as pseudo-escape sequences
794  * \x{ABCD}, or it may be in some other form of approximation.
795  * 
796  * Return value: the newly-allocated string, or %NULL if the
797  *               conversion failed. (It should not fail for
798  *               any properly formed UTF-8 string unless system
799  *               limits like memory or file descriptors are exceeded.)
800  **/
801 gchar *
802 gdk_utf8_to_string_target (const gchar *str)
803 {
804   return sanitize_utf8 (str, TRUE);
805 }
806
807 /**
808  * gdk_utf8_to_compound_text_for_display:
809  * @display:  a #GdkDisplay
810  * @str:      a UTF-8 string
811  * @encoding: location to store resulting encoding
812  * @format:   location to store format of the result
813  * @ctext:    location to store the data of the result
814  * @length:   location to store the length of the data
815  *            stored in @ctext
816  * 
817  * Converts from UTF-8 to compound text. 
818  * 
819  * Return value: %TRUE if the conversion succeeded, otherwise
820  *               %FALSE.
821  *
822  * Since: 2.2
823  **/
824 gboolean
825 gdk_utf8_to_compound_text_for_display (GdkDisplay  *display,
826                                        const gchar *str,
827                                        GdkAtom     *encoding,
828                                        gint        *format,
829                                        guchar     **ctext,
830                                        gint        *length)
831 {
832   gboolean need_conversion;
833   const gchar *charset;
834   gchar *locale_str, *tmp_str;
835   GError *error = NULL;
836   gboolean result;
837
838   g_return_val_if_fail (str != NULL, FALSE);
839   g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
840
841   need_conversion = !g_get_charset (&charset);
842
843   tmp_str = sanitize_utf8 (str, FALSE);
844
845   if (need_conversion)
846     {
847       locale_str = g_convert (tmp_str, -1,
848                               charset, "UTF-8",
849                               NULL, NULL, &error);
850       g_free (tmp_str);
851
852       if (!locale_str)
853         {
854           if (!(error->domain = G_CONVERT_ERROR &&
855                 error->code == G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
856             {
857               g_warning ("Error converting from UTF-8 to '%s': %s",
858                          charset, error->message);
859             }
860           g_error_free (error);
861
862           if (encoding)
863             *encoding = None;
864           if (format)
865             *format = None;
866           if (ctext)
867             *ctext = NULL;
868           if (length)
869             *length = 0;
870
871           return FALSE;
872         }
873     }
874   else
875     locale_str = tmp_str;
876     
877   result = gdk_string_to_compound_text_for_display (display, locale_str,
878                                                     encoding, format, 
879                                                     ctext, length);
880   result = (result == Success? TRUE : FALSE);
881   
882   g_free (locale_str);
883
884   return result;
885 }
886
887 void gdk_free_compound_text (guchar *ctext)
888 {
889   if (ctext)
890     XFree (ctext);
891 }