2 * Copyright (C) 1998-2000 Red Hat Software
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>
23 #include "pixmap_theme.h"
24 #include "pixmap_theme.h"
27 /* Theme functions to export */
28 void theme_init(GtkThemeEngine * engine);
29 void theme_exit(void);
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 },
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 },
83 { "TRUE", TOKEN_TRUE },
84 { "FALSE", TOKEN_FALSE },
88 { "BOTTOM", TOKEN_BOTTOM },
89 { "DOWN", TOKEN_DOWN },
90 { "LEFT", TOKEN_LEFT },
91 { "RIGHT", TOKEN_RIGHT },
93 { "NORMAL", TOKEN_NORMAL },
94 { "ACTIVE", TOKEN_ACTIVE },
95 { "PRELIGHT", TOKEN_PRELIGHT },
96 { "SELECTED", TOKEN_SELECTED },
97 { "INSENSITIVE", TOKEN_INSENSITIVE },
99 { "NONE", TOKEN_NONE },
101 { "OUT", TOKEN_OUT },
102 { "ETCHED_IN", TOKEN_ETCHED_IN },
103 { "ETCHED_OUT", TOKEN_ETCHED_OUT },
104 { "HORIZONTAL", TOKEN_HORIZONTAL },
105 { "VERTICAL", TOKEN_VERTICAL },
108 static guint n_theme_symbols = sizeof(theme_symbols) / sizeof(theme_symbols[0]);
111 theme_parse_file(GScanner *scanner,
112 ThemePixbuf **theme_pb)
117 /* Skip 'blah_file' */
118 token = g_scanner_get_next_token(scanner);
120 token = g_scanner_get_next_token(scanner);
121 if (token != G_TOKEN_EQUAL_SIGN)
122 return G_TOKEN_EQUAL_SIGN;
124 token = g_scanner_get_next_token(scanner);
125 if (token != G_TOKEN_STRING)
126 return G_TOKEN_STRING;
129 *theme_pb = theme_pixbuf_new ();
131 pixmap = gtk_rc_find_pixmap_in_path(scanner, scanner->value.v_string);
134 theme_pixbuf_set_filename (*theme_pb, pixmap);
142 theme_parse_border (GScanner *scanner,
143 ThemePixbuf **theme_pb)
146 gint left, right, top, bottom;
148 /* Skip 'blah_border' */
149 token = g_scanner_get_next_token(scanner);
151 token = g_scanner_get_next_token(scanner);
152 if (token != G_TOKEN_EQUAL_SIGN)
153 return G_TOKEN_EQUAL_SIGN;
155 token = g_scanner_get_next_token(scanner);
156 if (token != G_TOKEN_LEFT_CURLY)
157 return G_TOKEN_LEFT_CURLY;
159 token = g_scanner_get_next_token(scanner);
160 if (token != 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;
167 token = g_scanner_get_next_token(scanner);
168 if (token != 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;
175 token = g_scanner_get_next_token(scanner);
176 if (token != 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;
183 token = g_scanner_get_next_token(scanner);
184 if (token != G_TOKEN_INT)
186 bottom = scanner->value.v_int;
188 token = g_scanner_get_next_token(scanner);
189 if (token != G_TOKEN_RIGHT_CURLY)
190 return G_TOKEN_RIGHT_CURLY;
193 *theme_pb = theme_pixbuf_new ();
195 theme_pixbuf_set_border (*theme_pb, left, right, top, bottom);
201 theme_parse_stretch(GScanner *scanner,
202 ThemePixbuf **theme_pb)
207 /* Skip 'blah_stretch' */
208 token = g_scanner_get_next_token(scanner);
210 token = g_scanner_get_next_token(scanner);
211 if (token != G_TOKEN_EQUAL_SIGN)
212 return G_TOKEN_EQUAL_SIGN;
214 token = g_scanner_get_next_token(scanner);
215 if (token == TOKEN_TRUE)
217 else if (token == TOKEN_FALSE)
223 *theme_pb = theme_pixbuf_new ();
225 theme_pixbuf_set_stretch (*theme_pb, stretch);
231 theme_parse_recolorable(GScanner * scanner,
236 token = g_scanner_get_next_token(scanner);
237 if (token != TOKEN_RECOLORABLE)
238 return TOKEN_RECOLORABLE;
240 token = g_scanner_get_next_token(scanner);
241 if (token != G_TOKEN_EQUAL_SIGN)
242 return G_TOKEN_EQUAL_SIGN;
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;
256 theme_parse_function(GScanner * scanner,
261 token = g_scanner_get_next_token(scanner);
262 if (token != TOKEN_FUNCTION)
263 return TOKEN_FUNCTION;
265 token = g_scanner_get_next_token(scanner);
266 if (token != G_TOKEN_EQUAL_SIGN)
267 return G_TOKEN_EQUAL_SIGN;
269 token = g_scanner_get_next_token(scanner);
270 if ((token >= TOKEN_D_HLINE) && (token <= TOKEN_D_HANDLE))
271 data->match_data.function = token;
277 theme_parse_detail(GScanner * scanner,
282 token = g_scanner_get_next_token(scanner);
283 if (token != TOKEN_DETAIL)
286 token = g_scanner_get_next_token(scanner);
287 if (token != G_TOKEN_EQUAL_SIGN)
288 return G_TOKEN_EQUAL_SIGN;
290 token = g_scanner_get_next_token(scanner);
291 if (token != G_TOKEN_STRING)
292 return G_TOKEN_STRING;
294 if (data->match_data.detail)
295 g_free (data->match_data.detail);
297 data->match_data.detail = g_strdup(scanner->value.v_string);
303 theme_parse_state(GScanner * scanner,
308 token = g_scanner_get_next_token(scanner);
309 if (token != TOKEN_STATE)
312 token = g_scanner_get_next_token(scanner);
313 if (token != G_TOKEN_EQUAL_SIGN)
314 return G_TOKEN_EQUAL_SIGN;
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;
330 data->match_data.flags |= THEME_MATCH_STATE;
336 theme_parse_shadow(GScanner * scanner,
341 token = g_scanner_get_next_token(scanner);
342 if (token != TOKEN_SHADOW)
345 token = g_scanner_get_next_token(scanner);
346 if (token != G_TOKEN_EQUAL_SIGN)
347 return G_TOKEN_EQUAL_SIGN;
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;
363 data->match_data.flags |= THEME_MATCH_SHADOW;
369 theme_parse_arrow_direction(GScanner * scanner,
374 token = g_scanner_get_next_token(scanner);
375 if (token != TOKEN_ARROW_DIRECTION)
376 return TOKEN_ARROW_DIRECTION;
378 token = g_scanner_get_next_token(scanner);
379 if (token != G_TOKEN_EQUAL_SIGN)
380 return G_TOKEN_EQUAL_SIGN;
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;
394 data->match_data.flags |= THEME_MATCH_ARROW_DIRECTION;
400 theme_parse_gap_side(GScanner * scanner,
405 token = g_scanner_get_next_token(scanner);
406 if (token != TOKEN_GAP_SIDE)
407 return TOKEN_GAP_SIDE;
409 token = g_scanner_get_next_token(scanner);
410 if (token != G_TOKEN_EQUAL_SIGN)
411 return G_TOKEN_EQUAL_SIGN;
413 token = g_scanner_get_next_token(scanner);
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;
426 data->match_data.flags |= THEME_MATCH_GAP_SIDE;
432 theme_parse_orientation(GScanner * scanner,
437 token = g_scanner_get_next_token(scanner);
438 if (token != TOKEN_ORIENTATION)
439 return TOKEN_ORIENTATION;
441 token = g_scanner_get_next_token(scanner);
442 if (token != G_TOKEN_EQUAL_SIGN)
443 return G_TOKEN_EQUAL_SIGN;
445 token = g_scanner_get_next_token(scanner);
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;
452 return TOKEN_HORIZONTAL;
454 data->match_data.flags |= THEME_MATCH_ORIENTATION;
460 theme_image_ref (ThemeImage *data)
466 theme_image_unref (ThemeImage *data)
469 if (data->refcount == 0)
471 if (data->match_data.detail)
472 g_free (data->match_data.detail);
473 if (data->background)
474 theme_pixbuf_destroy (data->background);
476 theme_pixbuf_destroy (data->overlay);
478 theme_pixbuf_destroy (data->gap_start);
480 theme_pixbuf_destroy (data->gap);
482 theme_pixbuf_destroy (data->gap_end);
488 theme_data_ref (ThemeData *theme_data)
490 theme_data->refcount++;
494 theme_data_unref (ThemeData *theme_data)
496 theme_data->refcount--;
497 if (theme_data->refcount == 0)
499 g_list_foreach (theme_data->img_list, (GFunc) theme_image_unref, NULL);
500 g_list_free (theme_data->img_list);
506 theme_parse_image(GScanner *scanner,
507 ThemeData *theme_data,
508 ThemeImage **data_return)
514 token = g_scanner_get_next_token(scanner);
515 if (token != TOKEN_IMAGE)
518 token = g_scanner_get_next_token(scanner);
519 if (token != G_TOKEN_LEFT_CURLY)
520 return G_TOKEN_LEFT_CURLY;
522 data = g_malloc(sizeof(ThemeImage));
526 data->background = NULL;
527 data->overlay = NULL;
528 data->gap_start = NULL;
530 data->gap_end = NULL;
532 data->recolorable = FALSE;
534 data->match_data.function = 0;
535 data->match_data.detail = NULL;
536 data->match_data.flags = 0;
538 token = g_scanner_peek_next_token(scanner);
539 while (token != G_TOKEN_RIGHT_CURLY)
544 token = theme_parse_function(scanner, data);
546 case TOKEN_RECOLORABLE:
547 token = theme_parse_recolorable(scanner, data);
550 token = theme_parse_detail(scanner, data);
553 token = theme_parse_state(scanner, data);
556 token = theme_parse_shadow(scanner, data);
559 token = theme_parse_gap_side(scanner, data);
561 case TOKEN_ARROW_DIRECTION:
562 token = theme_parse_arrow_direction(scanner, data);
564 case TOKEN_ORIENTATION:
565 token = theme_parse_orientation(scanner, data);
568 token = theme_parse_file(scanner, &data->background);
571 token = theme_parse_border(scanner, &data->background);
574 token = theme_parse_stretch(scanner, &data->background);
577 token = theme_parse_file(scanner, &data->gap);
579 case TOKEN_GAP_BORDER:
580 token = theme_parse_border(scanner, &data->gap);
582 case TOKEN_GAP_START_FILE:
583 token = theme_parse_file(scanner, &data->gap_start);
585 case TOKEN_GAP_START_BORDER:
586 token = theme_parse_border(scanner, &data->gap_start);
588 case TOKEN_GAP_END_FILE:
589 token = theme_parse_file(scanner, &data->gap_end);
591 case TOKEN_GAP_END_BORDER:
592 token = theme_parse_border(scanner, &data->gap_end);
594 case TOKEN_OVERLAY_FILE:
595 token = theme_parse_file(scanner, &data->overlay);
597 case TOKEN_OVERLAY_BORDER:
598 token = theme_parse_border(scanner, &data->overlay);
600 case TOKEN_OVERLAY_STRETCH:
601 token = theme_parse_stretch(scanner, &data->overlay);
604 g_scanner_get_next_token(scanner);
605 token = G_TOKEN_RIGHT_CURLY;
608 if (token != G_TOKEN_NONE)
610 /* error - cleanup for exit */
611 theme_image_unref (data);
615 token = g_scanner_peek_next_token(scanner);
618 token = g_scanner_get_next_token(scanner);
620 if (token != G_TOKEN_RIGHT_CURLY)
622 /* error - cleanup for exit */
623 theme_image_unref (data);
625 return G_TOKEN_RIGHT_CURLY;
628 /* everything is fine now - insert yer cruft */
634 theme_parse_rc_style(GScanner * scanner,
635 GtkRcStyle * rc_style)
637 static GQuark scope_id = 0;
638 ThemeData *theme_data;
644 /* Set up a new scope in this scanner. */
647 scope_id = g_quark_from_string("theme_engine");
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.
652 old_scope = g_scanner_set_scope(scanner, scope_id);
654 /* Now check if we already added our symbols to this scope
655 * (in some previous call to theme_parse_rc_style for the
659 if (!g_scanner_lookup_symbol(scanner, theme_symbols[0].name))
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);
669 /* We're ready to go, now parse the top level */
671 theme_data = g_new(ThemeData, 1);
672 theme_data->img_list = NULL;
673 theme_data->refcount = 1;
675 token = g_scanner_peek_next_token(scanner);
676 while (token != G_TOKEN_RIGHT_CURLY)
682 token = theme_parse_image(scanner, theme_data, &img);
685 g_scanner_get_next_token(scanner);
686 token = G_TOKEN_RIGHT_CURLY;
690 if (token != G_TOKEN_NONE)
692 g_list_foreach (theme_data->img_list, (GFunc)theme_image_unref, NULL);
693 g_list_free (theme_data->img_list);
699 theme_data->img_list = g_list_append(theme_data->img_list, img);
701 token = g_scanner_peek_next_token(scanner);
704 g_scanner_get_next_token(scanner);
706 rc_style->engine_data = theme_data;
707 g_scanner_set_scope(scanner, old_scope);
713 theme_merge_rc_style(GtkRcStyle * dest,
716 ThemeData *src_data = src->engine_data;
717 ThemeData *dest_data = dest->engine_data;
718 GList *tmp_list1, *tmp_list2;
722 dest_data = g_new(ThemeData, 1);
723 dest_data->img_list = NULL;
724 dest_data->refcount = 1;
725 dest->engine_data = dest_data;
728 if (src_data->img_list)
730 /* Copy src image list and append to dest image list */
732 tmp_list2 = g_list_last (dest_data->img_list);
733 tmp_list1 = src_data->img_list;
739 tmp_list2->next = g_list_alloc();
740 tmp_list2->next->data = tmp_list1->data;
741 tmp_list2->next->prev = tmp_list2;
743 tmp_list2 = tmp_list2->next;
747 dest_data->img_list = g_list_append (NULL, tmp_list1->data);
748 tmp_list2 = dest_data->img_list;
751 theme_data_ref (tmp_list1->data);
752 tmp_list1 = tmp_list1->next;
758 theme_rc_style_to_style(GtkStyle * style,
759 GtkRcStyle * rc_style)
761 ThemeData *data = rc_style->engine_data;
763 style->klass = &pixmap_default_class;
764 style->engine_data = data;
765 theme_data_ref (data);
769 theme_duplicate_style(GtkStyle * dest,
772 ThemeData *src_data = src->engine_data;
773 ThemeData *dest_data;
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);
779 dest->klass = &pixmap_default_class;
780 dest->engine_data = dest_data;
781 theme_data_ref (dest_data);
785 theme_realize_style(GtkStyle * style)
790 theme_unrealize_style(GtkStyle * style)
795 theme_destroy_rc_style(GtkRcStyle * rc_style)
797 theme_data_unref (rc_style->engine_data);
801 theme_destroy_style(GtkStyle * style)
803 theme_data_unref (style->engine_data);
807 theme_set_background(GtkStyle * style,
809 GtkStateType state_type)
812 gint parent_relative;
814 g_return_if_fail(style != NULL);
815 g_return_if_fail(window != NULL);
817 if (style->bg_pixmap[state_type])
819 if (style->bg_pixmap[state_type] == (GdkPixmap *) GDK_PARENT_RELATIVE)
822 parent_relative = TRUE;
826 pixmap = style->bg_pixmap[state_type];
827 parent_relative = FALSE;
830 gdk_window_set_back_pixmap(window, pixmap, parent_relative);
833 gdk_window_set_background(window, &style->bg[state_type]);
837 theme_init(GtkThemeEngine * engine)
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;
852 * We enable the caches unconditionally (the -1 is used
853 * to inform gnome-libs to ignore its setting for the
857 gtk_widget_push_visual(gdk_imlib_get_visual());
858 gtk_widget_push_colormap(gdk_imlib_get_colormap());
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.
871 G_MODULE_EXPORT const gchar* g_module_check_init (GModule *module);
873 g_module_check_init (GModule *module)
875 return gtk_check_version (GTK_MAJOR_VERSION,
877 GTK_MICRO_VERSION - GTK_INTERFACE_AGE);