]> Pileus Git - ~andy/gtk/blob - modules/engines/pixbuf/pixbuf-rc-style.c
Deprecate widget flag: GTK_WIDGET_CAN_FOCUS
[~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_foreach (rc_style->img_list, (GFunc) theme_image_unref, NULL);
188   g_list_free (rc_style->img_list);
189
190   G_OBJECT_CLASS (parent_class)->finalize (object);
191 }
192
193 static guint
194 theme_parse_file(GtkSettings  *settings,
195                  GScanner     *scanner,
196                  ThemePixbuf **theme_pb)
197 {
198   guint token;
199   gchar *pixmap;
200
201   /* Skip 'blah_file' */
202   token = g_scanner_get_next_token(scanner);
203
204   token = g_scanner_get_next_token(scanner);
205   if (token != G_TOKEN_EQUAL_SIGN)
206     return G_TOKEN_EQUAL_SIGN;
207
208   token = g_scanner_get_next_token(scanner);
209   if (token != G_TOKEN_STRING)
210     return G_TOKEN_STRING;
211
212   if (!*theme_pb)
213     *theme_pb = theme_pixbuf_new ();
214
215   pixmap = gtk_rc_find_pixmap_in_path(settings, scanner, scanner->value.v_string);
216   if (pixmap)
217     {
218       theme_pixbuf_set_filename (*theme_pb, pixmap);
219       g_free (pixmap);
220     }
221
222   return G_TOKEN_NONE;
223 }
224
225 static guint
226 theme_parse_border (GScanner     *scanner,
227                     ThemePixbuf **theme_pb)
228 {
229   guint               token;
230   gint left, right, top, bottom;
231
232   /* Skip 'blah_border' */
233   token = g_scanner_get_next_token(scanner);
234
235   token = g_scanner_get_next_token(scanner);
236   if (token != G_TOKEN_EQUAL_SIGN)
237     return G_TOKEN_EQUAL_SIGN;
238
239   token = g_scanner_get_next_token(scanner);
240   if (token != G_TOKEN_LEFT_CURLY)
241     return G_TOKEN_LEFT_CURLY;
242
243   token = g_scanner_get_next_token(scanner);
244   if (token != G_TOKEN_INT)
245     return G_TOKEN_INT;
246   left = scanner->value.v_int;
247   token = g_scanner_get_next_token(scanner);
248   if (token != G_TOKEN_COMMA)
249     return G_TOKEN_COMMA;
250
251   token = g_scanner_get_next_token(scanner);
252   if (token != G_TOKEN_INT)
253     return G_TOKEN_INT;
254   right = scanner->value.v_int;
255   token = g_scanner_get_next_token(scanner);
256   if (token != G_TOKEN_COMMA)
257     return G_TOKEN_COMMA;
258
259   token = g_scanner_get_next_token(scanner);
260   if (token != G_TOKEN_INT)
261     return G_TOKEN_INT;
262   top = scanner->value.v_int;
263   token = g_scanner_get_next_token(scanner);
264   if (token != G_TOKEN_COMMA)
265     return G_TOKEN_COMMA;
266
267   token = g_scanner_get_next_token(scanner);
268   if (token != G_TOKEN_INT)
269     return G_TOKEN_INT;
270   bottom = scanner->value.v_int;
271
272   token = g_scanner_get_next_token(scanner);
273   if (token != G_TOKEN_RIGHT_CURLY)
274     return G_TOKEN_RIGHT_CURLY;
275
276   if (!*theme_pb)
277     *theme_pb = theme_pixbuf_new ();
278   
279   theme_pixbuf_set_border (*theme_pb, left, right, top, bottom);
280   
281   return G_TOKEN_NONE;
282 }
283
284 static guint
285 theme_parse_stretch(GScanner     *scanner,
286                     ThemePixbuf **theme_pb)
287 {
288   guint token;
289   gboolean stretch;
290
291   /* Skip 'blah_stretch' */
292   token = g_scanner_get_next_token(scanner);
293
294   token = g_scanner_get_next_token(scanner);
295   if (token != G_TOKEN_EQUAL_SIGN)
296     return G_TOKEN_EQUAL_SIGN;
297
298   token = g_scanner_get_next_token(scanner);
299   if (token == TOKEN_TRUE)
300     stretch = TRUE;
301   else if (token == TOKEN_FALSE)
302     stretch = FALSE;
303   else
304     return TOKEN_TRUE;
305
306   if (!*theme_pb)
307     *theme_pb = theme_pixbuf_new ();
308   
309   theme_pixbuf_set_stretch (*theme_pb, stretch);
310   
311   return G_TOKEN_NONE;
312 }
313
314 static guint
315 theme_parse_recolorable(GScanner * scanner,
316                         ThemeImage * data)
317 {
318   guint               token;
319
320   token = g_scanner_get_next_token(scanner);
321   if (token != TOKEN_RECOLORABLE)
322     return TOKEN_RECOLORABLE;
323
324   token = g_scanner_get_next_token(scanner);
325   if (token != G_TOKEN_EQUAL_SIGN)
326     return G_TOKEN_EQUAL_SIGN;
327
328   token = g_scanner_get_next_token(scanner);
329   if (token == TOKEN_TRUE)
330     data->recolorable = 1;
331   else if (token == TOKEN_FALSE)
332     data->recolorable = 0;
333   else
334     return TOKEN_TRUE;
335
336   return G_TOKEN_NONE;
337 }
338
339 static guint
340 theme_parse_function(GScanner * scanner,
341                      ThemeImage *data)
342 {
343   guint               token;
344
345   token = g_scanner_get_next_token(scanner);
346   if (token != TOKEN_FUNCTION)
347     return TOKEN_FUNCTION;
348
349   token = g_scanner_get_next_token(scanner);
350   if (token != G_TOKEN_EQUAL_SIGN)
351     return G_TOKEN_EQUAL_SIGN;
352
353   token = g_scanner_get_next_token(scanner);
354   if ((token >= TOKEN_D_HLINE) && (token <= TOKEN_D_RESIZE_GRIP))
355     data->match_data.function = token;
356
357   return G_TOKEN_NONE;
358 }
359
360 static guint
361 theme_parse_detail(GScanner * scanner,
362                    ThemeImage * data)
363 {
364   guint               token;
365
366   token = g_scanner_get_next_token(scanner);
367   if (token != TOKEN_DETAIL)
368     return TOKEN_DETAIL;
369
370   token = g_scanner_get_next_token(scanner);
371   if (token != G_TOKEN_EQUAL_SIGN)
372     return G_TOKEN_EQUAL_SIGN;
373
374   token = g_scanner_get_next_token(scanner);
375   if (token != G_TOKEN_STRING)
376     return G_TOKEN_STRING;
377
378   g_free (data->match_data.detail);
379   
380   data->match_data.detail = g_strdup(scanner->value.v_string);
381
382   return G_TOKEN_NONE;
383 }
384
385 static guint
386 theme_parse_state(GScanner * scanner,
387                   ThemeImage * data)
388 {
389   guint               token;
390
391   token = g_scanner_get_next_token(scanner);
392   if (token != TOKEN_STATE)
393     return TOKEN_STATE;
394
395   token = g_scanner_get_next_token(scanner);
396   if (token != G_TOKEN_EQUAL_SIGN)
397     return G_TOKEN_EQUAL_SIGN;
398
399   token = g_scanner_get_next_token(scanner);
400   if (token == TOKEN_NORMAL)
401     data->match_data.state = GTK_STATE_NORMAL;
402   else if (token == TOKEN_ACTIVE)
403     data->match_data.state = GTK_STATE_ACTIVE;
404   else if (token == TOKEN_PRELIGHT)
405     data->match_data.state = GTK_STATE_PRELIGHT;
406   else if (token == TOKEN_SELECTED)
407     data->match_data.state = GTK_STATE_SELECTED;
408   else if (token == TOKEN_INSENSITIVE)
409     data->match_data.state = GTK_STATE_INSENSITIVE;
410   else
411     return TOKEN_NORMAL;
412
413   data->match_data.flags |= THEME_MATCH_STATE;
414   
415   return G_TOKEN_NONE;
416 }
417
418 static guint
419 theme_parse_shadow(GScanner * scanner,
420                    ThemeImage * data)
421 {
422   guint               token;
423
424   token = g_scanner_get_next_token(scanner);
425   if (token != TOKEN_SHADOW)
426     return TOKEN_SHADOW;
427
428   token = g_scanner_get_next_token(scanner);
429   if (token != G_TOKEN_EQUAL_SIGN)
430     return G_TOKEN_EQUAL_SIGN;
431
432   token = g_scanner_get_next_token(scanner);
433   if (token == TOKEN_NONE)
434     data->match_data.shadow = GTK_SHADOW_NONE;
435   else if (token == TOKEN_IN)
436     data->match_data.shadow = GTK_SHADOW_IN;
437   else if (token == TOKEN_OUT)
438     data->match_data.shadow = GTK_SHADOW_OUT;
439   else if (token == TOKEN_ETCHED_IN)
440     data->match_data.shadow = GTK_SHADOW_ETCHED_IN;
441   else if (token == TOKEN_ETCHED_OUT)
442     data->match_data.shadow = GTK_SHADOW_ETCHED_OUT;
443   else
444     return TOKEN_NONE;
445
446   data->match_data.flags |= THEME_MATCH_SHADOW;
447   
448   return G_TOKEN_NONE;
449 }
450
451 static guint
452 theme_parse_arrow_direction(GScanner * scanner,
453                             ThemeImage * data)
454 {
455   guint               token;
456
457   token = g_scanner_get_next_token(scanner);
458   if (token != TOKEN_ARROW_DIRECTION)
459     return TOKEN_ARROW_DIRECTION;
460
461   token = g_scanner_get_next_token(scanner);
462   if (token != G_TOKEN_EQUAL_SIGN)
463     return G_TOKEN_EQUAL_SIGN;
464
465   token = g_scanner_get_next_token(scanner);
466   if (token == TOKEN_UP)
467     data->match_data.arrow_direction = GTK_ARROW_UP;
468   else if (token == TOKEN_DOWN)
469     data->match_data.arrow_direction = GTK_ARROW_DOWN;
470   else if (token == TOKEN_LEFT)
471     data->match_data.arrow_direction = GTK_ARROW_LEFT;
472   else if (token == TOKEN_RIGHT)
473     data->match_data.arrow_direction = GTK_ARROW_RIGHT;
474   else
475     return TOKEN_UP;
476
477   data->match_data.flags |= THEME_MATCH_ARROW_DIRECTION;
478   
479   return G_TOKEN_NONE;
480 }
481
482 static guint
483 theme_parse_gap_side(GScanner * scanner,
484                      ThemeImage * data)
485 {
486   guint               token;
487
488   token = g_scanner_get_next_token(scanner);
489   if (token != TOKEN_GAP_SIDE)
490     return TOKEN_GAP_SIDE;
491
492   token = g_scanner_get_next_token(scanner);
493   if (token != G_TOKEN_EQUAL_SIGN)
494     return G_TOKEN_EQUAL_SIGN;
495
496   token = g_scanner_get_next_token(scanner);
497
498   if (token == TOKEN_TOP)
499     data->match_data.gap_side = GTK_POS_TOP;
500   else if (token == TOKEN_BOTTOM)
501     data->match_data.gap_side = GTK_POS_BOTTOM;
502   else if (token == TOKEN_LEFT)
503     data->match_data.gap_side = GTK_POS_LEFT;
504   else if (token == TOKEN_RIGHT)
505     data->match_data.gap_side = GTK_POS_RIGHT;
506   else
507     return TOKEN_TOP;
508
509   data->match_data.flags |= THEME_MATCH_GAP_SIDE;
510   
511   return G_TOKEN_NONE;
512 }
513
514 static guint
515 theme_parse_orientation(GScanner * scanner,
516                         ThemeImage * data)
517 {
518   guint               token;
519
520   token = g_scanner_get_next_token(scanner);
521   if (token != TOKEN_ORIENTATION)
522     return TOKEN_ORIENTATION;
523
524   token = g_scanner_get_next_token(scanner);
525   if (token != G_TOKEN_EQUAL_SIGN)
526     return G_TOKEN_EQUAL_SIGN;
527
528   token = g_scanner_get_next_token(scanner);
529
530   if (token == TOKEN_HORIZONTAL)
531     data->match_data.orientation = GTK_ORIENTATION_HORIZONTAL;
532   else if (token == TOKEN_VERTICAL)
533     data->match_data.orientation = GTK_ORIENTATION_VERTICAL;
534   else
535     return TOKEN_HORIZONTAL;
536
537   data->match_data.flags |= THEME_MATCH_ORIENTATION;
538   
539   return G_TOKEN_NONE;
540 }
541
542 static guint
543 theme_parse_expander_style(GScanner * scanner,
544                            ThemeImage * data)
545 {
546   guint               token;
547
548   token = g_scanner_get_next_token(scanner);
549   if (token != TOKEN_EXPANDER_STYLE)
550     return TOKEN_EXPANDER_STYLE;
551
552   token = g_scanner_get_next_token(scanner);
553   if (token != G_TOKEN_EQUAL_SIGN)
554     return G_TOKEN_EQUAL_SIGN;
555
556   token = g_scanner_get_next_token(scanner);
557   if (token == TOKEN_COLLAPSED)
558     data->match_data.expander_style = GTK_EXPANDER_COLLAPSED;
559   else if (token == TOKEN_SEMI_COLLAPSED)
560     data->match_data.expander_style = GTK_EXPANDER_SEMI_COLLAPSED;
561   else if (token == TOKEN_SEMI_EXPANDED)
562     data->match_data.expander_style = GTK_EXPANDER_SEMI_EXPANDED;
563   else if (token == TOKEN_EXPANDED)
564     data->match_data.expander_style = GTK_EXPANDER_EXPANDED;
565   else
566     return TOKEN_COLLAPSED;
567
568   data->match_data.flags |= THEME_MATCH_EXPANDER_STYLE;
569
570   return G_TOKEN_NONE;
571 }
572
573 static guint
574 theme_parse_window_edge(GScanner * scanner,
575                         ThemeImage * data)
576 {
577   guint               token;
578
579   token = g_scanner_get_next_token(scanner);
580   if (token != TOKEN_WINDOW_EDGE)
581     return TOKEN_WINDOW_EDGE;
582
583   token = g_scanner_get_next_token(scanner);
584   if (token != G_TOKEN_EQUAL_SIGN)
585     return G_TOKEN_EQUAL_SIGN;
586
587   token = g_scanner_get_next_token(scanner);
588   if (token == TOKEN_NORTH_WEST)
589     data->match_data.window_edge = GDK_WINDOW_EDGE_NORTH_WEST;
590   else if (token == TOKEN_NORTH)
591     data->match_data.window_edge = GDK_WINDOW_EDGE_NORTH;
592   else if (token == TOKEN_NORTH_EAST)
593     data->match_data.window_edge = GDK_WINDOW_EDGE_NORTH_EAST;
594   else if (token == TOKEN_WEST)
595     data->match_data.window_edge = GDK_WINDOW_EDGE_WEST;
596   else if (token == TOKEN_EAST)
597     data->match_data.window_edge = GDK_WINDOW_EDGE_EAST;
598   else if (token == TOKEN_SOUTH_WEST)
599     data->match_data.window_edge = GDK_WINDOW_EDGE_SOUTH_WEST;
600   else if (token == TOKEN_SOUTH)
601     data->match_data.window_edge = GDK_WINDOW_EDGE_SOUTH;
602   else if (token == TOKEN_SOUTH_EAST)
603     data->match_data.window_edge = GDK_WINDOW_EDGE_SOUTH_EAST;
604   else
605     return TOKEN_NORTH_WEST;
606
607   data->match_data.flags |= THEME_MATCH_WINDOW_EDGE;
608
609   return G_TOKEN_NONE;
610 }
611
612 static void
613 theme_image_ref (ThemeImage *data)
614 {
615   data->refcount++;
616 }
617
618 static void
619 theme_image_unref (ThemeImage *data)
620 {
621   data->refcount--;
622   if (data->refcount == 0)
623     {
624       g_free (data->match_data.detail);
625       if (data->background)
626         theme_pixbuf_destroy (data->background);
627       if (data->overlay)
628         theme_pixbuf_destroy (data->overlay);
629       if (data->gap_start)
630         theme_pixbuf_destroy (data->gap_start);
631       if (data->gap)
632         theme_pixbuf_destroy (data->gap);
633       if (data->gap_end)
634         theme_pixbuf_destroy (data->gap_end);
635       g_free (data);
636     }
637 }
638
639 static guint
640 theme_parse_image(GtkSettings  *settings,
641                   GScanner      *scanner,
642                   PixbufRcStyle *pixbuf_style,
643                   ThemeImage   **data_return)
644 {
645   guint               token;
646   ThemeImage *data;
647
648   data = NULL;
649   token = g_scanner_get_next_token(scanner);
650   if (token != TOKEN_IMAGE)
651     return TOKEN_IMAGE;
652
653   token = g_scanner_get_next_token(scanner);
654   if (token != G_TOKEN_LEFT_CURLY)
655     return G_TOKEN_LEFT_CURLY;
656
657   data = g_malloc(sizeof(ThemeImage));
658
659   data->refcount = 1;
660
661   data->background = NULL;
662   data->overlay = NULL;
663   data->gap_start = NULL;
664   data->gap = NULL;
665   data->gap_end = NULL;
666
667   data->recolorable = FALSE;
668
669   data->match_data.function = 0;
670   data->match_data.detail = NULL;
671   data->match_data.flags = 0;
672
673   token = g_scanner_peek_next_token(scanner);
674   while (token != G_TOKEN_RIGHT_CURLY)
675     {
676       switch (token)
677         {
678         case TOKEN_FUNCTION:
679           token = theme_parse_function(scanner, data);
680           break;
681         case TOKEN_RECOLORABLE:
682           token = theme_parse_recolorable(scanner, data);
683           break;
684         case TOKEN_DETAIL:
685           token = theme_parse_detail(scanner, data);
686           break;
687         case TOKEN_STATE:
688           token = theme_parse_state(scanner, data);
689           break;
690         case TOKEN_SHADOW:
691           token = theme_parse_shadow(scanner, data);
692           break;
693         case TOKEN_GAP_SIDE:
694           token = theme_parse_gap_side(scanner, data);
695           break;
696         case TOKEN_ARROW_DIRECTION:
697           token = theme_parse_arrow_direction(scanner, data);
698           break;
699         case TOKEN_ORIENTATION:
700           token = theme_parse_orientation(scanner, data);
701           break;
702         case TOKEN_FILE:
703           token = theme_parse_file(settings, scanner, &data->background);
704           break;
705         case TOKEN_BORDER:
706           token = theme_parse_border(scanner, &data->background);
707           break;
708         case TOKEN_STRETCH:
709           token = theme_parse_stretch(scanner, &data->background);
710           break;
711         case TOKEN_GAP_FILE:
712           token = theme_parse_file(settings, scanner, &data->gap);
713           break;
714         case TOKEN_GAP_BORDER:
715           token = theme_parse_border(scanner, &data->gap);
716           break;
717         case TOKEN_GAP_START_FILE:
718           token = theme_parse_file(settings, scanner, &data->gap_start);
719           break;
720         case TOKEN_GAP_START_BORDER:
721           token = theme_parse_border(scanner, &data->gap_start);
722           break;
723         case TOKEN_GAP_END_FILE:
724           token = theme_parse_file(settings, scanner, &data->gap_end);
725           break;
726         case TOKEN_GAP_END_BORDER:
727           token = theme_parse_border(scanner, &data->gap_end);
728           break;
729         case TOKEN_OVERLAY_FILE:
730           token = theme_parse_file(settings, scanner, &data->overlay);
731           break;
732         case TOKEN_OVERLAY_BORDER:
733           token = theme_parse_border(scanner, &data->overlay);
734           break;
735         case TOKEN_OVERLAY_STRETCH:
736           token = theme_parse_stretch(scanner, &data->overlay);
737           break;
738         case TOKEN_EXPANDER_STYLE:
739           token = theme_parse_expander_style(scanner, data);
740           break;
741         case TOKEN_WINDOW_EDGE:
742           token = theme_parse_window_edge(scanner, data);
743           break;
744         default:
745           g_scanner_get_next_token(scanner);
746           token = G_TOKEN_RIGHT_CURLY;
747           break;
748         }
749       if (token != G_TOKEN_NONE)
750         {
751           /* error - cleanup for exit */
752           theme_image_unref (data);
753           *data_return = NULL;
754           return token;
755         }
756       token = g_scanner_peek_next_token(scanner);
757     }
758
759   token = g_scanner_get_next_token(scanner);
760
761   if (data->background && !data->background->filename)
762     {
763       g_scanner_warn (scanner, "Background image options specified without filename");
764       theme_pixbuf_destroy (data->background);
765       data->background = NULL;
766     }
767
768   if (data->overlay && !data->overlay->filename)
769     {
770       g_scanner_warn (scanner, "Overlay image options specified without filename");
771       theme_pixbuf_destroy (data->overlay);
772       data->overlay = NULL;
773     }
774
775   if (token != G_TOKEN_RIGHT_CURLY)
776     {
777       /* error - cleanup for exit */
778       theme_image_unref (data);
779       *data_return = NULL;
780       return G_TOKEN_RIGHT_CURLY;
781     }
782
783   /* everything is fine now - insert yer cruft */
784   *data_return = data;
785   return G_TOKEN_NONE;
786 }
787
788 static guint
789 pixbuf_rc_style_parse (GtkRcStyle *rc_style,
790                        GtkSettings  *settings,
791                        GScanner   *scanner)
792                      
793 {
794   static GQuark scope_id = 0;
795   PixbufRcStyle *pixbuf_style = PIXBUF_RC_STYLE (rc_style);
796
797   guint old_scope;
798   guint token;
799   gint i;
800   ThemeImage *img;
801   
802   /* Set up a new scope in this scanner. */
803
804   if (!scope_id)
805     scope_id = g_quark_from_string("pixbuf_theme_engine");
806
807   /* If we bail out due to errors, we *don't* reset the scope, so the
808    * error messaging code can make sense of our tokens.
809    */
810   old_scope = g_scanner_set_scope(scanner, scope_id);
811
812   /* Now check if we already added our symbols to this scope
813    * (in some previous call to theme_parse_rc_style for the
814    * same scanner.
815    */
816
817   if (!g_scanner_lookup_symbol(scanner, theme_symbols[0].name))
818     {
819       for (i = 0; i < G_N_ELEMENTS (theme_symbols); i++)
820         g_scanner_scope_add_symbol(scanner, scope_id,
821                                    theme_symbols[i].name,
822                                    GINT_TO_POINTER(theme_symbols[i].token));
823     }
824
825   /* We're ready to go, now parse the top level */
826
827   token = g_scanner_peek_next_token(scanner);
828   while (token != G_TOKEN_RIGHT_CURLY)
829     {
830       switch (token)
831         {
832         case TOKEN_IMAGE:
833           img = NULL;
834           token = theme_parse_image(settings, scanner, pixbuf_style, &img);
835           break;
836         default:
837           g_scanner_get_next_token(scanner);
838           token = G_TOKEN_RIGHT_CURLY;
839           break;
840         }
841
842       if (token != G_TOKEN_NONE)
843         return token;
844       else
845         pixbuf_style->img_list = g_list_append(pixbuf_style->img_list, img);
846
847       token = g_scanner_peek_next_token(scanner);
848     }
849
850   g_scanner_get_next_token(scanner);
851
852   g_scanner_set_scope(scanner, old_scope);
853
854   return G_TOKEN_NONE;
855 }
856
857 static void
858 pixbuf_rc_style_merge (GtkRcStyle *dest,
859                        GtkRcStyle *src)
860 {
861   if (PIXBUF_IS_RC_STYLE (src))
862     {
863       PixbufRcStyle *pixbuf_dest = PIXBUF_RC_STYLE (dest);
864       PixbufRcStyle *pixbuf_src = PIXBUF_RC_STYLE (src);
865       GList *tmp_list1, *tmp_list2;
866       
867       if (pixbuf_src->img_list)
868         {
869           /* Copy src image list and append to dest image list */
870           
871           tmp_list2 = g_list_last (pixbuf_dest->img_list);
872           tmp_list1 = pixbuf_src->img_list;
873           
874           while (tmp_list1)
875             {
876               if (tmp_list2)
877                 {
878                   tmp_list2->next = g_list_alloc();
879                   tmp_list2->next->data = tmp_list1->data;
880                   tmp_list2->next->prev = tmp_list2;
881                   
882                   tmp_list2 = tmp_list2->next;
883                 }
884               else
885                 {
886                   pixbuf_dest->img_list = g_list_append (NULL, tmp_list1->data);
887                   tmp_list2 = pixbuf_dest->img_list;
888                 }
889               
890               theme_image_ref (tmp_list1->data);
891               tmp_list1 = tmp_list1->next;
892             }
893         }
894     }
895
896   parent_class->merge (dest, src);
897 }
898
899 /* Create an empty style suitable to this RC style
900  */
901 static GtkStyle *
902 pixbuf_rc_style_create_style (GtkRcStyle *rc_style)
903 {
904   return g_object_new (PIXBUF_TYPE_STYLE, NULL);
905 }
906