diff options
Diffstat (limited to 'eazel-engine/src/eazel-theme-main.c')
-rw-r--r-- | eazel-engine/src/eazel-theme-main.c | 1253 |
1 files changed, 1253 insertions, 0 deletions
diff --git a/eazel-engine/src/eazel-theme-main.c b/eazel-engine/src/eazel-theme-main.c new file mode 100644 index 0000000..9deebe0 --- /dev/null +++ b/eazel-engine/src/eazel-theme-main.c @@ -0,0 +1,1253 @@ +/* eazel-theme-main.c -- entry point + + Copyright (C) 1998 Randy Gordon, Integrand Systems + Copyright (C) 2000 Eazel, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id: eazel-theme-main.c,v 1.15 2001/03/23 22:53:04 jsh Exp $ + + Authors: The Rasterman <raster@redhat.com> + Owen Taylor <otaylor@redhat.com> + Randy Gordon <randy@integrand.com> + John Harper <jsh@eazel.com> */ + +/* adapted from gtk-engines/notif/notif_theme_main.c + and gtk-engines/metal/metal_theme_main.c */ + +#include "eazel-theme.h" +#include <gdk/gdkrgb.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <memory.h> +#include <gmodule.h> + +#ifndef G_TOKEN_COLON +# define G_TOKEN_COLON ((guint) ':') +#endif +#ifndef G_TOKEN_LESS_THAN +# define G_TOKEN_LESS_THAN ((guint) '<') +#endif +#ifndef G_TOKEN_GREATER_THAN +# define G_TOKEN_GREATER_THAN ((guint) '>') +#endif + +#define WINDOW_FOCUS_DATA_KEY "eazel-engine-focus-data" +static GQuark window_focus_data_key; + +/* saved values */ +static int range_slider_width, range_min_slider_size, range_stepper_size, + range_stepper_slider_spacing, scale_slider_length, + check_size, check_spacing; + +static void install_focus_hooks (GdkWindow *window); +static void uninstall_all_focus_hooks (void); + + +/* utilities */ + +static char * +read_line_from_file (char *filename) +{ + gboolean free_filename = FALSE; + FILE *fh; + char *ret = 0; + + if (!g_path_is_absolute (filename)) + { + char *home = g_get_home_dir (); + char buf[PATH_MAX]; + g_snprintf (buf, PATH_MAX, "%s/%s", home, filename); + filename = g_strdup (buf); + free_filename = TRUE; + } + + fh = fopen (filename, "r"); + if (fh != 0) + { + char buf[256]; + if (fgets (buf, sizeof (buf), fh) != 0) + { + int len = strlen (buf); + if (len > 0) + buf[len-1] = 0; + ret = g_strdup (buf); + } + fclose (fh); + } + + if (free_filename) + g_free (filename); + + return ret; +} + + +/* internals */ + +struct symbol_struct { + gchar *name; + guint token; +}; + +static struct symbol_struct theme_symbols[] = { THEME_SYMBOLS }; + +static guint n_theme_symbols = (sizeof(theme_symbols) / sizeof(theme_symbols[0])) - 1; + +static struct symbol_struct stock_symbols[] = { STOCK_SYMBOLS }; + +static guint n_stock_symbols = (sizeof(stock_symbols) / sizeof(stock_symbols[0])) - 1; + +static eazel_theme_data *default_theme_data; +static eazel_engine_stock_table *default_stock_data; + +static eazel_theme_data original_theme_data = DEFAULT_THEME_DATA; + +static int +stock_index (const char *symbol) +{ + int i; + for (i = 0; i < n_stock_symbols; i++) + { + if (strcmp (symbol, stock_symbols[i].name) == 0) + return i; + } + g_error ("Unknown stock symbol: `%s'\n", symbol); + exit (1); +} + +static void +theme_data_ref (eazel_theme_data *theme_data) +{ + theme_data->refcount++; +} + +static void +theme_data_unref (eazel_theme_data *theme_data) +{ + theme_data->refcount--; + if (theme_data->refcount == 0) + { + int i; + for (i = 0; i < 5; i++) + { + if (theme_data->gradients[i] != NULL) + eazel_engine_gradient_unref (theme_data->gradients[i]); + } + eazel_engine_stock_table_unref (theme_data->stock); + g_free (theme_data); + } +} + +static guint +parse_int_assign (GScanner *scanner, guint *value) +{ + guint token; + + (void) g_scanner_get_next_token (scanner); + + token = g_scanner_get_next_token (scanner); + if (token != G_TOKEN_EQUAL_SIGN) + return G_TOKEN_EQUAL_SIGN; + + token = g_scanner_get_next_token (scanner); + if (token != G_TOKEN_INT) + return G_TOKEN_INT; + + *value = scanner->value.v_int; + return G_TOKEN_NONE; +} + +static guint +parse_int_array_assign (GScanner *scanner, guint *value, int size) +{ + guint token; + int i; + + (void) g_scanner_get_next_token (scanner); + + token = g_scanner_get_next_token (scanner); + if (token != G_TOKEN_EQUAL_SIGN) + return G_TOKEN_EQUAL_SIGN; + + token = g_scanner_get_next_token (scanner); + if (token != G_TOKEN_LEFT_CURLY) + return G_TOKEN_LEFT_CURLY; + + for (i = 0; i < size; i++) + { + if (i != 0) + { + token = g_scanner_get_next_token (scanner); + if (token != G_TOKEN_COMMA) + return G_TOKEN_COMMA; + } + + token = g_scanner_get_next_token (scanner); + if (token != G_TOKEN_INT) + return G_TOKEN_INT; + + value[i] = scanner->value.v_int; + } + + token = g_scanner_get_next_token (scanner); + if (token != G_TOKEN_RIGHT_CURLY) + return G_TOKEN_RIGHT_CURLY; + + return G_TOKEN_NONE; +} + +static guint +parse_boolean_assign (GScanner *scanner, gboolean *value) +{ + guint token; + + (void) g_scanner_get_next_token (scanner); + + token = g_scanner_get_next_token (scanner); + if (token != G_TOKEN_EQUAL_SIGN) + return G_TOKEN_EQUAL_SIGN; + + token = g_scanner_get_next_token (scanner); + if (token == TOKEN_YES) + { + *value = TRUE; + return G_TOKEN_NONE; + } + else + { + *value = FALSE; + return G_TOKEN_NONE; + } +} + +static guint +parse_string_assign (GScanner *scanner, char **value) +{ + guint token; + + (void) g_scanner_get_next_token (scanner); + + token = g_scanner_get_next_token (scanner); + if (token != G_TOKEN_EQUAL_SIGN) + return G_TOKEN_EQUAL_SIGN; + + token = g_scanner_get_next_token (scanner); + if (token != G_TOKEN_STRING) + return G_TOKEN_STRING; + + *value = g_strdup (scanner->value.v_string); + return G_TOKEN_NONE; +} + +static guint +parse_color (eazel_theme_data *theme_data, GScanner *scanner, GdkColor *color) +{ + guint token; + + token = g_scanner_peek_next_token (scanner); + if (theme_data != 0 && token == G_TOKEN_LESS_THAN) + { + (void) g_scanner_get_next_token (scanner); + + token = g_scanner_get_next_token (scanner); + + if (token == G_TOKEN_INT) + { + int index = scanner->value.v_int; + + if (index < 0 || index >= EAZEL_ENGINE_PALETTE_SIZE) + { + g_error ("Palette only has %d entries\n", + EAZEL_ENGINE_PALETTE_SIZE); + } + + index = CLAMP (index, 0, EAZEL_ENGINE_PALETTE_SIZE - 1); + + /* XXX just taking the first color of the gradient.. */ + if (theme_data->palette[index] != 0) + *color = theme_data->palette[index]->from; + else + g_error ("No color in palette with index %d", index); + + token = g_scanner_peek_next_token (scanner); + if (token == G_TOKEN_COMMA) + { + float tem; + + (void) g_scanner_get_next_token (scanner); + + token = g_scanner_get_next_token (scanner); + if (token != G_TOKEN_FLOAT) + return G_TOKEN_FLOAT; + + tem = color->red; + tem *= scanner->value.v_float; + color->red = tem; + + tem = color->green; + tem *= scanner->value.v_float; + color->green = tem; + + tem = color->blue; + tem *= scanner->value.v_float; + color->blue = tem; + } + } + else if (token == G_TOKEN_STRING) + { + char *string; + gboolean ret = FALSE; + + string = read_line_from_file (scanner->value.v_string); + if (string != 0) + ret = gdk_color_parse (string, color); + + if (!ret) + { + /* Default to the Eazel Teal color */ + color->red = 0x5050; + color->green = 0x8080; + color->blue = 0x8383; + } + + g_free (string); + } + else + return G_TOKEN_INT; + + token = g_scanner_get_next_token (scanner); + if (token != G_TOKEN_GREATER_THAN) + return G_TOKEN_GREATER_THAN; + + return G_TOKEN_NONE; + } + else + return gtk_rc_parse_color (scanner, color); +} + +static guint +parse_n_colors (eazel_theme_data *theme_data, + GScanner *scanner, GdkColor *value, int n) +{ + guint token; + int i; + for (i = 0; i < n; i++) + { + if (i != 0) + { + token = g_scanner_get_next_token (scanner); + if (token != G_TOKEN_COMMA) + return G_TOKEN_COMMA; + } + + token = parse_color (theme_data, scanner, value + i); + if (token != G_TOKEN_NONE) + return token; + } + return G_TOKEN_NONE; +} + +static guint +parse_n_color_assign (eazel_theme_data *theme_data, + GScanner *scanner, GdkColor *value, int n) +{ + guint token; + + (void) g_scanner_get_next_token (scanner); + + token = g_scanner_get_next_token (scanner); + if (token != G_TOKEN_EQUAL_SIGN) + return G_TOKEN_EQUAL_SIGN; + + return parse_n_colors (theme_data, scanner, value, n); +} + +static guint +parse_color_assign (eazel_theme_data *theme_data, + GScanner *scanner, GdkColor *color) +{ + return parse_n_color_assign (theme_data, scanner, color, 1); +} + +static guint +parse_standard_color_assign (eazel_theme_data *theme_data, GScanner *scanner, + GtkRcStyle *rc_style, GdkColor *colors, int flag) +{ + GtkStateType state; + guint token; + + (void) g_scanner_get_next_token (scanner); + + token = gtk_rc_parse_state (scanner, &state); + if (token != G_TOKEN_NONE) + return token; + + token = g_scanner_get_next_token (scanner); + if (token != G_TOKEN_EQUAL_SIGN) + return G_TOKEN_EQUAL_SIGN; + + token = parse_color (theme_data, scanner, colors + state); + if (token != G_TOKEN_NONE) + return token; + + rc_style->color_flags[state] |= flag; + return G_TOKEN_NONE; +} + +static guint +parse_gradient (eazel_theme_data *theme_data, GScanner *scanner, + eazel_engine_gradient **gradient) +{ + eazel_engine_gradient_direction direction; + GdkColor from; + GSList *colors = NULL; + + guint token; + + token = g_scanner_get_next_token (scanner); + + if (token == G_TOKEN_LESS_THAN) + { + int index; + + /* a palette reference */ + + token = g_scanner_get_next_token (scanner); + if (token != G_TOKEN_INT) + return G_TOKEN_INT; + + index = scanner->value.v_int; + if (index < 0 || index >= EAZEL_ENGINE_PALETTE_SIZE) + { + g_error ("Palette only has %d entries\n", + EAZEL_ENGINE_PALETTE_SIZE); + } + + if (theme_data->palette[index] != 0) + *gradient = eazel_engine_gradient_ref (theme_data->palette[index]); + else + g_error ("No color in palette with index %d", index); + + token = g_scanner_get_next_token (scanner); + if (token != G_TOKEN_GREATER_THAN) + return G_TOKEN_GREATER_THAN; + + return G_TOKEN_NONE; + } + else if (token != G_TOKEN_LEFT_CURLY) + return G_TOKEN_LEFT_CURLY; + + token = g_scanner_peek_next_token (scanner); + if (token == TOKEN_HORIZONTAL) + { + direction = GRADIENT_HORIZONTAL; + (void) g_scanner_get_next_token (scanner); + + token = g_scanner_get_next_token (scanner); + if (token != G_TOKEN_COLON) + return G_TOKEN_COLON; + } + else if (token == TOKEN_VERTICAL) + { + direction = GRADIENT_VERTICAL; + (void) g_scanner_get_next_token (scanner); + + token = g_scanner_get_next_token (scanner); + if (token != G_TOKEN_COLON) + return G_TOKEN_COLON; + } + else if (token == G_TOKEN_RIGHT_CURLY) + direction = GRADIENT_NONE; + else + direction = GRADIENT_VERTICAL; + + if (direction != GRADIENT_NONE) + { + token = parse_color (theme_data, scanner, &from); + if (token != G_TOKEN_NONE) + return token; + + while (1) + { + float weight = 1.0; + GdkColor color; + eazel_engine_gradient_component *component; + + token = g_scanner_peek_next_token (scanner); + if (token == G_TOKEN_COMMA) + (void) g_scanner_get_next_token (scanner); + else if (token == G_TOKEN_LEFT_BRACE) + { + (void) g_scanner_get_next_token (scanner); + + token = g_scanner_get_next_token (scanner); + if (token == G_TOKEN_FLOAT) + weight = scanner->value.v_float; + else if (token == G_TOKEN_INT) + weight = scanner->value.v_int; + else + return G_TOKEN_FLOAT; + + token = g_scanner_get_next_token (scanner); + if (token != G_TOKEN_RIGHT_BRACE) + return G_TOKEN_RIGHT_BRACE; + } + else + break; + + token = parse_color (theme_data, scanner, &color); + if (token != G_TOKEN_NONE) + return token; + + component = g_new (eazel_engine_gradient_component, 1); + component->color = color; + component->weight = weight; + colors = g_slist_prepend (colors, component); + } + + colors = g_slist_reverse (colors); + } + + token = g_scanner_get_next_token (scanner); + if (token != G_TOKEN_RIGHT_CURLY) + return G_TOKEN_RIGHT_CURLY; + + if (*gradient != NULL) + eazel_engine_gradient_unref (*gradient); + *gradient = eazel_engine_gradient_new (direction, &from, colors); + + return G_TOKEN_NONE; +} + +static guint +parse_gradient_assign (eazel_theme_data *theme_data, GScanner *scanner, + eazel_engine_gradient **gradients) +{ + guint token, state; + + (void) g_scanner_get_next_token (scanner); + + token = gtk_rc_parse_state (scanner, &state); + if (token != G_TOKEN_NONE) + return token; + + token = g_scanner_get_next_token (scanner); + if (token != G_TOKEN_EQUAL_SIGN) + return G_TOKEN_EQUAL_SIGN; + + token = parse_gradient (theme_data, scanner, gradients + state); + if (token != G_TOKEN_NONE) + return token; + + return G_TOKEN_NONE; +} + +static guint +parse_1_gradient_assign (eazel_theme_data *theme_data, GScanner *scanner, + eazel_engine_gradient **gradient) +{ + guint token; + + (void) g_scanner_get_next_token (scanner); + + token = g_scanner_get_next_token (scanner); + if (token != G_TOKEN_EQUAL_SIGN) + return G_TOKEN_EQUAL_SIGN; + + token = parse_gradient (theme_data, scanner, gradient); + if (token != G_TOKEN_NONE) + return token; + + return G_TOKEN_NONE; +} + +static guint +parse_stock_stmt (GScanner *scanner, eazel_theme_data *theme_data, + eazel_engine_stock_table **table_ptr) +{ + eazel_engine_stock_table *table = g_new0 (eazel_engine_stock_table, 1); + guint token; + + table->ref_count = 1; + + (void) g_scanner_get_next_token (scanner); + + token = g_scanner_get_next_token (scanner); + if (token != G_TOKEN_LEFT_CURLY) + return G_TOKEN_LEFT_CURLY; + + token = g_scanner_peek_next_token (scanner); + while (token != G_TOKEN_RIGHT_CURLY) + { + switch (token) + { + guint stock; + eazel_engine_image *image; + + case G_TOKEN_STRING: + (void) g_scanner_get_next_token (scanner); + + stock = stock_index (scanner->value.v_string); + image = &table->images[stock]; + + token = g_scanner_get_next_token (scanner); + if (token != G_TOKEN_LEFT_CURLY) + return G_TOKEN_LEFT_CURLY; + + token = g_scanner_peek_next_token (scanner); + while (token != G_TOKEN_RIGHT_CURLY) + { + switch (token) + { + case TOKEN_IMAGE: + token = parse_string_assign (scanner, &image->filename); + break; + + case TOKEN_BORDER: + token = parse_int_array_assign (scanner, image->border, 4); + break; + + case TOKEN_RECOLOR: + token = parse_1_gradient_assign (theme_data, scanner, + &image->recolor); + break; + + default: + g_scanner_get_next_token (scanner); + token = G_TOKEN_RIGHT_CURLY; + break; + } + + if (token != G_TOKEN_NONE) + goto out; + + token = g_scanner_peek_next_token (scanner); + } + + token = g_scanner_get_next_token (scanner); + if (token == G_TOKEN_RIGHT_CURLY) + token = G_TOKEN_NONE; + else + token = G_TOKEN_RIGHT_CURLY; + break; + + default: + g_scanner_get_next_token (scanner); + token = G_TOKEN_RIGHT_CURLY; + break; + } + + out: + if (token != G_TOKEN_NONE) + { + g_free (table); + return token; + } + + token = g_scanner_peek_next_token (scanner); + } + + token = g_scanner_get_next_token (scanner); + if (token != G_TOKEN_RIGHT_CURLY) + return G_TOKEN_RIGHT_CURLY; + + *table_ptr = table; + return G_TOKEN_NONE; +} + +static guint +parse_palette_assign (GScanner *scanner, eazel_theme_data *theme_data) +{ + int index; + guint token; + + (void) g_scanner_get_next_token (scanner); + + token = g_scanner_get_next_token (scanner); + if (token != G_TOKEN_LEFT_BRACE) + return G_TOKEN_LEFT_BRACE; + + token = g_scanner_get_next_token (scanner); + if (token != G_TOKEN_INT) + return G_TOKEN_INT; + + index = scanner->value.v_int; + if (index < 0 || index >= EAZEL_ENGINE_PALETTE_SIZE) + { + g_error ("Only %d colors are allowed in the palette", + EAZEL_ENGINE_PALETTE_SIZE); + } + + token = g_scanner_get_next_token (scanner); + if (token != G_TOKEN_RIGHT_BRACE) + return G_TOKEN_RIGHT_BRACE; + + token = g_scanner_get_next_token (scanner); + if (token != G_TOKEN_EQUAL_SIGN) + return G_TOKEN_EQUAL_SIGN; + + token = parse_gradient (theme_data, scanner, &theme_data->palette[index]); + if (token != G_TOKEN_NONE) + return token; + + return G_TOKEN_NONE; +} + +/* external theme functions called */ + +static guint +theme_parse_rc_style (GScanner *scanner, GtkRcStyle *rc_style) +{ + static GQuark scope_id = 0; + guint old_scope; + guint token; + eazel_theme_data *theme_data; + gint i; + gboolean had_stock_table = FALSE; + + /* Set up a new scope in this scanner. */ + + if (!scope_id) + scope_id = g_quark_from_string ("theme_engine"); + + /* If we bail out due to errors, we *don't* reset the scope, so the + error messaging code can make sense of our tokens. */ + old_scope = g_scanner_set_scope (scanner, scope_id); + + /* Now check if we already added our symbols to this scope + (in some previous call to theme_parse_rc_style for the + same scanner. */ + + if (!g_scanner_lookup_symbol (scanner, theme_symbols[0].name)) + { + g_scanner_freeze_symbol_table(scanner); + for (i = 0; i < n_theme_symbols; i++) + { + g_scanner_scope_add_symbol (scanner, scope_id, + theme_symbols[i].name, + GINT_TO_POINTER(theme_symbols[i].token)); + } + g_scanner_thaw_symbol_table (scanner); + } + + /* We're ready to go, now parse the top level */ + + theme_data = g_new (eazel_theme_data, 1); + if (default_theme_data != 0) + memcpy (theme_data, default_theme_data, sizeof (eazel_theme_data)); + else + memcpy (theme_data, &original_theme_data, sizeof (eazel_theme_data)); + + /* ref all gradients */ + for (i = 0; i < 5; i++) + { + if (theme_data->gradients[i] != NULL) + theme_data->gradients[i] = (eazel_engine_gradient_ref + (theme_data->gradients[i])); + } + for (i = 0; i < EAZEL_ENGINE_PALETTE_SIZE; i++) + { + if (theme_data->palette[i] != NULL) + theme_data->palette[i] = (eazel_engine_gradient_ref + (theme_data->palette[i])); + } + + theme_data->refcount = 1; + + if (default_stock_data != 0) + theme_data->stock = eazel_engine_stock_table_ref (default_stock_data); + else + theme_data->stock = 0; + + token = g_scanner_peek_next_token(scanner); + while (token != G_TOKEN_RIGHT_CURLY) + { + switch (token) + { + gboolean tem; + eazel_engine_stock_table *stock_tem; + + case TOKEN_THICKNESS: + token = parse_int_assign (scanner, &theme_data->thickness); + break; + + case TOKEN_FOCUS_THICKNESS: + token = parse_int_assign (scanner, &theme_data->focus_thickness); + break; + + case TOKEN_DEFAULT_THICKNESS: + token = parse_int_assign (scanner, &theme_data->default_thickness); + break; + + case TOKEN_FOCUS_COLOR: + token = parse_color_assign (theme_data, scanner, + &theme_data->focus_color); + break; + + case TOKEN_INSENSITIVE_COLORS: + token = parse_n_color_assign (theme_data, scanner, + theme_data->insensitive_colors, 2); + break; + + case TOKEN_GRADIENT: + token = parse_gradient_assign (theme_data, scanner, + theme_data->gradients); + break; + + case TOKEN_SHADOW: + token = parse_boolean_assign (scanner, &tem); + if (token == G_TOKEN_NONE) + theme_data->no_shadow = !tem; + break; + + case TOKEN_INVERSE_SHADOW: + token = parse_boolean_assign (scanner, &tem); + if (token == G_TOKEN_NONE) + theme_data->inverse_shadow = tem; + break; + + case TOKEN_OUTLINE_SHADOW: + token = parse_boolean_assign (scanner, &tem); + if (token == G_TOKEN_NONE) + theme_data->outline_shadow = !tem; + break; + + case TOKEN_PALETTE: + token = parse_palette_assign (scanner, theme_data); + break; + + case TOKEN_STOCK: + token = parse_stock_stmt (scanner, theme_data, &stock_tem); + if (token == G_TOKEN_NONE) + { + if (theme_data->stock != 0) + eazel_engine_stock_table_unref (theme_data->stock); + + theme_data->stock = stock_tem; + had_stock_table = TRUE; + } + break; + + case TOKEN_FG: + token = parse_standard_color_assign (theme_data, scanner, rc_style, + rc_style->fg, GTK_RC_FG); + break; + + case TOKEN_BG: + token = parse_standard_color_assign (theme_data, scanner, rc_style, + rc_style->bg, GTK_RC_BG); + break; + + case TOKEN_BASE: + token = parse_standard_color_assign (theme_data, scanner, rc_style, + rc_style->base, GTK_RC_BASE); + break; + + case TOKEN_TEXT: + token = parse_standard_color_assign (theme_data, scanner, rc_style, + rc_style->text, GTK_RC_TEXT); + break; + + default: + g_scanner_get_next_token (scanner); + token = G_TOKEN_RIGHT_CURLY; + break; + } + + if (token != G_TOKEN_NONE) + { + g_free (theme_data); + return token; + } + + token = g_scanner_peek_next_token (scanner); + } + + g_scanner_get_next_token(scanner); + + if (theme_data->stock == 0) + g_error ("First `engine' section must include a `stock' section."); + + if (had_stock_table) + { + /* Defining a stock table makes it the default for any + engine sections in the future that don't have one.. */ + + if (default_stock_data != 0) + eazel_engine_stock_table_unref (default_stock_data); + + default_stock_data = eazel_engine_stock_table_ref (theme_data->stock); + + /* Engine data section with stock table (usually the first in + in the file) sets some other default values. A hack, but + a very useful one! */ + + if (default_theme_data != 0) + theme_data_unref (default_theme_data); + + theme_data_ref (theme_data); + default_theme_data = theme_data; + } + + rc_style->engine_data = theme_data; + g_scanner_set_scope (scanner, old_scope); + + return G_TOKEN_NONE; +} + +static void +theme_merge_rc_style (GtkRcStyle *dest, GtkRcStyle *src) +{ + eazel_theme_data *src_data = src->engine_data; + + if (!dest->engine_data) + { + if (src_data) + { + theme_data_ref (src_data); + dest->engine_data = src_data; + } + } +} + +static void +theme_rc_style_to_style (GtkStyle *style, GtkRcStyle *rc_style) +{ + eazel_theme_data *data = rc_style->engine_data; + + switch (data->thickness) + { + case 0: + style->klass = &eazel_class_0; + break; + case 1: + style->klass = &eazel_class_1; + break; + case 3: + style->klass = &eazel_class_3; + break; + default: + g_warning ("eazel theme: Invalid thickness %d in RC file\n", + data->thickness); + /* FALL THROUGH */ + case 2: + style->klass = &eazel_class_2; + break; + } + style->engine_data = data; + theme_data_ref (data); +} + +static void +theme_duplicate_style (GtkStyle *dest, GtkStyle *src) +{ + dest->klass = src->klass; + dest->engine_data = src->engine_data; + theme_data_ref (dest->engine_data); +} + +static void +theme_realize_style (GtkStyle *style) +{ +} + +static void +theme_unrealize_style (GtkStyle *style) +{ +} + +static void +theme_destroy_rc_style (GtkRcStyle *rc_style) +{ + theme_data_unref (rc_style->engine_data); + rc_style->engine_data = NULL; +} + +static void +theme_destroy_style (GtkStyle *style) +{ + theme_data_unref (style->engine_data); + style->engine_data = NULL; +} + +static void +theme_set_background(GtkStyle *style, GdkWindow *window, + GtkStateType state_type) +{ + GdkPixmap *pixmap; + gint parent_relative; + eazel_theme_data *theme_data; + + g_return_if_fail (style != NULL); + g_return_if_fail (window != NULL); + + theme_data = STYLE_THEME_DATA (style); + g_assert (theme_data != NULL); + + if (style->bg_pixmap[state_type]) + { + if (style->bg_pixmap[state_type] == (GdkPixmap *) GDK_PARENT_RELATIVE) + { + pixmap = NULL; + parent_relative = TRUE; + } + else + { + pixmap = style->bg_pixmap[state_type]; + parent_relative = FALSE; + } + + gdk_window_set_back_pixmap (window, pixmap, parent_relative); + } + else + gdk_window_set_background (window, &style->bg[state_type]); + + install_focus_hooks (window); +} + +void +theme_init (GtkThemeEngine * engine) +{ + GtkRangeClass *rangeclass; + GtkScaleClass *scaleclass; + GtkCheckButtonClass *checkclass; + + window_focus_data_key = g_quark_from_static_string (WINDOW_FOCUS_DATA_KEY); + + engine->parse_rc_style = theme_parse_rc_style; + engine->merge_rc_style = theme_merge_rc_style; + engine->rc_style_to_style = theme_rc_style_to_style; + engine->duplicate_style = theme_duplicate_style; + engine->realize_style = theme_realize_style; + engine->unrealize_style = theme_unrealize_style; + engine->destroy_rc_style = theme_destroy_rc_style; + engine->destroy_style = theme_destroy_style; + engine->set_background = theme_set_background; + + /* make scrollbar the correct width */ + rangeclass = (GtkRangeClass *) gtk_type_class (gtk_range_get_type ()); + range_slider_width = rangeclass->slider_width; + range_min_slider_size = rangeclass->min_slider_size; + range_stepper_size = rangeclass->stepper_size; + range_stepper_slider_spacing = rangeclass->stepper_slider_spacing; + rangeclass->slider_width = RANGE_WIDTH - 4; + rangeclass->min_slider_size = MIN_THUMB_HEIGHT; + rangeclass->stepper_size = RANGE_WIDTH - 4; + rangeclass->stepper_slider_spacing = 0; + + /* Make scale slider smaller */ + scaleclass = (GtkScaleClass *)gtk_type_class(gtk_scale_get_type()); + scale_slider_length = scaleclass->slider_length; + scaleclass->slider_length = SCALE_HEIGHT; + + /* Make check/radio buttons bigger */ + checkclass = (GtkCheckButtonClass *)gtk_type_class(gtk_check_button_get_type()); + check_size = checkclass->indicator_size; + check_spacing = checkclass->indicator_spacing; + checkclass->indicator_size = CHECK_SIZE; + checkclass->indicator_spacing = CHECK_SPACING; + + eazel_engine_install_hacks (); + + gdk_rgb_init (); + + /* Initialize default theme data */ + original_theme_data.gradients[0] = eazel_engine_make_two_point_gradient (GRADIENT_VERTICAL, 0xeeeeee, 0xaaaaaa); + original_theme_data.gradients[1] = eazel_engine_make_two_point_gradient (GRADIENT_VERTICAL, 0xcccccc, 0x888888); + original_theme_data.gradients[2] = eazel_engine_make_two_point_gradient (GRADIENT_VERTICAL, 0xffffff, 0xbbbbbb); + original_theme_data.gradients[3] = eazel_engine_make_two_point_gradient (GRADIENT_VERTICAL, 0xcccccc, 0x888888); +} + +void +theme_exit (void) +{ + GtkRangeClass *rangeclass; + GtkScaleClass *scaleclass; + GtkCheckButtonClass *checkclass; + + rangeclass = (GtkRangeClass *)gtk_type_class(gtk_range_get_type()); + scaleclass = (GtkScaleClass *)gtk_type_class(gtk_scale_get_type()); + checkclass = (GtkCheckButtonClass *)gtk_type_class(gtk_check_button_get_type()); + + rangeclass->slider_width = range_slider_width; + rangeclass->min_slider_size = range_min_slider_size; + rangeclass->stepper_size = range_stepper_size; + rangeclass->stepper_slider_spacing = range_stepper_slider_spacing; + + scaleclass->slider_length = scale_slider_length; + + checkclass->indicator_size = check_size; + checkclass->indicator_spacing = check_spacing; + + eazel_engine_remove_hacks (); + uninstall_all_focus_hooks (); +} + + +/* Focus change hooks */ + +typedef struct focus_change_data_struct focus_change_data; +struct focus_change_data_struct { + GtkWidget *widget; + + gboolean is_focused; + gboolean connected; + + guint focus_in_signal_id; + guint focus_out_signal_id; + guint destroyed_signal_id; +}; + +static GSList *focus_data_list = NULL; + +static focus_change_data * +get_focus_data (GtkWidget *widget, gboolean add) +{ + GtkWidget *toplevel = gtk_widget_get_toplevel (widget); + focus_change_data *data = gtk_object_get_data_by_id (GTK_OBJECT (toplevel), + window_focus_data_key); + if (data == 0 && add) + { + data = g_new0 (focus_change_data, 1); + data->widget = widget; + gtk_object_set_data_by_id_full (GTK_OBJECT (toplevel), + window_focus_data_key, + data, g_free); + focus_data_list = g_slist_prepend (focus_data_list, data); + } + + return data; +} + +static void +focus_change_helper (GtkWidget *widget, gpointer data) +{ + if (GTK_IS_CONTAINER (widget)) + { + gtk_container_forall (GTK_CONTAINER (widget), + focus_change_helper, NULL); + } + else if (GTK_IS_RANGE (widget)) + { + gtk_widget_queue_draw (widget); + } + else if (GTK_IS_PROGRESS (widget)) + { + /* This is horrible. But the GtkProgress widget stores the + progress bar in a pixmap, then blits it on expose. But + it will refresh the pixmap when asked to resize.. */ + if (GTK_WIDGET_DRAWABLE (widget)) + gtk_widget_queue_resize (widget); + } +} + +static void +window_focus_in_callback (GtkWidget *window, GdkEventFocus *event, + focus_change_data *data) +{ + data->is_focused = TRUE; + gtk_container_forall (GTK_CONTAINER (window), + focus_change_helper, NULL); +} + +static void +window_focus_out_callback (GtkWidget *window, GdkEventFocus *event, + focus_change_data *data) +{ + data->is_focused = FALSE; + gtk_container_forall (GTK_CONTAINER (window), + focus_change_helper, NULL); +} + +static void +window_destroyed_callback (GtkWidget *window, focus_change_data *data) +{ + focus_data_list = g_slist_remove (focus_data_list, data); +} + +gboolean +eazel_engine_widget_in_focused_window_p (GtkWidget *widget) +{ + focus_change_data *data = get_focus_data (widget, FALSE); + return (data != 0) ? data->is_focused : FALSE; +} + +static void +install_focus_hooks (GdkWindow *window) +{ + /* Evilness */ + GtkWidget *widget; + gdk_window_get_user_data (window, (gpointer *) &widget); + if (widget != NULL && GTK_IS_WINDOW (widget)) + { + focus_change_data *data = get_focus_data (widget, TRUE); + if (!data->connected) + { + /* Connect to this window so we get focus-in/out events */ + data->focus_in_signal_id + = gtk_signal_connect (GTK_OBJECT (widget), "focus_in_event", + window_focus_in_callback, data); + data->focus_out_signal_id + = gtk_signal_connect (GTK_OBJECT (widget), "focus_out_event", + window_focus_out_callback, data); + data->destroyed_signal_id + = gtk_signal_connect (GTK_OBJECT (widget), "destroy", + window_destroyed_callback, data); + + data->connected = TRUE; + } + } +} + +static void +uninstall_all_focus_hooks (void) +{ + GSList *ptr; + for (ptr = focus_data_list; ptr != 0; ptr = ptr->next) + { + focus_change_data *data = ptr->data; + if (data->connected) + { + gtk_signal_disconnect (GTK_OBJECT (data->widget), + data->focus_in_signal_id); + gtk_signal_disconnect (GTK_OBJECT (data->widget), + data->focus_out_signal_id); + gtk_signal_disconnect (GTK_OBJECT (data->widget), + data->destroyed_signal_id); + } + gtk_object_remove_data_by_id (GTK_OBJECT (data->widget), + window_focus_data_key); + } + g_slist_free (focus_data_list); + focus_data_list = NULL; +} + + +/* The following function will be called by GTK+ when the module + * is loaded and checks to see if we are compatible with the + * version of GTK+ that loads us. + */ + +G_MODULE_EXPORT const gchar *g_module_check_init (GModule *module); + +const gchar * +g_module_check_init (GModule *module) +{ + return gtk_check_version (GTK_MAJOR_VERSION, + GTK_MINOR_VERSION, + GTK_MICRO_VERSION - GTK_INTERFACE_AGE); +} |