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, see <http://www.gnu.org/licenses/>. |
16 | */ |
17 | |
18 | /* |
19 | * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS |
20 | * file for a list of people on the GTK+ Team. See the ChangeLog |
21 | * files for a list of changes. These files are distributed with |
22 | * GTK+ at ftp://ftp.gtk.org/pub/gtk/. |
23 | */ |
24 | |
25 | #include "config.h" |
26 | |
27 | #include "gdkprivate-x11.h" |
28 | #include "gdkdisplay-x11.h" |
29 | |
30 | #include <X11/Xlib.h> |
31 | #include <X11/Xatom.h> |
32 | #include <string.h> |
33 | |
34 | |
35 | /** |
36 | * gdk_x11_display_text_property_to_text_list: |
37 | * @display: (type GdkX11Display): The `GdkDisplay` where the encoding is defined |
38 | * @encoding: a string representing the encoding. The most |
39 | * common values for this are "STRING", or "COMPOUND_TEXT". |
40 | * This is value used as the type for the property |
41 | * @format: the format of the property |
42 | * @text: The text data |
43 | * @length: The number of items to transform |
44 | * @list: location to store an array of strings in |
45 | * the encoding of the current locale. This array should be |
46 | * freed using gdk_x11_free_text_list(). |
47 | * |
48 | * Convert a text string from the encoding as it is stored |
49 | * in a property into an array of strings in the encoding of |
50 | * the current locale. (The elements of the array represent the |
51 | * nul-separated elements of the original text string.) |
52 | * |
53 | * Returns: the number of strings stored in list, or 0, |
54 | * if the conversion failed |
55 | */ |
56 | int |
57 | gdk_x11_display_text_property_to_text_list (GdkDisplay *display, |
58 | const char *encoding, |
59 | int format, |
60 | const guchar *text, |
61 | int length, |
62 | char ***list) |
63 | { |
64 | XTextProperty property; |
65 | int count = 0; |
66 | int res; |
67 | char **local_list; |
68 | g_return_val_if_fail (GDK_IS_DISPLAY (display), 0); |
69 | |
70 | if (list) |
71 | *list = NULL; |
72 | |
73 | if (gdk_display_is_closed (display)) |
74 | return 0; |
75 | |
76 | property.value = (guchar *)text; |
77 | property.encoding = gdk_x11_get_xatom_by_name_for_display (display, atom_name: encoding); |
78 | property.format = format; |
79 | property.nitems = length; |
80 | res = XmbTextPropertyToTextList (GDK_DISPLAY_XDISPLAY (display), text_prop: &property, |
81 | list_return: &local_list, count_return: &count); |
82 | if (res == XNoMemory || res == XLocaleNotSupported || res == XConverterNotFound) |
83 | return 0; |
84 | else |
85 | { |
86 | if (list) |
87 | *list = local_list; |
88 | else |
89 | XFreeStringList (local_list); |
90 | |
91 | return count; |
92 | } |
93 | } |
94 | |
95 | /** |
96 | * gdk_x11_free_text_list: |
97 | * @list: the value stored in the @list parameter by |
98 | * a call to gdk_x11_display_text_property_to_text_list(). |
99 | * |
100 | * Frees the array of strings created by |
101 | * gdk_x11_display_text_property_to_text_list(). |
102 | */ |
103 | void |
104 | gdk_x11_free_text_list (char **list) |
105 | { |
106 | g_return_if_fail (list != NULL); |
107 | |
108 | XFreeStringList (list); |
109 | } |
110 | |
111 | static int |
112 | make_list (const char *text, |
113 | int length, |
114 | gboolean latin1, |
115 | char ***list) |
116 | { |
117 | GSList *strings = NULL; |
118 | int n_strings = 0; |
119 | int i; |
120 | const char *p = text; |
121 | const char *q; |
122 | GSList *tmp_list; |
123 | GError *error = NULL; |
124 | |
125 | while (p < text + length) |
126 | { |
127 | char *str; |
128 | |
129 | q = p; |
130 | while (*q && q < text + length) |
131 | q++; |
132 | |
133 | if (latin1) |
134 | { |
135 | str = g_convert (str: p, len: q - p, |
136 | to_codeset: "UTF-8" , from_codeset: "ISO-8859-1" , |
137 | NULL, NULL, error: &error); |
138 | |
139 | if (!str) |
140 | { |
141 | g_warning ("Error converting selection from STRING: %s" , |
142 | error->message); |
143 | g_error_free (error); |
144 | } |
145 | } |
146 | else |
147 | { |
148 | str = g_strndup (str: p, n: q - p); |
149 | if (!g_utf8_validate (str, max_len: -1, NULL)) |
150 | { |
151 | g_warning ("Error converting selection from UTF8_STRING" ); |
152 | g_free (mem: str); |
153 | str = NULL; |
154 | } |
155 | } |
156 | |
157 | if (str) |
158 | { |
159 | strings = g_slist_prepend (list: strings, data: str); |
160 | n_strings++; |
161 | } |
162 | |
163 | p = q + 1; |
164 | } |
165 | |
166 | if (list) |
167 | { |
168 | *list = g_new (char *, n_strings + 1); |
169 | (*list)[n_strings] = NULL; |
170 | } |
171 | |
172 | i = n_strings; |
173 | tmp_list = strings; |
174 | while (tmp_list) |
175 | { |
176 | if (list) |
177 | (*list)[--i] = tmp_list->data; |
178 | else |
179 | g_free (mem: tmp_list->data); |
180 | |
181 | tmp_list = tmp_list->next; |
182 | } |
183 | |
184 | g_slist_free (list: strings); |
185 | |
186 | return n_strings; |
187 | } |
188 | |
189 | int |
190 | _gdk_x11_display_text_property_to_utf8_list (GdkDisplay *display, |
191 | const char *encoding, |
192 | int format, |
193 | const guchar *text, |
194 | int length, |
195 | char ***list) |
196 | { |
197 | if (g_str_equal (v1: encoding, v2: "STRING" )) |
198 | { |
199 | return make_list (text: (char *)text, length, TRUE, list); |
200 | } |
201 | else if (g_str_equal (v1: encoding, v2: "UTF8_STRING" )) |
202 | { |
203 | return make_list (text: (char *)text, length, FALSE, list); |
204 | } |
205 | else |
206 | { |
207 | char **local_list; |
208 | int local_count; |
209 | int i; |
210 | const char *charset = NULL; |
211 | gboolean need_conversion = !g_get_charset (charset: &charset); |
212 | int count = 0; |
213 | GError *error = NULL; |
214 | |
215 | /* Probably COMPOUND text, we fall back to Xlib routines |
216 | */ |
217 | local_count = gdk_x11_display_text_property_to_text_list (display, |
218 | encoding, |
219 | format, |
220 | text, |
221 | length, |
222 | list: &local_list); |
223 | if (list) |
224 | *list = g_new (char *, local_count + 1); |
225 | |
226 | for (i=0; i<local_count; i++) |
227 | { |
228 | /* list contains stuff in our default encoding |
229 | */ |
230 | if (need_conversion) |
231 | { |
232 | char *utf = g_convert (str: local_list[i], len: -1, |
233 | to_codeset: "UTF-8" , from_codeset: charset, |
234 | NULL, NULL, error: &error); |
235 | if (utf) |
236 | { |
237 | if (list) |
238 | (*list)[count++] = utf; |
239 | else |
240 | g_free (mem: utf); |
241 | } |
242 | else |
243 | { |
244 | g_warning ("Error converting to UTF-8 from '%s': %s" , |
245 | charset, error->message); |
246 | g_error_free (error); |
247 | error = NULL; |
248 | } |
249 | } |
250 | else |
251 | { |
252 | if (list) |
253 | { |
254 | if (g_utf8_validate (str: local_list[i], max_len: -1, NULL)) |
255 | (*list)[count++] = g_strdup (str: local_list[i]); |
256 | else |
257 | g_warning ("Error converting selection" ); |
258 | } |
259 | } |
260 | } |
261 | |
262 | if (local_count) |
263 | gdk_x11_free_text_list (list: local_list); |
264 | |
265 | if (list) |
266 | (*list)[count] = NULL; |
267 | |
268 | return count; |
269 | } |
270 | } |
271 | |
272 | /** |
273 | * gdk_x11_display_string_to_compound_text: |
274 | * @display: (type GdkX11Display): the `GdkDisplay` where the encoding is defined |
275 | * @str: a nul-terminated string |
276 | * @encoding: (out) (transfer none): location to store the encoding |
277 | * (to be used as the type for the property) |
278 | * @format: (out): location to store the format of the property |
279 | * @ctext: (out) (array length=length): location to store newly |
280 | * allocated data for the property |
281 | * @length: the length of @ctext, in bytes |
282 | * |
283 | * Convert a string from the encoding of the current |
284 | * locale into a form suitable for storing in a window property. |
285 | * |
286 | * Returns: 0 upon success, non-zero upon failure |
287 | */ |
288 | int |
289 | gdk_x11_display_string_to_compound_text (GdkDisplay *display, |
290 | const char *str, |
291 | const char **encoding, |
292 | int *format, |
293 | guchar **ctext, |
294 | int *length) |
295 | { |
296 | int res; |
297 | XTextProperty property; |
298 | |
299 | g_return_val_if_fail (GDK_IS_DISPLAY (display), 0); |
300 | |
301 | if (gdk_display_is_closed (display)) |
302 | res = XLocaleNotSupported; |
303 | else |
304 | res = XmbTextListToTextProperty (GDK_DISPLAY_XDISPLAY (display), |
305 | list: (char **)&str, count: 1, style: XCompoundTextStyle, |
306 | text_prop_return: &property); |
307 | if (res != Success) |
308 | { |
309 | property.encoding = None; |
310 | property.format = None; |
311 | property.value = NULL; |
312 | property.nitems = 0; |
313 | } |
314 | |
315 | if (encoding) |
316 | *encoding = gdk_x11_get_xatom_name_for_display (display, xatom: property.encoding); |
317 | if (format) |
318 | *format = property.format; |
319 | if (ctext) |
320 | *ctext = property.value; |
321 | if (length) |
322 | *length = property.nitems; |
323 | |
324 | return res; |
325 | } |
326 | |
327 | /** |
328 | * gdk_x11_display_utf8_to_compound_text: |
329 | * @display: (type GdkX11Display): a `GdkDisplay` |
330 | * @str: a UTF-8 string |
331 | * @encoding: (out) (transfer none): location to store resulting encoding |
332 | * @format: (out): location to store format of the result |
333 | * @ctext: (out) (array length=length): location to store the data of the result |
334 | * @length: location to store the length of the data stored in @ctext |
335 | * |
336 | * Converts from UTF-8 to compound text. |
337 | * |
338 | * Returns: %TRUE if the conversion succeeded, otherwise %FALSE |
339 | */ |
340 | gboolean |
341 | gdk_x11_display_utf8_to_compound_text (GdkDisplay *display, |
342 | const char *str, |
343 | const char **encoding, |
344 | int *format, |
345 | guchar **ctext, |
346 | int *length) |
347 | { |
348 | gboolean need_conversion; |
349 | const char *charset; |
350 | char *locale_str, *tmp_str; |
351 | GError *error = NULL; |
352 | gboolean result; |
353 | |
354 | g_return_val_if_fail (str != NULL, FALSE); |
355 | g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE); |
356 | |
357 | need_conversion = !g_get_charset (charset: &charset); |
358 | |
359 | tmp_str = gdk_x11_utf8_to_string_target (utf8_str: str, FALSE); |
360 | |
361 | if (need_conversion) |
362 | { |
363 | locale_str = g_convert (str: tmp_str, len: -1, |
364 | to_codeset: charset, from_codeset: "UTF-8" , |
365 | NULL, NULL, error: &error); |
366 | g_free (mem: tmp_str); |
367 | |
368 | if (!locale_str) |
369 | { |
370 | if (!g_error_matches (error, G_CONVERT_ERROR, code: G_CONVERT_ERROR_ILLEGAL_SEQUENCE)) |
371 | { |
372 | g_warning ("Error converting from UTF-8 to '%s': %s" , |
373 | charset, error->message); |
374 | } |
375 | g_error_free (error); |
376 | |
377 | if (encoding) |
378 | *encoding = NULL; |
379 | if (format) |
380 | *format = None; |
381 | if (ctext) |
382 | *ctext = NULL; |
383 | if (length) |
384 | *length = 0; |
385 | |
386 | return FALSE; |
387 | } |
388 | } |
389 | else |
390 | locale_str = tmp_str; |
391 | |
392 | result = gdk_x11_display_string_to_compound_text (display, str: locale_str, |
393 | encoding, format, |
394 | ctext, length); |
395 | result = (result == Success? TRUE : FALSE); |
396 | |
397 | g_free (mem: locale_str); |
398 | |
399 | return result; |
400 | } |
401 | |
402 | /** |
403 | * gdk_x11_free_compound_text: |
404 | * @ctext: The pointer stored in @ctext from a call to |
405 | * gdk_x11_display_string_to_compound_text(). |
406 | * |
407 | * Frees the data returned from gdk_x11_display_string_to_compound_text(). |
408 | */ |
409 | void |
410 | gdk_x11_free_compound_text (guchar *ctext) |
411 | { |
412 | if (ctext) |
413 | XFree (ctext); |
414 | } |
415 | |