]> Pileus Git - ~andy/gtk/blob - modules/engines/pixbuf/pixbuf-rc-style.c
Updates to work with GTK+-2.0 theme engine architecture. It won't even
[~andy/gtk] / modules / engines / pixbuf / pixbuf-rc-style.c
1 /* GTK+ Pixbuf Engine
2  * Copyright (C) 1998-2000 Red Hat, Inc.
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  * Written by Owen Taylor <otaylor@redhat.com>, based on code by
20  * Carsten Haitzler <raster@rasterman.com>
21  */
22
23 #include "pixbuf.h"
24 #include "pixbuf-style.h"
25 #include "pixbuf-rc-style.h"
26
27 static void      pixbuf_rc_style_init         (PixbufRcStyle      *style);
28 static void      pixbuf_rc_style_class_init   (PixbufRcStyleClass *klass);
29 static void      pixbuf_rc_style_finalize     (GObject            *object);
30 static guint     pixbuf_rc_style_parse        (GtkRcStyle         *rc_style,
31                                                GScanner           *scanner);
32 static void      pixbuf_rc_style_merge        (GtkRcStyle         *dest,
33                                                GtkRcStyle         *src);
34 static GtkStyle *pixbuf_rc_style_create_style (GtkRcStyle         *rc_style);
35
36 static void theme_image_unref (ThemeImage *data);
37
38 static struct
39   {
40     gchar              *name;
41     guint               token;
42   }
43 theme_symbols[] =
44 {
45   { "image",            TOKEN_IMAGE  },
46   { "function",         TOKEN_FUNCTION },
47   { "file",             TOKEN_FILE },
48   { "stretch",          TOKEN_STRETCH },
49   { "recolorable",      TOKEN_RECOLORABLE },
50   { "border",           TOKEN_BORDER },
51   { "detail",           TOKEN_DETAIL },
52   { "state",            TOKEN_STATE },
53   { "shadow",           TOKEN_SHADOW },
54   { "gap_side",         TOKEN_GAP_SIDE },
55   { "gap_file",         TOKEN_GAP_FILE },
56   { "gap_border",       TOKEN_GAP_BORDER },
57   { "gap_start_file",   TOKEN_GAP_START_FILE },
58   { "gap_start_border", TOKEN_GAP_START_BORDER },
59   { "gap_end_file",     TOKEN_GAP_END_FILE },
60   { "gap_end_border",   TOKEN_GAP_END_BORDER },
61   { "overlay_file",     TOKEN_OVERLAY_FILE },
62   { "overlay_border",   TOKEN_OVERLAY_BORDER },
63   { "overlay_stretch",  TOKEN_OVERLAY_STRETCH },
64   { "arrow_direction",  TOKEN_ARROW_DIRECTION },
65   { "orientation",      TOKEN_ORIENTATION },
66
67   { "HLINE",            TOKEN_D_HLINE },
68   { "VLINE",            TOKEN_D_VLINE },
69   { "SHADOW",           TOKEN_D_SHADOW },
70   { "POLYGON",          TOKEN_D_POLYGON },
71   { "ARROW",            TOKEN_D_ARROW },
72   { "DIAMOND",          TOKEN_D_DIAMOND },
73   { "OVAL",             TOKEN_D_OVAL },
74   { "STRING",           TOKEN_D_STRING },
75   { "BOX",              TOKEN_D_BOX },
76   { "FLAT_BOX",         TOKEN_D_FLAT_BOX },
77   { "CHECK",            TOKEN_D_CHECK },
78   { "OPTION",           TOKEN_D_OPTION },
79   { "CROSS",            TOKEN_D_CROSS },
80   { "RAMP",             TOKEN_D_RAMP },
81   { "TAB",              TOKEN_D_TAB },
82   { "SHADOW_GAP",       TOKEN_D_SHADOW_GAP },
83   { "BOX_GAP",          TOKEN_D_BOX_GAP },
84   { "EXTENSION",        TOKEN_D_EXTENSION },
85   { "FOCUS",            TOKEN_D_FOCUS },
86   { "SLIDER",           TOKEN_D_SLIDER },
87   { "ENTRY",            TOKEN_D_ENTRY },
88   { "HANDLE",           TOKEN_D_HANDLE },
89
90   { "TRUE",             TOKEN_TRUE },
91   { "FALSE",            TOKEN_FALSE },
92
93   { "TOP",              TOKEN_TOP },
94   { "UP",               TOKEN_UP },
95   { "BOTTOM",           TOKEN_BOTTOM },
96   { "DOWN",             TOKEN_DOWN },
97   { "LEFT",             TOKEN_LEFT },
98   { "RIGHT",            TOKEN_RIGHT },
99
100   { "NORMAL",           TOKEN_NORMAL },
101   { "ACTIVE",           TOKEN_ACTIVE },
102   { "PRELIGHT",         TOKEN_PRELIGHT },
103   { "SELECTED",         TOKEN_SELECTED },
104   { "INSENSITIVE",      TOKEN_INSENSITIVE },
105
106   { "NONE",             TOKEN_NONE },
107   { "IN",               TOKEN_IN },
108   { "OUT",              TOKEN_OUT },
109   { "ETCHED_IN",        TOKEN_ETCHED_IN },
110   { "ETCHED_OUT",       TOKEN_ETCHED_OUT },
111   { "HORIZONTAL",       TOKEN_HORIZONTAL },
112   { "VERTICAL",         TOKEN_VERTICAL },
113 };
114
115 static GtkRcStyleClass *parent_class;
116
117 GType pixbuf_type_rc_style = 0;
118
119 void
120 pixbuf_rc_style_register_type (GtkThemeEngine *engine)
121 {
122   static const GTypeInfo object_info =
123   {
124     sizeof (PixbufRcStyleClass),
125     (GBaseInitFunc) NULL,
126     (GBaseFinalizeFunc) NULL,
127     (GClassInitFunc) pixbuf_rc_style_class_init,
128     NULL,           /* class_finalize */
129     NULL,           /* class_data */
130     sizeof (PixbufRcStyle),
131     0,              /* n_preallocs */
132     (GInstanceInitFunc) pixbuf_rc_style_init,
133   };
134   
135   pixbuf_type_rc_style = gtk_theme_engine_register_type (engine,
136                                                          GTK_TYPE_RC_STYLE,
137                                                          "PixbufRcStyle",
138                                                          &object_info);
139 }
140
141 static void
142 pixbuf_rc_style_init (PixbufRcStyle *style)
143 {
144 }
145
146 static void
147 pixbuf_rc_style_class_init (PixbufRcStyleClass *klass)
148 {
149   GtkRcStyleClass *rc_style_class = GTK_RC_STYLE_CLASS (klass);
150   GObjectClass *object_class = G_OBJECT_CLASS (klass);
151
152   parent_class = g_type_class_peek_parent (klass);
153
154   rc_style_class->parse = pixbuf_rc_style_parse;
155   rc_style_class->merge = pixbuf_rc_style_merge;
156   rc_style_class->create_style = pixbuf_rc_style_create_style;
157   
158   object_class->finalize = pixbuf_rc_style_finalize;
159 }
160
161 static void
162 pixbuf_rc_style_finalize (GObject *object)
163 {
164   PixbufRcStyle *rc_style = PIXBUF_RC_STYLE (object);
165   
166   g_list_foreach (rc_style->img_list, (GFunc) theme_image_unref, NULL);
167   g_list_free (rc_style->img_list);
168
169   G_OBJECT_CLASS (parent_class)->finalize (object);
170 }
171
172 static guint
173 theme_parse_file(GScanner     *scanner,
174                  ThemePixbuf **theme_pb)
175 {
176   guint token;
177   gchar *pixmap;
178
179   /* Skip 'blah_file' */
180   token = g_scanner_get_next_token(scanner);
181
182   token = g_scanner_get_next_token(scanner);
183   if (token != G_TOKEN_EQUAL_SIGN)
184     return G_TOKEN_EQUAL_SIGN;
185
186   token = g_scanner_get_next_token(scanner);
187   if (token != G_TOKEN_STRING)
188     return G_TOKEN_STRING;
189
190   if (!*theme_pb)
191     *theme_pb = theme_pixbuf_new ();
192
193   pixmap = gtk_rc_find_pixmap_in_path(scanner, scanner->value.v_string);
194   if (pixmap)
195     {
196       theme_pixbuf_set_filename (*theme_pb, pixmap);
197       g_free (pixmap);
198     }
199
200   return G_TOKEN_NONE;
201 }
202
203 static guint
204 theme_parse_border (GScanner     *scanner,
205                     ThemePixbuf **theme_pb)
206 {
207   guint               token;
208   gint left, right, top, bottom;
209
210   /* Skip 'blah_border' */
211   token = g_scanner_get_next_token(scanner);
212
213   token = g_scanner_get_next_token(scanner);
214   if (token != G_TOKEN_EQUAL_SIGN)
215     return G_TOKEN_EQUAL_SIGN;
216
217   token = g_scanner_get_next_token(scanner);
218   if (token != G_TOKEN_LEFT_CURLY)
219     return G_TOKEN_LEFT_CURLY;
220
221   token = g_scanner_get_next_token(scanner);
222   if (token != G_TOKEN_INT)
223     return G_TOKEN_INT;
224   left = scanner->value.v_int;
225   token = g_scanner_get_next_token(scanner);
226   if (token != G_TOKEN_COMMA)
227     return G_TOKEN_COMMA;
228
229   token = g_scanner_get_next_token(scanner);
230   if (token != G_TOKEN_INT)
231     return G_TOKEN_INT;
232   right = scanner->value.v_int;
233   token = g_scanner_get_next_token(scanner);
234   if (token != G_TOKEN_COMMA)
235     return G_TOKEN_COMMA;
236
237   token = g_scanner_get_next_token(scanner);
238   if (token != G_TOKEN_INT)
239     return G_TOKEN_INT;
240   top = scanner->value.v_int;
241   token = g_scanner_get_next_token(scanner);
242   if (token != G_TOKEN_COMMA)
243     return G_TOKEN_COMMA;
244
245   token = g_scanner_get_next_token(scanner);
246   if (token != G_TOKEN_INT)
247     return G_TOKEN_INT;
248   bottom = scanner->value.v_int;
249
250   token = g_scanner_get_next_token(scanner);
251   if (token != G_TOKEN_RIGHT_CURLY)
252     return G_TOKEN_RIGHT_CURLY;
253
254   if (!*theme_pb)
255     *theme_pb = theme_pixbuf_new ();
256   
257   theme_pixbuf_set_border (*theme_pb, left, right, top, bottom);
258   
259   return G_TOKEN_NONE;
260 }
261
262 static guint
263 theme_parse_stretch(GScanner     *scanner,
264                     ThemePixbuf **theme_pb)
265 {
266   guint token;
267   gboolean stretch;
268
269   /* Skip 'blah_stretch' */
270   token = g_scanner_get_next_token(scanner);
271
272   token = g_scanner_get_next_token(scanner);
273   if (token != G_TOKEN_EQUAL_SIGN)
274     return G_TOKEN_EQUAL_SIGN;
275
276   token = g_scanner_get_next_token(scanner);
277   if (token == TOKEN_TRUE)
278     stretch = TRUE;
279   else if (token == TOKEN_FALSE)
280     stretch = FALSE;
281   else
282     return TOKEN_TRUE;
283
284   if (!*theme_pb)
285     *theme_pb = theme_pixbuf_new ();
286   
287   theme_pixbuf_set_stretch (*theme_pb, stretch);
288   
289   return G_TOKEN_NONE;
290 }
291
292 static guint
293 theme_parse_recolorable(GScanner * scanner,
294                         ThemeImage * data)
295 {
296   guint               token;
297
298   token = g_scanner_get_next_token(scanner);
299   if (token != TOKEN_RECOLORABLE)
300     return TOKEN_RECOLORABLE;
301
302   token = g_scanner_get_next_token(scanner);
303   if (token != G_TOKEN_EQUAL_SIGN)
304     return G_TOKEN_EQUAL_SIGN;
305
306   token = g_scanner_get_next_token(scanner);
307   if (token == TOKEN_TRUE)
308     data->recolorable = 1;
309   else if (token == TOKEN_FALSE)
310     data->recolorable = 0;
311   else
312     return TOKEN_TRUE;
313
314   return G_TOKEN_NONE;
315 }
316
317 static guint
318 theme_parse_function(GScanner * scanner,
319                      ThemeImage *data)
320 {
321   guint               token;
322
323   token = g_scanner_get_next_token(scanner);
324   if (token != TOKEN_FUNCTION)
325     return TOKEN_FUNCTION;
326
327   token = g_scanner_get_next_token(scanner);
328   if (token != G_TOKEN_EQUAL_SIGN)
329     return G_TOKEN_EQUAL_SIGN;
330
331   token = g_scanner_get_next_token(scanner);
332   if ((token >= TOKEN_D_HLINE) && (token <= TOKEN_D_HANDLE))
333     data->match_data.function = token;
334
335   return G_TOKEN_NONE;
336 }
337
338 static guint
339 theme_parse_detail(GScanner * scanner,
340                    ThemeImage * data)
341 {
342   guint               token;
343
344   token = g_scanner_get_next_token(scanner);
345   if (token != TOKEN_DETAIL)
346     return TOKEN_DETAIL;
347
348   token = g_scanner_get_next_token(scanner);
349   if (token != G_TOKEN_EQUAL_SIGN)
350     return G_TOKEN_EQUAL_SIGN;
351
352   token = g_scanner_get_next_token(scanner);
353   if (token != G_TOKEN_STRING)
354     return G_TOKEN_STRING;
355
356   if (data->match_data.detail)
357     g_free (data->match_data.detail);
358   
359   data->match_data.detail = g_strdup(scanner->value.v_string);
360
361   return G_TOKEN_NONE;
362 }
363
364 static guint
365 theme_parse_state(GScanner * scanner,
366                   ThemeImage * data)
367 {
368   guint               token;
369
370   token = g_scanner_get_next_token(scanner);
371   if (token != TOKEN_STATE)
372     return TOKEN_STATE;
373
374   token = g_scanner_get_next_token(scanner);
375   if (token != G_TOKEN_EQUAL_SIGN)
376     return G_TOKEN_EQUAL_SIGN;
377
378   token = g_scanner_get_next_token(scanner);
379   if (token == TOKEN_NORMAL)
380     data->match_data.state = GTK_STATE_NORMAL;
381   else if (token == TOKEN_ACTIVE)
382     data->match_data.state = GTK_STATE_ACTIVE;
383   else if (token == TOKEN_PRELIGHT)
384     data->match_data.state = GTK_STATE_PRELIGHT;
385   else if (token == TOKEN_SELECTED)
386     data->match_data.state = GTK_STATE_SELECTED;
387   else if (token == TOKEN_INSENSITIVE)
388     data->match_data.state = GTK_STATE_INSENSITIVE;
389   else
390     return TOKEN_NORMAL;
391
392   data->match_data.flags |= THEME_MATCH_STATE;
393   
394   return G_TOKEN_NONE;
395 }
396
397 static guint
398 theme_parse_shadow(GScanner * scanner,
399                    ThemeImage * data)
400 {
401   guint               token;
402
403   token = g_scanner_get_next_token(scanner);
404   if (token != TOKEN_SHADOW)
405     return TOKEN_SHADOW;
406
407   token = g_scanner_get_next_token(scanner);
408   if (token != G_TOKEN_EQUAL_SIGN)
409     return G_TOKEN_EQUAL_SIGN;
410
411   token = g_scanner_get_next_token(scanner);
412   if (token == TOKEN_NONE)
413     data->match_data.shadow = GTK_SHADOW_NONE;
414   else if (token == TOKEN_IN)
415     data->match_data.shadow = GTK_SHADOW_IN;
416   else if (token == TOKEN_OUT)
417     data->match_data.shadow = GTK_SHADOW_OUT;
418   else if (token == TOKEN_ETCHED_IN)
419     data->match_data.shadow = GTK_SHADOW_ETCHED_IN;
420   else if (token == TOKEN_ETCHED_OUT)
421     data->match_data.shadow = GTK_SHADOW_ETCHED_OUT;
422   else
423     return TOKEN_NONE;
424
425   data->match_data.flags |= THEME_MATCH_SHADOW;
426   
427   return G_TOKEN_NONE;
428 }
429
430 static guint
431 theme_parse_arrow_direction(GScanner * scanner,
432                             ThemeImage * data)
433 {
434   guint               token;
435
436   token = g_scanner_get_next_token(scanner);
437   if (token != TOKEN_ARROW_DIRECTION)
438     return TOKEN_ARROW_DIRECTION;
439
440   token = g_scanner_get_next_token(scanner);
441   if (token != G_TOKEN_EQUAL_SIGN)
442     return G_TOKEN_EQUAL_SIGN;
443
444   token = g_scanner_get_next_token(scanner);
445   if (token == TOKEN_UP)
446     data->match_data.arrow_direction = GTK_ARROW_UP;
447   else if (token == TOKEN_DOWN)
448     data->match_data.arrow_direction = GTK_ARROW_DOWN;
449   else if (token == TOKEN_LEFT)
450     data->match_data.arrow_direction = GTK_ARROW_LEFT;
451   else if (token == TOKEN_RIGHT)
452     data->match_data.arrow_direction = GTK_ARROW_RIGHT;
453   else
454     return TOKEN_UP;
455
456   data->match_data.flags |= THEME_MATCH_ARROW_DIRECTION;
457   
458   return G_TOKEN_NONE;
459 }
460
461 static guint
462 theme_parse_gap_side(GScanner * scanner,
463                      ThemeImage * data)
464 {
465   guint               token;
466
467   token = g_scanner_get_next_token(scanner);
468   if (token != TOKEN_GAP_SIDE)
469     return TOKEN_GAP_SIDE;
470
471   token = g_scanner_get_next_token(scanner);
472   if (token != G_TOKEN_EQUAL_SIGN)
473     return G_TOKEN_EQUAL_SIGN;
474
475   token = g_scanner_get_next_token(scanner);
476
477   if (token == TOKEN_TOP)
478     data->match_data.gap_side = GTK_POS_TOP;
479   else if (token == TOKEN_BOTTOM)
480     data->match_data.gap_side = GTK_POS_BOTTOM;
481   else if (token == TOKEN_LEFT)
482     data->match_data.gap_side = GTK_POS_LEFT;
483   else if (token == TOKEN_RIGHT)
484     data->match_data.gap_side = GTK_POS_RIGHT;
485   else
486     return TOKEN_TOP;
487
488   data->match_data.flags |= THEME_MATCH_GAP_SIDE;
489   
490   return G_TOKEN_NONE;
491 }
492
493 static guint
494 theme_parse_orientation(GScanner * scanner,
495                         ThemeImage * data)
496 {
497   guint               token;
498
499   token = g_scanner_get_next_token(scanner);
500   if (token != TOKEN_ORIENTATION)
501     return TOKEN_ORIENTATION;
502
503   token = g_scanner_get_next_token(scanner);
504   if (token != G_TOKEN_EQUAL_SIGN)
505     return G_TOKEN_EQUAL_SIGN;
506
507   token = g_scanner_get_next_token(scanner);
508
509   if (token == TOKEN_HORIZONTAL)
510     data->match_data.orientation = GTK_ORIENTATION_HORIZONTAL;
511   else if (token == TOKEN_VERTICAL)
512     data->match_data.orientation = GTK_ORIENTATION_VERTICAL;
513   else
514     return TOKEN_HORIZONTAL;
515
516   data->match_data.flags |= THEME_MATCH_ORIENTATION;
517   
518   return G_TOKEN_NONE;
519 }
520
521 static void
522 theme_image_ref (ThemeImage *data)
523 {
524   data->refcount++;
525 }
526
527 static void
528 theme_image_unref (ThemeImage *data)
529 {
530   data->refcount--;
531   if (data->refcount == 0)
532     {
533       if (data->match_data.detail)
534         g_free (data->match_data.detail);
535       if (data->background)
536         theme_pixbuf_destroy (data->background);
537       if (data->overlay)
538         theme_pixbuf_destroy (data->overlay);
539       if (data->gap_start)
540         theme_pixbuf_destroy (data->gap_start);
541       if (data->gap)
542         theme_pixbuf_destroy (data->gap);
543       if (data->gap_end)
544         theme_pixbuf_destroy (data->gap_end);
545       g_free (data);
546     }
547 }
548
549 static guint
550 theme_parse_image(GScanner      *scanner,
551                   PixbufRcStyle *pixbuf_style,
552                   ThemeImage   **data_return)
553 {
554   guint               token;
555   ThemeImage *data;
556
557   data = NULL;
558   token = g_scanner_get_next_token(scanner);
559   if (token != TOKEN_IMAGE)
560     return TOKEN_IMAGE;
561
562   token = g_scanner_get_next_token(scanner);
563   if (token != G_TOKEN_LEFT_CURLY)
564     return G_TOKEN_LEFT_CURLY;
565
566   data = g_malloc(sizeof(ThemeImage));
567
568   data->refcount = 1;
569
570   data->background = NULL;
571   data->overlay = NULL;
572   data->gap_start = NULL;
573   data->gap = NULL;
574   data->gap_end = NULL;
575
576   data->recolorable = FALSE;
577
578   data->match_data.function = 0;
579   data->match_data.detail = NULL;
580   data->match_data.flags = 0;
581
582   token = g_scanner_peek_next_token(scanner);
583   while (token != G_TOKEN_RIGHT_CURLY)
584     {
585       switch (token)
586         {
587         case TOKEN_FUNCTION:
588           token = theme_parse_function(scanner, data);
589           break;
590         case TOKEN_RECOLORABLE:
591           token = theme_parse_recolorable(scanner, data);
592           break;
593         case TOKEN_DETAIL:
594           token = theme_parse_detail(scanner, data);
595           break;
596         case TOKEN_STATE:
597           token = theme_parse_state(scanner, data);
598           break;
599         case TOKEN_SHADOW:
600           token = theme_parse_shadow(scanner, data);
601           break;
602         case TOKEN_GAP_SIDE:
603           token = theme_parse_gap_side(scanner, data);
604           break;
605         case TOKEN_ARROW_DIRECTION:
606           token = theme_parse_arrow_direction(scanner, data);
607           break;
608         case TOKEN_ORIENTATION:
609           token = theme_parse_orientation(scanner, data);
610           break;
611         case TOKEN_FILE:
612           token = theme_parse_file(scanner, &data->background);
613           break;
614         case TOKEN_BORDER:
615           token = theme_parse_border(scanner, &data->background);
616           break;
617         case TOKEN_STRETCH:
618           token = theme_parse_stretch(scanner, &data->background);
619           break;
620         case TOKEN_GAP_FILE:
621           token = theme_parse_file(scanner, &data->gap);
622           break;
623         case TOKEN_GAP_BORDER:
624           token = theme_parse_border(scanner, &data->gap);
625           break;
626         case TOKEN_GAP_START_FILE:
627           token = theme_parse_file(scanner, &data->gap_start);
628           break;
629         case TOKEN_GAP_START_BORDER:
630           token = theme_parse_border(scanner, &data->gap_start);
631           break;
632         case TOKEN_GAP_END_FILE:
633           token = theme_parse_file(scanner, &data->gap_end);
634           break;
635         case TOKEN_GAP_END_BORDER:
636           token = theme_parse_border(scanner, &data->gap_end);
637           break;
638         case TOKEN_OVERLAY_FILE:
639           token = theme_parse_file(scanner, &data->overlay);
640           break;
641         case TOKEN_OVERLAY_BORDER:
642           token = theme_parse_border(scanner, &data->overlay);
643           break;
644         case TOKEN_OVERLAY_STRETCH:
645           token = theme_parse_stretch(scanner, &data->overlay);
646           break;
647         default:
648           g_scanner_get_next_token(scanner);
649           token = G_TOKEN_RIGHT_CURLY;
650           break;
651         }
652       if (token != G_TOKEN_NONE)
653         {
654           /* error - cleanup for exit */
655           theme_image_unref (data);
656           *data_return = NULL;
657           return token;
658         }
659       token = g_scanner_peek_next_token(scanner);
660     }
661
662   token = g_scanner_get_next_token(scanner);
663
664   if (token != G_TOKEN_RIGHT_CURLY)
665     {
666       /* error - cleanup for exit */
667       theme_image_unref (data);
668       *data_return = NULL;
669       return G_TOKEN_RIGHT_CURLY;
670     }
671
672   /* everything is fine now - insert yer cruft */
673   *data_return = data;
674   return G_TOKEN_NONE;
675 }
676
677 static guint
678 pixbuf_rc_style_parse (GtkRcStyle *rc_style,
679                        GScanner   *scanner)
680                      
681 {
682   static GQuark scope_id = 0;
683   PixbufRcStyle *pixbuf_style = PIXBUF_RC_STYLE (rc_style);
684
685   guint old_scope;
686   guint token;
687   gint i;
688   ThemeImage *img;
689   
690   /* Set up a new scope in this scanner. */
691
692   if (!scope_id)
693     scope_id = g_quark_from_string("pixbuf_theme_engine");
694
695   /* If we bail out due to errors, we *don't* reset the scope, so the
696    * error messaging code can make sense of our tokens.
697    */
698   old_scope = g_scanner_set_scope(scanner, scope_id);
699
700   /* Now check if we already added our symbols to this scope
701    * (in some previous call to theme_parse_rc_style for the
702    * same scanner.
703    */
704
705   if (!g_scanner_lookup_symbol(scanner, theme_symbols[0].name))
706     {
707       g_scanner_freeze_symbol_table(scanner);
708       for (i = 0; i < G_N_ELEMENTS (theme_symbols); i++)
709         g_scanner_scope_add_symbol(scanner, scope_id,
710                                    theme_symbols[i].name,
711                                    GINT_TO_POINTER(theme_symbols[i].token));
712       g_scanner_thaw_symbol_table(scanner);
713     }
714
715   /* We're ready to go, now parse the top level */
716
717   token = g_scanner_peek_next_token(scanner);
718   while (token != G_TOKEN_RIGHT_CURLY)
719     {
720       switch (token)
721         {
722         case TOKEN_IMAGE:
723           img = NULL;
724           token = theme_parse_image(scanner, pixbuf_style, &img);
725           break;
726         default:
727           g_scanner_get_next_token(scanner);
728           token = G_TOKEN_RIGHT_CURLY;
729           break;
730         }
731
732       if (token != G_TOKEN_NONE)
733         return token;
734       else
735         pixbuf_style->img_list = g_list_append(pixbuf_style->img_list, img);
736
737       token = g_scanner_peek_next_token(scanner);
738     }
739
740   g_scanner_get_next_token(scanner);
741
742   g_scanner_set_scope(scanner, old_scope);
743
744   return G_TOKEN_NONE;
745 }
746
747 static void
748 pixbuf_rc_style_merge (GtkRcStyle *dest,
749                        GtkRcStyle *src)
750 {
751   if (PIXBUF_IS_RC_STYLE (src))
752     {
753       PixbufRcStyle *pixbuf_dest = PIXBUF_RC_STYLE (dest);
754       PixbufRcStyle *pixbuf_src = PIXBUF_RC_STYLE (src);
755       GList *tmp_list1, *tmp_list2;
756       
757       if (pixbuf_src->img_list)
758         {
759           /* Copy src image list and append to dest image list */
760           
761           tmp_list2 = g_list_last (pixbuf_dest->img_list);
762           tmp_list1 = pixbuf_src->img_list;
763           
764           while (tmp_list1)
765             {
766               if (tmp_list2)
767                 {
768                   tmp_list2->next = g_list_alloc();
769                   tmp_list2->next->data = tmp_list1->data;
770                   tmp_list2->next->prev = tmp_list2;
771                   
772                   tmp_list2 = tmp_list2->next;
773                 }
774               else
775                 {
776                   pixbuf_dest->img_list = g_list_append (NULL, tmp_list1->data);
777                   tmp_list2 = pixbuf_dest->img_list;
778                 }
779               
780               theme_image_ref (tmp_list1->data);
781               tmp_list1 = tmp_list1->next;
782             }
783         }
784     }
785
786   parent_class->merge (dest, src);
787 }
788
789 /* Create an empty style suitable to this RC style
790  */
791 static GtkStyle *
792 pixbuf_rc_style_create_style (GtkRcStyle *rc_style)
793 {
794   return GTK_STYLE (g_object_new (PIXBUF_TYPE_STYLE, NULL));
795 }
796