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