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