From baae80fa414926de5b8db55863181d47c3ee0340 Mon Sep 17 00:00:00 2001 From: Daiki Ueno Date: Tue, 8 Mar 2011 06:58:20 +0900 Subject: [PATCH] Port st-theme-context.[ch] to eek-theme-context.[ch]. --- eek/Makefile.am | 4 +- eek/eek-renderer.c | 18 +- eek/eek-renderer.h | 1 + eek/eek-theme-context.c | 288 +++++++++++++++++++++++ eek/eek-theme-context.h | 78 +++++++ eek/eek-theme-node.c | 501 ++++++++++++++++++++++++++++++++++++++-- eek/eek-theme-node.h | 11 +- eek/eek-theme.h | 2 +- eek/eek-types.h | 1 + 9 files changed, 883 insertions(+), 21 deletions(-) create mode 100644 eek/eek-theme-context.c create mode 100644 eek/eek-theme-context.h diff --git a/eek/Makefile.am b/eek/Makefile.am index 800240c9..e616f322 100644 --- a/eek/Makefile.am +++ b/eek/Makefile.am @@ -47,7 +47,8 @@ libeek_private_headers = \ $(srcdir)/eek-special-keysym-entries.h \ $(srcdir)/eek-unicode-keysym-entries.h \ $(srcdir)/eek-xkeysym-keysym-entries.h \ - $(srcdir)/eek-marshalers.h \ + $(srcdir)/eek-marshalers.h \ + $(srcdir)/eek-theme-context.h \ $(srcdir)/eek-theme-node.h libeek_sources = \ @@ -66,6 +67,7 @@ libeek_sources = \ $(srcdir)/eek-renderer.c \ $(srcdir)/eek-keyboard-drawing.c \ $(srcdir)/eek-theme.c \ + $(srcdir)/eek-theme-context.c \ $(srcdir)/eek-theme-node.c libeek_keysym_sources = \ diff --git a/eek/eek-renderer.c b/eek/eek-renderer.c index ce010e77..92e9988f 100644 --- a/eek/eek-renderer.c +++ b/eek/eek-renderer.c @@ -1256,6 +1256,7 @@ eek_renderer_find_key_by_position (EekRenderer *renderer, } struct _CreateThemeNodeData { + EekThemeContext *context; EekThemeNode *parent; EekRenderer *renderer; }; @@ -1263,7 +1264,7 @@ typedef struct _CreateThemeNodeData CreateThemeNodeData; void create_theme_node_key_callback (EekElement *element, - gpointer user_data) + gpointer user_data) { CreateThemeNodeData *data = user_data; EekRendererPrivate *priv; @@ -1271,7 +1272,8 @@ create_theme_node_key_callback (EekElement *element, priv = EEK_RENDERER_GET_PRIVATE(data->renderer); - theme_node = eek_theme_node_new (data->parent, + theme_node = eek_theme_node_new (data->context, + data->parent, priv->theme, EEK_TYPE_KEY, eek_element_get_name (element), @@ -1283,7 +1285,8 @@ create_theme_node_key_callback (EekElement *element, theme_node, (GDestroyNotify)g_object_unref); - theme_node = eek_theme_node_new (data->parent, + theme_node = eek_theme_node_new (data->context, + data->parent, priv->theme, EEK_TYPE_KEY, eek_element_get_name (element), @@ -1306,7 +1309,8 @@ create_theme_node_section_callback (EekElement *element, priv = EEK_RENDERER_GET_PRIVATE(data->renderer); - theme_node = eek_theme_node_new (data->parent, + theme_node = eek_theme_node_new (data->context, + data->parent, priv->theme, EEK_TYPE_SECTION, eek_element_get_name (element), @@ -1331,6 +1335,7 @@ eek_renderer_set_theme (EekRenderer *renderer, EekTheme *theme) { EekRendererPrivate *priv; + EekThemeContext *theme_context; EekThemeNode *theme_node; CreateThemeNodeData data; @@ -1344,7 +1349,9 @@ eek_renderer_set_theme (EekRenderer *renderer, g_object_unref (priv->theme); priv->theme = g_object_ref (theme); - theme_node = eek_theme_node_new (NULL, + theme_context = eek_theme_context_new (); + theme_node = eek_theme_node_new (theme_context, + NULL, priv->theme, EEK_TYPE_KEYBOARD, "keyboard", @@ -1356,6 +1363,7 @@ eek_renderer_set_theme (EekRenderer *renderer, theme_node, (GDestroyNotify)g_object_unref); + data.context = theme_context; data.parent = theme_node; data.renderer = renderer; eek_container_foreach_child (EEK_CONTAINER(priv->keyboard), diff --git a/eek/eek-renderer.h b/eek/eek-renderer.h index b125df7f..2f4d0930 100644 --- a/eek/eek-renderer.h +++ b/eek/eek-renderer.h @@ -26,6 +26,7 @@ #include "eek-keysym.h" #include "eek-types.h" #include "eek-theme.h" +#include "eek-theme-context.h" G_BEGIN_DECLS diff --git a/eek/eek-theme-context.c b/eek/eek-theme-context.c new file mode 100644 index 00000000..80f7af0e --- /dev/null +++ b/eek/eek-theme-context.c @@ -0,0 +1,288 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; c-basic-offset: 2; -*- */ +/* + * eek-theme-context.c: holds global information about a tree of styled objects + * + * Copyright 2009, 2010 Red Hat, Inc. + * Copyright 2009 Florian Müllner + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include "eek-theme.h" +#include "eek-theme-context.h" + +struct _EekThemeContext { + GObject parent; + + double resolution; + PangoFontDescription *font; + EekThemeNode *root_node; + EekTheme *theme; +}; + +struct _EekThemeContextClass { + GObjectClass parent_class; +}; + +#define DEFAULT_RESOLUTION 96. +#define DEFAULT_FONT "sans-serif 10" + +enum +{ + CHANGED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0, }; + +G_DEFINE_TYPE (EekThemeContext, eek_theme_context, G_TYPE_OBJECT) + +static void +eek_theme_context_finalize (GObject *object) +{ + EekThemeContext *context = EEK_THEME_CONTEXT (object); + + if (context->root_node) + g_object_unref (context->root_node); + if (context->theme) + g_object_unref (context->theme); + + pango_font_description_free (context->font); + + G_OBJECT_CLASS (eek_theme_context_parent_class)->finalize (object); +} + +static void +eek_theme_context_class_init (EekThemeContextClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = eek_theme_context_finalize; + + signals[CHANGED] = + g_signal_new ("changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, /* no default handler slot */ + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} + +static void +eek_theme_context_init (EekThemeContext *context) +{ + context->resolution = DEFAULT_RESOLUTION; + context->font = pango_font_description_from_string (DEFAULT_FONT); +} + +/** + * eek_theme_context_new: + * + * Create a new theme context. + */ +EekThemeContext * +eek_theme_context_new (void) +{ + EekThemeContext *context; + + context = g_object_new (EEK_TYPE_THEME_CONTEXT, NULL); + + return context; +} + +static void +eek_theme_context_changed (EekThemeContext *context) +{ + EekThemeNode *old_root = context->root_node; + context->root_node = NULL; + + g_signal_emit (context, signals[CHANGED], 0); + + if (old_root) + g_object_unref (old_root); +} + +/** + * eek_theme_context_set_theme: + * @context: a #EekThemeContext + * + * Sets the default set of theme stylesheets for the context. This theme will + * be used for the root node and for nodes descending from it, unless some other + * style is explicitely specified. + */ +void +eek_theme_context_set_theme (EekThemeContext *context, + EekTheme *theme) +{ + g_return_if_fail (EEK_IS_THEME_CONTEXT (context)); + g_return_if_fail (theme == NULL || EEK_IS_THEME (theme)); + + if (context->theme != theme) + { + if (context->theme) + g_object_unref (context->theme); + + context->theme = theme; + + if (context->theme) + g_object_ref (context->theme); + + eek_theme_context_changed (context); + } +} + +/** + * eek_theme_context_get_theme: + * @context: a #EekThemeContext + * + * Gets the default theme for the context. See eek_theme_context_set_theme() + * + * Return value: (transfer none): the default theme for the context + */ +EekTheme * +eek_theme_context_get_theme (EekThemeContext *context) +{ + g_return_val_if_fail (EEK_IS_THEME_CONTEXT (context), NULL); + + return context->theme; +} + +/** + * eek_theme_context_set_resolution: + * @context: a #EekThemeContext + * @resolution: resolution of the context (number of pixels in an "inch") + * + * Sets the resolution of the theme context. This is the scale factor + * used to convert between points and the length units pt, in, and cm. + * This does not necessarily need to correspond to the actual number + * resolution of the device. A value of 72. means that points and + * pixels are identical. The default value is 96. + */ +void +eek_theme_context_set_resolution (EekThemeContext *context, + double resolution) +{ + g_return_if_fail (EEK_IS_THEME_CONTEXT (context)); + + if (resolution == context->resolution) + return; + + context->resolution = resolution; + eek_theme_context_changed (context); +} + +/** + * eek_theme_context_set_default_resolution: + * @context: a #EekThemeContext + * + * Sets the resolution of the theme context to the default value of 96. + * See eek_theme_context_set_resolution(). + */ +void +eek_theme_context_set_default_resolution (EekThemeContext *context) +{ + g_return_if_fail (EEK_IS_THEME_CONTEXT (context)); + + if (context->resolution == DEFAULT_RESOLUTION) + return; + + context->resolution = DEFAULT_RESOLUTION; + eek_theme_context_changed (context); +} + +/** + * eek_theme_context_get_resolution: + * @context: a #EekThemeContext + * + * Gets the current resolution of the theme context. + * See eek_theme_context_set_resolution(). + * + * Return value: the resolution (in dots-per-"inch") + */ +double +eek_theme_context_get_resolution (EekThemeContext *context) +{ + g_return_val_if_fail (EEK_IS_THEME_CONTEXT (context), DEFAULT_RESOLUTION); + + return context->resolution; +} + +/** + * eek_theme_context_set_font: + * @context: a #EekThemeContext + * @font: the default font for theme context + * + * Sets the default font for the theme context. This is the font that + * is inherited by the root node of the tree of theme nodes. If the + * font is not overriden, then this font will be used. If the font is + * partially modified (for example, with 'font-size: 110%', then that + * modification is based on this font. + */ +void +eek_theme_context_set_font (EekThemeContext *context, + const PangoFontDescription *font) +{ + g_return_if_fail (EEK_IS_THEME_CONTEXT (context)); + g_return_if_fail (font != NULL); + + if (context->font == font || + pango_font_description_equal (context->font, font)) + return; + + pango_font_description_free (context->font); + context->font = pango_font_description_copy (font); + eek_theme_context_changed (context); +} + +/** + * eek_theme_context_get_font: + * @context: a #EekThemeContext + * + * Gets the default font for the theme context. See eek_theme_context_set_font(). + * + * Return value: the default font for the theme context. + */ +const PangoFontDescription * +eek_theme_context_get_font (EekThemeContext *context) +{ + g_return_val_if_fail (EEK_IS_THEME_CONTEXT (context), NULL); + + return context->font; +} + +/** + * eek_theme_context_get_root_node: + * @context: a #EekThemeContext + * + * Gets the root node of the tree of theme style nodes that associated with this + * context. For the node tree associated with a stage, this node represents + * styles applied to the stage itself. + * + * Return value: (transfer none): the root node of the context's style tree + */ +EekThemeNode * +eek_theme_context_get_root_node (EekThemeContext *context) +{ + if (context->root_node == NULL) + context->root_node = eek_theme_node_new (context, NULL, context->theme, + G_TYPE_NONE, NULL, NULL, NULL, NULL); + + return context->root_node; +} diff --git a/eek/eek-theme-context.h b/eek/eek-theme-context.h new file mode 100644 index 00000000..861ddf8b --- /dev/null +++ b/eek/eek-theme-context.h @@ -0,0 +1,78 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* + * eek-theme-context.c: holds global information about a tree of styled objects + * + * Copyright 2009, 2010 Red Hat, Inc. + * Copyright 2009 Florian Müllner + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#ifndef __EEK_THEME_CONTEXT_H__ +#define __EEK_THEME_CONTEXT_H__ + +#include +#include "eek-theme-node.h" + +G_BEGIN_DECLS + +/** + * SECTION:EekThemeContext + * @short_description: holds global information about a tree of styled objects + * + * #EekThemeContext is responsible for managing information global to + * a tree of styled objects, such as the set of stylesheets or the + * default font. + */ + +typedef struct _EekThemeContextClass EekThemeContextClass; + +#define EEK_TYPE_THEME_CONTEXT (eek_theme_context_get_type ()) +#define EEK_THEME_CONTEXT(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), EEK_TYPE_THEME_CONTEXT, EekThemeContext)) +#define EEK_THEME_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EEK_TYPE_THEME_CONTEXT, EekThemeContextClass)) +#define EEK_IS_THEME_CONTEXT(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), EEK_TYPE_THEME_CONTEXT)) +#define EEK_IS_THEME_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EEK_TYPE_THEME_CONTEXT)) +#define EEK_THEME_CONTEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EEK_TYPE_THEME_CONTEXT, EekThemeContextClass)) + +GType eek_theme_context_get_type + (void) G_GNUC_CONST; + +EekThemeContext *eek_theme_context_new + (void); + +void eek_theme_context_set_theme + (EekThemeContext *context, + EekTheme *theme); +EekTheme * eek_theme_context_get_theme + (EekThemeContext *context); + +void eek_theme_context_set_resolution + (EekThemeContext *context, + gdouble resolution); +void eek_theme_context_set_default_resolution + (EekThemeContext *context); +double eek_theme_context_get_resolution + (EekThemeContext *context); +void eek_theme_context_set_font + (EekThemeContext *context, + const PangoFontDescription *font); +const PangoFontDescription *eek_theme_context_get_font + (EekThemeContext *context); + +EekThemeNode * eek_theme_context_get_root_node + (EekThemeContext *context); + +G_END_DECLS + +#endif /* __EEK_THEME_CONTEXT_H__ */ diff --git a/eek/eek-theme-node.c b/eek/eek-theme-node.c index 1ebe962e..4722cbf7 100644 --- a/eek/eek-theme-node.c +++ b/eek/eek-theme-node.c @@ -31,15 +31,19 @@ #include #include +#include "eek-theme-context.h" #include "eek-theme-node.h" #include "eek-theme-private.h" struct _EekThemeNode { GObject parent; + EekThemeContext *context; EekThemeNode *parent_node; EekTheme *theme; + PangoFontDescription *font_desc; + EekGradientType background_gradient_type; EekColor background_color; @@ -108,6 +112,12 @@ eek_theme_node_dispose (GObject *gobject) { EekThemeNode *node = EEK_THEME_NODE (gobject); + if (node->context) + { + g_object_unref (node->context); + node->context = NULL; + } + if (node->theme) { g_object_unref (node->theme); @@ -146,6 +156,12 @@ eek_theme_node_finalize (GObject *object) cr_declaration_destroy (node->inline_properties); } + if (node->font_desc) + { + pango_font_description_free (node->font_desc); + node->font_desc = NULL; + } + G_OBJECT_CLASS (eek_theme_node_parent_class)->finalize (object); } @@ -171,20 +187,23 @@ eek_theme_node_finalize (GObject *object) * Return value: (transfer full): the theme node */ EekThemeNode * -eek_theme_node_new (EekThemeNode *parent_node, - EekTheme *theme, - GType element_type, - const char *element_id, - const char *element_class, - const char *pseudo_class, - const char *inline_style) +eek_theme_node_new (EekThemeContext *context, + EekThemeNode *parent_node, + EekTheme *theme, + GType element_type, + const char *element_id, + const char *element_class, + const char *pseudo_class, + const char *inline_style) { EekThemeNode *node; + g_return_val_if_fail (EEK_IS_THEME_CONTEXT (context), NULL); g_return_val_if_fail (parent_node == NULL || EEK_IS_THEME_NODE (parent_node), NULL); node = g_object_new (EEK_TYPE_THEME_NODE, NULL); + node->context = g_object_ref (context); if (parent_node != NULL) node->parent_node = g_object_ref (parent_node); else @@ -635,6 +654,15 @@ eek_theme_node_get_double (EekThemeNode *node, } } +static const PangoFontDescription * +get_parent_font (EekThemeNode *node) +{ + if (node->parent_node) + return eek_theme_node_get_font (node->parent_node); + else + return eek_theme_context_get_font (node->context); +} + static GetFromTermResult get_length_from_term (EekThemeNode *node, CRTerm *term, @@ -750,16 +778,11 @@ get_length_from_term (EekThemeNode *node, *length = num->val * multiplier; break; case POINTS: -#if 0 { double resolution = eek_theme_context_get_resolution (node->context); *length = num->val * multiplier * (resolution / 72.); } -#else - *length = num->val * multiplier; -#endif break; -#if 0 case FONT_RELATIVE: { const PangoFontDescription *desc; @@ -783,7 +806,7 @@ get_length_from_term (EekThemeNode *node, } } break; -#endif + default: g_assert_not_reached (); } @@ -1392,3 +1415,455 @@ eek_theme_node_get_background_gradient (EekThemeNode *node, *end = node->background_gradient_end; } } + +static gboolean +font_family_from_terms (CRTerm *term, + char **family) +{ + GString *family_string; + gboolean result = FALSE; + gboolean last_was_quoted = FALSE; + + if (!term) + return FALSE; + + family_string = g_string_new (NULL); + + while (term) + { + if (term->type != TERM_STRING && term->type != TERM_IDENT) + { + goto out; + } + + if (family_string->len > 0) + { + if (term->the_operator != COMMA && term->the_operator != NO_OP) + goto out; + /* Can concatenate two bare words, but not two quoted strings */ + if ((term->the_operator == NO_OP && last_was_quoted) || term->type == TERM_STRING) + goto out; + + if (term->the_operator == NO_OP) + g_string_append (family_string, " "); + else + g_string_append (family_string, ", "); + } + else + { + if (term->the_operator != NO_OP) + goto out; + } + + g_string_append (family_string, term->content.str->stryng->str); + + term = term->next; + } + + result = TRUE; + + out: + if (result) + { + *family = g_string_free (family_string, FALSE); + return TRUE; + } + else + { + *family = g_string_free (family_string, TRUE); + return FALSE; + } +} + +/* In points */ +static int font_sizes[] = { + 6 * 1024, /* xx-small */ + 8 * 1024, /* x-small */ + 10 * 1024, /* small */ + 12 * 1024, /* medium */ + 16 * 1024, /* large */ + 20 * 1024, /* x-large */ + 24 * 1024, /* xx-large */ +}; + +static gboolean +font_size_from_term (EekThemeNode *node, + CRTerm *term, + double *size) +{ + if (term->type == TERM_IDENT) + { + double resolution = eek_theme_context_get_resolution (node->context); + /* We work in integers to avoid double comparisons when converting back + * from a size in pixels to a logical size. + */ + int size_points = (int)(0.5 + *size * (72. / resolution)); + + if (strcmp (term->content.str->stryng->str, "xx-small") == 0) + size_points = font_sizes[0]; + else if (strcmp (term->content.str->stryng->str, "x-small") == 0) + size_points = font_sizes[1]; + else if (strcmp (term->content.str->stryng->str, "small") == 0) + size_points = font_sizes[2]; + else if (strcmp (term->content.str->stryng->str, "medium") == 0) + size_points = font_sizes[3]; + else if (strcmp (term->content.str->stryng->str, "large") == 0) + size_points = font_sizes[4]; + else if (strcmp (term->content.str->stryng->str, "x-large") == 0) + size_points = font_sizes[5]; + else if (strcmp (term->content.str->stryng->str, "xx-large") == 0) + size_points = font_sizes[6]; + else if (strcmp (term->content.str->stryng->str, "smaller") == 0) + { + /* Find the standard size equal to or smaller than the current size */ + int i = 0; + + while (i <= 6 && font_sizes[i] < size_points) + i++; + + if (i > 6) + { + /* original size greater than any standard size */ + size_points = (int)(0.5 + size_points / 1.2); + } + else + { + /* Go one smaller than that, if possible */ + if (i > 0) + i--; + + size_points = font_sizes[i]; + } + } + else if (strcmp (term->content.str->stryng->str, "larger") == 0) + { + /* Find the standard size equal to or larger than the current size */ + int i = 6; + + while (i >= 0 && font_sizes[i] > size_points) + i--; + + if (i < 0) /* original size smaller than any standard size */ + i = 0; + + /* Go one larger than that, if possible */ + if (i < 6) + i++; + + size_points = font_sizes[i]; + } + else + { + return FALSE; + } + + *size = size_points * (resolution / 72.); + return TRUE; + + } + else if (term->type == TERM_NUMBER && term->content.num->type == NUM_PERCENTAGE) + { + *size *= term->content.num->val / 100.; + return TRUE; + } + else if (get_length_from_term (node, term, TRUE, size) == VALUE_FOUND) + { + /* Convert from pixels to Pango units */ + *size *= 1024; + return TRUE; + } + + return FALSE; +} + +static gboolean +font_weight_from_term (CRTerm *term, + PangoWeight *weight, + gboolean *weight_absolute) +{ + if (term->type == TERM_NUMBER) + { + int weight_int; + + /* The spec only allows numeric weights from 100-900, though Pango + * will handle any number. We just let anything through. + */ + if (term->content.num->type != NUM_GENERIC) + return FALSE; + + weight_int = (int)(0.5 + term->content.num->val); + + *weight = weight_int; + *weight_absolute = TRUE; + + } + else if (term->type == TERM_IDENT) + { + /* FIXME: handle INHERIT */ + + if (strcmp (term->content.str->stryng->str, "bold") == 0) + { + *weight = PANGO_WEIGHT_BOLD; + *weight_absolute = TRUE; + } + else if (strcmp (term->content.str->stryng->str, "normal") == 0) + { + *weight = PANGO_WEIGHT_NORMAL; + *weight_absolute = TRUE; + } + else if (strcmp (term->content.str->stryng->str, "bolder") == 0) + { + *weight = PANGO_WEIGHT_BOLD; + *weight_absolute = FALSE; + } + else if (strcmp (term->content.str->stryng->str, "lighter") == 0) + { + *weight = PANGO_WEIGHT_LIGHT; + *weight_absolute = FALSE; + } + else + { + return FALSE; + } + + } + else + { + return FALSE; + } + + return TRUE; +} + +static gboolean +font_style_from_term (CRTerm *term, + PangoStyle *style) +{ + if (term->type != TERM_IDENT) + return FALSE; + + /* FIXME: handle INHERIT */ + + if (strcmp (term->content.str->stryng->str, "normal") == 0) + *style = PANGO_STYLE_NORMAL; + else if (strcmp (term->content.str->stryng->str, "oblique") == 0) + *style = PANGO_STYLE_OBLIQUE; + else if (strcmp (term->content.str->stryng->str, "italic") == 0) + *style = PANGO_STYLE_ITALIC; + else + return FALSE; + + return TRUE; +} + +static gboolean +font_variant_from_term (CRTerm *term, + PangoVariant *variant) +{ + if (term->type != TERM_IDENT) + return FALSE; + + /* FIXME: handle INHERIT */ + + if (strcmp (term->content.str->stryng->str, "normal") == 0) + *variant = PANGO_VARIANT_NORMAL; + else if (strcmp (term->content.str->stryng->str, "small-caps") == 0) + *variant = PANGO_VARIANT_SMALL_CAPS; + else + return FALSE; + + return TRUE; +} + +const PangoFontDescription * +eek_theme_node_get_font (EekThemeNode *node) +{ + /* Initialized despite _set flags to suppress compiler warnings */ + PangoStyle font_style = PANGO_STYLE_NORMAL; + gboolean font_style_set = FALSE; + PangoVariant variant = PANGO_VARIANT_NORMAL; + gboolean variant_set = FALSE; + PangoWeight weight = PANGO_WEIGHT_NORMAL; + gboolean weight_absolute = TRUE; + gboolean weight_set = FALSE; + double size = 0.; + gboolean size_set = FALSE; + + char *family = NULL; + double parent_size; + int i; + + if (node->font_desc) + return node->font_desc; + + node->font_desc = pango_font_description_copy (get_parent_font (node)); + parent_size = pango_font_description_get_size (node->font_desc); + if (!pango_font_description_get_size_is_absolute (node->font_desc)) + { + double resolution = eek_theme_context_get_resolution (node->context); + parent_size *= (resolution / 72.); + } + + ensure_properties (node); + + for (i = 0; i < node->n_properties; i++) + { + CRDeclaration *decl = node->properties[i]; + + if (strcmp (decl->property->stryng->str, "font") == 0) + { + PangoStyle tmp_style = PANGO_STYLE_NORMAL; + PangoVariant tmp_variant = PANGO_VARIANT_NORMAL; + PangoWeight tmp_weight = PANGO_WEIGHT_NORMAL; + gboolean tmp_weight_absolute = TRUE; + double tmp_size; + CRTerm *term = decl->value; + + /* A font specification starts with node/variant/weight + * in any order. Each is allowed to be specified only once, + * but we don't enforce that. + */ + for (; term; term = term->next) + { + if (font_style_from_term (term, &tmp_style)) + continue; + if (font_variant_from_term (term, &tmp_variant)) + continue; + if (font_weight_from_term (term, &tmp_weight, &tmp_weight_absolute)) + continue; + + break; + } + + /* The size is mandatory */ + + if (term == NULL || term->type != TERM_NUMBER) + { + g_warning ("Size missing from font property"); + continue; + } + + tmp_size = parent_size; + if (!font_size_from_term (node, term, &tmp_size)) + { + g_warning ("Couldn't parse size in font property"); + continue; + } + + term = term->next; + + if (term != NULL && term->type && TERM_NUMBER && term->the_operator == DIVIDE) + { + /* Ignore line-height specification */ + term = term->next; + } + + /* the font family is mandatory - it is a comma-separated list of + * names. + */ + if (!font_family_from_terms (term, &family)) + { + g_warning ("Couldn't parse family in font property"); + continue; + } + + font_style = tmp_style; + font_style_set = TRUE; + weight = tmp_weight; + weight_absolute = tmp_weight_absolute; + weight_set = TRUE; + variant = tmp_variant; + variant_set = TRUE; + + size = tmp_size; + size_set = TRUE; + + } + else if (strcmp (decl->property->stryng->str, "font-family") == 0) + { + if (!font_family_from_terms (decl->value, &family)) + { + g_warning ("Couldn't parse family in font property"); + continue; + } + } + else if (strcmp (decl->property->stryng->str, "font-weight") == 0) + { + if (decl->value == NULL || decl->value->next != NULL) + continue; + + if (font_weight_from_term (decl->value, &weight, &weight_absolute)) + weight_set = TRUE; + } + else if (strcmp (decl->property->stryng->str, "font-style") == 0) + { + if (decl->value == NULL || decl->value->next != NULL) + continue; + + if (font_style_from_term (decl->value, &font_style)) + font_style_set = TRUE; + } + else if (strcmp (decl->property->stryng->str, "font-variant") == 0) + { + if (decl->value == NULL || decl->value->next != NULL) + continue; + + if (font_variant_from_term (decl->value, &variant)) + variant_set = TRUE; + } + else if (strcmp (decl->property->stryng->str, "font-size") == 0) + { + gdouble tmp_size; + if (decl->value == NULL || decl->value->next != NULL) + continue; + + tmp_size = parent_size; + if (font_size_from_term (node, decl->value, &tmp_size)) + { + size = tmp_size; + size_set = TRUE; + } + } + } + + if (family) + { + pango_font_description_set_family (node->font_desc, family); + g_free (family); + } + + if (size_set) + pango_font_description_set_absolute_size (node->font_desc, size); + + if (weight_set) + { + if (!weight_absolute) + { + /* bolder/lighter are supposed to switch between available styles, but with + * font substitution, that gets to be a pretty fuzzy concept. So we use + * a fixed step of 200. (The spec says 100, but that might not take us from + * normal to bold. + */ + + PangoWeight old_weight = pango_font_description_get_weight (node->font_desc); + if (weight == PANGO_WEIGHT_BOLD) + weight = old_weight + 200; + else + weight = old_weight - 200; + + if (weight < 100) + weight = 100; + if (weight > 900) + weight = 900; + } + + pango_font_description_set_weight (node->font_desc, weight); + } + + if (font_style_set) + pango_font_description_set_style (node->font_desc, font_style); + if (variant_set) + pango_font_description_set_variant (node->font_desc, variant); + + return node->font_desc; +} diff --git a/eek/eek-theme-node.h b/eek/eek-theme-node.h index bcab4542..b2bcd8b0 100644 --- a/eek/eek-theme-node.h +++ b/eek/eek-theme-node.h @@ -20,6 +20,7 @@ #ifndef __EEK_THEME_NODE_H__ #define __EEK_THEME_NODE_H__ +#include #include "eek-types.h" G_BEGIN_DECLS @@ -70,7 +71,8 @@ typedef struct _EekThemeNodePrivate EekThemeNodePrivate; GType eek_theme_node_get_type (void) G_GNUC_CONST; -EekThemeNode *eek_theme_node_new (EekThemeNode *parent_node, +EekThemeNode *eek_theme_node_new (EekThemeContext *context, + EekThemeNode *parent_node, /* can be null */ EekTheme *theme, /* can be null */ GType element_type, const char *element_id, @@ -127,6 +129,13 @@ void eek_theme_node_get_border_color EekSide side, EekColor *color); +/* Font rule processing is pretty complicated, so we just hardcode it + * under the standard font/font-family/font-size/etc names. This means + * you can't have multiple separate styled fonts for a single item, + * but that should be OK. + */ +const PangoFontDescription *eek_theme_node_get_font (EekThemeNode *node); + G_END_DECLS #endif /* __EEK_THEME_NODE_H__ */ diff --git a/eek/eek-theme.h b/eek/eek-theme.h index 4065641c..031c8d7c 100644 --- a/eek/eek-theme.h +++ b/eek/eek-theme.h @@ -4,7 +4,7 @@ #include -#include "eek-theme-node.h" +#include "eek-types.h" G_BEGIN_DECLS diff --git a/eek/eek-types.h b/eek/eek-types.h index db52008f..c60b9ad3 100644 --- a/eek/eek-types.h +++ b/eek/eek-types.h @@ -137,6 +137,7 @@ typedef struct _EekKeyboard EekKeyboard; typedef struct _EekSymbol EekSymbol; typedef struct _EekKeysym EekKeysym; typedef struct _EekTheme EekTheme; +typedef struct _EekThemeContext EekThemeContext; typedef struct _EekThemeNode EekThemeNode; typedef struct _EekSymbolMatrix EekSymbolMatrix;