source: trunk/third/bonobo/bonobo/bonobo-ui-util.c @ 17169

Revision 17169, 41.6 KB checked in by ghudson, 23 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r17168, which included commits to RCS files with non-trunk default branches.
Line 
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
22static 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
27static 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
46static inline void
47write_byte (char *start, guint8 byte)
48{
49        start[0] = write_lut[byte >> 4];
50        start[1] = write_lut[byte & 15];
51}
52
53static inline void
54write_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
62static void
63read_warning (const char *start)
64{
65        g_warning ("Format error in stream '%c', '%c'", start[0], start[1]);
66}
67
68static inline guint8
69read_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
89static inline const guint32
90read_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 **/
108char *
109bonobo_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 **/
168GdkPixbuf *
169bonobo_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 */
224static GdkPixbuf *
225convert_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
273static GdkPixbuf *
274pixbuf_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
324static GdkPixbuf *
325get_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
365static gchar *
366find_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 **/
429GdkPixbuf *
430bonobo_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 **/
508GtkWidget *
509bonobo_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 **/
538void
539bonobo_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 **/
561void
562bonobo_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 **/
583void
584bonobo_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 **/
601void
602bonobo_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
613static void
614free_help_menu_entry (GtkWidget *widget, GnomeHelpMenuEntry *entry)
615{
616        g_free (entry->name);
617        g_free (entry->path);
618        g_free (entry);
619}
620
621static void
622bonobo_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 */
632static char *
633bonobo_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 **/
686void
687bonobo_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 **/
777BonoboUINode *
778bonobo_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 **/
807BonoboUINode *
808bonobo_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 **/
854BonoboUINode *
855bonobo_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 **/
884void
885bonobo_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 **/
903void
904bonobo_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 **/
928BonoboUINode *
929bonobo_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                                             
968BonoboUINode *
969bonobo_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 **/
1009char *
1010bonobo_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 **/
1067void
1068bonobo_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 **/
1126void
1127bonobo_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 **/
1164void
1165bonobo_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 **/
1222BonoboUINode *
1223bonobo_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
1245typedef struct {
1246        char *file_name;
1247        char *app_datadir;
1248        char *app_name;
1249        char *tree;
1250} BonoboUINodeCacheEntry;
1251
1252static guint
1253node_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
1261static gint
1262node_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
1275static GHashTable *loaded_node_cache = NULL;
1276
1277static void
1278free_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
1287static void
1288free_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 **/
1311void
1312bonobo_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
1380static inline gboolean
1381is_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
1390static inline gboolean
1391is_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
1400static inline gboolean
1401is_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
1411static inline gboolean
1412is_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
1422static inline gboolean
1423is_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
1433static inline gboolean
1434is_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
1445static inline gboolean
1446is_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
1459static inline gboolean
1460is_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 **/
1484void
1485bonobo_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 **/
1602gchar *
1603bonobo_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 **/
1701void
1702bonobo_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 */
1735char *
1736bonobo_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
1756char *
1757bonobo_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}
Note: See TracBrowser for help on using the repository browser.