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