2 * Copyright (C) 1998-2000 Red Hat, Inc.
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.
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.
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.
19 * Written by Owen Taylor <otaylor@redhat.com>, based on code by
20 * Carsten Haitzler <raster@rasterman.com>
24 #include "pixbuf-style.h"
25 #include "pixbuf-rc-style.h"
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,
32 static void pixbuf_rc_style_merge (GtkRcStyle *dest,
34 static GtkStyle *pixbuf_rc_style_create_style (GtkRcStyle *rc_style);
36 static void theme_image_unref (ThemeImage *data);
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 },
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 },
90 { "TRUE", TOKEN_TRUE },
91 { "FALSE", TOKEN_FALSE },
95 { "BOTTOM", TOKEN_BOTTOM },
96 { "DOWN", TOKEN_DOWN },
97 { "LEFT", TOKEN_LEFT },
98 { "RIGHT", TOKEN_RIGHT },
100 { "NORMAL", TOKEN_NORMAL },
101 { "ACTIVE", TOKEN_ACTIVE },
102 { "PRELIGHT", TOKEN_PRELIGHT },
103 { "SELECTED", TOKEN_SELECTED },
104 { "INSENSITIVE", TOKEN_INSENSITIVE },
106 { "NONE", TOKEN_NONE },
108 { "OUT", TOKEN_OUT },
109 { "ETCHED_IN", TOKEN_ETCHED_IN },
110 { "ETCHED_OUT", TOKEN_ETCHED_OUT },
111 { "HORIZONTAL", TOKEN_HORIZONTAL },
112 { "VERTICAL", TOKEN_VERTICAL },
115 static GtkRcStyleClass *parent_class;
117 GType pixbuf_type_rc_style = 0;
120 pixbuf_rc_style_register_type (GtkThemeEngine *engine)
122 static const GTypeInfo object_info =
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),
132 (GInstanceInitFunc) pixbuf_rc_style_init,
135 pixbuf_type_rc_style = gtk_theme_engine_register_type (engine,
142 pixbuf_rc_style_init (PixbufRcStyle *style)
147 pixbuf_rc_style_class_init (PixbufRcStyleClass *klass)
149 GtkRcStyleClass *rc_style_class = GTK_RC_STYLE_CLASS (klass);
150 GObjectClass *object_class = G_OBJECT_CLASS (klass);
152 parent_class = g_type_class_peek_parent (klass);
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;
158 object_class->finalize = pixbuf_rc_style_finalize;
162 pixbuf_rc_style_finalize (GObject *object)
164 PixbufRcStyle *rc_style = PIXBUF_RC_STYLE (object);
166 g_list_foreach (rc_style->img_list, (GFunc) theme_image_unref, NULL);
167 g_list_free (rc_style->img_list);
169 G_OBJECT_CLASS (parent_class)->finalize (object);
173 theme_parse_file(GScanner *scanner,
174 ThemePixbuf **theme_pb)
179 /* Skip 'blah_file' */
180 token = g_scanner_get_next_token(scanner);
182 token = g_scanner_get_next_token(scanner);
183 if (token != G_TOKEN_EQUAL_SIGN)
184 return G_TOKEN_EQUAL_SIGN;
186 token = g_scanner_get_next_token(scanner);
187 if (token != G_TOKEN_STRING)
188 return G_TOKEN_STRING;
191 *theme_pb = theme_pixbuf_new ();
193 pixmap = gtk_rc_find_pixmap_in_path(scanner, scanner->value.v_string);
196 theme_pixbuf_set_filename (*theme_pb, pixmap);
204 theme_parse_border (GScanner *scanner,
205 ThemePixbuf **theme_pb)
208 gint left, right, top, bottom;
210 /* Skip 'blah_border' */
211 token = g_scanner_get_next_token(scanner);
213 token = g_scanner_get_next_token(scanner);
214 if (token != G_TOKEN_EQUAL_SIGN)
215 return G_TOKEN_EQUAL_SIGN;
217 token = g_scanner_get_next_token(scanner);
218 if (token != G_TOKEN_LEFT_CURLY)
219 return G_TOKEN_LEFT_CURLY;
221 token = g_scanner_get_next_token(scanner);
222 if (token != 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;
229 token = g_scanner_get_next_token(scanner);
230 if (token != 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;
237 token = g_scanner_get_next_token(scanner);
238 if (token != 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;
245 token = g_scanner_get_next_token(scanner);
246 if (token != G_TOKEN_INT)
248 bottom = scanner->value.v_int;
250 token = g_scanner_get_next_token(scanner);
251 if (token != G_TOKEN_RIGHT_CURLY)
252 return G_TOKEN_RIGHT_CURLY;
255 *theme_pb = theme_pixbuf_new ();
257 theme_pixbuf_set_border (*theme_pb, left, right, top, bottom);
263 theme_parse_stretch(GScanner *scanner,
264 ThemePixbuf **theme_pb)
269 /* Skip 'blah_stretch' */
270 token = g_scanner_get_next_token(scanner);
272 token = g_scanner_get_next_token(scanner);
273 if (token != G_TOKEN_EQUAL_SIGN)
274 return G_TOKEN_EQUAL_SIGN;
276 token = g_scanner_get_next_token(scanner);
277 if (token == TOKEN_TRUE)
279 else if (token == TOKEN_FALSE)
285 *theme_pb = theme_pixbuf_new ();
287 theme_pixbuf_set_stretch (*theme_pb, stretch);
293 theme_parse_recolorable(GScanner * scanner,
298 token = g_scanner_get_next_token(scanner);
299 if (token != TOKEN_RECOLORABLE)
300 return TOKEN_RECOLORABLE;
302 token = g_scanner_get_next_token(scanner);
303 if (token != G_TOKEN_EQUAL_SIGN)
304 return G_TOKEN_EQUAL_SIGN;
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;
318 theme_parse_function(GScanner * scanner,
323 token = g_scanner_get_next_token(scanner);
324 if (token != TOKEN_FUNCTION)
325 return TOKEN_FUNCTION;
327 token = g_scanner_get_next_token(scanner);
328 if (token != G_TOKEN_EQUAL_SIGN)
329 return G_TOKEN_EQUAL_SIGN;
331 token = g_scanner_get_next_token(scanner);
332 if ((token >= TOKEN_D_HLINE) && (token <= TOKEN_D_HANDLE))
333 data->match_data.function = token;
339 theme_parse_detail(GScanner * scanner,
344 token = g_scanner_get_next_token(scanner);
345 if (token != TOKEN_DETAIL)
348 token = g_scanner_get_next_token(scanner);
349 if (token != G_TOKEN_EQUAL_SIGN)
350 return G_TOKEN_EQUAL_SIGN;
352 token = g_scanner_get_next_token(scanner);
353 if (token != G_TOKEN_STRING)
354 return G_TOKEN_STRING;
356 if (data->match_data.detail)
357 g_free (data->match_data.detail);
359 data->match_data.detail = g_strdup(scanner->value.v_string);
365 theme_parse_state(GScanner * scanner,
370 token = g_scanner_get_next_token(scanner);
371 if (token != TOKEN_STATE)
374 token = g_scanner_get_next_token(scanner);
375 if (token != G_TOKEN_EQUAL_SIGN)
376 return G_TOKEN_EQUAL_SIGN;
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;
392 data->match_data.flags |= THEME_MATCH_STATE;
398 theme_parse_shadow(GScanner * scanner,
403 token = g_scanner_get_next_token(scanner);
404 if (token != TOKEN_SHADOW)
407 token = g_scanner_get_next_token(scanner);
408 if (token != G_TOKEN_EQUAL_SIGN)
409 return G_TOKEN_EQUAL_SIGN;
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;
425 data->match_data.flags |= THEME_MATCH_SHADOW;
431 theme_parse_arrow_direction(GScanner * scanner,
436 token = g_scanner_get_next_token(scanner);
437 if (token != TOKEN_ARROW_DIRECTION)
438 return TOKEN_ARROW_DIRECTION;
440 token = g_scanner_get_next_token(scanner);
441 if (token != G_TOKEN_EQUAL_SIGN)
442 return G_TOKEN_EQUAL_SIGN;
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;
456 data->match_data.flags |= THEME_MATCH_ARROW_DIRECTION;
462 theme_parse_gap_side(GScanner * scanner,
467 token = g_scanner_get_next_token(scanner);
468 if (token != TOKEN_GAP_SIDE)
469 return TOKEN_GAP_SIDE;
471 token = g_scanner_get_next_token(scanner);
472 if (token != G_TOKEN_EQUAL_SIGN)
473 return G_TOKEN_EQUAL_SIGN;
475 token = g_scanner_get_next_token(scanner);
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;
488 data->match_data.flags |= THEME_MATCH_GAP_SIDE;
494 theme_parse_orientation(GScanner * scanner,
499 token = g_scanner_get_next_token(scanner);
500 if (token != TOKEN_ORIENTATION)
501 return TOKEN_ORIENTATION;
503 token = g_scanner_get_next_token(scanner);
504 if (token != G_TOKEN_EQUAL_SIGN)
505 return G_TOKEN_EQUAL_SIGN;
507 token = g_scanner_get_next_token(scanner);
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;
514 return TOKEN_HORIZONTAL;
516 data->match_data.flags |= THEME_MATCH_ORIENTATION;
522 theme_image_ref (ThemeImage *data)
528 theme_image_unref (ThemeImage *data)
531 if (data->refcount == 0)
533 if (data->match_data.detail)
534 g_free (data->match_data.detail);
535 if (data->background)
536 theme_pixbuf_destroy (data->background);
538 theme_pixbuf_destroy (data->overlay);
540 theme_pixbuf_destroy (data->gap_start);
542 theme_pixbuf_destroy (data->gap);
544 theme_pixbuf_destroy (data->gap_end);
550 theme_parse_image(GScanner *scanner,
551 PixbufRcStyle *pixbuf_style,
552 ThemeImage **data_return)
558 token = g_scanner_get_next_token(scanner);
559 if (token != TOKEN_IMAGE)
562 token = g_scanner_get_next_token(scanner);
563 if (token != G_TOKEN_LEFT_CURLY)
564 return G_TOKEN_LEFT_CURLY;
566 data = g_malloc(sizeof(ThemeImage));
570 data->background = NULL;
571 data->overlay = NULL;
572 data->gap_start = NULL;
574 data->gap_end = NULL;
576 data->recolorable = FALSE;
578 data->match_data.function = 0;
579 data->match_data.detail = NULL;
580 data->match_data.flags = 0;
582 token = g_scanner_peek_next_token(scanner);
583 while (token != G_TOKEN_RIGHT_CURLY)
588 token = theme_parse_function(scanner, data);
590 case TOKEN_RECOLORABLE:
591 token = theme_parse_recolorable(scanner, data);
594 token = theme_parse_detail(scanner, data);
597 token = theme_parse_state(scanner, data);
600 token = theme_parse_shadow(scanner, data);
603 token = theme_parse_gap_side(scanner, data);
605 case TOKEN_ARROW_DIRECTION:
606 token = theme_parse_arrow_direction(scanner, data);
608 case TOKEN_ORIENTATION:
609 token = theme_parse_orientation(scanner, data);
612 token = theme_parse_file(scanner, &data->background);
615 token = theme_parse_border(scanner, &data->background);
618 token = theme_parse_stretch(scanner, &data->background);
621 token = theme_parse_file(scanner, &data->gap);
623 case TOKEN_GAP_BORDER:
624 token = theme_parse_border(scanner, &data->gap);
626 case TOKEN_GAP_START_FILE:
627 token = theme_parse_file(scanner, &data->gap_start);
629 case TOKEN_GAP_START_BORDER:
630 token = theme_parse_border(scanner, &data->gap_start);
632 case TOKEN_GAP_END_FILE:
633 token = theme_parse_file(scanner, &data->gap_end);
635 case TOKEN_GAP_END_BORDER:
636 token = theme_parse_border(scanner, &data->gap_end);
638 case TOKEN_OVERLAY_FILE:
639 token = theme_parse_file(scanner, &data->overlay);
641 case TOKEN_OVERLAY_BORDER:
642 token = theme_parse_border(scanner, &data->overlay);
644 case TOKEN_OVERLAY_STRETCH:
645 token = theme_parse_stretch(scanner, &data->overlay);
648 g_scanner_get_next_token(scanner);
649 token = G_TOKEN_RIGHT_CURLY;
652 if (token != G_TOKEN_NONE)
654 /* error - cleanup for exit */
655 theme_image_unref (data);
659 token = g_scanner_peek_next_token(scanner);
662 token = g_scanner_get_next_token(scanner);
664 if (token != G_TOKEN_RIGHT_CURLY)
666 /* error - cleanup for exit */
667 theme_image_unref (data);
669 return G_TOKEN_RIGHT_CURLY;
672 /* everything is fine now - insert yer cruft */
678 pixbuf_rc_style_parse (GtkRcStyle *rc_style,
682 static GQuark scope_id = 0;
683 PixbufRcStyle *pixbuf_style = PIXBUF_RC_STYLE (rc_style);
690 /* Set up a new scope in this scanner. */
693 scope_id = g_quark_from_string("pixbuf_theme_engine");
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.
698 old_scope = g_scanner_set_scope(scanner, scope_id);
700 /* Now check if we already added our symbols to this scope
701 * (in some previous call to theme_parse_rc_style for the
705 if (!g_scanner_lookup_symbol(scanner, theme_symbols[0].name))
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);
715 /* We're ready to go, now parse the top level */
717 token = g_scanner_peek_next_token(scanner);
718 while (token != G_TOKEN_RIGHT_CURLY)
724 token = theme_parse_image(scanner, pixbuf_style, &img);
727 g_scanner_get_next_token(scanner);
728 token = G_TOKEN_RIGHT_CURLY;
732 if (token != G_TOKEN_NONE)
735 pixbuf_style->img_list = g_list_append(pixbuf_style->img_list, img);
737 token = g_scanner_peek_next_token(scanner);
740 g_scanner_get_next_token(scanner);
742 g_scanner_set_scope(scanner, old_scope);
748 pixbuf_rc_style_merge (GtkRcStyle *dest,
751 if (PIXBUF_IS_RC_STYLE (src))
753 PixbufRcStyle *pixbuf_dest = PIXBUF_RC_STYLE (dest);
754 PixbufRcStyle *pixbuf_src = PIXBUF_RC_STYLE (src);
755 GList *tmp_list1, *tmp_list2;
757 if (pixbuf_src->img_list)
759 /* Copy src image list and append to dest image list */
761 tmp_list2 = g_list_last (pixbuf_dest->img_list);
762 tmp_list1 = pixbuf_src->img_list;
768 tmp_list2->next = g_list_alloc();
769 tmp_list2->next->data = tmp_list1->data;
770 tmp_list2->next->prev = tmp_list2;
772 tmp_list2 = tmp_list2->next;
776 pixbuf_dest->img_list = g_list_append (NULL, tmp_list1->data);
777 tmp_list2 = pixbuf_dest->img_list;
780 theme_image_ref (tmp_list1->data);
781 tmp_list1 = tmp_list1->next;
786 parent_class->merge (dest, src);
789 /* Create an empty style suitable to this RC style
792 pixbuf_rc_style_create_style (GtkRcStyle *rc_style)
794 return GTK_STYLE (g_object_new (PIXBUF_TYPE_STYLE, NULL));