]> Pileus Git - ~andy/gtk/blob - gtk/gtkrc.c
Initial revision
[~andy/gtk] / gtk / gtkrc.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the Free
16  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18 #include <ctype.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include "gtkrc.h"
23
24
25 enum {
26   TOKEN_EOF,
27   TOKEN_LEFT_CURLY,
28   TOKEN_RIGHT_CURLY,
29   TOKEN_LEFT_BRACE,
30   TOKEN_RIGHT_BRACE,
31   TOKEN_EQUAL_SIGN,
32   TOKEN_COMMA,
33   TOKEN_INTEGER,
34   TOKEN_FLOAT,
35   TOKEN_STRING,
36   TOKEN_SYMBOL,
37   TOKEN_ACTIVE,
38   TOKEN_BASE,
39   TOKEN_BG,
40   TOKEN_BG_PIXMAP,
41   TOKEN_FG,
42   TOKEN_FONT,
43   TOKEN_FONTSET,
44   TOKEN_INSENSITIVE,
45   TOKEN_NORMAL,
46   TOKEN_PIXMAP_PATH,
47   TOKEN_PRELIGHT,
48   TOKEN_SELECTED,
49   TOKEN_STYLE,
50   TOKEN_TEXT,
51   TOKEN_WIDGET,
52   TOKEN_WIDGET_CLASS
53 };
54
55 enum {
56   PARSE_OK,
57   PARSE_ERROR,
58   PARSE_SYNTAX
59 };
60
61 enum {
62   PARSE_START,
63   PARSE_COMMENT,
64   PARSE_STRING,
65   PARSE_SYMBOL,
66   PARSE_NUMBER
67 };
68
69
70 typedef struct _GtkRcStyle  GtkRcStyle;
71 typedef struct _GtkRcSet    GtkRcSet;
72
73 struct _GtkRcStyle
74 {
75   int initialize;
76   char *name;
77   char *font_name;
78   char *fontset_name;
79   char *bg_pixmap_name[5];
80   GtkStyle *style;
81 };
82
83 struct _GtkRcSet
84 {
85   char *set;
86   GtkRcStyle *rc_style;
87 };
88
89
90 static guint       gtk_rc_style_hash               (const char *name);
91 static gint        gtk_rc_style_compare            (const char *a,
92                                                     const char *b);
93 static GtkRcStyle* gtk_rc_style_find               (const char *name);
94 static GtkRcStyle* gtk_rc_styles_match             (GSList *sets,
95                                                     const char   *path);
96 static gint        gtk_rc_style_match              (const char *set,
97                                                     const char *path);
98 static void        gtk_rc_style_init               (GtkRcStyle *rc_style);
99 static gint        gtk_rc_get_token                (void);
100 static gint        gtk_rc_simple_token             (char ch);
101 static gint        gtk_rc_symbol_token             (const char *sym);
102 static gint        gtk_rc_get_next_token           (void);
103 static gint        gtk_rc_peek_next_token          (void);
104 static gint        gtk_rc_parse_statement          (void);
105 static gint        gtk_rc_parse_style              (void);
106 static gint        gtk_rc_parse_style_option       (GtkRcStyle   *rc_style);
107 static gint        gtk_rc_parse_base               (GtkStyle     *style);
108 static gint        gtk_rc_parse_bg                 (GtkStyle     *style);
109 static gint        gtk_rc_parse_fg                 (GtkStyle     *style);
110 static gint        gtk_rc_parse_bg_pixmap          (GtkRcStyle   *rc_style);
111 static gint        gtk_rc_parse_font               (GtkRcStyle   *rc_style);
112 static gint        gtk_rc_parse_fontset            (GtkRcStyle   *rc_style);
113 static gint        gtk_rc_parse_state              (GtkStateType *state);
114 static gint        gtk_rc_parse_color              (GdkColor     *color);
115 static gint        gtk_rc_parse_pixmap_path        (void);
116 static void        gtk_rc_parse_pixmap_path_string (gchar *pix_path);
117 static char*       gtk_rc_find_pixmap_in_path      (gchar *pixmap_file);
118 static gint        gtk_rc_parse_widget_style       (void);
119 static gint        gtk_rc_parse_widget_class_style (void);
120 static char*       gtk_rc_widget_path              (GtkWidget *widget);
121 static char*       gtk_rc_widget_class_path        (GtkWidget *widget);
122
123
124 static struct
125 {
126   char *name;
127   int token;
128 } symbols[] =
129   {
130     { "ACTIVE", TOKEN_ACTIVE },
131     { "base", TOKEN_BASE },
132     { "bg", TOKEN_BG },
133     { "bg_pixmap", TOKEN_BG_PIXMAP },
134     { "fg", TOKEN_FG },
135     { "font", TOKEN_FONT },
136     { "fontset", TOKEN_FONTSET },
137     { "INSENSITIVE", TOKEN_INSENSITIVE },
138     { "NORMAL", TOKEN_NORMAL },
139     { "pixmap_path", TOKEN_PIXMAP_PATH },
140     { "PRELIGHT", TOKEN_PRELIGHT },
141     { "SELECTED", TOKEN_SELECTED },
142     { "style", TOKEN_STYLE },
143     { "text", TOKEN_TEXT },
144     { "widget", TOKEN_WIDGET },
145     { "widget_class", TOKEN_WIDGET_CLASS },
146   };
147
148 static int nsymbols = sizeof (symbols) / sizeof (symbols[0]);
149
150 static struct
151 {
152   char ch;
153   int token;
154 } simple_tokens[] =
155   {
156     { '{', TOKEN_LEFT_CURLY },
157     { '}', TOKEN_RIGHT_CURLY },
158     { '[', TOKEN_LEFT_BRACE },
159     { ']', TOKEN_RIGHT_BRACE },
160     { '=', TOKEN_EQUAL_SIGN },
161     { ',', TOKEN_COMMA },
162   };
163
164 static int nsimple_tokens = sizeof (simple_tokens) / sizeof (simple_tokens[0]);
165
166 static FILE *input_fp = NULL;
167 static char *buffer = NULL;
168 static char *tokenbuf = NULL;
169 static int position = 0;
170 static int linenum = 1;
171 static int buffer_size = 1024;
172 static int tokenbuf_size = 1024;
173
174 static int done;
175 static int cur_token;
176 static int next_token;
177
178 static char *token_str;
179 static double token_float;
180 static int token_int;
181
182 static GHashTable *rc_style_ht = NULL;
183 static GSList *widget_sets = NULL;
184 static GSList *widget_class_sets = NULL;
185
186 #define GTK_RC_MAX_PIXMAP_PATHS 128
187 static gchar *pixmap_path[GTK_RC_MAX_PIXMAP_PATHS];
188
189
190 void
191 gtk_rc_init ()
192 {
193   rc_style_ht = g_hash_table_new ((GHashFunc) gtk_rc_style_hash,
194                                   (GCompareFunc) gtk_rc_style_compare);
195 }
196
197 void
198 gtk_rc_parse (const char *filename)
199 {
200   input_fp = fopen (filename, "r");
201   if (!input_fp)
202           return;
203
204   buffer = g_new (char, buffer_size + tokenbuf_size);
205   tokenbuf = buffer + buffer_size;
206   position = 0;
207   linenum = 1;
208
209   cur_token = -1;
210   next_token = -1;
211   done = FALSE;
212
213   while (!done)
214     {
215       if (gtk_rc_parse_statement () != PARSE_OK)
216         {
217           g_warning ("rc file parse error: \"%s\" line %d",
218                      filename, linenum);
219           done = TRUE;
220         }
221     }
222
223   fclose (input_fp);
224
225   g_free (buffer);
226
227   buffer = NULL;
228   tokenbuf = NULL;
229   position = 0;
230   linenum = 1;
231 }
232
233 GtkStyle*
234 gtk_rc_get_style (GtkWidget *widget)
235 {
236   GtkRcStyle *rc_style;
237   char *path;
238
239   if (widget_sets)
240     {
241       path = gtk_rc_widget_path (widget);
242       if (path)
243         {
244           rc_style = gtk_rc_styles_match (widget_sets, path);
245           g_free (path);
246
247           if (rc_style)
248             {
249               gtk_rc_style_init (rc_style);
250               return rc_style->style;
251             }
252         }
253     }
254
255   if (widget_class_sets)
256     {
257       path = gtk_rc_widget_class_path (widget);
258       if (path)
259         {
260           rc_style = gtk_rc_styles_match (widget_class_sets, path);
261           g_free (path);
262
263           if (rc_style)
264             {
265               gtk_rc_style_init (rc_style);
266               return rc_style->style;
267             }
268         }
269     }
270
271   return widget->style;
272 }
273
274 void
275 gtk_rc_add_widget_name_style (GtkStyle *style,
276                               const char     *pattern)
277 {
278   GtkRcStyle *rc_style;
279   GtkRcSet *rc_set;
280   int i;
281
282   gtk_style_ref (style);
283
284   rc_style = g_new (GtkRcStyle, 1);
285   rc_style->initialize = FALSE;
286   rc_style->name = NULL;
287   rc_style->font_name = NULL;
288   rc_style->fontset_name = NULL;
289
290   for (i = 0; i < 5; i++)
291     rc_style->bg_pixmap_name[i] = NULL;
292
293   rc_style->style = style;
294
295   rc_set = g_new (GtkRcSet, 1);
296   rc_set->set = g_strdup (pattern);
297   rc_set->rc_style = rc_style;
298
299   widget_sets = g_slist_append (widget_sets, rc_set);
300 }
301
302 void
303 gtk_rc_add_widget_class_style (GtkStyle *style,
304                                const char     *pattern)
305 {
306   GtkRcStyle *rc_style;
307   GtkRcSet *rc_set;
308   int i;
309
310   gtk_style_ref (style);
311
312   rc_style = g_new (GtkRcStyle, 1);
313   rc_style->initialize = FALSE;
314   rc_style->name = NULL;
315   rc_style->font_name = NULL;
316   rc_style->fontset_name = NULL;
317
318   for (i = 0; i < 5; i++)
319     rc_style->bg_pixmap_name[i] = NULL;
320
321   rc_style->style = style;
322
323   rc_set = g_new (GtkRcSet, 1);
324   rc_set->set = g_strdup (pattern);
325   rc_set->rc_style = rc_style;
326
327   widget_class_sets = g_slist_append (widget_class_sets, rc_set);
328 }
329
330
331 static guint
332 gtk_rc_style_hash (const char *name)
333 {
334   guint result;
335
336   result = 0;
337   while (*name)
338     result += (result << 3) + *name++;
339
340   return result;
341 }
342
343 static gint
344 gtk_rc_style_compare (const char *a,
345                       const char *b)
346 {
347   return (strcmp (a, b) == 0);
348 }
349
350 static GtkRcStyle*
351 gtk_rc_style_find (const char *name)
352 {
353   GtkRcStyle *rc_style;
354
355   rc_style = g_hash_table_lookup (rc_style_ht, (gpointer) name);
356
357   return rc_style;
358 }
359
360 static GtkRcStyle*
361 gtk_rc_styles_match (GSList       *sets,
362                      const char   *path)
363 {
364   GtkRcSet *rc_set;
365
366   while (sets)
367     {
368       rc_set = sets->data;
369       sets = sets->next;
370
371       if (gtk_rc_style_match (rc_set->set, path))
372         return rc_set->rc_style;
373     }
374
375   return NULL;
376 }
377
378 static gint
379 gtk_rc_style_match (const char *set,
380                     const char *path)
381 {
382   char ch;
383
384   while (1)
385     {
386       ch = *set++;
387       if (ch == '\0')
388         return (*path == '\0');
389
390       switch (ch)
391         {
392         case '*':
393           while (*set == '*')
394             set++;
395
396           ch = *set++;
397           if (ch == '\0')
398             return TRUE;
399
400           while (*path)
401             {
402               while (*path && (ch != *path))
403                 path++;
404
405               if (!(*path))
406                 return FALSE;
407
408               path++;
409               if (gtk_rc_style_match (set, path))
410                 return TRUE;
411             }
412           break;
413
414         case '?':
415           break;
416
417         default:
418           if (ch == *path)
419             path++;
420           else
421             return FALSE;
422           break;
423         }
424     }
425
426   return TRUE;
427 }
428
429 static void
430 gtk_rc_style_init (GtkRcStyle *rc_style)
431 {
432   GdkFont *old_font;
433   gint i;
434
435   if (rc_style->initialize)
436     {
437       rc_style->initialize = FALSE;
438
439       if (rc_style->fontset_name)
440         {
441           old_font = rc_style->style->font;
442           rc_style->style->font = gdk_fontset_load (rc_style->fontset_name);
443           if (rc_style->style->font)
444             gdk_fontset_free (old_font);
445           else
446             rc_style->style->font = old_font;
447         }
448       else if (rc_style->font_name)
449         {
450           old_font = rc_style->style->font;
451           rc_style->style->font = gdk_font_load (rc_style->font_name);
452           if (rc_style->style->font)
453             gdk_font_free (old_font);
454           else
455             rc_style->style->font = old_font;
456         }
457
458       for (i = 0; i < 5; i++)
459         if (rc_style->bg_pixmap_name[i])
460           {
461             if (strcmp (rc_style->bg_pixmap_name[i], "<parent>") == 0)
462               rc_style->style->bg_pixmap[i] = (GdkPixmap*) GDK_PARENT_RELATIVE;
463             else
464               rc_style->style->bg_pixmap[i] = gdk_pixmap_create_from_xpm (NULL, NULL,
465                                                                           &rc_style->style->bg[i],
466                                                                           rc_style->bg_pixmap_name[i]);
467           }
468     }
469 }
470
471 static gint
472 gtk_rc_get_token ()
473 {
474   int tokenpos;
475   int state;
476   int count;
477   int token;
478   int hex_number = FALSE;
479   int float_number = FALSE;
480   char ch;
481
482   tokenpos = 0;
483   state = PARSE_START;
484
485   while (1)
486     {
487       if (position >= (buffer_size - 1))
488         position = 0;
489       if (!position || (buffer[position] == '\0'))
490         {
491           count = fread (buffer, sizeof (char), buffer_size - 1, input_fp);
492           if ((count == 0) && feof (input_fp))
493             return TOKEN_EOF;
494           buffer[count] = '\0';
495         }
496
497       ch = buffer[position++];
498       if (ch == '\n')
499         linenum += 1;
500
501       switch (state)
502         {
503         case PARSE_START:
504           token = gtk_rc_simple_token (ch);
505
506           if (token)
507             return token;
508           else if (ch == '#')
509             state = PARSE_COMMENT;
510           else if (ch == '"')
511             state = PARSE_STRING;
512           else if ((ch == ' ') || (ch == '\t') || (ch == '\n'))
513             break;
514           else if (ch == '.')
515             {
516               hex_number = FALSE;
517               float_number = TRUE;
518               tokenbuf[tokenpos++] = ch;
519               state = PARSE_NUMBER;
520             }
521           else if ((ch == '$') || (ch == '#'))
522             {
523               hex_number = TRUE;
524               float_number = FALSE;
525               state = PARSE_NUMBER;
526             }
527           else if (isdigit (ch))
528             {
529               hex_number = FALSE;
530               float_number = FALSE;
531               tokenbuf[tokenpos++] = ch;
532               state = PARSE_NUMBER;
533             }
534           else
535             {
536               tokenbuf[tokenpos++] = ch;
537               state = PARSE_SYMBOL;
538             }
539           break;
540
541         case PARSE_COMMENT:
542           if (ch == '\n')
543             state = PARSE_START;
544           break;
545
546         case PARSE_STRING:
547           if (ch != '"')
548             {
549               tokenbuf[tokenpos++] = ch;
550             }
551           else
552             {
553               tokenbuf[tokenpos] = '\0';
554               token_str = tokenbuf;
555               return TOKEN_STRING;
556             }
557           break;
558
559         case PARSE_SYMBOL:
560           if ((ch != ' ') && (ch != '\t') && (ch != '\n') &&
561               (gtk_rc_simple_token (ch) == 0))
562             {
563               tokenbuf[tokenpos++] = ch;
564             }
565           else
566             {
567               position -= 1;
568               tokenbuf[tokenpos] = '\0';
569               token_str = tokenbuf;
570               return gtk_rc_symbol_token (tokenbuf);
571             }
572           break;
573
574         case PARSE_NUMBER:
575           if (isdigit (ch) || (hex_number && isxdigit (ch)))
576             {
577               tokenbuf[tokenpos++] = ch;
578             }
579           else if (!hex_number && !float_number && (ch == '.'))
580             {
581               float_number = TRUE;
582               tokenbuf[tokenpos++] = ch;
583             }
584           else if (!float_number &&
585                    (ch == 'x') && (tokenpos == 1) &&
586                    (tokenbuf[0] == '0'))
587             {
588               hex_number = TRUE;
589               tokenpos = 0;
590             }
591           else
592             {
593               position -= 1;
594               tokenbuf[tokenpos] = '\0';
595               if (float_number)
596                 {
597                   sscanf (tokenbuf, "%lf", &token_float);
598                   return TOKEN_FLOAT;
599                 }
600               else
601                 {
602                   sscanf (tokenbuf, (hex_number ? "%x" : "%d"), &token_int);
603                   return TOKEN_INTEGER;
604                 }
605             }
606           break;
607         }
608     }
609 }
610
611 static gint
612 gtk_rc_simple_token (char ch)
613 {
614   gint i;
615
616   for (i = 0; i < nsimple_tokens; i++)
617     if (simple_tokens[i].ch == ch)
618       return simple_tokens[i].token;
619
620   return 0;
621 }
622
623 static gint
624 gtk_rc_symbol_token (const char *sym)
625 {
626   gint i;
627
628   for (i = 0; i < nsymbols; i++)
629     if (strcmp (symbols[i].name, sym) == 0)
630       return symbols[i].token;
631
632   return TOKEN_STRING;
633 }
634
635 static gint
636 gtk_rc_get_next_token ()
637 {
638   if (next_token != -1)
639     {
640       cur_token = next_token;
641       next_token = -1;
642     }
643   else
644     {
645       cur_token = gtk_rc_get_token ();
646     }
647
648   return cur_token;
649 }
650
651 static gint
652 gtk_rc_peek_next_token ()
653 {
654   if (next_token == -1)
655     next_token = gtk_rc_get_token ();
656
657   return next_token;
658 }
659
660 static gint
661 gtk_rc_parse_statement ()
662 {
663   gint token;
664   gint error;
665
666   token = gtk_rc_peek_next_token ();
667   if (!token)
668     {
669       done = TRUE;
670       return PARSE_OK;
671     }
672
673   error = gtk_rc_parse_style ();
674   if (error != PARSE_SYNTAX)
675     return error;
676
677   error = gtk_rc_parse_pixmap_path ();
678   if (error != PARSE_SYNTAX)
679     return error;
680
681   error = gtk_rc_parse_widget_style ();
682   if (error != PARSE_SYNTAX)
683     return error;
684
685   error = gtk_rc_parse_widget_class_style ();
686
687   return error;
688 }
689
690 static gint
691 gtk_rc_parse_style ()
692 {
693   GtkRcStyle *rc_style;
694   GtkRcStyle *parent_style;
695   gint token;
696   gint error;
697   gint insert;
698   gint i;
699
700   token = gtk_rc_peek_next_token ();
701   if (!token)
702     return PARSE_ERROR;
703   if (token != TOKEN_STYLE)
704     return PARSE_SYNTAX;
705   token = gtk_rc_get_next_token ();
706
707   token = gtk_rc_get_next_token ();
708   if (!token || (token != TOKEN_STRING))
709     return PARSE_ERROR;
710
711   insert = FALSE;
712   rc_style = g_hash_table_lookup (rc_style_ht, token_str);
713
714   if (!rc_style)
715     {
716       insert = TRUE;
717       rc_style = g_new (GtkRcStyle, 1);
718       rc_style->initialize = TRUE;
719       rc_style->name = g_strdup (token_str);
720       rc_style->font_name = NULL;
721       rc_style->fontset_name = NULL;
722
723       for (i = 0; i < 5; i++)
724         rc_style->bg_pixmap_name[i] = NULL;
725
726       rc_style->style = gtk_style_new ();
727       gtk_style_ref (rc_style->style);
728     }
729
730   token = gtk_rc_peek_next_token ();
731   if (token == TOKEN_EQUAL_SIGN)
732     {
733       token = gtk_rc_get_next_token ();
734
735       token = gtk_rc_get_next_token ();
736       if (!token || (token != TOKEN_STRING))
737         {
738           if (insert)
739             {
740               gtk_style_unref (rc_style->style);
741               g_free (rc_style);
742             }
743           return PARSE_ERROR;
744         }
745
746       parent_style = g_hash_table_lookup (rc_style_ht, token_str);
747       if (parent_style)
748         {
749           for (i = 0; i < 5; i++)
750             {
751               rc_style->style->fg[i] = parent_style->style->fg[i];
752               rc_style->style->bg[i] = parent_style->style->bg[i];
753               rc_style->style->light[i] = parent_style->style->light[i];
754               rc_style->style->dark[i] = parent_style->style->dark[i];
755               rc_style->style->mid[i] = parent_style->style->mid[i];
756               rc_style->style->text[i] = parent_style->style->text[i];
757               rc_style->style->base[i] = parent_style->style->base[i];
758             }
759
760           rc_style->style->black = parent_style->style->black;
761           rc_style->style->white = parent_style->style->white;
762
763           if (rc_style->fontset_name)
764             {
765               g_free (rc_style->fontset_name);
766               rc_style->fontset_name = g_strdup (parent_style->fontset_name);
767             }
768           else if (rc_style->font_name)
769             {
770               g_free (rc_style->font_name);
771               rc_style->font_name = g_strdup (parent_style->font_name);
772             }
773
774           for (i = 0; i < 5; i++)
775             {
776               if (rc_style->bg_pixmap_name[i])
777                 g_free (rc_style->bg_pixmap_name[i]);
778               rc_style->bg_pixmap_name[i] = g_strdup (parent_style->bg_pixmap_name[i]);
779             }
780         }
781     }
782
783   token = gtk_rc_get_next_token ();
784   if (!token || (token != TOKEN_LEFT_CURLY))
785     {
786       if (insert)
787         {
788           gtk_style_unref (rc_style->style);
789           g_free (rc_style);
790         }
791       return PARSE_ERROR;
792     }
793
794   while (1)
795     {
796       error = gtk_rc_parse_style_option (rc_style);
797       if (error == PARSE_SYNTAX)
798         break;
799       if (error == PARSE_ERROR)
800         {
801           if (insert)
802             {
803               gtk_style_unref (rc_style->style);
804               g_free (rc_style);
805             }
806           return error;
807         }
808     }
809
810   token = gtk_rc_get_next_token ();
811   if (!token || (token != TOKEN_RIGHT_CURLY))
812     {
813       if (insert)
814         {
815           if (rc_style->fontset_name)
816             g_free (rc_style->fontset_name);
817           else if (rc_style->font_name)
818             g_free (rc_style->font_name);
819
820           for (i = 0; i < 5; i++)
821             if (rc_style->bg_pixmap_name[i])
822               g_free (rc_style->bg_pixmap_name[i]);
823
824           gtk_style_unref (rc_style->style);
825           g_free (rc_style);
826         }
827       return PARSE_ERROR;
828     }
829
830   if (insert)
831     g_hash_table_insert (rc_style_ht, rc_style->name, rc_style);
832
833   return PARSE_OK;
834 }
835
836 static gint
837 gtk_rc_parse_style_option (GtkRcStyle *rc_style)
838 {
839   gint token;
840   gint error;
841
842   token = gtk_rc_peek_next_token ();
843   if (!token)
844     return PARSE_ERROR;
845
846   error = gtk_rc_parse_base (rc_style->style);
847   if (error != PARSE_SYNTAX)
848     return error;
849
850   error = gtk_rc_parse_bg (rc_style->style);
851   if (error != PARSE_SYNTAX)
852     return error;
853
854   error = gtk_rc_parse_fg (rc_style->style);
855   if (error != PARSE_SYNTAX)
856     return error;
857
858   error = gtk_rc_parse_bg_pixmap (rc_style);
859   if (error != PARSE_SYNTAX)
860     return error;
861
862   error = gtk_rc_parse_font (rc_style);
863   if (error != PARSE_SYNTAX)
864     return error;
865
866   error = gtk_rc_parse_fontset (rc_style);
867
868   return error;
869 }
870
871 static gint
872 gtk_rc_parse_base (GtkStyle *style)
873 {
874   GtkStateType state;
875   gint token;
876   gint error;
877
878   token = gtk_rc_peek_next_token ();
879   if (!token)
880     return PARSE_ERROR;
881   if (token != TOKEN_BASE)
882     return PARSE_SYNTAX;
883   token = gtk_rc_get_next_token ();
884
885   error = gtk_rc_parse_state (&state);
886   if (error != PARSE_OK)
887     return error;
888
889   token = gtk_rc_get_next_token ();
890   if (!token || (token != TOKEN_EQUAL_SIGN))
891     return PARSE_ERROR;
892
893   error = gtk_rc_parse_color (&style->base[state]);
894
895   return error;
896 }
897
898 static gint
899 gtk_rc_parse_bg (GtkStyle *style)
900 {
901   GtkStateType state;
902   gint token;
903   gint error;
904
905   token = gtk_rc_peek_next_token ();
906   if (!token)
907     return PARSE_ERROR;
908   if (token != TOKEN_BG)
909     return PARSE_SYNTAX;
910   token = gtk_rc_get_next_token ();
911
912   error = gtk_rc_parse_state (&state);
913   if (error != PARSE_OK)
914     return error;
915
916   token = gtk_rc_get_next_token ();
917   if (!token || (token != TOKEN_EQUAL_SIGN))
918     return PARSE_ERROR;
919
920   error = gtk_rc_parse_color (&style->bg[state]);
921
922   return error;
923 }
924
925 static gint
926 gtk_rc_parse_fg (GtkStyle *style)
927 {
928   GtkStateType state;
929   gint token;
930   gint error;
931
932   token = gtk_rc_peek_next_token ();
933   if (!token)
934     return PARSE_ERROR;
935   if (token != TOKEN_FG)
936     return PARSE_SYNTAX;
937   token = gtk_rc_get_next_token ();
938
939   error = gtk_rc_parse_state (&state);
940   if (error != PARSE_OK)
941     return error;
942
943   token = gtk_rc_get_next_token ();
944   if (!token || (token != TOKEN_EQUAL_SIGN))
945     return PARSE_ERROR;
946
947   error = gtk_rc_parse_color (&style->fg[state]);
948
949   return error;
950 }
951
952 static gint
953 gtk_rc_parse_bg_pixmap (GtkRcStyle *rc_style)
954 {
955   GtkStateType state;
956   gint token;
957   gint error;
958   gchar *pixmap_file;
959
960   token = gtk_rc_peek_next_token ();
961   if (!token)
962     return PARSE_ERROR;
963   if (token != TOKEN_BG_PIXMAP)
964     return PARSE_SYNTAX;
965   token = gtk_rc_get_next_token ();
966
967   error = gtk_rc_parse_state (&state);
968   if (error != PARSE_OK)
969     return error;
970
971   token = gtk_rc_get_next_token ();
972   if (!token || (token != TOKEN_EQUAL_SIGN))
973     return PARSE_ERROR;
974
975   token = gtk_rc_get_next_token ();
976   if (!token || (token != TOKEN_STRING))
977     return PARSE_ERROR;
978
979   if (strcmp (token_str, "<parent>"))
980     pixmap_file = gtk_rc_find_pixmap_in_path (token_str);
981   else
982     pixmap_file = g_strdup(token_str);
983
984   if (pixmap_file)
985     {
986       if (rc_style->bg_pixmap_name[state])
987         g_free (rc_style->bg_pixmap_name[state]);
988       rc_style->bg_pixmap_name[state] = pixmap_file;
989     }
990
991   return PARSE_OK;
992 }
993
994 static char*
995 gtk_rc_find_pixmap_in_path (gchar *pixmap_file)
996 {
997   gint i;
998   FILE *fp;
999   gchar *buf;
1000
1001   for (i = 0; (i < GTK_RC_MAX_PIXMAP_PATHS) && (pixmap_path[i] != NULL); i++)
1002     {
1003       buf = g_malloc (strlen (pixmap_path[i]) + strlen (pixmap_file) + 2);
1004       sprintf (buf, "%s%c%s", pixmap_path[i], '/', pixmap_file);
1005
1006       fp = fopen (buf, "r");
1007       if (fp)
1008         {
1009           fclose (fp);
1010           return buf;
1011         }
1012
1013       g_free (buf);
1014     }
1015
1016   g_warning ("Unable to locate image file in pixmap_path: \"%s\" line %d",
1017              pixmap_file, linenum);
1018
1019   return NULL;
1020 }
1021
1022 static gint
1023 gtk_rc_parse_font (GtkRcStyle *rc_style)
1024 {
1025   gint token;
1026
1027   token = gtk_rc_peek_next_token ();
1028   if (!token)
1029     return PARSE_ERROR;
1030   if (token != TOKEN_FONT)
1031     return PARSE_SYNTAX;
1032   token = gtk_rc_get_next_token ();
1033
1034   token = gtk_rc_get_next_token ();
1035   if (!token || (token != TOKEN_EQUAL_SIGN))
1036     return PARSE_ERROR;
1037
1038   token = gtk_rc_get_next_token ();
1039   if (!token || (token != TOKEN_STRING))
1040     return PARSE_ERROR;
1041
1042   if (rc_style->font_name)
1043     g_free (rc_style->font_name);
1044   rc_style->font_name = g_strdup (token_str);
1045
1046   return PARSE_OK;
1047 }
1048
1049 static gint
1050 gtk_rc_parse_fontset (GtkRcStyle *rc_style)
1051 {
1052   gint token;
1053
1054   token = gtk_rc_peek_next_token ();
1055   if (!token)
1056     return PARSE_ERROR;
1057   if (token != TOKEN_FONTSET)
1058     return PARSE_SYNTAX;
1059   token = gtk_rc_get_next_token ();
1060
1061   token = gtk_rc_get_next_token ();
1062   if (!token || (token != TOKEN_EQUAL_SIGN))
1063     return PARSE_ERROR;
1064
1065   token = gtk_rc_get_next_token ();
1066   if (!token || (token != TOKEN_STRING))
1067     return PARSE_ERROR;
1068
1069   if (rc_style->fontset_name)
1070     g_free (rc_style->fontset_name);
1071   rc_style->fontset_name = g_strdup (token_str);
1072
1073   return PARSE_OK;
1074 }
1075
1076 static gint
1077 gtk_rc_parse_state (GtkStateType *state)
1078 {
1079   gint token;
1080
1081   token = gtk_rc_peek_next_token ();
1082   if (!token)
1083     return PARSE_ERROR;
1084   if (token != TOKEN_LEFT_BRACE)
1085     return PARSE_SYNTAX;
1086   token = gtk_rc_get_next_token ();
1087
1088   token = gtk_rc_get_next_token ();
1089   if (token == TOKEN_ACTIVE)
1090     *state = GTK_STATE_ACTIVE;
1091   else if (token == TOKEN_INSENSITIVE)
1092     *state = GTK_STATE_INSENSITIVE;
1093   else if (token == TOKEN_NORMAL)
1094     *state = GTK_STATE_NORMAL;
1095   else if (token == TOKEN_PRELIGHT)
1096     *state = GTK_STATE_PRELIGHT;
1097   else if (token == TOKEN_SELECTED)
1098     *state = GTK_STATE_SELECTED;
1099   else
1100     return PARSE_ERROR;
1101
1102   token = gtk_rc_get_next_token ();
1103   if (!token || (token != TOKEN_RIGHT_BRACE))
1104     return PARSE_ERROR;
1105
1106   return PARSE_OK;
1107 }
1108
1109 static gint
1110 gtk_rc_parse_color (GdkColor *color)
1111 {
1112   gint token;
1113   gint length;
1114   gint temp;
1115   gchar buf[9];
1116   gint i, j;
1117
1118   token = gtk_rc_peek_next_token ();
1119   if (!token)
1120     return PARSE_ERROR;
1121
1122   switch (token)
1123     {
1124     case TOKEN_LEFT_CURLY:
1125       token = gtk_rc_get_next_token ();
1126
1127       token = gtk_rc_get_next_token ();
1128       if (!token || ((token != TOKEN_INTEGER) && (token != TOKEN_FLOAT)))
1129         return PARSE_ERROR;
1130
1131       if (token == TOKEN_FLOAT)
1132         token_int = token_float * 65535.0;
1133       if (token_int < 0)
1134         token_int = 0;
1135       if (token_int > 65535)
1136         token_int = 65535;
1137
1138       color->red = token_int;
1139
1140       token = gtk_rc_get_next_token ();
1141       if (!token || (token != TOKEN_COMMA))
1142         return PARSE_ERROR;
1143
1144       token = gtk_rc_get_next_token ();
1145       if (!token || ((token != TOKEN_INTEGER) && (token != TOKEN_FLOAT)))
1146         return PARSE_ERROR;
1147
1148       if (token == TOKEN_FLOAT)
1149         token_int = token_float * 65535.0;
1150       if (token_int < 0)
1151         token_int = 0;
1152       if (token_int > 65535)
1153         token_int = 65535;
1154
1155       color->green = token_int;
1156
1157       token = gtk_rc_get_next_token ();
1158       if (!token || (token != TOKEN_COMMA))
1159         return PARSE_ERROR;
1160
1161       token = gtk_rc_get_next_token ();
1162       if (!token || ((token != TOKEN_INTEGER) && (token != TOKEN_FLOAT)))
1163         return PARSE_ERROR;
1164
1165       if (token == TOKEN_FLOAT)
1166         token_int = token_float * 65535.0;
1167       if (token_int < 0)
1168         token_int = 0;
1169       if (token_int > 65535)
1170         token_int = 65535;
1171
1172       color->blue = token_int;
1173
1174       token = gtk_rc_get_next_token ();
1175       if (!token || (token != TOKEN_RIGHT_CURLY))
1176         return PARSE_ERROR;
1177       break;
1178
1179     case TOKEN_STRING:
1180       token = gtk_rc_get_next_token ();
1181
1182       if (token_str[0] != '#')
1183         return PARSE_ERROR;
1184
1185       length = strlen (token_str) - 1;
1186       if (((length % 3) != 0) || (length > 12))
1187         return PARSE_ERROR;
1188       length /= 3;
1189
1190       for (i = 0, j = 1; i < length; i++, j++)
1191         buf[i] = token_str[j];
1192       buf[i] = '\0';
1193
1194       sscanf (buf, "%x", &temp);
1195       color->red = temp;
1196
1197       for (i = 0; i < length; i++, j++)
1198         buf[i] = token_str[j];
1199       buf[i] = '\0';
1200
1201       sscanf (buf, "%x", &temp);
1202       color->green = temp;
1203
1204       for (i = 0; i < length; i++, j++)
1205         buf[i] = token_str[j];
1206       buf[i] = '\0';
1207
1208       sscanf (buf, "%x", &temp);
1209       color->blue = temp;
1210
1211       if (length == 1)
1212         {
1213           color->red *= 4369;
1214           color->green *= 4369;
1215           color->blue *= 4369;
1216         }
1217       else if (length == 2)
1218         {
1219           color->red *= 257;
1220           color->green *= 257;
1221           color->blue *= 257;
1222         }
1223       else if (length == 3)
1224         {
1225           color->red *= 16;
1226           color->green *= 16;
1227           color->blue *= 16;
1228         }
1229       break;
1230
1231     default:
1232       return PARSE_SYNTAX;
1233     }
1234
1235   return PARSE_OK;
1236 }
1237
1238 static gint
1239 gtk_rc_parse_pixmap_path ()
1240 {
1241   gint token;
1242
1243   token = gtk_rc_peek_next_token ();
1244   if (!token)
1245     return PARSE_ERROR;
1246   if (token != TOKEN_PIXMAP_PATH)
1247     return PARSE_SYNTAX;
1248   token = gtk_rc_get_next_token ();
1249
1250   token = gtk_rc_get_next_token ();
1251
1252   if (!token || (token != TOKEN_STRING))
1253     return PARSE_ERROR;
1254
1255   gtk_rc_parse_pixmap_path_string(token_str);
1256
1257   return PARSE_OK;
1258 }
1259
1260 static void gtk_rc_parse_pixmap_path_string(gchar *pix_path)
1261 {
1262   gchar *buf;
1263   gint end_offset;
1264   gint start_offset = 0;
1265   gint path_len;
1266   gint path_num;
1267
1268   /* free the old one, or just add to the old one ? */
1269   for (path_num=0; pixmap_path[path_num]; path_num++)
1270     {
1271       g_free(pixmap_path[path_num]);
1272       pixmap_path[path_num] = NULL;
1273     }
1274
1275   path_num = 0;
1276
1277   path_len = strlen(pix_path);
1278
1279   buf = g_strdup(pix_path);
1280
1281   for(end_offset = 0; end_offset <= path_len; end_offset++)
1282     {
1283       if ( (buf[end_offset] == ':') || (end_offset == path_len) )
1284         {
1285           buf[end_offset] = '\0';
1286           pixmap_path[path_num] = g_strdup(buf + start_offset);
1287           path_num++;
1288           pixmap_path[path_num] = NULL;
1289           start_offset = end_offset + 1;
1290           g_free(buf);
1291           buf = g_strdup(pix_path);
1292         }
1293     }
1294   g_free(buf);
1295 }
1296
1297 static gint
1298 gtk_rc_parse_widget_style ()
1299 {
1300   GtkRcSet *rc_set;
1301   gint token;
1302
1303   token = gtk_rc_peek_next_token ();
1304   if (!token)
1305     return PARSE_ERROR;
1306   if (token != TOKEN_WIDGET)
1307     return PARSE_SYNTAX;
1308   token = gtk_rc_get_next_token ();
1309
1310   token = gtk_rc_get_next_token ();
1311   if (!token || (token != TOKEN_STRING))
1312     return PARSE_ERROR;
1313
1314   rc_set = g_new (GtkRcSet, 1);
1315   rc_set->set = g_strdup (token_str);
1316
1317   token = gtk_rc_get_next_token ();
1318   if (!token || (token != TOKEN_STYLE))
1319     {
1320       g_free (rc_set->set);
1321       g_free (rc_set);
1322       return PARSE_ERROR;
1323     }
1324
1325   token = gtk_rc_get_next_token ();
1326   if (!token || (token != TOKEN_STRING))
1327     {
1328       g_free (rc_set->set);
1329       g_free (rc_set);
1330       return PARSE_ERROR;
1331     }
1332
1333   rc_set->rc_style = gtk_rc_style_find (token_str);
1334   if (!rc_set->rc_style)
1335     {
1336       g_free (rc_set->set);
1337       g_free (rc_set);
1338       return PARSE_ERROR;
1339     }
1340
1341   widget_sets = g_slist_append (widget_sets, rc_set);
1342
1343   return PARSE_OK;
1344 }
1345
1346 static gint
1347 gtk_rc_parse_widget_class_style ()
1348 {
1349   GtkRcSet *rc_set;
1350   gint token;
1351
1352   token = gtk_rc_peek_next_token ();
1353   if (!token)
1354     return PARSE_ERROR;
1355   if (token != TOKEN_WIDGET_CLASS)
1356     return PARSE_SYNTAX;
1357   token = gtk_rc_get_next_token ();
1358
1359   token = gtk_rc_get_next_token ();
1360   if (!token || (token != TOKEN_STRING))
1361     return PARSE_ERROR;
1362
1363   rc_set = g_new (GtkRcSet, 1);
1364   rc_set->set = g_strdup (token_str);
1365
1366   token = gtk_rc_get_next_token ();
1367   if (!token || (token != TOKEN_STYLE))
1368     {
1369       g_free (rc_set->set);
1370       g_free (rc_set);
1371       return PARSE_ERROR;
1372     }
1373
1374   token = gtk_rc_get_next_token ();
1375   if (!token || (token != TOKEN_STRING))
1376     {
1377       g_free (rc_set->set);
1378       g_free (rc_set);
1379       return PARSE_ERROR;
1380     }
1381
1382   rc_set->rc_style = gtk_rc_style_find (token_str);
1383   if (!rc_set->rc_style)
1384     {
1385       g_free (rc_set->set);
1386       g_free (rc_set);
1387       return PARSE_ERROR;
1388     }
1389
1390   widget_class_sets = g_slist_append (widget_class_sets, rc_set);
1391
1392   return PARSE_OK;
1393 }
1394
1395 static char*
1396 gtk_rc_widget_path (GtkWidget *widget)
1397 {
1398   GtkWidget *tmp_widget;
1399   char *path;
1400   char *name;
1401   int pathlength;
1402   int namelength;
1403
1404   path = NULL;
1405   pathlength = 0;
1406
1407   tmp_widget = widget;
1408   while (tmp_widget)
1409     {
1410       name = gtk_widget_get_name (tmp_widget);
1411       pathlength += strlen (name);
1412
1413       tmp_widget = tmp_widget->parent;
1414
1415       if (tmp_widget)
1416         pathlength += 1;
1417     }
1418
1419   path = g_new (char, pathlength + 1);
1420   path[pathlength] = '\0';
1421
1422   tmp_widget = widget;
1423   while (tmp_widget)
1424     {
1425       name = gtk_widget_get_name (tmp_widget);
1426       namelength = strlen (name);
1427
1428       strncpy (&path[pathlength - namelength], name, namelength);
1429       pathlength -= namelength;
1430
1431       tmp_widget = tmp_widget->parent;
1432
1433       if (tmp_widget)
1434         {
1435           pathlength -= 1;
1436           path[pathlength] = '.';
1437         }
1438     }
1439
1440   return path;
1441 }
1442
1443 static char*
1444 gtk_rc_widget_class_path (GtkWidget *widget)
1445 {
1446   GtkWidget *tmp_widget;
1447   char *path;
1448   char *name;
1449   int pathlength;
1450   int namelength;
1451
1452   path = NULL;
1453   pathlength = 0;
1454
1455   tmp_widget = widget;
1456   while (tmp_widget)
1457     {
1458       name = gtk_type_name (GTK_WIDGET_TYPE (tmp_widget));
1459       pathlength += strlen (name);
1460
1461       tmp_widget = tmp_widget->parent;
1462
1463       if (tmp_widget)
1464         pathlength += 1;
1465     }
1466
1467   path = g_new (char, pathlength + 1);
1468   path[pathlength] = '\0';
1469
1470   tmp_widget = widget;
1471   while (tmp_widget)
1472     {
1473       name = gtk_type_name (GTK_WIDGET_TYPE (tmp_widget));
1474       namelength = strlen (name);
1475
1476       strncpy (&path[pathlength - namelength], name, namelength);
1477       pathlength -= namelength;
1478
1479       tmp_widget = tmp_widget->parent;
1480
1481       if (tmp_widget)
1482         {
1483           pathlength -= 1;
1484           path[pathlength] = '.';
1485         }
1486     }
1487
1488   return path;
1489 }