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