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