1 | /* |
---|
2 | * bonobo-ui-util.c: Bonobo UI utility functions |
---|
3 | * |
---|
4 | * Author: |
---|
5 | * Michael Meeks (michael@helixcode.com) |
---|
6 | * |
---|
7 | * Copyright 2000 Helix Code, Inc. |
---|
8 | */ |
---|
9 | |
---|
10 | #include "config.h" |
---|
11 | #include <gnome.h> |
---|
12 | #include <ctype.h> |
---|
13 | |
---|
14 | #include <bonobo/bonobo-ui-xml.h> |
---|
15 | #include <bonobo/bonobo-ui-util.h> |
---|
16 | #include "bonobo-ui-icon.h" |
---|
17 | |
---|
18 | #include <gnome-xml/tree.h> |
---|
19 | #include <gnome-xml/parser.h> |
---|
20 | #include <gnome-xml/xmlmemory.h> |
---|
21 | |
---|
22 | static const char write_lut[16] = { |
---|
23 | '0', '1', '2', '3', '4', '5', '6', '7', |
---|
24 | '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' |
---|
25 | }; |
---|
26 | |
---|
27 | static const gint8 read_lut[128] = { |
---|
28 | -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00 -> 0x07 */ |
---|
29 | -1, -1, -1, -1, -1, -1, -1, -1, |
---|
30 | -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10 -> 0x17 */ |
---|
31 | -1, -1, -1, -1, -1, -1, -1, -1, |
---|
32 | -1, -1, -1, -1, -1, -1, -1, -1, /* 0x20 -> 0x27 */ |
---|
33 | -1, -1, -1, -1, -1, -1, -1, -1, |
---|
34 | 0, 1, 2, 3, 4, 5, 6, 7, /* 0x30 -> 0x37 */ |
---|
35 | 8, 9, -1, -1, -1, -1, -1, -1, |
---|
36 | -1, 10, 11, 12, 13, 14, 15, -1, /* 0x40 -> 0x47 */ |
---|
37 | -1, -1, -1, -1, -1, -1, -1, -1, |
---|
38 | -1, -1, -1, -1, -1, -1, -1, -1, /* 0x50 -> 0x57 */ |
---|
39 | -1, -1, -1, -1, -1, -1, -1, -1, |
---|
40 | -1, 10, 11, 12, 13, 14, 15, -1, /* 0x60 -> 0x67 */ |
---|
41 | -1, -1, -1, -1, -1, -1, -1, -1, |
---|
42 | -1, -1, -1, -1, -1, -1, -1, -1, /* 0x70 -> 0x77 */ |
---|
43 | -1, -1, -1, -1, -1, -1, -1, -1, |
---|
44 | }; |
---|
45 | |
---|
46 | static inline void |
---|
47 | write_byte (char *start, guint8 byte) |
---|
48 | { |
---|
49 | start[0] = write_lut[byte >> 4]; |
---|
50 | start[1] = write_lut[byte & 15]; |
---|
51 | } |
---|
52 | |
---|
53 | static inline void |
---|
54 | write_four_bytes (char *pos, int value) |
---|
55 | { |
---|
56 | write_byte (pos + 0, value >> 24); |
---|
57 | write_byte (pos + 2, value >> 16); |
---|
58 | write_byte (pos + 4, value >> 8); |
---|
59 | write_byte (pos + 6, value); |
---|
60 | } |
---|
61 | |
---|
62 | static void |
---|
63 | read_warning (const char *start) |
---|
64 | { |
---|
65 | g_warning ("Format error in stream '%c', '%c'", start[0], start[1]); |
---|
66 | } |
---|
67 | |
---|
68 | static inline guint8 |
---|
69 | read_byte (const char *start) |
---|
70 | { |
---|
71 | guint8 byte1, byte2; |
---|
72 | gint8 nibble1, nibble2; |
---|
73 | |
---|
74 | byte1 = start[0]; |
---|
75 | byte2 = start[1]; |
---|
76 | |
---|
77 | if (byte1 >= 128 || byte2 >= 128) |
---|
78 | read_warning (start); |
---|
79 | |
---|
80 | nibble1 = read_lut[byte1]; |
---|
81 | nibble2 = read_lut[byte2]; |
---|
82 | |
---|
83 | if (nibble1 < 0 || nibble2 < 0) |
---|
84 | read_warning (start); |
---|
85 | |
---|
86 | return (nibble1 << 4) + nibble2; |
---|
87 | } |
---|
88 | |
---|
89 | static inline const guint32 |
---|
90 | read_four_bytes (const char *pos) |
---|
91 | { |
---|
92 | return ((read_byte (pos) << 24) | |
---|
93 | (read_byte (pos + 2) << 16) | |
---|
94 | (read_byte (pos + 4) << 8) | |
---|
95 | (read_byte (pos + 6))); |
---|
96 | } |
---|
97 | |
---|
98 | /** |
---|
99 | * bonobo_ui_util_pixbuf_to_xml: |
---|
100 | * @pixbuf: a GdkPixbuf |
---|
101 | * |
---|
102 | * Convert a @pixbuf to a string representation suitable |
---|
103 | * for passing as a "pixname" attribute with a pixtype |
---|
104 | * attribute = "pixbuf". |
---|
105 | * |
---|
106 | * Return value: the stringified pixbuf. |
---|
107 | **/ |
---|
108 | char * |
---|
109 | bonobo_ui_util_pixbuf_to_xml (GdkPixbuf *pixbuf) |
---|
110 | { |
---|
111 | char *xml, *dst, *src; |
---|
112 | int size, width, height, row, row_stride, col, byte_width; |
---|
113 | gboolean has_alpha; |
---|
114 | |
---|
115 | g_return_val_if_fail (pixbuf != NULL, NULL); |
---|
116 | |
---|
117 | width = gdk_pixbuf_get_width (pixbuf); |
---|
118 | height = gdk_pixbuf_get_height (pixbuf); |
---|
119 | has_alpha = gdk_pixbuf_get_has_alpha (pixbuf); |
---|
120 | byte_width = width * (3 + (has_alpha ? 1 : 0)); |
---|
121 | |
---|
122 | size = 4 * 2 * 2 + /* width, height */ |
---|
123 | 1 + 1 + /* alpha, terminator */ |
---|
124 | height * byte_width * 2; |
---|
125 | |
---|
126 | xml = g_malloc (size); |
---|
127 | xml [size - 1] = '\0'; |
---|
128 | |
---|
129 | dst = xml; |
---|
130 | |
---|
131 | write_four_bytes (dst, gdk_pixbuf_get_width (pixbuf)); |
---|
132 | dst+= 4 * 2; |
---|
133 | |
---|
134 | write_four_bytes (dst, gdk_pixbuf_get_height (pixbuf)); |
---|
135 | dst+= 4 * 2; |
---|
136 | |
---|
137 | if (has_alpha) |
---|
138 | *dst = 'A'; |
---|
139 | else |
---|
140 | *dst = 'N'; |
---|
141 | dst++; |
---|
142 | |
---|
143 | /* Copy over bitmap information */ |
---|
144 | src = gdk_pixbuf_get_pixels (pixbuf); |
---|
145 | row_stride = gdk_pixbuf_get_rowstride (pixbuf); |
---|
146 | |
---|
147 | for (row = 0; row < height; row++) { |
---|
148 | |
---|
149 | for (col = 0; col < byte_width; col++) { |
---|
150 | write_byte (dst, src [col]); |
---|
151 | dst+= 2; |
---|
152 | } |
---|
153 | |
---|
154 | src += row_stride; |
---|
155 | } |
---|
156 | |
---|
157 | return xml; |
---|
158 | } |
---|
159 | |
---|
160 | /** |
---|
161 | * bonobo_ui_util_xml_to_pixbuf: |
---|
162 | * @xml: a string |
---|
163 | * |
---|
164 | * This converts a stringified pixbuf in @xml into a GdkPixbuf |
---|
165 | * |
---|
166 | * Return value: a handed reference to the created GdkPixbuf. |
---|
167 | **/ |
---|
168 | GdkPixbuf * |
---|
169 | bonobo_ui_util_xml_to_pixbuf (const char *xml) |
---|
170 | { |
---|
171 | GdkPixbuf *pixbuf; |
---|
172 | int width, height, byte_width; |
---|
173 | int length, row_stride, col, row; |
---|
174 | gboolean has_alpha; |
---|
175 | guint8 *dst; |
---|
176 | |
---|
177 | g_return_val_if_fail (xml != NULL, NULL); |
---|
178 | |
---|
179 | while (*xml && isspace ((unsigned char) (*xml))) |
---|
180 | xml++; |
---|
181 | |
---|
182 | length = strlen (xml); |
---|
183 | g_return_val_if_fail (length > 4 * 2 * 2 + 1, NULL); |
---|
184 | |
---|
185 | width = read_four_bytes (xml); |
---|
186 | xml += 4 * 2; |
---|
187 | height = read_four_bytes (xml); |
---|
188 | xml += 4 * 2; |
---|
189 | |
---|
190 | if (*xml == 'A') |
---|
191 | has_alpha = TRUE; |
---|
192 | else if (*xml == 'N') |
---|
193 | has_alpha = FALSE; |
---|
194 | else { |
---|
195 | g_warning ("Unknown type '%c'", *xml); |
---|
196 | return NULL; |
---|
197 | } |
---|
198 | xml++; |
---|
199 | |
---|
200 | byte_width = width * (3 + (has_alpha ? 1 : 0)); |
---|
201 | g_return_val_if_fail (length >= (byte_width * height * 2 + 4 * 2 * 2 + 1), NULL); |
---|
202 | |
---|
203 | pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, has_alpha, 8, width, height); |
---|
204 | |
---|
205 | dst = gdk_pixbuf_get_pixels (pixbuf); |
---|
206 | row_stride = gdk_pixbuf_get_rowstride (pixbuf); |
---|
207 | |
---|
208 | for (row = 0; row < height; row++) { |
---|
209 | |
---|
210 | for (col = 0; col < byte_width; col++) { |
---|
211 | dst [col] = read_byte (xml); |
---|
212 | xml += 2; |
---|
213 | } |
---|
214 | |
---|
215 | dst += row_stride; |
---|
216 | } |
---|
217 | |
---|
218 | return pixbuf; |
---|
219 | } |
---|
220 | |
---|
221 | /* Converts an Imlib RGB buffer with its chroma key (!) into a pixbuf with an |
---|
222 | * alpha channel. |
---|
223 | */ |
---|
224 | static GdkPixbuf * |
---|
225 | convert_from_imlib_rgb_chromakey (const unsigned char *src_pixels, int width, int height, |
---|
226 | GdkImlibColor shape) |
---|
227 | { |
---|
228 | GdkPixbuf *new; |
---|
229 | unsigned char *dst_pixels; |
---|
230 | unsigned char *dst_row; |
---|
231 | int dst_rowstride; |
---|
232 | int i, j; |
---|
233 | unsigned char key_r, key_g, key_b; |
---|
234 | |
---|
235 | new = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, width, height); |
---|
236 | if (!new) |
---|
237 | return NULL; |
---|
238 | |
---|
239 | key_r = shape.r; |
---|
240 | key_g = shape.g; |
---|
241 | key_b = shape.b; |
---|
242 | |
---|
243 | dst_pixels = gdk_pixbuf_get_pixels (new); |
---|
244 | dst_rowstride = gdk_pixbuf_get_rowstride (new); |
---|
245 | |
---|
246 | dst_row = dst_pixels; |
---|
247 | for (i = 0; i < height; i++) { |
---|
248 | unsigned char *dp; |
---|
249 | |
---|
250 | dp = dst_row; |
---|
251 | for (j = 0; j < width; j++) { |
---|
252 | if (src_pixels[0] == key_r |
---|
253 | && src_pixels[1] == key_g |
---|
254 | && src_pixels[2] == key_b) |
---|
255 | dp[0] = dp[1] = dp[2] = dp[3] = 0; |
---|
256 | else { |
---|
257 | dp[0] = src_pixels[0]; |
---|
258 | dp[1] = src_pixels[1]; |
---|
259 | dp[2] = src_pixels[2]; |
---|
260 | dp[3] = 0xff; |
---|
261 | } |
---|
262 | |
---|
263 | src_pixels += 3; |
---|
264 | dp += 4; |
---|
265 | } |
---|
266 | |
---|
267 | dst_row += dst_rowstride; |
---|
268 | } |
---|
269 | |
---|
270 | return new; |
---|
271 | } |
---|
272 | |
---|
273 | static GdkPixbuf * |
---|
274 | pixbuf_from_imlib (const GnomeStockPixmapEntry *entry) |
---|
275 | { |
---|
276 | const GnomeStockPixmapEntryImlib *imlib_entry; |
---|
277 | const GnomeStockPixmapEntryImlibScaled *scaled_imlib_entry; |
---|
278 | GdkPixbuf *alpha_pixbuf; |
---|
279 | GdkPixbuf *scaled_pixbuf; |
---|
280 | |
---|
281 | imlib_entry = (const GnomeStockPixmapEntryImlib *) entry; |
---|
282 | |
---|
283 | alpha_pixbuf = convert_from_imlib_rgb_chromakey (imlib_entry->rgb_data, |
---|
284 | imlib_entry->width, |
---|
285 | imlib_entry->height, |
---|
286 | imlib_entry->shape); |
---|
287 | |
---|
288 | /* If we could not create the pixbuf or if we succeeded and the image is |
---|
289 | * not scaled, just return it. |
---|
290 | */ |
---|
291 | if (!alpha_pixbuf || imlib_entry->type == GNOME_STOCK_PIXMAP_TYPE_IMLIB) |
---|
292 | return alpha_pixbuf; |
---|
293 | |
---|
294 | g_assert (imlib_entry->type == GNOME_STOCK_PIXMAP_TYPE_IMLIB_SCALED); |
---|
295 | |
---|
296 | scaled_imlib_entry = (const GnomeStockPixmapEntryImlibScaled *) entry; |
---|
297 | |
---|
298 | if (scaled_imlib_entry->scaled_width == imlib_entry->width |
---|
299 | && scaled_imlib_entry->scaled_height == imlib_entry->height) |
---|
300 | return alpha_pixbuf; |
---|
301 | |
---|
302 | scaled_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, |
---|
303 | scaled_imlib_entry->scaled_width, |
---|
304 | scaled_imlib_entry->scaled_height); |
---|
305 | if (!scaled_pixbuf) { |
---|
306 | gdk_pixbuf_unref (alpha_pixbuf); |
---|
307 | return NULL; |
---|
308 | } |
---|
309 | |
---|
310 | gdk_pixbuf_scale (alpha_pixbuf, scaled_pixbuf, |
---|
311 | 0, 0, |
---|
312 | scaled_imlib_entry->scaled_width, |
---|
313 | scaled_imlib_entry->scaled_height, |
---|
314 | 0.0, 0.0, |
---|
315 | (double) scaled_imlib_entry->scaled_width / (double) imlib_entry->width, |
---|
316 | (double) scaled_imlib_entry->scaled_height / (double) imlib_entry->height, |
---|
317 | GDK_INTERP_BILINEAR); |
---|
318 | |
---|
319 | gdk_pixbuf_unref (alpha_pixbuf); |
---|
320 | |
---|
321 | return scaled_pixbuf; |
---|
322 | } |
---|
323 | |
---|
324 | static GdkPixbuf * |
---|
325 | get_stock_pixbuf (const char *name) |
---|
326 | { |
---|
327 | GnomeStockPixmapEntry *entry; |
---|
328 | GdkPixbuf *pixbuf; |
---|
329 | char *path; |
---|
330 | |
---|
331 | if (!name) |
---|
332 | return NULL; |
---|
333 | |
---|
334 | entry = gnome_stock_pixmap_checkfor (name, GNOME_STOCK_PIXMAP_REGULAR); |
---|
335 | if (entry == NULL) |
---|
336 | return NULL; |
---|
337 | |
---|
338 | switch (entry->type) { |
---|
339 | case GNOME_STOCK_PIXMAP_TYPE_DATA: |
---|
340 | pixbuf = gdk_pixbuf_new_from_xpm_data ((const char **) ((GnomeStockPixmapEntryData *) entry)->xpm_data); |
---|
341 | break; |
---|
342 | case GNOME_STOCK_PIXMAP_TYPE_FILE: |
---|
343 | path = gnome_pixmap_file (((GnomeStockPixmapEntryFile *) entry)->filename); |
---|
344 | pixbuf = gdk_pixbuf_new_from_file (path); |
---|
345 | free (path); |
---|
346 | break; |
---|
347 | case GNOME_STOCK_PIXMAP_TYPE_PATH: |
---|
348 | pixbuf = gdk_pixbuf_new_from_file (((const GnomeStockPixmapEntryPath *) entry)->pathname); |
---|
349 | break; |
---|
350 | case GNOME_STOCK_PIXMAP_TYPE_IMLIB: |
---|
351 | case GNOME_STOCK_PIXMAP_TYPE_IMLIB_SCALED: |
---|
352 | pixbuf = pixbuf_from_imlib (entry); |
---|
353 | break; |
---|
354 | case GNOME_STOCK_PIXMAP_TYPE_NONE: |
---|
355 | /* (Don't know how to handle these.) */ |
---|
356 | case GNOME_STOCK_PIXMAP_TYPE_WIDGET: |
---|
357 | case GNOME_STOCK_PIXMAP_TYPE_GPIXMAP: |
---|
358 | default: |
---|
359 | pixbuf = NULL; |
---|
360 | } |
---|
361 | |
---|
362 | return pixbuf; |
---|
363 | } |
---|
364 | |
---|
365 | static gchar * |
---|
366 | find_pixmap_in_path (const gchar *filename) |
---|
367 | { |
---|
368 | gchar *path, *file; |
---|
369 | |
---|
370 | if (filename [0] == '/') |
---|
371 | return g_strdup (filename); |
---|
372 | |
---|
373 | file = gnome_pixmap_file (filename); |
---|
374 | if (file) |
---|
375 | return file; |
---|
376 | |
---|
377 | path = g_strconcat (g_get_prgname (), "/", filename, NULL); |
---|
378 | file = gnome_pixmap_file (path); |
---|
379 | if (file) { |
---|
380 | g_free (path); |
---|
381 | return file; |
---|
382 | } |
---|
383 | g_free (path); |
---|
384 | |
---|
385 | path = g_getenv ("GNOME_PATH"); |
---|
386 | if (path != NULL) { |
---|
387 | gchar **pathv; |
---|
388 | gint i; |
---|
389 | |
---|
390 | pathv = g_strsplit (path, ":", 0); |
---|
391 | for (i = 0; pathv[i] != NULL; i++) { |
---|
392 | gchar *s; |
---|
393 | |
---|
394 | s = g_strconcat (pathv[i], "/share/pixmaps/", |
---|
395 | filename, NULL); |
---|
396 | if (g_file_exists (s)) { |
---|
397 | g_strfreev (pathv); |
---|
398 | return s; |
---|
399 | } |
---|
400 | g_free (s); |
---|
401 | |
---|
402 | s = g_strconcat (pathv[i], "/share/pixmaps/", |
---|
403 | g_get_prgname (), "/", |
---|
404 | filename, NULL); |
---|
405 | if (g_file_exists (s)) { |
---|
406 | g_strfreev (pathv); |
---|
407 | return s; |
---|
408 | } |
---|
409 | g_free (s); |
---|
410 | } |
---|
411 | g_strfreev (pathv); |
---|
412 | } |
---|
413 | |
---|
414 | return NULL; |
---|
415 | } |
---|
416 | |
---|
417 | /** |
---|
418 | * bonobo_ui_util_xml_get_icon_pixbuf: |
---|
419 | * @node: the node |
---|
420 | * @prepend_menu: whether the pixbuf is for a menu item |
---|
421 | * |
---|
422 | * This routine returns a GdkPixbuf for a @node, if @prepend_menu is |
---|
423 | * TRUE then if it is a stock pixbuf 'Menu_' will be prepended to |
---|
424 | * the stock name. Otherwise the pixbuf is extracted either from the |
---|
425 | * node, a filename, or the stock system. |
---|
426 | * |
---|
427 | * Return value: A handed reference to the extracted pixbuf. |
---|
428 | **/ |
---|
429 | GdkPixbuf * |
---|
430 | bonobo_ui_util_xml_get_icon_pixbuf (BonoboUINode *node, gboolean prepend_menu) |
---|
431 | { |
---|
432 | char *key; |
---|
433 | char *type, *text; |
---|
434 | GdkPixbuf *icon_pixbuf = NULL; |
---|
435 | static GHashTable *pixbuf_cache = NULL; |
---|
436 | |
---|
437 | g_return_val_if_fail (node != NULL, NULL); |
---|
438 | |
---|
439 | if (!(type = bonobo_ui_node_get_attr (node, "pixtype"))) |
---|
440 | return NULL; |
---|
441 | |
---|
442 | if (!(text = bonobo_ui_node_get_attr (node, "pixname"))) { |
---|
443 | bonobo_ui_node_free_string (type); |
---|
444 | return NULL; |
---|
445 | } |
---|
446 | |
---|
447 | key = g_strdup_printf ("%s!%s!%d", type, text, prepend_menu?1:0); |
---|
448 | |
---|
449 | if (!pixbuf_cache) |
---|
450 | pixbuf_cache = g_hash_table_new (g_str_hash, g_str_equal); |
---|
451 | |
---|
452 | if ((icon_pixbuf = g_hash_table_lookup (pixbuf_cache, key))) { |
---|
453 | g_free (key); |
---|
454 | bonobo_ui_node_free_string (text); |
---|
455 | bonobo_ui_node_free_string (type); |
---|
456 | gdk_pixbuf_ref (icon_pixbuf); |
---|
457 | return icon_pixbuf; |
---|
458 | } |
---|
459 | |
---|
460 | if (!strcmp (type, "stock")) { |
---|
461 | |
---|
462 | if (prepend_menu) { |
---|
463 | char *fullname = g_strconcat ("Menu_", text, NULL); |
---|
464 | icon_pixbuf = get_stock_pixbuf (fullname); |
---|
465 | g_free (fullname); |
---|
466 | } else |
---|
467 | icon_pixbuf = get_stock_pixbuf (text); |
---|
468 | |
---|
469 | } else if (!strcmp (type, "filename")) { |
---|
470 | char *name = find_pixmap_in_path (text); |
---|
471 | |
---|
472 | if ((name == NULL) || !g_file_exists (name)) |
---|
473 | g_warning ("Could not find GNOME pixmap file %s", text); |
---|
474 | else |
---|
475 | icon_pixbuf = gdk_pixbuf_new_from_file (name); |
---|
476 | |
---|
477 | g_free (name); |
---|
478 | } else if (!strcmp (type, "pixbuf")) { |
---|
479 | |
---|
480 | /* Get pointer to GdkPixbuf */ |
---|
481 | icon_pixbuf = bonobo_ui_util_xml_to_pixbuf (text); |
---|
482 | |
---|
483 | g_return_val_if_fail (icon_pixbuf != NULL, NULL); |
---|
484 | } else |
---|
485 | g_warning ("Unknown icon_pixbuf type '%s'", type); |
---|
486 | |
---|
487 | bonobo_ui_node_free_string (text); |
---|
488 | bonobo_ui_node_free_string (type); |
---|
489 | |
---|
490 | if (icon_pixbuf) { |
---|
491 | gdk_pixbuf_ref (icon_pixbuf); |
---|
492 | g_hash_table_insert (pixbuf_cache, key, icon_pixbuf); |
---|
493 | } |
---|
494 | |
---|
495 | return icon_pixbuf; |
---|
496 | } |
---|
497 | |
---|
498 | /** |
---|
499 | * bonobo_ui_util_xml_get_icon_pixmap_widget: |
---|
500 | * @node: the node |
---|
501 | * @prepend_menu: whether the pixbuf is for a menu item |
---|
502 | * |
---|
503 | * This function extracts a pixbuf from the node and returns a GtkWidget |
---|
504 | * containing a display of the pixbuf. |
---|
505 | * |
---|
506 | * Return value: the widget. |
---|
507 | **/ |
---|
508 | GtkWidget * |
---|
509 | bonobo_ui_util_xml_get_icon_pixmap_widget (BonoboUINode *node, gboolean prepend_menu) |
---|
510 | { |
---|
511 | GdkPixbuf *pixbuf; |
---|
512 | GtkWidget *icon; |
---|
513 | |
---|
514 | g_return_val_if_fail (node != NULL, NULL); |
---|
515 | |
---|
516 | pixbuf = bonobo_ui_util_xml_get_icon_pixbuf (node, prepend_menu); |
---|
517 | if (pixbuf == NULL) |
---|
518 | return NULL; |
---|
519 | |
---|
520 | icon = bonobo_ui_icon_new (); |
---|
521 | if (!bonobo_ui_icon_set_from_pixbuf (BONOBO_UI_ICON (icon), pixbuf)) { |
---|
522 | gtk_widget_unref (icon); |
---|
523 | icon = NULL; |
---|
524 | } |
---|
525 | |
---|
526 | gdk_pixbuf_unref (pixbuf); |
---|
527 | return icon; |
---|
528 | } |
---|
529 | |
---|
530 | /** |
---|
531 | * bonobo_ui_util_xml_set_pixbuf: |
---|
532 | * @node: the node |
---|
533 | * @pixbuf: the pixbuf |
---|
534 | * |
---|
535 | * Associate @pixbuf with this @node by stringifying it and setting |
---|
536 | * the requisite attributes. |
---|
537 | **/ |
---|
538 | void |
---|
539 | bonobo_ui_util_xml_set_pixbuf (BonoboUINode *node, |
---|
540 | GdkPixbuf *pixbuf) |
---|
541 | { |
---|
542 | char *data; |
---|
543 | |
---|
544 | g_return_if_fail (node != NULL); |
---|
545 | g_return_if_fail (pixbuf != NULL); |
---|
546 | |
---|
547 | bonobo_ui_node_set_attr (node, "pixtype", "pixbuf"); |
---|
548 | data = bonobo_ui_util_pixbuf_to_xml (pixbuf); |
---|
549 | bonobo_ui_node_set_attr (node, "pixname", data); |
---|
550 | g_free (data); |
---|
551 | } |
---|
552 | |
---|
553 | /** |
---|
554 | * bonobo_ui_util_xml_set_pix_xpm: |
---|
555 | * @node: the node |
---|
556 | * @xpm: an xpm |
---|
557 | * |
---|
558 | * Associate @xpm with this @node by stringifying it and setting |
---|
559 | * the requisite attributes. |
---|
560 | **/ |
---|
561 | void |
---|
562 | bonobo_ui_util_xml_set_pix_xpm (BonoboUINode *node, |
---|
563 | const char **xpm) |
---|
564 | { |
---|
565 | GdkPixbuf *pixbuf; |
---|
566 | |
---|
567 | g_return_if_fail (xpm != NULL); |
---|
568 | g_return_if_fail (node != NULL); |
---|
569 | |
---|
570 | pixbuf = gdk_pixbuf_new_from_xpm_data (xpm); |
---|
571 | |
---|
572 | bonobo_ui_util_xml_set_pixbuf (node, pixbuf); |
---|
573 | |
---|
574 | gdk_pixbuf_unref (pixbuf); |
---|
575 | } |
---|
576 | /** |
---|
577 | * bonobo_ui_util_xml_set_pix_stock: |
---|
578 | * @node: the node |
---|
579 | * @name: the stock name |
---|
580 | * |
---|
581 | * Associate the stock pixmap named @name with this @node |
---|
582 | **/ |
---|
583 | void |
---|
584 | bonobo_ui_util_xml_set_pix_stock (BonoboUINode *node, |
---|
585 | const char *name) |
---|
586 | { |
---|
587 | g_return_if_fail (node != NULL); |
---|
588 | g_return_if_fail (name != NULL); |
---|
589 | |
---|
590 | bonobo_ui_node_set_attr (node, "pixtype", "stock"); |
---|
591 | bonobo_ui_node_set_attr (node, "pixname", name); |
---|
592 | } |
---|
593 | |
---|
594 | /** |
---|
595 | * bonobo_ui_util_xml_set_pix_fname: |
---|
596 | * @node: the node |
---|
597 | * @name: the filename |
---|
598 | * |
---|
599 | * Associate a pixmap filename @name with a @node |
---|
600 | **/ |
---|
601 | void |
---|
602 | bonobo_ui_util_xml_set_pix_fname (BonoboUINode *node, |
---|
603 | const char *name) |
---|
604 | { |
---|
605 | g_return_if_fail (node != NULL); |
---|
606 | g_return_if_fail (name != NULL); |
---|
607 | |
---|
608 | bonobo_ui_node_set_attr (node, "pixtype", "filename"); |
---|
609 | bonobo_ui_node_set_attr (node, "pixname", name); |
---|
610 | } |
---|
611 | |
---|
612 | |
---|
613 | static void |
---|
614 | free_help_menu_entry (GtkWidget *widget, GnomeHelpMenuEntry *entry) |
---|
615 | { |
---|
616 | g_free (entry->name); |
---|
617 | g_free (entry->path); |
---|
618 | g_free (entry); |
---|
619 | } |
---|
620 | |
---|
621 | static void |
---|
622 | bonobo_help_display_cb (BonoboUIComponent *component, |
---|
623 | gpointer user_data, |
---|
624 | const char *cname) |
---|
625 | { |
---|
626 | gnome_help_display (component, user_data); |
---|
627 | } |
---|
628 | |
---|
629 | /* |
---|
630 | * Cut and paste job so we can overcome gnome-libs brokenness. |
---|
631 | */ |
---|
632 | static char * |
---|
633 | bonobo_help_file_find_file (const char *datadir, const char *app, |
---|
634 | const char *path) |
---|
635 | { |
---|
636 | GList *language_list; |
---|
637 | GString *buf; |
---|
638 | |
---|
639 | gchar *res= NULL; |
---|
640 | gchar *p, c = 0; |
---|
641 | |
---|
642 | language_list= gnome_i18n_get_language_list ("LC_MESSAGES"); |
---|
643 | |
---|
644 | while (!res && language_list) { |
---|
645 | const gchar *lang; |
---|
646 | |
---|
647 | lang = language_list->data; |
---|
648 | |
---|
649 | buf = g_string_new (NULL); |
---|
650 | g_string_sprintf (buf, "%s/gnome/help/%s/%s/%s", |
---|
651 | datadir, app, lang, path); |
---|
652 | res = g_strdup (buf->str); |
---|
653 | p = strrchr (res, '#'); |
---|
654 | if (p) { |
---|
655 | c = *p; |
---|
656 | *p = '\0'; |
---|
657 | } |
---|
658 | g_string_free (buf, TRUE); |
---|
659 | |
---|
660 | if (!g_file_exists (res)) { |
---|
661 | g_free (res); |
---|
662 | res = NULL; |
---|
663 | } |
---|
664 | |
---|
665 | if (c && res) { |
---|
666 | *p = c; |
---|
667 | c = 0; |
---|
668 | } |
---|
669 | |
---|
670 | language_list = language_list->next; |
---|
671 | } |
---|
672 | |
---|
673 | return res; |
---|
674 | } |
---|
675 | |
---|
676 | /** |
---|
677 | * bonobo_ui_util_build_help_menu: |
---|
678 | * @listener: associated component |
---|
679 | * @app_datadir: application datadir |
---|
680 | * @app_name: application name |
---|
681 | * @parent: toplevel node |
---|
682 | * |
---|
683 | * This routine inserts all the help menu items appropriate for this |
---|
684 | * application as children of the @parent node. |
---|
685 | **/ |
---|
686 | void |
---|
687 | bonobo_ui_util_build_help_menu (BonoboUIComponent *listener, |
---|
688 | const char *app_datadir, |
---|
689 | const char *app_name, |
---|
690 | BonoboUINode *parent) |
---|
691 | { |
---|
692 | char buf [1024]; |
---|
693 | char *topic_file; |
---|
694 | FILE *file; |
---|
695 | |
---|
696 | g_return_if_fail (parent != NULL); |
---|
697 | g_return_if_fail (app_name != NULL); |
---|
698 | g_return_if_fail (BONOBO_IS_UI_COMPONENT (listener)); |
---|
699 | |
---|
700 | /* Try to open help topics file */ |
---|
701 | topic_file = gnome_help_file_find_file ((char *)app_name, "topic.dat"); |
---|
702 | |
---|
703 | /* Do something sensible */ |
---|
704 | if (!topic_file && app_datadir) |
---|
705 | topic_file = bonobo_help_file_find_file ( |
---|
706 | app_datadir, app_name, "topic.dat"); |
---|
707 | |
---|
708 | if (!topic_file || !(file = fopen (topic_file, "rt"))) { |
---|
709 | g_warning ("Could not open help topics file %s for app %s", |
---|
710 | topic_file ? topic_file : "NULL", app_name); |
---|
711 | |
---|
712 | g_free (topic_file); |
---|
713 | return; |
---|
714 | } |
---|
715 | g_free (topic_file); |
---|
716 | |
---|
717 | /* Read in the help topics and create menu items for them */ |
---|
718 | while (fgets (buf, sizeof (buf), file)) { |
---|
719 | unsigned char *s, *id; |
---|
720 | GnomeHelpMenuEntry *entry; |
---|
721 | BonoboUINode *node; |
---|
722 | |
---|
723 | /* Format of lines is "help_file_name whitespace* menu_title" */ |
---|
724 | for (s = buf; *s && !isspace (*s); s++) |
---|
725 | ; |
---|
726 | |
---|
727 | *s++ = '\0'; |
---|
728 | |
---|
729 | for (; *s && isspace (*s); s++) |
---|
730 | ; |
---|
731 | |
---|
732 | if (s [strlen (s) - 1] == '\n') |
---|
733 | s [strlen (s) - 1] = '\0'; |
---|
734 | |
---|
735 | node = bonobo_ui_node_new ("menuitem"); |
---|
736 | /* Try and make something unique */ |
---|
737 | id = g_strdup_printf ("Help%s%s", app_name, buf); |
---|
738 | bonobo_ui_node_set_attr (node, "name", id); |
---|
739 | bonobo_ui_node_set_attr (node, "verb", id); |
---|
740 | |
---|
741 | { |
---|
742 | char *encoded = bonobo_ui_util_encode_str (s); |
---|
743 | bonobo_ui_node_set_attr (node, "label", encoded); |
---|
744 | g_free (encoded); |
---|
745 | } |
---|
746 | |
---|
747 | bonobo_ui_node_add_child (parent, node); |
---|
748 | |
---|
749 | /* Create help menu entry */ |
---|
750 | entry = g_new (GnomeHelpMenuEntry, 1); |
---|
751 | entry->name = g_strdup (app_name); |
---|
752 | entry->path = g_strdup (buf); |
---|
753 | |
---|
754 | bonobo_ui_component_add_verb (listener, id, |
---|
755 | bonobo_help_display_cb, entry); |
---|
756 | |
---|
757 | gtk_signal_connect (GTK_OBJECT (listener), "destroy", |
---|
758 | (GtkSignalFunc) free_help_menu_entry, |
---|
759 | entry); |
---|
760 | g_free (id); |
---|
761 | } |
---|
762 | |
---|
763 | fclose (file); |
---|
764 | } |
---|
765 | |
---|
766 | /** |
---|
767 | * bonobo_ui_util_build_accel: |
---|
768 | * @accelerator_key: the accelerator key |
---|
769 | * @accelerator_mods: the accelerator mods |
---|
770 | * @verb: the associated verb. |
---|
771 | * |
---|
772 | * This routine builds an accelerator node from the key and mod mask |
---|
773 | * and associates it with a verb. |
---|
774 | * |
---|
775 | * Return value: the built node. |
---|
776 | **/ |
---|
777 | BonoboUINode * |
---|
778 | bonobo_ui_util_build_accel (guint accelerator_key, |
---|
779 | GdkModifierType accelerator_mods, |
---|
780 | const char *verb) |
---|
781 | { |
---|
782 | char *name; |
---|
783 | BonoboUINode *ret; |
---|
784 | |
---|
785 | name = bonobo_ui_util_accel_name (accelerator_key, accelerator_mods); |
---|
786 | ret = bonobo_ui_node_new ("accel"); |
---|
787 | bonobo_ui_node_set_attr (ret, "name", name); |
---|
788 | g_free (name); |
---|
789 | bonobo_ui_node_set_attr (ret, "verb", verb); |
---|
790 | |
---|
791 | return ret; |
---|
792 | } |
---|
793 | |
---|
794 | /** |
---|
795 | * bonobo_ui_util_new_menu: |
---|
796 | * @submenu: whether it is a menu or submenu |
---|
797 | * @name: the path element name of the menu |
---|
798 | * @label: the label |
---|
799 | * @tip: the description |
---|
800 | * @verb: the associated verb |
---|
801 | * |
---|
802 | * A helper routine to create a menu or submenu with associated |
---|
803 | * information - this routine is strongly deprecated. |
---|
804 | * |
---|
805 | * Return value: the constructed node. |
---|
806 | **/ |
---|
807 | BonoboUINode * |
---|
808 | bonobo_ui_util_new_menu (gboolean submenu, |
---|
809 | const char *name, |
---|
810 | const char *label, |
---|
811 | const char *tip, |
---|
812 | const char *verb) |
---|
813 | { |
---|
814 | BonoboUINode *node; |
---|
815 | |
---|
816 | g_return_val_if_fail (name != NULL, NULL); |
---|
817 | |
---|
818 | if (submenu) |
---|
819 | node = bonobo_ui_node_new ("submenu"); |
---|
820 | else |
---|
821 | node = bonobo_ui_node_new ("menuitem"); |
---|
822 | |
---|
823 | bonobo_ui_node_set_attr (node, "name", name); |
---|
824 | if (label) { |
---|
825 | char *encoded = bonobo_ui_util_encode_str (label); |
---|
826 | bonobo_ui_node_set_attr (node, "label", encoded); |
---|
827 | g_free (encoded); |
---|
828 | } |
---|
829 | |
---|
830 | if (tip) { |
---|
831 | char *encoded = bonobo_ui_util_encode_str (tip); |
---|
832 | bonobo_ui_node_set_attr (node, "tip", encoded); |
---|
833 | g_free (encoded); |
---|
834 | } |
---|
835 | |
---|
836 | if (verb) |
---|
837 | bonobo_ui_node_set_attr (node, "verb", verb); |
---|
838 | |
---|
839 | return node; |
---|
840 | } |
---|
841 | |
---|
842 | /** |
---|
843 | * bonobo_ui_util_new_placeholder: |
---|
844 | * @name: path element name of the placeholder |
---|
845 | * @top: whether to delimit at the top |
---|
846 | * @bottom: whether to delimit at the bottom |
---|
847 | * |
---|
848 | * A helper routine to create a menu or submenu with associated |
---|
849 | * information - this routine is strongly deprecated - it is also |
---|
850 | * broken. |
---|
851 | * |
---|
852 | * Return value: the new node |
---|
853 | **/ |
---|
854 | BonoboUINode * |
---|
855 | bonobo_ui_util_new_placeholder (const char *name, |
---|
856 | gboolean top, |
---|
857 | gboolean bottom) |
---|
858 | { |
---|
859 | BonoboUINode *node; |
---|
860 | |
---|
861 | node = bonobo_ui_node_new ("placeholder"); |
---|
862 | |
---|
863 | if (name) |
---|
864 | bonobo_ui_node_set_attr (node, "name", name); |
---|
865 | |
---|
866 | if (top && bottom) |
---|
867 | bonobo_ui_node_set_attr (node, "delimit", "both"); |
---|
868 | else if (top) |
---|
869 | bonobo_ui_node_set_attr (node, "delimit", "top"); |
---|
870 | else if (bottom) |
---|
871 | bonobo_ui_node_set_attr (node, "delimit", "bottom"); |
---|
872 | |
---|
873 | return node; |
---|
874 | } |
---|
875 | |
---|
876 | /** |
---|
877 | * bonobo_ui_util_set_radiogroup: |
---|
878 | * @node: the node |
---|
879 | * @group_name: the group name. |
---|
880 | * |
---|
881 | * This is a helper function that sets the radiogroup to |
---|
882 | * the requested group - deprecated |
---|
883 | **/ |
---|
884 | void |
---|
885 | bonobo_ui_util_set_radiogroup (BonoboUINode *node, |
---|
886 | const char *group_name) |
---|
887 | { |
---|
888 | g_return_if_fail (node != NULL); |
---|
889 | g_return_if_fail (group_name != NULL); |
---|
890 | |
---|
891 | bonobo_ui_node_set_attr (node, "type", "radio"); |
---|
892 | bonobo_ui_node_set_attr (node, "group", group_name); |
---|
893 | } |
---|
894 | |
---|
895 | /** |
---|
896 | * bonobo_ui_util_set_toggle: |
---|
897 | * @node: the node |
---|
898 | * @id: the associated id |
---|
899 | * @init_state: |
---|
900 | * |
---|
901 | * Deprecated, makes a node toggleable. |
---|
902 | **/ |
---|
903 | void |
---|
904 | bonobo_ui_util_set_toggle (BonoboUINode *node, |
---|
905 | const char *id, |
---|
906 | const char *init_state) |
---|
907 | { |
---|
908 | g_return_if_fail (node != NULL); |
---|
909 | |
---|
910 | bonobo_ui_node_set_attr (node, "type", "toggle"); |
---|
911 | if (id) |
---|
912 | bonobo_ui_node_set_attr (node, "id", id); |
---|
913 | if (init_state) |
---|
914 | bonobo_ui_node_set_attr (node, "state", init_state); |
---|
915 | } |
---|
916 | |
---|
917 | /** |
---|
918 | * bonobo_ui_util_new_std_toolbar: |
---|
919 | * @name: |
---|
920 | * @label: |
---|
921 | * @tip: |
---|
922 | * @verb: |
---|
923 | * |
---|
924 | * Deprecated - created a new toolbar item. |
---|
925 | * |
---|
926 | * Return value: |
---|
927 | **/ |
---|
928 | BonoboUINode * |
---|
929 | bonobo_ui_util_new_std_toolbar (const char *name, |
---|
930 | const char *label, |
---|
931 | const char *tip, |
---|
932 | const char *verb) |
---|
933 | { |
---|
934 | BonoboUINode *node; |
---|
935 | |
---|
936 | g_return_val_if_fail (name != NULL, NULL); |
---|
937 | |
---|
938 | node = bonobo_ui_node_new ("toolitem"); |
---|
939 | bonobo_ui_node_set_attr (node, "name", name); |
---|
940 | |
---|
941 | if (label) { |
---|
942 | char *encoded = bonobo_ui_util_encode_str (label); |
---|
943 | bonobo_ui_node_set_attr (node, "label", encoded); |
---|
944 | g_free (encoded); |
---|
945 | } |
---|
946 | if (tip) { |
---|
947 | char *encoded = bonobo_ui_util_encode_str (tip); |
---|
948 | bonobo_ui_node_set_attr (node, "tip", encoded); |
---|
949 | g_free (encoded); |
---|
950 | } |
---|
951 | if (verb) |
---|
952 | bonobo_ui_node_set_attr (node, "verb", verb); |
---|
953 | |
---|
954 | return node; |
---|
955 | } |
---|
956 | /** |
---|
957 | * bonobo_ui_util_new_toggle_toolbar: |
---|
958 | * @name: |
---|
959 | * @label: |
---|
960 | * @tip: |
---|
961 | * @id: |
---|
962 | * |
---|
963 | * Deprecated - creates a new toggle toolbar item |
---|
964 | * |
---|
965 | * Return value: |
---|
966 | **/ |
---|
967 | |
---|
968 | BonoboUINode * |
---|
969 | bonobo_ui_util_new_toggle_toolbar (const char *name, |
---|
970 | const char *label, |
---|
971 | const char *tip, |
---|
972 | const char *id) |
---|
973 | { |
---|
974 | BonoboUINode *node; |
---|
975 | |
---|
976 | g_return_val_if_fail (name != NULL, NULL); |
---|
977 | |
---|
978 | node = bonobo_ui_node_new ("toolitem"); |
---|
979 | bonobo_ui_node_set_attr (node, "type", "toggle"); |
---|
980 | bonobo_ui_node_set_attr (node, "name", name); |
---|
981 | |
---|
982 | if (label) { |
---|
983 | char *encoded = bonobo_ui_util_encode_str (label); |
---|
984 | bonobo_ui_node_set_attr (node, "label", encoded); |
---|
985 | g_free (encoded); |
---|
986 | } |
---|
987 | if (tip) { |
---|
988 | char *encoded = bonobo_ui_util_encode_str (tip); |
---|
989 | bonobo_ui_node_set_attr (node, "tip", encoded); |
---|
990 | g_free (encoded); |
---|
991 | } |
---|
992 | if (id) |
---|
993 | bonobo_ui_node_set_attr (node, "id", id); |
---|
994 | |
---|
995 | return node; |
---|
996 | } |
---|
997 | |
---|
998 | |
---|
999 | /** |
---|
1000 | * bonobo_ui_util_get_ui_fname: |
---|
1001 | * @component_datadir: the datadir for the component. |
---|
1002 | * @file_name: the file name of the xml file. |
---|
1003 | * |
---|
1004 | * Builds a path to the xml file that stores the GUI. |
---|
1005 | * |
---|
1006 | * Return value: the path to the file that describes the |
---|
1007 | * UI or NULL if it is not found. |
---|
1008 | **/ |
---|
1009 | char * |
---|
1010 | bonobo_ui_util_get_ui_fname (const char *component_datadir, |
---|
1011 | const char *file_name) |
---|
1012 | { |
---|
1013 | char *fname, *name; |
---|
1014 | |
---|
1015 | #if 0 |
---|
1016 | /* |
---|
1017 | * This is fundamentally broken. The user has no business defining |
---|
1018 | * his own user interfaces. |
---|
1019 | */ |
---|
1020 | /* |
---|
1021 | * The user copy ? |
---|
1022 | */ |
---|
1023 | fname = g_strdup_printf ("%s/.gnome/ui/%s", |
---|
1024 | g_get_home_dir (), file_name); |
---|
1025 | |
---|
1026 | /* |
---|
1027 | * FIXME: we should compare timestamps vs. the master copy. |
---|
1028 | */ |
---|
1029 | if (g_file_exists (fname)) |
---|
1030 | return fname; |
---|
1031 | g_free (fname); |
---|
1032 | #endif |
---|
1033 | |
---|
1034 | /* |
---|
1035 | * The master copy |
---|
1036 | */ |
---|
1037 | if (component_datadir) { |
---|
1038 | fname = g_strdup_printf ("%s/gnome/ui/%s", |
---|
1039 | component_datadir, file_name); |
---|
1040 | if (g_file_exists (fname)) |
---|
1041 | return fname; |
---|
1042 | g_free (fname); |
---|
1043 | } |
---|
1044 | |
---|
1045 | name = g_strconcat (BONOBO_UIDIR, file_name, NULL); |
---|
1046 | if (g_file_exists (name)) |
---|
1047 | return name; |
---|
1048 | g_free (name); |
---|
1049 | |
---|
1050 | return NULL; |
---|
1051 | } |
---|
1052 | |
---|
1053 | |
---|
1054 | /* To avoid exporting property iterators on BonoboUINode |
---|
1055 | * (not sure those should be public), this hack is used. |
---|
1056 | */ |
---|
1057 | #define XML_NODE(x) ((xmlNode*)(x)) |
---|
1058 | |
---|
1059 | /** |
---|
1060 | * bonobo_ui_util_translate_ui: |
---|
1061 | * @node: the node to start at. |
---|
1062 | * |
---|
1063 | * Quest through a tree looking for translatable properties |
---|
1064 | * ( those prefixed with an '_' ). Translates the value of the |
---|
1065 | * property and removes the leading '_'. |
---|
1066 | **/ |
---|
1067 | void |
---|
1068 | bonobo_ui_util_translate_ui (BonoboUINode *bnode) |
---|
1069 | { |
---|
1070 | BonoboUINode *l; |
---|
1071 | xmlNode *node; |
---|
1072 | xmlAttr *prop; |
---|
1073 | |
---|
1074 | if (!bnode) |
---|
1075 | return; |
---|
1076 | |
---|
1077 | bonobo_ui_node_strip (&bnode); |
---|
1078 | if (!bnode) { |
---|
1079 | g_warning ("All xml stripped away"); |
---|
1080 | return; |
---|
1081 | } |
---|
1082 | |
---|
1083 | node = XML_NODE (bnode); |
---|
1084 | for (prop = node->properties; prop; prop = prop->next) { |
---|
1085 | |
---|
1086 | /* FIXME: with more evilness we can make this yet faster */ |
---|
1087 | |
---|
1088 | /* Find translatable properties */ |
---|
1089 | if (prop->name && prop->name [0] == '_') { |
---|
1090 | char *encoded; |
---|
1091 | xmlChar *value; |
---|
1092 | xmlChar *newname; |
---|
1093 | |
---|
1094 | value = xmlNodeListGetString (NULL, prop->val, 1); |
---|
1095 | |
---|
1096 | encoded = bonobo_ui_util_encode_str (_(value)); |
---|
1097 | if (prop->val) |
---|
1098 | xmlFreeNodeList (prop->val); |
---|
1099 | |
---|
1100 | /* We know there are no entities, it's a hex string */ |
---|
1101 | prop->val = xmlStringGetNodeList (NULL, encoded); |
---|
1102 | g_free (encoded); |
---|
1103 | |
---|
1104 | bonobo_ui_node_free_string (value); |
---|
1105 | |
---|
1106 | newname = xmlStrdup (prop->name + 1); |
---|
1107 | xmlFree ((xmlChar *)prop->name); |
---|
1108 | prop->name = newname; |
---|
1109 | } |
---|
1110 | } |
---|
1111 | |
---|
1112 | for (l = bonobo_ui_node_children (bnode); l; l = bonobo_ui_node_next (l)) |
---|
1113 | bonobo_ui_util_translate_ui (l); |
---|
1114 | } |
---|
1115 | |
---|
1116 | /** |
---|
1117 | * bonobo_ui_util_fixup_help: |
---|
1118 | * @component: the UI component |
---|
1119 | * @node: the node to search under |
---|
1120 | * @app_datadir: the application datadir |
---|
1121 | * @app_name: the application name |
---|
1122 | * |
---|
1123 | * This searches for 'BuiltMenuItems' placeholders, and then |
---|
1124 | * fills them with the application's menu items. |
---|
1125 | **/ |
---|
1126 | void |
---|
1127 | bonobo_ui_util_fixup_help (BonoboUIComponent *component, |
---|
1128 | BonoboUINode *node, |
---|
1129 | const char *app_datadir, |
---|
1130 | const char *app_name) |
---|
1131 | { |
---|
1132 | BonoboUINode *l; |
---|
1133 | gboolean build_here = FALSE; |
---|
1134 | |
---|
1135 | if (!node) |
---|
1136 | return; |
---|
1137 | |
---|
1138 | if (bonobo_ui_node_has_name (node, "placeholder")) { |
---|
1139 | char *txt; |
---|
1140 | |
---|
1141 | if ((txt = bonobo_ui_node_get_attr (node, "name"))) { |
---|
1142 | build_here = !strcmp (txt, "BuiltMenuItems"); |
---|
1143 | bonobo_ui_node_free_string (txt); |
---|
1144 | } |
---|
1145 | } |
---|
1146 | |
---|
1147 | if (build_here) { |
---|
1148 | bonobo_ui_util_build_help_menu ( |
---|
1149 | component, app_datadir, app_name, node); |
---|
1150 | } |
---|
1151 | |
---|
1152 | for (l = bonobo_ui_node_children (node); l; l = bonobo_ui_node_next (l)) |
---|
1153 | bonobo_ui_util_fixup_help (component, l, app_datadir, app_name); |
---|
1154 | } |
---|
1155 | |
---|
1156 | /** |
---|
1157 | * bonobo_ui_util_fixup_icons: |
---|
1158 | * @node: the node |
---|
1159 | * |
---|
1160 | * This function is used to ensure filename pixbuf attributes are |
---|
1161 | * converted to in-line pixbufs on the server side, so that we don't |
---|
1162 | * sent a ( possibly invalid ) filename across the wire. |
---|
1163 | **/ |
---|
1164 | void |
---|
1165 | bonobo_ui_util_fixup_icons (BonoboUINode *node) |
---|
1166 | { |
---|
1167 | BonoboUINode *l; |
---|
1168 | gboolean fixup_here = FALSE; |
---|
1169 | char *txt; |
---|
1170 | |
---|
1171 | if (!node) |
---|
1172 | return; |
---|
1173 | |
---|
1174 | if ((txt = bonobo_ui_node_get_attr (node, "pixtype"))) { |
---|
1175 | fixup_here = !strcmp (txt, "filename"); |
---|
1176 | bonobo_ui_node_free_string (txt); |
---|
1177 | } |
---|
1178 | |
---|
1179 | if (fixup_here && |
---|
1180 | ((txt = bonobo_ui_node_get_attr (node, "pixname")))) { |
---|
1181 | GdkPixbuf *pixbuf = NULL; |
---|
1182 | |
---|
1183 | if (g_path_is_absolute (txt)) |
---|
1184 | pixbuf = gdk_pixbuf_new_from_file (txt); |
---|
1185 | else { |
---|
1186 | gchar *name = find_pixmap_in_path (txt); |
---|
1187 | |
---|
1188 | if (name) { |
---|
1189 | pixbuf = gdk_pixbuf_new_from_file (name); |
---|
1190 | g_free (name); |
---|
1191 | } |
---|
1192 | } |
---|
1193 | |
---|
1194 | if (pixbuf) { |
---|
1195 | gchar *xml = bonobo_ui_util_pixbuf_to_xml (pixbuf); |
---|
1196 | |
---|
1197 | bonobo_ui_node_set_attr (node, "pixtype", "pixbuf"); |
---|
1198 | bonobo_ui_node_set_attr (node, "pixname", xml); |
---|
1199 | g_free (xml); |
---|
1200 | |
---|
1201 | gdk_pixbuf_unref (pixbuf); |
---|
1202 | } |
---|
1203 | |
---|
1204 | bonobo_ui_node_free_string (txt); |
---|
1205 | } |
---|
1206 | |
---|
1207 | for (l = bonobo_ui_node_children (node); l; l = bonobo_ui_node_next (l)) |
---|
1208 | bonobo_ui_util_fixup_icons (l); |
---|
1209 | } |
---|
1210 | |
---|
1211 | /** |
---|
1212 | * bonobo_ui_util_new_ui: |
---|
1213 | * @component: The component help callback should be on |
---|
1214 | * @file_name: Filename of the UI file |
---|
1215 | * @app_name: Application name ( for finding help ) |
---|
1216 | * |
---|
1217 | * Loads an xml tree from a file, cleans the |
---|
1218 | * doc cruft from its nodes; and translates the nodes. |
---|
1219 | * |
---|
1220 | * Return value: The translated tree ready to be merged. |
---|
1221 | **/ |
---|
1222 | BonoboUINode * |
---|
1223 | bonobo_ui_util_new_ui (BonoboUIComponent *component, |
---|
1224 | const char *file_name, |
---|
1225 | const char *app_datadir, |
---|
1226 | const char *app_name) |
---|
1227 | { |
---|
1228 | BonoboUINode *node; |
---|
1229 | |
---|
1230 | g_return_val_if_fail (app_name != NULL, NULL); |
---|
1231 | g_return_val_if_fail (file_name != NULL, NULL); |
---|
1232 | |
---|
1233 | node = bonobo_ui_node_from_file (file_name); |
---|
1234 | |
---|
1235 | bonobo_ui_util_translate_ui (node); |
---|
1236 | |
---|
1237 | if (component) |
---|
1238 | bonobo_ui_util_fixup_help (component, node, app_datadir, app_name); |
---|
1239 | |
---|
1240 | bonobo_ui_util_fixup_icons (node); |
---|
1241 | |
---|
1242 | return node; |
---|
1243 | } |
---|
1244 | |
---|
1245 | typedef struct { |
---|
1246 | char *file_name; |
---|
1247 | char *app_datadir; |
---|
1248 | char *app_name; |
---|
1249 | char *tree; |
---|
1250 | } BonoboUINodeCacheEntry; |
---|
1251 | |
---|
1252 | static guint |
---|
1253 | node_hash (gconstpointer key) |
---|
1254 | { |
---|
1255 | BonoboUINodeCacheEntry *entry = (BonoboUINodeCacheEntry *)key; |
---|
1256 | /* Ignore the app_datadir in the hash, since that |
---|
1257 | is also in file_name (always?) */ |
---|
1258 | return g_str_hash (entry->file_name) ^ g_str_hash (entry->app_name); |
---|
1259 | } |
---|
1260 | |
---|
1261 | static gint |
---|
1262 | node_equal (gconstpointer a, |
---|
1263 | gconstpointer b) |
---|
1264 | { |
---|
1265 | BonoboUINodeCacheEntry *entry_a = (BonoboUINodeCacheEntry *)a; |
---|
1266 | BonoboUINodeCacheEntry *entry_b = (BonoboUINodeCacheEntry *)b; |
---|
1267 | |
---|
1268 | return (strcmp (entry_a->file_name, entry_b->file_name) == 0) && |
---|
1269 | (strcmp (entry_a->app_name, entry_b->app_name) == 0) && |
---|
1270 | ((entry_a->app_datadir == NULL && entry_b->app_datadir == NULL) || |
---|
1271 | (entry_a->app_datadir != NULL && entry_b->app_datadir != NULL && |
---|
1272 | (strcmp (entry_a->app_datadir, entry_b->app_datadir) == 0))); |
---|
1273 | } |
---|
1274 | |
---|
1275 | static GHashTable *loaded_node_cache = NULL; |
---|
1276 | |
---|
1277 | static void |
---|
1278 | free_node_cache_entry (BonoboUINodeCacheEntry *entry) |
---|
1279 | { |
---|
1280 | g_free (entry->file_name); |
---|
1281 | g_free (entry->app_datadir); |
---|
1282 | g_free (entry->app_name); |
---|
1283 | xmlFree (entry->tree); |
---|
1284 | g_free (entry); |
---|
1285 | } |
---|
1286 | |
---|
1287 | static void |
---|
1288 | free_loaded_node_cache (void) |
---|
1289 | { |
---|
1290 | if (loaded_node_cache) { |
---|
1291 | g_hash_table_foreach (loaded_node_cache, |
---|
1292 | (GHFunc) free_node_cache_entry, |
---|
1293 | NULL); |
---|
1294 | g_hash_table_destroy (loaded_node_cache); |
---|
1295 | loaded_node_cache = NULL; |
---|
1296 | } |
---|
1297 | } |
---|
1298 | |
---|
1299 | /** |
---|
1300 | * bonobo_ui_util_set_ui: |
---|
1301 | * @component: the component |
---|
1302 | * @app_datadir: the application datadir eg. /opt/gnome/share |
---|
1303 | * @file_name: the filename of the file to merge relative to the datadir. |
---|
1304 | * @app_name: the application name - for help merging |
---|
1305 | * |
---|
1306 | * This function loads the UI from the associated file, translates it, |
---|
1307 | * fixes up all the menus, ensures pixbuf filenames are resolved to xml |
---|
1308 | * and then merges the XML to the remote container - this is the best |
---|
1309 | * and most simple entry point for the new UI code. |
---|
1310 | **/ |
---|
1311 | void |
---|
1312 | bonobo_ui_util_set_ui (BonoboUIComponent *component, |
---|
1313 | const char *app_datadir, |
---|
1314 | const char *file_name, |
---|
1315 | const char *app_name) |
---|
1316 | { |
---|
1317 | char *fname; |
---|
1318 | char *ui; |
---|
1319 | BonoboUINodeCacheEntry entry, *cached; |
---|
1320 | BonoboUINode *node; |
---|
1321 | |
---|
1322 | if (!loaded_node_cache) { |
---|
1323 | loaded_node_cache = g_hash_table_new (node_hash, |
---|
1324 | node_equal); |
---|
1325 | g_atexit (free_loaded_node_cache); |
---|
1326 | } |
---|
1327 | |
---|
1328 | if (bonobo_ui_component_get_container (component) == CORBA_OBJECT_NIL) { |
---|
1329 | g_warning ("Component must be associated with a container first " |
---|
1330 | "see bonobo_component_set_container"); |
---|
1331 | return; |
---|
1332 | } |
---|
1333 | |
---|
1334 | fname = bonobo_ui_util_get_ui_fname (app_datadir, file_name); |
---|
1335 | if (!fname) { |
---|
1336 | g_warning ("Can't find '%s' to load ui from", file_name); |
---|
1337 | return; |
---|
1338 | } |
---|
1339 | |
---|
1340 | /* FIXME: May want to stat the file to see if it changed? */ |
---|
1341 | entry.file_name = (char *)fname; |
---|
1342 | entry.app_datadir = (char *)app_datadir; |
---|
1343 | entry.app_name = (char *)app_name; |
---|
1344 | |
---|
1345 | cached = g_hash_table_lookup (loaded_node_cache, &entry); |
---|
1346 | if (cached) |
---|
1347 | ui = cached->tree; |
---|
1348 | else { |
---|
1349 | node = bonobo_ui_util_new_ui (component, fname, app_datadir, app_name); |
---|
1350 | ui = bonobo_ui_node_to_string (node, TRUE); |
---|
1351 | bonobo_ui_node_free (node); |
---|
1352 | |
---|
1353 | cached = g_new (BonoboUINodeCacheEntry, 1); |
---|
1354 | |
---|
1355 | cached->file_name = g_strdup (fname); |
---|
1356 | cached->app_datadir = g_strdup (app_datadir); |
---|
1357 | cached->app_name = g_strdup (app_name); |
---|
1358 | cached->tree = ui; |
---|
1359 | |
---|
1360 | g_hash_table_insert (loaded_node_cache, |
---|
1361 | cached, cached); |
---|
1362 | } |
---|
1363 | |
---|
1364 | if (ui) |
---|
1365 | bonobo_ui_component_set (component, "/", ui, NULL); |
---|
1366 | |
---|
1367 | g_free (fname); |
---|
1368 | } |
---|
1369 | |
---|
1370 | /* |
---|
1371 | * Evil code, cut and pasted from gtkaccelgroup.c |
---|
1372 | * needs de-Soptimizing :-) |
---|
1373 | */ |
---|
1374 | |
---|
1375 | #define DELIM_PRE '*' |
---|
1376 | #define DELIM_PRE_S "*" |
---|
1377 | #define DELIM_POST '*' |
---|
1378 | #define DELIM_POST_S "*" |
---|
1379 | |
---|
1380 | static inline gboolean |
---|
1381 | is_alt (const gchar *string) |
---|
1382 | { |
---|
1383 | return ((string[0] == DELIM_PRE) && |
---|
1384 | (string[1] == 'a' || string[1] == 'A') && |
---|
1385 | (string[2] == 'l' || string[2] == 'L') && |
---|
1386 | (string[3] == 't' || string[3] == 'T') && |
---|
1387 | (string[4] == DELIM_POST)); |
---|
1388 | } |
---|
1389 | |
---|
1390 | static inline gboolean |
---|
1391 | is_ctl (const gchar *string) |
---|
1392 | { |
---|
1393 | return ((string[0] == DELIM_PRE) && |
---|
1394 | (string[1] == 'c' || string[1] == 'C') && |
---|
1395 | (string[2] == 't' || string[2] == 'T') && |
---|
1396 | (string[3] == 'l' || string[3] == 'L') && |
---|
1397 | (string[4] == DELIM_POST)); |
---|
1398 | } |
---|
1399 | |
---|
1400 | static inline gboolean |
---|
1401 | is_modx (const gchar *string) |
---|
1402 | { |
---|
1403 | return ((string[0] == DELIM_PRE) && |
---|
1404 | (string[1] == 'm' || string[1] == 'M') && |
---|
1405 | (string[2] == 'o' || string[2] == 'O') && |
---|
1406 | (string[3] == 'd' || string[3] == 'D') && |
---|
1407 | (string[4] >= '1' && string[4] <= '5') && |
---|
1408 | (string[5] == DELIM_POST)); |
---|
1409 | } |
---|
1410 | |
---|
1411 | static inline gboolean |
---|
1412 | is_ctrl (const gchar *string) |
---|
1413 | { |
---|
1414 | return ((string[0] == DELIM_PRE) && |
---|
1415 | (string[1] == 'c' || string[1] == 'C') && |
---|
1416 | (string[2] == 't' || string[2] == 'T') && |
---|
1417 | (string[3] == 'r' || string[3] == 'R') && |
---|
1418 | (string[4] == 'l' || string[4] == 'L') && |
---|
1419 | (string[5] == DELIM_POST)); |
---|
1420 | } |
---|
1421 | |
---|
1422 | static inline gboolean |
---|
1423 | is_shft (const gchar *string) |
---|
1424 | { |
---|
1425 | return ((string[0] == DELIM_PRE) && |
---|
1426 | (string[1] == 's' || string[1] == 'S') && |
---|
1427 | (string[2] == 'h' || string[2] == 'H') && |
---|
1428 | (string[3] == 'f' || string[3] == 'F') && |
---|
1429 | (string[4] == 't' || string[4] == 'T') && |
---|
1430 | (string[5] == DELIM_POST)); |
---|
1431 | } |
---|
1432 | |
---|
1433 | static inline gboolean |
---|
1434 | is_shift (const gchar *string) |
---|
1435 | { |
---|
1436 | return ((string[0] == DELIM_PRE) && |
---|
1437 | (string[1] == 's' || string[1] == 'S') && |
---|
1438 | (string[2] == 'h' || string[2] == 'H') && |
---|
1439 | (string[3] == 'i' || string[3] == 'I') && |
---|
1440 | (string[4] == 'f' || string[4] == 'F') && |
---|
1441 | (string[5] == 't' || string[5] == 'T') && |
---|
1442 | (string[6] == DELIM_POST)); |
---|
1443 | } |
---|
1444 | |
---|
1445 | static inline gboolean |
---|
1446 | is_control (const gchar *string) |
---|
1447 | { |
---|
1448 | return ((string[0] == DELIM_PRE) && |
---|
1449 | (string[1] == 'c' || string[1] == 'C') && |
---|
1450 | (string[2] == 'o' || string[2] == 'O') && |
---|
1451 | (string[3] == 'n' || string[3] == 'N') && |
---|
1452 | (string[4] == 't' || string[4] == 'T') && |
---|
1453 | (string[5] == 'r' || string[5] == 'R') && |
---|
1454 | (string[6] == 'o' || string[6] == 'O') && |
---|
1455 | (string[7] == 'l' || string[7] == 'L') && |
---|
1456 | (string[8] == DELIM_POST)); |
---|
1457 | } |
---|
1458 | |
---|
1459 | static inline gboolean |
---|
1460 | is_release (const gchar *string) |
---|
1461 | { |
---|
1462 | return ((string[0] == DELIM_PRE) && |
---|
1463 | (string[1] == 'r' || string[1] == 'R') && |
---|
1464 | (string[2] == 'e' || string[2] == 'E') && |
---|
1465 | (string[3] == 'l' || string[3] == 'L') && |
---|
1466 | (string[4] == 'e' || string[4] == 'E') && |
---|
1467 | (string[5] == 'a' || string[5] == 'A') && |
---|
1468 | (string[6] == 's' || string[6] == 'S') && |
---|
1469 | (string[7] == 'e' || string[7] == 'E') && |
---|
1470 | (string[8] == DELIM_POST)); |
---|
1471 | } |
---|
1472 | |
---|
1473 | /** |
---|
1474 | * bonobo_ui_util_accel_parse: |
---|
1475 | * @accelerator: the accelerator name |
---|
1476 | * @accelerator_key: output of the key |
---|
1477 | * @accelerator_mods: output of the mods |
---|
1478 | * |
---|
1479 | * This parses the accelerator string and returns the key and mods |
---|
1480 | * associated with it - using a similar format to Gtk+ but one which |
---|
1481 | * doesn't involve inefficient XML entities and avoids other misc. |
---|
1482 | * problems. |
---|
1483 | **/ |
---|
1484 | void |
---|
1485 | bonobo_ui_util_accel_parse (char *accelerator, |
---|
1486 | guint *accelerator_key, |
---|
1487 | GdkModifierType *accelerator_mods) |
---|
1488 | { |
---|
1489 | guint keyval; |
---|
1490 | GdkModifierType mods; |
---|
1491 | gint len; |
---|
1492 | |
---|
1493 | g_return_if_fail (accelerator_key != NULL); |
---|
1494 | *accelerator_key = 0; |
---|
1495 | g_return_if_fail (accelerator_mods != NULL); |
---|
1496 | *accelerator_mods = 0; |
---|
1497 | g_return_if_fail (accelerator != NULL); |
---|
1498 | |
---|
1499 | if (accelerator_key) |
---|
1500 | *accelerator_key = 0; |
---|
1501 | if (accelerator_mods) |
---|
1502 | *accelerator_mods = 0; |
---|
1503 | |
---|
1504 | keyval = 0; |
---|
1505 | mods = 0; |
---|
1506 | len = strlen (accelerator); |
---|
1507 | while (len) |
---|
1508 | { |
---|
1509 | if (*accelerator == DELIM_PRE) |
---|
1510 | { |
---|
1511 | if (len >= 9 && is_release (accelerator)) |
---|
1512 | { |
---|
1513 | accelerator += 9; |
---|
1514 | len -= 9; |
---|
1515 | mods |= GDK_RELEASE_MASK; |
---|
1516 | } |
---|
1517 | else if (len >= 9 && is_control (accelerator)) |
---|
1518 | { |
---|
1519 | accelerator += 9; |
---|
1520 | len -= 9; |
---|
1521 | mods |= GDK_CONTROL_MASK; |
---|
1522 | } |
---|
1523 | else if (len >= 7 && is_shift (accelerator)) |
---|
1524 | { |
---|
1525 | accelerator += 7; |
---|
1526 | len -= 7; |
---|
1527 | mods |= GDK_SHIFT_MASK; |
---|
1528 | } |
---|
1529 | else if (len >= 6 && is_shft (accelerator)) |
---|
1530 | { |
---|
1531 | accelerator += 6; |
---|
1532 | len -= 6; |
---|
1533 | mods |= GDK_SHIFT_MASK; |
---|
1534 | } |
---|
1535 | else if (len >= 6 && is_ctrl (accelerator)) |
---|
1536 | { |
---|
1537 | accelerator += 6; |
---|
1538 | len -= 6; |
---|
1539 | mods |= GDK_CONTROL_MASK; |
---|
1540 | } |
---|
1541 | else if (len >= 6 && is_modx (accelerator)) |
---|
1542 | { |
---|
1543 | static const guint mod_vals[] = { |
---|
1544 | GDK_MOD1_MASK, GDK_MOD2_MASK, GDK_MOD3_MASK, |
---|
1545 | GDK_MOD4_MASK, GDK_MOD5_MASK |
---|
1546 | }; |
---|
1547 | |
---|
1548 | len -= 6; |
---|
1549 | accelerator += 4; |
---|
1550 | mods |= mod_vals[*accelerator - '1']; |
---|
1551 | accelerator += 2; |
---|
1552 | } |
---|
1553 | else if (len >= 5 && is_ctl (accelerator)) |
---|
1554 | { |
---|
1555 | accelerator += 5; |
---|
1556 | len -= 5; |
---|
1557 | mods |= GDK_CONTROL_MASK; |
---|
1558 | } |
---|
1559 | else if (len >= 5 && is_alt (accelerator)) |
---|
1560 | { |
---|
1561 | accelerator += 5; |
---|
1562 | len -= 5; |
---|
1563 | mods |= GDK_MOD1_MASK; |
---|
1564 | } |
---|
1565 | else |
---|
1566 | { |
---|
1567 | gchar last_ch; |
---|
1568 | |
---|
1569 | last_ch = *accelerator; |
---|
1570 | while (last_ch && last_ch != DELIM_POST) |
---|
1571 | { |
---|
1572 | last_ch = *accelerator; |
---|
1573 | accelerator += 1; |
---|
1574 | len -= 1; |
---|
1575 | } |
---|
1576 | } |
---|
1577 | } |
---|
1578 | else |
---|
1579 | { |
---|
1580 | keyval = gdk_keyval_from_name (accelerator); |
---|
1581 | accelerator += len; |
---|
1582 | len -= len; |
---|
1583 | } |
---|
1584 | } |
---|
1585 | |
---|
1586 | if (accelerator_key) |
---|
1587 | *accelerator_key = gdk_keyval_to_lower (keyval); |
---|
1588 | if (accelerator_mods) |
---|
1589 | *accelerator_mods = mods; |
---|
1590 | } |
---|
1591 | |
---|
1592 | /** |
---|
1593 | * bonobo_ui_util_accel_name: |
---|
1594 | * @accelerator_key: the key |
---|
1595 | * @accelerator_mods: the modifiers |
---|
1596 | * |
---|
1597 | * This stringifies an @accelerator_key and some @accelerator_mods |
---|
1598 | * it is the converse of bonobo_ui_util_accel_parse |
---|
1599 | * |
---|
1600 | * Return value: the stringified representation |
---|
1601 | **/ |
---|
1602 | gchar * |
---|
1603 | bonobo_ui_util_accel_name (guint accelerator_key, |
---|
1604 | GdkModifierType accelerator_mods) |
---|
1605 | { |
---|
1606 | static const gchar text_release[] = DELIM_PRE_S "Release" DELIM_POST_S; |
---|
1607 | static const gchar text_shift[] = DELIM_PRE_S "Shift" DELIM_POST_S; |
---|
1608 | static const gchar text_control[] = DELIM_PRE_S "Control" DELIM_POST_S; |
---|
1609 | static const gchar text_mod1[] = DELIM_PRE_S "Alt" DELIM_POST_S; |
---|
1610 | static const gchar text_mod2[] = DELIM_PRE_S "Mod2" DELIM_POST_S; |
---|
1611 | static const gchar text_mod3[] = DELIM_PRE_S "Mod3" DELIM_POST_S; |
---|
1612 | static const gchar text_mod4[] = DELIM_PRE_S "Mod4" DELIM_POST_S; |
---|
1613 | static const gchar text_mod5[] = DELIM_PRE_S "Mod5" DELIM_POST_S; |
---|
1614 | guint l; |
---|
1615 | gchar *keyval_name; |
---|
1616 | gchar *accelerator; |
---|
1617 | |
---|
1618 | accelerator_mods &= GDK_MODIFIER_MASK; |
---|
1619 | |
---|
1620 | keyval_name = gdk_keyval_name (gdk_keyval_to_lower (accelerator_key)); |
---|
1621 | if (!keyval_name) |
---|
1622 | keyval_name = ""; |
---|
1623 | |
---|
1624 | l = 0; |
---|
1625 | if (accelerator_mods & GDK_RELEASE_MASK) |
---|
1626 | l += sizeof (text_release) - 1; |
---|
1627 | if (accelerator_mods & GDK_SHIFT_MASK) |
---|
1628 | l += sizeof (text_shift) - 1; |
---|
1629 | if (accelerator_mods & GDK_CONTROL_MASK) |
---|
1630 | l += sizeof (text_control) - 1; |
---|
1631 | if (accelerator_mods & GDK_MOD1_MASK) |
---|
1632 | l += sizeof (text_mod1) - 1; |
---|
1633 | if (accelerator_mods & GDK_MOD2_MASK) |
---|
1634 | l += sizeof (text_mod2) - 1; |
---|
1635 | if (accelerator_mods & GDK_MOD3_MASK) |
---|
1636 | l += sizeof (text_mod3) - 1; |
---|
1637 | if (accelerator_mods & GDK_MOD4_MASK) |
---|
1638 | l += sizeof (text_mod4) - 1; |
---|
1639 | if (accelerator_mods & GDK_MOD5_MASK) |
---|
1640 | l += sizeof (text_mod5) - 1; |
---|
1641 | l += strlen (keyval_name); |
---|
1642 | |
---|
1643 | accelerator = g_new (gchar, l + 1); |
---|
1644 | |
---|
1645 | l = 0; |
---|
1646 | accelerator[l] = 0; |
---|
1647 | if (accelerator_mods & GDK_RELEASE_MASK) |
---|
1648 | { |
---|
1649 | strcpy (accelerator + l, text_release); |
---|
1650 | l += sizeof (text_release) - 1; |
---|
1651 | } |
---|
1652 | if (accelerator_mods & GDK_SHIFT_MASK) |
---|
1653 | { |
---|
1654 | strcpy (accelerator + l, text_shift); |
---|
1655 | l += sizeof (text_shift) - 1; |
---|
1656 | } |
---|
1657 | if (accelerator_mods & GDK_CONTROL_MASK) |
---|
1658 | { |
---|
1659 | strcpy (accelerator + l, text_control); |
---|
1660 | l += sizeof (text_control) - 1; |
---|
1661 | } |
---|
1662 | if (accelerator_mods & GDK_MOD1_MASK) |
---|
1663 | { |
---|
1664 | strcpy (accelerator + l, text_mod1); |
---|
1665 | l += sizeof (text_mod1) - 1; |
---|
1666 | } |
---|
1667 | if (accelerator_mods & GDK_MOD2_MASK) |
---|
1668 | { |
---|
1669 | strcpy (accelerator + l, text_mod2); |
---|
1670 | l += sizeof (text_mod2) - 1; |
---|
1671 | } |
---|
1672 | if (accelerator_mods & GDK_MOD3_MASK) |
---|
1673 | { |
---|
1674 | strcpy (accelerator + l, text_mod3); |
---|
1675 | l += sizeof (text_mod3) - 1; |
---|
1676 | } |
---|
1677 | if (accelerator_mods & GDK_MOD4_MASK) |
---|
1678 | { |
---|
1679 | strcpy (accelerator + l, text_mod4); |
---|
1680 | l += sizeof (text_mod4) - 1; |
---|
1681 | } |
---|
1682 | if (accelerator_mods & GDK_MOD5_MASK) |
---|
1683 | { |
---|
1684 | strcpy (accelerator + l, text_mod5); |
---|
1685 | l += sizeof (text_mod5) - 1; |
---|
1686 | } |
---|
1687 | strcpy (accelerator + l, keyval_name); |
---|
1688 | |
---|
1689 | return accelerator; |
---|
1690 | } |
---|
1691 | |
---|
1692 | /** |
---|
1693 | * bonobo_ui_util_set_pixbuf: |
---|
1694 | * @component: the component |
---|
1695 | * @path: the path into the xml tree |
---|
1696 | * @pixbuf: the pixbuf |
---|
1697 | * |
---|
1698 | * This helper function sets a pixbuf at a certain path into an |
---|
1699 | * xml tree. |
---|
1700 | **/ |
---|
1701 | void |
---|
1702 | bonobo_ui_util_set_pixbuf (BonoboUIComponent *component, |
---|
1703 | const char *path, |
---|
1704 | GdkPixbuf *pixbuf) |
---|
1705 | { |
---|
1706 | char *parent_path; |
---|
1707 | BonoboUINode *node; |
---|
1708 | |
---|
1709 | node = bonobo_ui_component_get_tree (component, path, FALSE, NULL); |
---|
1710 | |
---|
1711 | g_return_if_fail (node != NULL); |
---|
1712 | |
---|
1713 | bonobo_ui_util_xml_set_pixbuf (node, pixbuf); |
---|
1714 | |
---|
1715 | parent_path = bonobo_ui_xml_get_parent_path (path); |
---|
1716 | bonobo_ui_component_set_tree (component, parent_path, node, NULL); |
---|
1717 | |
---|
1718 | bonobo_ui_node_free (node); |
---|
1719 | |
---|
1720 | g_free (parent_path); |
---|
1721 | } |
---|
1722 | |
---|
1723 | /* |
---|
1724 | * And here we start to have a major headache. |
---|
1725 | * Not only does libxml1 not support utf-8 encoding |
---|
1726 | * correctly; [ mostly this is in xmlSetProp and |
---|
1727 | * variants ] but it seems impossible to discover |
---|
1728 | * whether a translated string is encoded as utf8 as |
---|
1729 | * opposed to some other 8 bit encoding. Consequently |
---|
1730 | * I add a simple hexification encoding. This has the |
---|
1731 | * merits of being totaly libxml1 clean, avoiding any |
---|
1732 | * utf problems, only doubling the size, and being |
---|
1733 | * detectable later. |
---|
1734 | */ |
---|
1735 | char * |
---|
1736 | bonobo_ui_util_encode_str (const char *str) |
---|
1737 | { |
---|
1738 | const char *a; |
---|
1739 | char *b, *ret; |
---|
1740 | |
---|
1741 | if (!str) |
---|
1742 | return NULL; |
---|
1743 | |
---|
1744 | ret = g_malloc (strlen (str) * 2 + 1); |
---|
1745 | |
---|
1746 | b = ret; |
---|
1747 | for (a = str; *a; a++) { |
---|
1748 | write_byte (b, *a); |
---|
1749 | b += 2; |
---|
1750 | } |
---|
1751 | *b = '\0'; |
---|
1752 | |
---|
1753 | return ret; |
---|
1754 | } |
---|
1755 | |
---|
1756 | char * |
---|
1757 | bonobo_ui_util_decode_str (const char *str, gboolean *err) |
---|
1758 | { |
---|
1759 | const char *a; |
---|
1760 | char *b, *ret; |
---|
1761 | int encoded_len; |
---|
1762 | |
---|
1763 | g_return_val_if_fail (err != NULL, NULL); |
---|
1764 | *err = FALSE; |
---|
1765 | |
---|
1766 | if (!str) |
---|
1767 | return NULL; |
---|
1768 | |
---|
1769 | encoded_len = 0; |
---|
1770 | for (a = str; *a; a++) { |
---|
1771 | if (! ((*a >= '0' && *a <= '9') || |
---|
1772 | (*a >= 'a' && *a <= 'f'))) { |
---|
1773 | *err = TRUE; |
---|
1774 | return NULL; |
---|
1775 | } |
---|
1776 | encoded_len++; |
---|
1777 | } |
---|
1778 | |
---|
1779 | ret = g_malloc ((encoded_len + 1) / 2 + 1); |
---|
1780 | |
---|
1781 | b = ret; |
---|
1782 | for (a = str; *a && *(a + 1); a += 2) |
---|
1783 | *b++ = read_byte (a); |
---|
1784 | *b = '\0'; |
---|
1785 | |
---|
1786 | return ret; |
---|
1787 | } |
---|