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