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;