diff --git a/eek/eek-theme-context.c b/eek/eek-theme-context.c deleted file mode 100644 index 36b63370..00000000 --- a/eek/eek-theme-context.c +++ /dev/null @@ -1,287 +0,0 @@ -/* -*- 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 . - */ - -#include "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: an #EekThemeContext - * @theme: an #EekTheme - * - * 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 deleted file mode 100644 index a8498739..00000000 --- a/eek/eek-theme-context.h +++ /dev/null @@ -1,78 +0,0 @@ -/* -*- 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:eek-theme-context - * @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 deleted file mode 100644 index a3bb3607..00000000 --- a/eek/eek-theme-node.c +++ /dev/null @@ -1,1864 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; c-basic-offset: 2; -*- */ -/* - * eek-theme-node.c: style information for one node in a tree of themed objects - * - * Copyright 2008-2010 Red Hat, Inc. - * Copyright 2009 Steve Frécinaux - * Copyright 2009, 2010 Florian Müllner - * Copyright 2010 Adel Gadllah - * Copyright 2010 Giovanni Campagna - * Copyright 2010-2011 Daiki Ueno - * - * 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 . - */ - -#include "config.h" - -#include -#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; - EekColor background_gradient_end; - - EekColor foreground_color; - EekColor border_color[4]; - - int border_width[4]; - int border_radius[4]; - - GType element_type; - char *element_id; - char *element_class; - char *pseudo_class; - char *inline_style; - - CRDeclaration **properties; - int n_properties; - - /* We hold onto these separately so we can destroy them on finalize */ - CRDeclaration *inline_properties; - - guint properties_computed : 1; - guint geometry_computed : 1; - guint background_computed : 1; - guint foreground_computed : 1; -}; - -struct _EekThemeNodeClass { - GObjectClass parent_class; -}; - -G_DEFINE_TYPE (EekThemeNode, eek_theme_node, G_TYPE_OBJECT); - -#define EEK_THEME_NODE_GET_PRIVATE(obj) \ - (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EEK_TYPE_THEME_NODE, EekThemeNodePrivate)) - -static void eek_theme_node_init (EekThemeNode *node); -static void eek_theme_node_class_init (EekThemeNodeClass *klass); -static void eek_theme_node_dispose (GObject *object); -static void eek_theme_node_finalize (GObject *object); - -static const EekColor BLACK_COLOR = { 0, 0, 0, 0xff }; -static const EekColor TRANSPARENT_COLOR = { 0, 0, 0, 0 }; - -static void -eek_theme_node_init (EekThemeNode *self) -{ -} - -static void -eek_theme_node_class_init (EekThemeNodeClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->dispose = eek_theme_node_dispose; - object_class->finalize = eek_theme_node_finalize; -} - -static void -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); - node->theme = NULL; - } - - if (node->parent_node) - { - g_object_unref (node->parent_node); - node->parent_node = NULL; - } - - G_OBJECT_CLASS (eek_theme_node_parent_class)->dispose (gobject); -} - -static void -eek_theme_node_finalize (GObject *object) -{ - EekThemeNode *node = EEK_THEME_NODE (object); - - g_free (node->element_id); - g_free (node->element_class); - g_free (node->pseudo_class); - g_free (node->inline_style); - - if (node->properties) - { - g_free (node->properties); - node->properties = NULL; - node->n_properties = 0; - } - - if (node->inline_properties) - { - /* This destroys the list, not just the head of the list */ - 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); -} - -/** - * eek_theme_node_new: - * @parent_node: (allow-none): the parent node of this node - * @theme: (allow-none): a theme (stylesheet set) that overrides the - * theme inherited from the parent node - * @element_type: the type of the GObject represented by this node - * in the tree (corresponding to an element if we were theming an XML - * document. %G_TYPE_NONE means this style was created for the stage - * actor and matches a selector element name of 'stage'. - * @element_id: (allow-none): the ID to match CSS rules against - * @element_class: (allow-none): a whitespace-separated list of classes - * to match CSS rules against - * @pseudo_class: (allow-none): a whitespace-separated list of pseudo-classes - * (like 'hover' or 'visited') to match CSS rules against - * - * Creates a new #EekThemeNode. Once created, a node is immutable. Of any - * of the attributes of the node (like the @element_class) change the node - * and its child nodes must be destroyed and recreated. - * - * Return value: (transfer full): the theme node - */ -EekThemeNode * -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 - node->parent_node = NULL; - - if (theme == NULL && parent_node != NULL) - theme = eek_theme_node_get_theme (parent_node); - - if (theme != NULL) - node->theme = g_object_ref (theme); - - node->element_type = element_type; - node->element_id = g_strdup (element_id); - node->element_class = g_strdup (element_class); - node->pseudo_class = g_strdup (pseudo_class); - node->inline_style = g_strdup (inline_style); - - return node; -} - -/** - * eek_theme_node_get_parent: - * @node: a #EekThemeNode - * - * Gets the parent themed element node. - * - * Return value: (transfer none): the parent #EekThemeNode, or %NULL if this - * is the root node of the tree of theme elements. - */ -EekThemeNode * -eek_theme_node_get_parent (EekThemeNode *node) -{ - g_return_val_if_fail (EEK_IS_THEME_NODE (node), NULL); - - return node->parent_node; -} - -/** - * eek_theme_node_get_theme: - * @node: a #EekThemeNode - * - * Gets the theme stylesheet set that styles this node - * - * Return value: (transfer none): the theme stylesheet set - */ -EekTheme * -eek_theme_node_get_theme (EekThemeNode *node) -{ - g_return_val_if_fail (EEK_IS_THEME_NODE (node), NULL); - - return node->theme; -} - -GType -eek_theme_node_get_element_type (EekThemeNode *node) -{ - g_return_val_if_fail (EEK_IS_THEME_NODE (node), G_TYPE_NONE); - - return node->element_type; -} - -const char * -eek_theme_node_get_element_id (EekThemeNode *node) -{ - g_return_val_if_fail (EEK_IS_THEME_NODE (node), NULL); - - return node->element_id; -} - -const char * -eek_theme_node_get_element_class (EekThemeNode *node) -{ - g_return_val_if_fail (EEK_IS_THEME_NODE (node), NULL); - - return node->element_class; -} - -const char * -eek_theme_node_get_pseudo_class (EekThemeNode *node) -{ - g_return_val_if_fail (EEK_IS_THEME_NODE (node), NULL); - - return node->pseudo_class; -} - -static void -ensure_properties (EekThemeNode *node) -{ - if (!node->properties_computed) - { - GPtrArray *properties = NULL; - - node->properties_computed = TRUE; - - if (node->theme) - properties = _eek_theme_get_matched_properties (node->theme, node); - - if (node->inline_style) - { - CRDeclaration *cur_decl; - - if (!properties) - properties = g_ptr_array_new (); - - node->inline_properties = - _eek_theme_parse_declaration_list (node->inline_style); - for (cur_decl = node->inline_properties; - cur_decl; - cur_decl = cur_decl->next) - g_ptr_array_add (properties, cur_decl); - } - - if (properties) - { - node->n_properties = properties->len; - node->properties = (CRDeclaration **)g_ptr_array_free (properties, - FALSE); - } - } -} - -typedef enum { - VALUE_FOUND, - VALUE_NOT_FOUND, - VALUE_INHERIT -} GetFromTermResult; - -static gboolean -term_is_none (CRTerm *term) -{ - return (term->type == TERM_IDENT && - strcmp (term->content.str->stryng->str, "none") == 0); -} - -static gboolean -term_is_transparent (CRTerm *term) -{ - return (term->type == TERM_IDENT && - strcmp (term->content.str->stryng->str, "transparent") == 0); -} - -static GetFromTermResult -get_color_from_rgba_term (CRTerm *term, - EekColor *color) -{ - CRTerm *arg = term->ext_content.func_param; - CRNum *num; - double r = 0, g = 0, b = 0, a = 0; - int i; - - for (i = 0; i < 4; i++) - { - double value; - - if (arg == NULL) - return VALUE_NOT_FOUND; - - if ((i == 0 && arg->the_operator != NO_OP) || - (i > 0 && arg->the_operator != COMMA)) - return VALUE_NOT_FOUND; - - if (arg->type != TERM_NUMBER) - return VALUE_NOT_FOUND; - - num = arg->content.num; - - /* For simplicity, we convert a,r,g,b to [0,1.0] floats and then - * convert them back below. Then when we set them on a cairo content - * we convert them back to floats, and then cairo converts them - * back to integers to pass them to X, and so forth... - */ - if (i < 3) - { - if (num->type == NUM_PERCENTAGE) - value = num->val / 100; - else if (num->type == NUM_GENERIC) - value = num->val / 255; - else - return VALUE_NOT_FOUND; - } - else - { - if (num->type != NUM_GENERIC) - return VALUE_NOT_FOUND; - - value = num->val; - } - - value = CLAMP (value, 0, 1); - - switch (i) - { - case 0: - r = value; - break; - case 1: - g = value; - break; - case 2: - b = value; - break; - case 3: - a = value; - break; - } - - arg = arg->next; - } - - color->red = CLAMP(r, 0.0, 1.0); - color->green = CLAMP(g, 0.0, 1.0); - color->blue = CLAMP(b, 0.0, 1.0); - color->alpha = CLAMP(a, 0.0, 1.0); - - return VALUE_FOUND; -} - -static GetFromTermResult -get_color_from_term (EekThemeNode *node, - CRTerm *term, - EekColor *color) -{ - CRRgb rgb; - enum CRStatus status; - - /* Since libcroco doesn't know about rgba colors, it can't handle - * the transparent keyword - */ - if (term_is_transparent (term)) - { - *color = TRANSPARENT_COLOR; - return VALUE_FOUND; - } - /* rgba () colors - a CSS3 addition, are not supported by libcroco, - * but they are parsed as a "function", so we can emulate the - * functionality. - * - * libcroco < 0.6.2 has a bug where functions starting with 'r' are - * misparsed. We workaround this by pre-converting 'rgba' to 'RGBA' - * before parsing the stylesheet. Since libcroco isn't - * case-insensitive (a bug), it's fine with functions starting with - * 'R'. (In theory, we should be doing a case-insensitive compare - * everywhere, not just here, but that doesn't make much sense when - * the built-in parsing of libcroco is case-sensitive and things - * like 10PX don't work.) - */ - else if (term->type == TERM_FUNCTION && - term->content.str && - term->content.str->stryng && - term->content.str->stryng->str && - g_ascii_strcasecmp (term->content.str->stryng->str, "rgba") == 0) - { - return get_color_from_rgba_term (term, color); - } - - status = cr_rgb_set_from_term (&rgb, term); - if (status != CR_OK) - return VALUE_NOT_FOUND; - - if (rgb.inherit) - return VALUE_INHERIT; - - if (rgb.is_percentage) - cr_rgb_compute_from_percentage (&rgb); - - color->red = rgb.red / (gdouble)0xff; - color->green = rgb.green / (gdouble)0xff; - color->blue = rgb.blue / (gdouble)0xff; - color->alpha = 1.0; - - return VALUE_FOUND; -} - -/** - * eek_theme_node_lookup_color: - * @node: a #EekThemeNode - * @property_name: The name of the color property - * @inherit: if %TRUE, if a value is not found for the property on the - * node, then it will be looked up on the parent node, and then on the - * parent's parent, and so forth. Note that if the property has a - * value of 'inherit' it will be inherited even if %FALSE is passed - * in for @inherit; this only affects the default behavior for inheritance. - * @color: location to store the color that was determined. - * If the property is not found, the value in this location - * will not be changed. - * - * Generically looks up a property containing a single color value. When - * specific getters (like eek_theme_node_get_background_color()) exist, they - * should be used instead. They are cached, so more efficient, and have - * handling for shortcut properties and other details of CSS. - * - * Return value: %TRUE if the property was found in the properties for this - * theme node (or in the properties of parent nodes when inheriting.) - */ -gboolean -eek_theme_node_lookup_color (EekThemeNode *node, - const char *property_name, - gboolean inherit, - EekColor *color) -{ - int i; - - g_return_val_if_fail (EEK_IS_THEME_NODE(node), FALSE); - - ensure_properties (node); - - for (i = node->n_properties - 1; i >= 0; i--) - { - CRDeclaration *decl = node->properties[i]; - - if (strcmp (decl->property->stryng->str, property_name) == 0) - { - GetFromTermResult result = get_color_from_term (node, decl->value, color); - if (result == VALUE_FOUND) - { - return TRUE; - } - else if (result == VALUE_INHERIT) - { - if (node->parent_node) - return eek_theme_node_lookup_color (node->parent_node, property_name, inherit, color); - else - break; - } - } - } - - if (inherit && node->parent_node) - return eek_theme_node_lookup_color (node->parent_node, property_name, inherit, color); - - return FALSE; -} - -/** - * eek_theme_node_get_color: - * @node: a #EekThemeNode - * @property_name: The name of the color property - * @color: location to store the color that was determined. - * - * Generically looks up a property containing a single color value. When - * specific getters (like eek_theme_node_get_background_color()) exist, they - * should be used instead. They are cached, so more efficient, and have - * handling for shortcut properties and other details of CSS. - * - * If @property_name is not found, a warning will be logged and a - * default color returned. - * - * See also eek_theme_node_lookup_color(), which provides more options, - * and lets you handle the case where the theme does not specify the - * indicated color. - */ -void -eek_theme_node_get_color (EekThemeNode *node, - const char *property_name, - EekColor *color) -{ - if (!eek_theme_node_lookup_color (node, property_name, FALSE, color)) - { - g_warning ("Did not find color property '%s'", property_name); - memset (color, 0, sizeof (EekColor)); - } -} - -/** - * eek_theme_node_lookup_double: - * @node: a #EekThemeNode - * @property_name: The name of the numeric property - * @inherit: if %TRUE, if a value is not found for the property on the - * node, then it will be looked up on the parent node, and then on the - * parent's parent, and so forth. Note that if the property has a - * value of 'inherit' it will be inherited even if %FALSE is passed - * in for @inherit; this only affects the default behavior for inheritance. - * @value: (out): location to store the value that was determined. - * If the property is not found, the value in this location - * will not be changed. - * - * Generically looks up a property containing a single numeric value - * without units. - * - * See also eek_theme_node_get_double(), which provides a simpler API. - * - * Return value: %TRUE if the property was found in the properties for this - * theme node (or in the properties of parent nodes when inheriting.) - */ -gboolean -eek_theme_node_lookup_double (EekThemeNode *node, - const char *property_name, - gboolean inherit, - double *value) -{ - gboolean result = FALSE; - int i; - - g_return_val_if_fail (EEK_IS_THEME_NODE(node), FALSE); - - ensure_properties (node); - - for (i = node->n_properties - 1; i >= 0; i--) - { - CRDeclaration *decl = node->properties[i]; - - if (strcmp (decl->property->stryng->str, property_name) == 0) - { - CRTerm *term = decl->value; - - if (term->type != TERM_NUMBER || term->content.num->type != NUM_GENERIC) - continue; - - *value = term->content.num->val; - result = TRUE; - break; - } - } - - if (!result && inherit && node->parent_node) - result = eek_theme_node_lookup_double (node->parent_node, property_name, inherit, value); - - return result; -} - -/** - * eek_theme_node_get_double: - * @node: a #EekThemeNode - * @property_name: The name of the numeric property - * - * Generically looks up a property containing a single numeric value - * without units. - * - * See also eek_theme_node_lookup_double(), which provides more options, - * and lets you handle the case where the theme does not specify the - * indicated value. - * - * Return value: the value found. If @property_name is not - * found, a warning will be logged and 0 will be returned. - */ -gdouble -eek_theme_node_get_double (EekThemeNode *node, - const char *property_name) -{ - gdouble value; - - if (eek_theme_node_lookup_double (node, property_name, FALSE, &value)) - return value; - else - { - g_warning ("Did not find double property '%s'", property_name); - return 0.0; - } -} - -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, - gboolean use_parent_font, - gdouble *length) -{ - CRNum *num; - - enum { - ABSOLUTE, - POINTS, - FONT_RELATIVE, - } type = ABSOLUTE; - - double multiplier = 1.0; - - if (term->type != TERM_NUMBER) - { - g_warning ("Ignoring length property that isn't a number"); - return VALUE_NOT_FOUND; - } - - num = term->content.num; - - switch (num->type) - { - case NUM_LENGTH_PX: - type = ABSOLUTE; - multiplier = 1; - break; - case NUM_LENGTH_PT: - type = POINTS; - multiplier = 1; - break; - case NUM_LENGTH_IN: - type = POINTS; - multiplier = 72; - break; - case NUM_LENGTH_CM: - type = POINTS; - multiplier = 72. / 2.54; - break; - case NUM_LENGTH_MM: - type = POINTS; - multiplier = 72. / 25.4; - break; - case NUM_LENGTH_PC: - type = POINTS; - multiplier = 12. / 25.4; - break; - case NUM_LENGTH_EM: - { - type = FONT_RELATIVE; - multiplier = 1; - break; - } - case NUM_LENGTH_EX: - { - /* Doing better would require actually resolving the font description - * to a specific font, and Pango doesn't have an ex metric anyways, - * so we'd have to try and synthesize it by complicated means. - * - * The 0.5em is the CSS spec suggested thing to use when nothing - * better is available. - */ - type = FONT_RELATIVE; - multiplier = 0.5; - break; - } - - case NUM_INHERIT: - return VALUE_INHERIT; - - case NUM_AUTO: - g_warning ("'auto' not supported for lengths"); - return VALUE_NOT_FOUND; - - case NUM_GENERIC: - { - if (num->val != 0) - { - g_warning ("length values must specify a unit"); - return VALUE_NOT_FOUND; - } - else - { - type = ABSOLUTE; - multiplier = 0; - } - break; - } - - case NUM_PERCENTAGE: - g_warning ("percentage lengths not currently supported"); - return VALUE_NOT_FOUND; - - case NUM_ANGLE_DEG: - case NUM_ANGLE_RAD: - case NUM_ANGLE_GRAD: - case NUM_TIME_MS: - case NUM_TIME_S: - case NUM_FREQ_HZ: - case NUM_FREQ_KHZ: - case NUM_UNKNOWN_TYPE: - case NB_NUM_TYPE: - g_warning ("Ignoring invalid type of number of length property"); - return VALUE_NOT_FOUND; - } - - switch (type) - { - case ABSOLUTE: - *length = num->val * multiplier; - break; - case POINTS: - { - double resolution = eek_theme_context_get_resolution (node->context); - *length = num->val * multiplier * (resolution / 72.); - } - break; - case FONT_RELATIVE: - { - const PangoFontDescription *desc; - double font_size; - - if (use_parent_font) - desc = get_parent_font (node); - else - desc = eek_theme_node_get_font (node); - - font_size = (double)pango_font_description_get_size (desc) / PANGO_SCALE; - - if (pango_font_description_get_size_is_absolute (desc)) - { - *length = num->val * multiplier * font_size; - } - else - { - double resolution = eek_theme_context_get_resolution (node->context); - *length = num->val * multiplier * (resolution / 72.) * font_size; - } - } - break; - - default: - g_assert_not_reached (); - } - - return VALUE_FOUND; -} - -static GetFromTermResult -get_length_from_term_int (EekThemeNode *node, - CRTerm *term, - gboolean use_parent_font, - gint *length) -{ - double value; - GetFromTermResult result; - - result = get_length_from_term (node, term, use_parent_font, &value); - if (result == VALUE_FOUND) - *length = (int) (0.5 + value); - return result; -} - -static GetFromTermResult -get_length_internal (EekThemeNode *node, - const char *property_name, - const char *suffixed, - gdouble *length) -{ - int i; - - g_return_val_if_fail (EEK_IS_THEME_NODE(node), FALSE); - - ensure_properties (node); - - for (i = node->n_properties - 1; i >= 0; i--) - { - CRDeclaration *decl = node->properties[i]; - - if (strcmp (decl->property->stryng->str, property_name) == 0 || - (suffixed != NULL && strcmp (decl->property->stryng->str, suffixed) == 0)) - { - GetFromTermResult result = get_length_from_term (node, decl->value, FALSE, length); - if (result != VALUE_NOT_FOUND) - return result; - } - } - - return VALUE_NOT_FOUND; -} - -/** - * eek_theme_node_lookup_length: - * @node: a #EekThemeNode - * @property_name: The name of the length property - * @inherit: if %TRUE, if a value is not found for the property on the - * node, then it will be looked up on the parent node, and then on the - * parent's parent, and so forth. Note that if the property has a - * value of 'inherit' it will be inherited even if %FALSE is passed - * in for @inherit; this only affects the default behavior for inheritance. - * @length: (out): location to store the length that was determined. - * If the property is not found, the value in this location - * will not be changed. The returned length is resolved - * to pixels. - * - * Generically looks up a property containing a single length value. When - * specific getters (like eek_theme_node_get_border_width()) exist, they - * should be used instead. They are cached, so more efficient, and have - * handling for shortcut properties and other details of CSS. - * - * See also eek_theme_node_get_length(), which provides a simpler API. - * - * Return value: %TRUE if the property was found in the properties for this - * theme node (or in the properties of parent nodes when inheriting.) - */ -gboolean -eek_theme_node_lookup_length (EekThemeNode *node, - const char *property_name, - gboolean inherit, - gdouble *length) -{ - GetFromTermResult result; - - g_return_val_if_fail (EEK_IS_THEME_NODE(node), FALSE); - - result = get_length_internal (node, property_name, NULL, length); - if (result == VALUE_FOUND) - return TRUE; - else if (result == VALUE_INHERIT) - inherit = TRUE; - - if (inherit && node->parent_node && - eek_theme_node_lookup_length (node->parent_node, property_name, inherit, length)) - return TRUE; - else - return FALSE; -} - -/** - * eek_theme_node_get_length: - * @node: a #EekThemeNode - * @property_name: The name of the length property - * - * Generically looks up a property containing a single length value. When - * specific getters (like eek_theme_node_get_border_width()) exist, they - * should be used instead. They are cached, so more efficient, and have - * handling for shortcut properties and other details of CSS. - * - * Unlike eek_theme_node_get_color() and eek_theme_node_get_double(), - * this does not print a warning if the property is not found; it just - * returns 0. - * - * See also eek_theme_node_lookup_length(), which provides more options. - * - * Return value: the length, in pixels, or 0 if the property was not found. - */ -gdouble -eek_theme_node_get_length (EekThemeNode *node, - const char *property_name) -{ - gdouble length; - - if (eek_theme_node_lookup_length (node, property_name, FALSE, &length)) - return length; - else - return 0.0; -} - -static void -do_border_radius_term (EekThemeNode *node, - CRTerm *term, - gboolean topleft, - gboolean topright, - gboolean bottomright, - gboolean bottomleft) -{ - int value; - - g_return_if_fail (EEK_IS_THEME_NODE (node)); - - if (get_length_from_term_int (node, term, FALSE, &value) != VALUE_FOUND) - return; - - if (topleft) - node->border_radius[EEK_CORNER_TOPLEFT] = value; - if (topright) - node->border_radius[EEK_CORNER_TOPRIGHT] = value; - if (bottomright) - node->border_radius[EEK_CORNER_BOTTOMRIGHT] = value; - if (bottomleft) - node->border_radius[EEK_CORNER_BOTTOMLEFT] = value; -} - -static void -do_border_radius (EekThemeNode *node, - CRDeclaration *decl) -{ - const char *property_name = decl->property->stryng->str + 13; /* Skip 'border-radius' */ - - if (strcmp (property_name, "") == 0) - { - /* Slight deviation ... if we don't understand some of the terms and understand others, - * then we set the ones we understand and ignore the others instead of ignoring the - * whole thing - */ - if (decl->value == NULL) /* 0 values */ - return; - else if (decl->value->next == NULL) /* 1 value */ - { - do_border_radius_term (node, decl->value, TRUE, TRUE, TRUE, TRUE); /* all corners */ - return; - } - else if (decl->value->next->next == NULL) /* 2 values */ - { - do_border_radius_term (node, decl->value, TRUE, FALSE, TRUE, FALSE); /* topleft/bottomright */ - do_border_radius_term (node, decl->value->next, FALSE, TRUE, FALSE, TRUE); /* topright/bottomleft */ - } - else if (decl->value->next->next->next == NULL) /* 3 values */ - { - do_border_radius_term (node, decl->value, TRUE, FALSE, FALSE, FALSE); /* topleft */ - do_border_radius_term (node, decl->value->next, FALSE, TRUE, FALSE, TRUE); /* topright/bottomleft */ - do_border_radius_term (node, decl->value->next->next, FALSE, FALSE, TRUE, FALSE); /* bottomright */ - } - else if (decl->value->next->next->next->next == NULL) /* 4 values */ - { - do_border_radius_term (node, decl->value, TRUE, FALSE, FALSE, FALSE); /* topleft */ - do_border_radius_term (node, decl->value->next, FALSE, TRUE, FALSE, FALSE); /* topright */ - do_border_radius_term (node, decl->value->next->next, FALSE, FALSE, TRUE, FALSE); /* bottomright */ - do_border_radius_term (node, decl->value->next->next->next, FALSE, FALSE, FALSE, TRUE); /* bottomleft */ - } - else - { - g_warning ("Too many values for border-radius property"); - return; - } - } - else - { - if (decl->value == NULL || decl->value->next != NULL) - return; - - if (strcmp (property_name, "-topleft") == 0) - do_border_radius_term (node, decl->value, TRUE, FALSE, FALSE, FALSE); - else if (strcmp (property_name, "-topright") == 0) - do_border_radius_term (node, decl->value, FALSE, TRUE, FALSE, FALSE); - else if (strcmp (property_name, "-bottomright") == 0) - do_border_radius_term (node, decl->value, FALSE, FALSE, TRUE, FALSE); - else if (strcmp (property_name, "-bottomleft") == 0) - do_border_radius_term (node, decl->value, FALSE, FALSE, FALSE, TRUE); - } -} - -static void -do_border_property (EekThemeNode *node, - CRDeclaration *decl) -{ - const char *property_name = decl->property->stryng->str + 6; /* Skip 'border' */ - EekSide side = (EekSide)-1; - EekColor color; - gboolean color_set = FALSE; - int width = 0; /* suppress warning */ - gboolean width_set = FALSE; - int j; - - g_return_if_fail (EEK_IS_THEME_NODE (node)); - - if (g_str_has_prefix (property_name, "-radius")) - { - do_border_radius (node, decl); - return; - } - - if (g_str_has_prefix (property_name, "-left")) - { - side = EEK_SIDE_LEFT; - property_name += 5; - } - else if (g_str_has_prefix (property_name, "-right")) - { - side = EEK_SIDE_RIGHT; - property_name += 6; - } - else if (g_str_has_prefix (property_name, "-top")) - { - side = EEK_SIDE_TOP; - property_name += 4; - } - else if (g_str_has_prefix (property_name, "-bottom")) - { - side = EEK_SIDE_BOTTOM; - property_name += 7; - } - - if (strcmp (property_name, "") == 0) - { - /* Set value for width/color/style in any order */ - CRTerm *term; - - for (term = decl->value; term; term = term->next) - { - GetFromTermResult result; - - if (term->type == TERM_IDENT) - { - const char *ident = term->content.str->stryng->str; - if (strcmp (ident, "none") == 0 || strcmp (ident, "hidden") == 0) - { - width = 0; - width_set = TRUE; - continue; - } - else if (strcmp (ident, "solid") == 0) - { - /* The only thing we support */ - continue; - } - else if (strcmp (ident, "dotted") == 0 || - strcmp (ident, "dashed") == 0 || - strcmp (ident, "double") == 0 || - strcmp (ident, "groove") == 0 || - strcmp (ident, "ridge") == 0 || - strcmp (ident, "inset") == 0 || - strcmp (ident, "outset") == 0) - { - /* Treat the same as solid */ - continue; - } - - /* Presumably a color, fall through */ - } - - if (term->type == TERM_NUMBER) - { - result = get_length_from_term_int (node, term, FALSE, &width); - if (result != VALUE_NOT_FOUND) - { - width_set = result == VALUE_FOUND; - continue; - } - } - - result = get_color_from_term (node, term, &color); - if (result != VALUE_NOT_FOUND) - { - color_set = result == VALUE_FOUND; - continue; - } - } - - } - else if (strcmp (property_name, "-color") == 0) - { - if (decl->value == NULL || decl->value->next != NULL) - return; - - if (get_color_from_term (node, decl->value, &color) == VALUE_FOUND) - /* Ignore inherit */ - color_set = TRUE; - } - else if (strcmp (property_name, "-width") == 0) - { - if (decl->value == NULL || decl->value->next != NULL) - return; - - if (get_length_from_term_int (node, decl->value, FALSE, &width) == VALUE_FOUND) - /* Ignore inherit */ - width_set = TRUE; - } - - if (side == (EekSide)-1) - { - for (j = 0; j < 4; j++) - { - if (color_set) - node->border_color[j] = color; - if (width_set) - node->border_width[j] = width; - } - } - else - { - if (color_set) - node->border_color[side] = color; - if (width_set) - node->border_width[side] = width; - } -} - -void -_eek_theme_node_ensure_geometry (EekThemeNode *node) -{ - int i, j; - - g_return_if_fail (EEK_IS_THEME_NODE (node)); - - if (node->geometry_computed) - return; - - node->geometry_computed = TRUE; - - ensure_properties (node); - - for (j = 0; j < 4; j++) - { - node->border_width[j] = 0; - node->border_color[j] = TRANSPARENT_COLOR; - } - - for (i = 0; i < node->n_properties; i++) - { - CRDeclaration *decl = node->properties[i]; - const char *property_name = decl->property->stryng->str; - - if (g_str_has_prefix (property_name, "border")) - do_border_property (node, decl); - } -} - -int -eek_theme_node_get_border_width (EekThemeNode *node, - EekSide side) -{ - g_return_val_if_fail (EEK_IS_THEME_NODE (node), 0.); - g_return_val_if_fail (side >= EEK_SIDE_TOP && side <= EEK_SIDE_LEFT, 0.); - - _eek_theme_node_ensure_geometry (node); - - return node->border_width[side]; -} - -int -eek_theme_node_get_border_radius (EekThemeNode *node, - EekCorner corner) -{ - g_return_val_if_fail (EEK_IS_THEME_NODE (node), 0.); - g_return_val_if_fail (corner >= EEK_CORNER_TOPLEFT && corner <= EEK_CORNER_BOTTOMLEFT, 0.); - - _eek_theme_node_ensure_geometry (node); - - return node->border_radius[corner]; -} - -static GetFromTermResult -get_background_color_from_term (EekThemeNode *node, - CRTerm *term, - EekColor *color) -{ - GetFromTermResult result = get_color_from_term (node, term, color); - if (result == VALUE_NOT_FOUND) - { - if (term_is_transparent (term)) - { - *color = TRANSPARENT_COLOR; - return VALUE_FOUND; - } - } - - return result; -} - -void -_eek_theme_node_ensure_background (EekThemeNode *node) -{ - int i; - - if (node->background_computed) - return; - - node->background_computed = TRUE; - node->background_color = TRANSPARENT_COLOR; - node->background_gradient_type = EEK_GRADIENT_NONE; - - ensure_properties (node); - - for (i = 0; i < node->n_properties; i++) - { - CRDeclaration *decl = node->properties[i]; - const char *property_name = decl->property->stryng->str; - - if (g_str_has_prefix (property_name, "background")) - property_name += 10; - else - continue; - - if (strcmp (property_name, "") == 0) - { - /* We're very liberal here ... if we recognize any term in the expression we take it, and - * we ignore the rest. The actual specification is: - * - * background: [<'background-color'> || <'background-image'> || <'background-repeat'> || <'background-attachment'> || <'background-position'>] | inherit - */ - - CRTerm *term; - /* background: property sets all terms to specified or default values */ - node->background_color = TRANSPARENT_COLOR; - - for (term = decl->value; term; term = term->next) - { - GetFromTermResult result = get_background_color_from_term (node, term, &node->background_color); - if (result == VALUE_FOUND) - { - /* color stored in node->background_color */ - } - else if (result == VALUE_INHERIT) - { - if (node->parent_node) - { - eek_theme_node_get_background_color (node->parent_node, &node->background_color); - } - } - else if (term_is_none (term)) - { - /* leave node->background_color as transparent */ - } - } - } - else if (strcmp (property_name, "-color") == 0) - { - GetFromTermResult result; - - if (decl->value == NULL || decl->value->next != NULL) - continue; - - result = get_background_color_from_term (node, decl->value, &node->background_color); - if (result == VALUE_FOUND) - { - /* color stored in node->background_color */ - } - else if (result == VALUE_INHERIT) - { - if (node->parent_node) - eek_theme_node_get_background_color (node->parent_node, &node->background_color); - } - } - else if (strcmp (property_name, "-gradient-direction") == 0) - { - CRTerm *term = decl->value; - if (strcmp (term->content.str->stryng->str, "vertical") == 0) - { - node->background_gradient_type = EEK_GRADIENT_VERTICAL; - } - else if (strcmp (term->content.str->stryng->str, "horizontal") == 0) - { - node->background_gradient_type = EEK_GRADIENT_HORIZONTAL; - } - else if (strcmp (term->content.str->stryng->str, "radial") == 0) - { - node->background_gradient_type = EEK_GRADIENT_RADIAL; - } - else if (strcmp (term->content.str->stryng->str, "none") == 0) - { - node->background_gradient_type = EEK_GRADIENT_NONE; - } - else - { - g_warning ("Unrecognized background-gradient-direction \"%s\"", - term->content.str->stryng->str); - } - } - else if (strcmp (property_name, "-gradient-start") == 0) - { - get_color_from_term (node, decl->value, &node->background_color); - } - else if (strcmp (property_name, "-gradient-end") == 0) - { - get_color_from_term (node, decl->value, &node->background_gradient_end); - } - } -} - -void -eek_theme_node_get_background_color (EekThemeNode *node, - EekColor *color) -{ - g_return_if_fail (EEK_IS_THEME_NODE (node)); - - _eek_theme_node_ensure_background (node); - - *color = node->background_color; -} - -void -eek_theme_node_get_border_color (EekThemeNode *node, - EekSide side, - EekColor *color) -{ - g_return_if_fail (EEK_IS_THEME_NODE (node)); - g_return_if_fail (side >= EEK_SIDE_TOP && side <= EEK_SIDE_LEFT); - - _eek_theme_node_ensure_geometry (node); - - *color = node->border_color[side]; -} - -void -eek_theme_node_get_foreground_color (EekThemeNode *node, - EekColor *color) -{ - g_assert (EEK_IS_THEME_NODE (node)); - - if (!node->foreground_computed) - { - int i; - - node->foreground_computed = TRUE; - - ensure_properties (node); - - for (i = node->n_properties - 1; i >= 0; i--) - { - CRDeclaration *decl = node->properties[i]; - - if (strcmp (decl->property->stryng->str, "color") == 0) - { - GetFromTermResult result = get_color_from_term (node, decl->value, &node->foreground_color); - if (result == VALUE_FOUND) - goto out; - else if (result == VALUE_INHERIT) - break; - } - } - - if (node->parent_node) - eek_theme_node_get_foreground_color (node->parent_node, &node->foreground_color); - else - node->foreground_color = BLACK_COLOR; /* default to black */ - } - - out: - *color = node->foreground_color; -} - -void -eek_theme_node_get_background_gradient (EekThemeNode *node, - EekGradientType *type, - EekColor *start, - EekColor *end) -{ - g_assert (EEK_IS_THEME_NODE (node)); - - _eek_theme_node_ensure_background (node); - - *type = node->background_gradient_type; - if (*type != EEK_GRADIENT_NONE) - { - *start = node->background_color; - *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 deleted file mode 100644 index 8662ccfd..00000000 --- a/eek/eek-theme-node.h +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (C) 2010-2011 Daiki Ueno - * Copyright (C) 2010-2011 Red Hat, Inc. - * - * This library 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 of - * the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301 USA - */ -#ifndef __EEK_THEME_NODE_H__ -#define __EEK_THEME_NODE_H__ - -#include -#include "eek-types.h" - -G_BEGIN_DECLS - -/** - * SECTION:eek-theme-node - * @short_description: style information for one node in a tree of - * themed objects - * - * The #EekThemeNode class represents the CSS style information (the - * set of CSS properties) for one node in a tree of themed objects. In - * typical usage, it represents the style information for a single - * #EekElement. A #EekThemeNode is immutable: attributes such as the - * CSS classes for the node are passed in at construction. If the - * attributes of the node or any parent node change, the node should - * be discarded and a new node created. #EekThemeNode has generic - * accessors to look up properties by name and specific accessors for - * standard CSS properties that add caching and handling of various - * details of the CSS specification. #EekThemeNode also has - * convenience functions to help in implementing a #EekElement with - * borders and padding. - */ - -typedef enum { - EEK_SIDE_TOP, - EEK_SIDE_RIGHT, - EEK_SIDE_BOTTOM, - EEK_SIDE_LEFT -} EekSide; - -typedef enum { - EEK_CORNER_TOPLEFT, - EEK_CORNER_TOPRIGHT, - EEK_CORNER_BOTTOMRIGHT, - EEK_CORNER_BOTTOMLEFT -} EekCorner; - -#define EEK_TYPE_THEME_NODE (eek_theme_node_get_type()) -#define EEK_THEME_NODE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EEK_TYPE_THEME_NODE, EekThemeNode)) -#define EEK_THEME_NODE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EEK_TYPE_THEME_NODE, EekThemeNodeClass)) -#define EEK_IS_THEME_NODE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EEK_TYPE_THEME_NODE)) -#define EEK_IS_THEME_NODE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EEK_TYPE_THEME_NODE)) -#define EEK_THEME_NODE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EEK_TYPE_THEME_NODE, EekThemeNodeClass)) - -typedef struct _EekThemeNodeClass EekThemeNodeClass; -typedef struct _EekThemeNodePrivate EekThemeNodePrivate; - -GType eek_theme_node_get_type - (void) G_GNUC_CONST; - -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, - const char *element_class, - const char *pseudo_class, - const char *inline_style); - -EekThemeNode *eek_theme_node_get_parent - (EekThemeNode *node); - -EekTheme *eek_theme_node_get_theme - (EekThemeNode *node); - -GType eek_theme_node_get_element_type - (EekThemeNode *node); -const char *eek_theme_node_get_element_id - (EekThemeNode *node); -const char *eek_theme_node_get_element_class - (EekThemeNode *node); -const char *eek_theme_node_get_pseudo_class - (EekThemeNode *node); - -/* Generic getters ... these are not cached so are less efficient. The other - * reason for adding the more specific version is that we can handle the - * details of the actual CSS rules, which can be complicated, especially - * for fonts - */ -void eek_theme_node_get_color - (EekThemeNode *node, - const char *property_name, - EekColor *color); - -/* Specific getters for particular properties: cached - */ -void eek_theme_node_get_background_color - (EekThemeNode *node, - EekColor *color); -void eek_theme_node_get_foreground_color - (EekThemeNode *node, - EekColor *color); -void eek_theme_node_get_background_gradient - (EekThemeNode *node, - EekGradientType *type, - EekColor *start, - EekColor *end); -int eek_theme_node_get_border_width - (EekThemeNode *node, - EekSide side); -int eek_theme_node_get_border_radius - (EekThemeNode *node, - EekCorner corner); -void eek_theme_node_get_border_color - (EekThemeNode *node, - 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-private.h b/eek/eek-theme-private.h deleted file mode 100644 index ea17a747..00000000 --- a/eek/eek-theme-private.h +++ /dev/null @@ -1,22 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ -#ifndef __EEK_THEME_PRIVATE_H__ -#define __EEK_THEME_PRIVATE_H__ - -#include -#include "eek-theme.h" - -G_BEGIN_DECLS - -GPtrArray *_eek_theme_get_matched_properties (EekTheme *theme, - EekThemeNode *node); - -/* Resolve an URL from the stylesheet to a filename */ -char *_eek_theme_resolve_url (EekTheme *theme, - CRStyleSheet *base_stylesheet, - const char *url); - -CRDeclaration *_eek_theme_parse_declaration_list (const char *str); - -G_END_DECLS - -#endif /* __EEK_THEME_PRIVATE_H__ */ diff --git a/eek/eek-theme.c b/eek/eek-theme.c deleted file mode 100644 index 91d91783..00000000 --- a/eek/eek-theme.c +++ /dev/null @@ -1,1095 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/* This file started as a cut-and-paste of cr-sel-eng.c from libcroco. - * - * In moving it to hippo-canvas: - * - Reformatted and otherwise edited to match our coding style - * - Switched from handling xmlNode to handling HippoStyle - * - Simplified by removing things that we don't need or that don't - * make sense in our context. - * - The code to get a list of matching properties works quite differently; - * we order things in priority order, but we don't actually try to - * coalesce properties with the same name. - * - * In moving it to GNOME Shell: - * - Renamed again to StTheme - * - Reformatted to match the gnome-shell coding style - * - Removed notion of "theme engine" from hippo-canvas - * - pseudo-class matching changed from link enum to strings - * - Some code simplification - * - * In moving it to libeek: - * - Renamed again to EekTheme - */ - -/* - * This file is part of The Croco Library - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of version 2.1 of the GNU Lesser General Public - * License as published by the Free Software Foundation. - * - * 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 Lesser - * General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA - * - * Copyright (C) 2003-2004 Dodji Seketeli. All Rights Reserved. - */ - -#define G_LOG_DOMAIN "eek-theme" - -#include -#include - -#include -#include - -#include "eek-theme.h" -#include "eek-theme-node.h" - -static GObject *eek_theme_constructor (GType type, - guint n_construct_properties, - GObjectConstructParam *construct_properties); - -static void eek_theme_finalize (GObject *object); -static void eek_theme_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec); -static void eek_theme_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec); -static char *eek_theme_resolve_url (EekTheme *theme, - CRStyleSheet *base_stylesheet, - const char *url); - -struct _EekTheme -{ - GObject parent; - - char *application_stylesheet; - char *default_stylesheet; - char *theme_stylesheet; - GSList *custom_stylesheets; - - GHashTable *stylesheets_by_filename; - GHashTable *filenames_by_stylesheet; - - CRCascade *cascade; -}; - -struct _EekThemeClass -{ - GObjectClass parent_class; -}; - -enum -{ - PROP_0, - PROP_APPLICATION_STYLESHEET, - PROP_THEME_STYLESHEET, - PROP_DEFAULT_STYLESHEET -}; - -G_DEFINE_TYPE (EekTheme, eek_theme, G_TYPE_OBJECT); - -/* Quick strcmp. Test only for == 0 or != 0, not < 0 or > 0. */ -#define strqcmp(str,lit,lit_len) \ - (strlen (str) != (lit_len) || memcmp (str, lit, lit_len)) - -static void -eek_theme_init (EekTheme *theme) -{ - theme->stylesheets_by_filename = g_hash_table_new_full (g_str_hash, g_str_equal, - (GDestroyNotify)g_free, (GDestroyNotify)cr_stylesheet_unref); - theme->filenames_by_stylesheet = g_hash_table_new (g_direct_hash, g_direct_equal); -} - -static void -eek_theme_class_init (EekThemeClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->constructor = eek_theme_constructor; - object_class->finalize = eek_theme_finalize; - object_class->set_property = eek_theme_set_property; - object_class->get_property = eek_theme_get_property; - - /** - * EekTheme:application-stylesheet: - * - * The highest priority stylesheet, representing application-specific - * styling; this is associated with the CSS "author" stylesheet. - */ - g_object_class_install_property (object_class, - PROP_APPLICATION_STYLESHEET, - g_param_spec_string ("application-stylesheet", - "Application Stylesheet", - "Stylesheet with application-specific styling", - NULL, - G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); - - /** - * EekTheme:theme-stylesheet: - * - * The second priority stylesheet, representing theme-specific styling; - * this is associated with the CSS "user" stylesheet. - */ - g_object_class_install_property (object_class, - PROP_THEME_STYLESHEET, - g_param_spec_string ("theme-stylesheet", - "Theme Stylesheet", - "Stylesheet with theme-specific styling", - NULL, - G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); - - /** - * EekTheme:default-stylesheet: - * - * The lowest priority stylesheet, representing global default - * styling; this is associated with the CSS "user agent" stylesheet. - */ - g_object_class_install_property (object_class, - PROP_DEFAULT_STYLESHEET, - g_param_spec_string ("default-stylesheet", - "Default Stylesheet", - "Stylesheet with global default styling", - NULL, - G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); - -} - -static CRStyleSheet * -parse_stylesheet (const char *filename, - GError **error) -{ - enum CRStatus status; - CRStyleSheet *stylesheet; - g_autoptr (GFileInputStream) stream = NULL; - g_autoptr (GFileInfo) info = NULL; - g_autoptr (GFile) file = NULL; - g_autofree guchar* contents = NULL; - goffset size; - gsize out = 0; - GError *err = NULL; - - if (filename == NULL) - return NULL; - - g_debug ("Parsing %s", filename); - if (g_strcmp0 (filename, "resource://") > 0) { - file = g_file_new_for_uri (filename); - stream = g_file_read (file, NULL, &err); - if (!stream) { - g_warning ("Failed to open %s: %s", filename, err->message); - g_clear_error (&err); - return NULL; - } - - info = g_file_input_stream_query_info (stream, - G_FILE_ATTRIBUTE_STANDARD_SIZE, - NULL, - &err); - - if (!info) { - g_warning ("Failed to stat %s: %s", filename, err->message); - g_clear_error (&err); - return NULL; - } - - size = g_file_info_get_size (info); - contents = g_malloc0 (size); - if (!g_input_stream_read_all (G_INPUT_STREAM (stream), - contents, - size, - &out, - NULL, - &err)) { - g_warning ("Failed to read %s: %s", filename, err->message); - g_clear_error (&err); - return NULL; - } - status = cr_om_parser_simply_parse_buf (contents, - size, - CR_UTF_8, - &stylesheet); - } else { - status = cr_om_parser_simply_parse_file ((const guchar *) filename, - CR_UTF_8, - &stylesheet); - } - - if (status != CR_OK) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Error parsing stylesheet '%s'; errcode:%d", filename, status); - return NULL; - } - - return stylesheet; -} - -CRDeclaration * -_eek_theme_parse_declaration_list (const char *str) -{ - return cr_declaration_parse_list_from_buf ((const guchar *)str, - CR_UTF_8); -} - -/* Just g_warning for now until we have something nicer to do */ -static CRStyleSheet * -parse_stylesheet_nofail (const char *filename) -{ - GError *error = NULL; - CRStyleSheet *result; - - result = parse_stylesheet (filename, &error); - if (error) - { - g_warning ("%s", error->message); - g_clear_error (&error); - } - return result; -} - -static void -insert_stylesheet (EekTheme *theme, - const char *filename, - CRStyleSheet *stylesheet) -{ - char *filename_copy; - - if (stylesheet == NULL) - return; - - filename_copy = g_strdup(filename); - cr_stylesheet_ref (stylesheet); - - g_hash_table_insert (theme->stylesheets_by_filename, filename_copy, stylesheet); - g_hash_table_insert (theme->filenames_by_stylesheet, stylesheet, filename_copy); -} - -gboolean -eek_theme_load_stylesheet (EekTheme *theme, - const char *path, - GError **error) -{ - CRStyleSheet *stylesheet; - - stylesheet = parse_stylesheet (path, error); - if (!stylesheet) - return FALSE; - - insert_stylesheet (theme, path, stylesheet); - theme->custom_stylesheets = g_slist_prepend (theme->custom_stylesheets, stylesheet); - - return TRUE; -} - -void -eek_theme_unload_stylesheet (EekTheme *theme, - const char *path) -{ - CRStyleSheet *stylesheet; - - stylesheet = g_hash_table_lookup (theme->stylesheets_by_filename, path); - if (!stylesheet) - return; - - if (!g_slist_find (theme->custom_stylesheets, stylesheet)) - return; - - theme->custom_stylesheets = g_slist_remove (theme->custom_stylesheets, stylesheet); - g_hash_table_remove (theme->stylesheets_by_filename, path); - g_hash_table_remove (theme->filenames_by_stylesheet, stylesheet); - cr_stylesheet_unref (stylesheet); -} - -static GObject * -eek_theme_constructor (GType type, - guint n_construct_properties, - GObjectConstructParam *construct_properties) -{ - GObject *object; - EekTheme *theme; - CRStyleSheet *application_stylesheet; - CRStyleSheet *theme_stylesheet; - CRStyleSheet *default_stylesheet; - - object = (*G_OBJECT_CLASS (eek_theme_parent_class)->constructor) (type, - n_construct_properties, - construct_properties); - theme = EEK_THEME (object); - - application_stylesheet = parse_stylesheet_nofail (theme->application_stylesheet); - theme_stylesheet = parse_stylesheet_nofail (theme->theme_stylesheet); - default_stylesheet = parse_stylesheet_nofail (theme->default_stylesheet); - - theme->cascade = cr_cascade_new (application_stylesheet, - theme_stylesheet, - default_stylesheet); - - if (theme->cascade == NULL) - g_error ("Out of memory when creating cascade object"); - - insert_stylesheet (theme, theme->application_stylesheet, application_stylesheet); - insert_stylesheet (theme, theme->theme_stylesheet, theme_stylesheet); - insert_stylesheet (theme, theme->default_stylesheet, default_stylesheet); - - return object; -} - -static void -eek_theme_finalize (GObject * object) -{ - EekTheme *theme = EEK_THEME (object); - - g_slist_foreach (theme->custom_stylesheets, (GFunc) cr_stylesheet_unref, NULL); - g_slist_free (theme->custom_stylesheets); - theme->custom_stylesheets = NULL; - - g_hash_table_destroy (theme->stylesheets_by_filename); - g_hash_table_destroy (theme->filenames_by_stylesheet); - - g_free (theme->application_stylesheet); - g_free (theme->theme_stylesheet); - g_free (theme->default_stylesheet); - - if (theme->cascade) - { - cr_cascade_unref (theme->cascade); - theme->cascade = NULL; - } - - G_OBJECT_CLASS (eek_theme_parent_class)->finalize (object); -} - -static void -eek_theme_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - EekTheme *theme = EEK_THEME (object); - - switch (prop_id) - { - case PROP_APPLICATION_STYLESHEET: - { - const char *path = g_value_get_string (value); - - if (path != theme->application_stylesheet) - { - g_free (theme->application_stylesheet); - theme->application_stylesheet = g_strdup (path); - } - - break; - } - case PROP_THEME_STYLESHEET: - { - const char *path = g_value_get_string (value); - - if (path != theme->theme_stylesheet) - { - g_free (theme->theme_stylesheet); - theme->theme_stylesheet = g_strdup (path); - } - - break; - } - case PROP_DEFAULT_STYLESHEET: - { - const char *path = g_value_get_string (value); - - if (path != theme->default_stylesheet) - { - g_free (theme->default_stylesheet); - theme->default_stylesheet = g_strdup (path); - } - - break; - } - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -eek_theme_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - EekTheme *theme = EEK_THEME (object); - - switch (prop_id) - { - case PROP_APPLICATION_STYLESHEET: - g_value_set_string (value, theme->application_stylesheet); - break; - case PROP_THEME_STYLESHEET: - g_value_set_string (value, theme->theme_stylesheet); - break; - case PROP_DEFAULT_STYLESHEET: - g_value_set_string (value, theme->default_stylesheet); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -/** - * eek_theme_new: - * @application_stylesheet: (allow-none): The highest priority stylesheet, representing application-specific - * styling; this is associated with the CSS "author" stylesheet, may be %NULL - * @theme_stylesheet: (allow-none): The second priority stylesheet, representing theme-specific styling ; - * this is associated with the CSS "user" stylesheet, may be %NULL - * @default_stylesheet: (allow-none): The lowest priority stylesheet, representing global default styling; - * this is associated with the CSS "user agent" stylesheet, may be %NULL - * - * Return value: the newly created theme object - **/ -EekTheme * -eek_theme_new (const char *application_stylesheet, - const char *theme_stylesheet, - const char *default_stylesheet) -{ - EekTheme *theme = g_object_new (EEK_TYPE_THEME, - "application-stylesheet", application_stylesheet, - "theme-stylesheet", theme_stylesheet, - "default-stylesheet", default_stylesheet, - NULL); - - return theme; -} - -static gboolean -string_in_list (GString *stryng, - const char *list) -{ - const char *cur; - - for (cur = list; *cur;) - { - while (*cur && cr_utils_is_white_space (*cur)) - cur++; - - if (strncmp (cur, stryng->str, stryng->len) == 0) - { - cur += stryng->len; - if ((!*cur) || cr_utils_is_white_space (*cur)) - return TRUE; - } - - /* skip to next whitespace character */ - while (*cur && !cr_utils_is_white_space (*cur)) - cur++; - } - - return FALSE; -} - -static gboolean -pseudo_class_add_sel_matches_style (EekTheme *a_this, - CRAdditionalSel *a_add_sel, - EekThemeNode *a_node) -{ - const char *node_pseudo_class; - - g_return_val_if_fail (a_this - && a_add_sel - && a_add_sel->content.pseudo - && a_add_sel->content.pseudo->name - && a_add_sel->content.pseudo->name->stryng - && a_add_sel->content.pseudo->name->stryng->str - && a_node, FALSE); - - node_pseudo_class = eek_theme_node_get_pseudo_class (a_node); - - if (node_pseudo_class == NULL) - return FALSE; - - return string_in_list (a_add_sel->content.pseudo->name->stryng, node_pseudo_class); -} - -/** - *@param a_add_sel the class additional selector to consider. - *@param a_node the style node to consider. - *@return TRUE if the class additional selector matches - *the style node given in argument, FALSE otherwise. - */ -static gboolean -class_add_sel_matches_style (CRAdditionalSel *a_add_sel, - EekThemeNode *a_node) -{ - const char *element_class; - - g_return_val_if_fail (a_add_sel - && a_add_sel->type == CLASS_ADD_SELECTOR - && a_add_sel->content.class_name - && a_add_sel->content.class_name->stryng - && a_add_sel->content.class_name->stryng->str - && a_node, FALSE); - - element_class = eek_theme_node_get_element_class (a_node); - if (element_class == NULL) - return FALSE; - - return string_in_list (a_add_sel->content.class_name->stryng, element_class); -} - -/** - *@return TRUE if the additional attribute selector matches - *the current style node given in argument, FALSE otherwise. - *@param a_add_sel the additional attribute selector to consider. - *@param a_node the style node to consider. - */ -static gboolean -id_add_sel_matches_style (CRAdditionalSel *a_add_sel, - EekThemeNode *a_node) -{ - gboolean result = FALSE; - const char *id; - - g_return_val_if_fail (a_add_sel - && a_add_sel->type == ID_ADD_SELECTOR - && a_add_sel->content.id_name - && a_add_sel->content.id_name->stryng - && a_add_sel->content.id_name->stryng->str - && a_node, FALSE); - g_return_val_if_fail (a_add_sel - && a_add_sel->type == ID_ADD_SELECTOR - && a_node, FALSE); - - id = eek_theme_node_get_element_id (a_node); - - if (id != NULL) - { - if (!strqcmp (id, a_add_sel->content.id_name->stryng->str, - a_add_sel->content.id_name->stryng->len)) - { - result = TRUE; - } - } - - return result; -} - -/** - *additional_selector_matches_style: - *Evaluates if a given additional selector matches an style node. - *@param a_add_sel the additional selector to consider. - *@param a_node the style node to consider. - *@return TRUE is a_add_sel matches a_node, FALSE otherwise. - */ -static gboolean -additional_selector_matches_style (EekTheme *a_this, - CRAdditionalSel *a_add_sel, - EekThemeNode *a_node) -{ - CRAdditionalSel *cur_add_sel = NULL; - - g_return_val_if_fail (a_add_sel, FALSE); - - for (cur_add_sel = a_add_sel; cur_add_sel; cur_add_sel = cur_add_sel->next) - { - switch (cur_add_sel->type) - { - case NO_ADD_SELECTOR: - return FALSE; - case CLASS_ADD_SELECTOR: - if (!class_add_sel_matches_style (cur_add_sel, a_node)) - return FALSE; - break; - case ID_ADD_SELECTOR: - if (!id_add_sel_matches_style (cur_add_sel, a_node)) - return FALSE; - break; - case ATTRIBUTE_ADD_SELECTOR: - g_warning ("Attribute selectors not supported"); - return FALSE; - case PSEUDO_CLASS_ADD_SELECTOR: - if (!pseudo_class_add_sel_matches_style (a_this, cur_add_sel, a_node)) - return FALSE; - break; - } - } - - return TRUE; -} - -static gboolean -element_name_matches_type (const char *element_name, - GType element_type) -{ - if (element_type == G_TYPE_NONE) - { - return strcmp (element_name, "stage") == 0; - } - else - { - GType match_type = g_type_from_name (element_name); - if (match_type == G_TYPE_INVALID) - return FALSE; - - return g_type_is_a (element_type, match_type); - } -} - -/** - *Evaluate a selector (a simple selectors list) and says - *if it matches the style node given in parameter. - *The algorithm used here is the following: - *Walk the combinator separated list of simple selectors backward, starting - *from the end of the list. For each simple selector, looks if - *if matches the current style. - * - *@param a_this the selection engine. - *@param a_sel the simple selection list. - *@param a_node the style node. - *@param a_result out parameter. Set to true if the - *selector matches the style node, FALSE otherwise. - *@param a_recurse if set to TRUE, the function will walk to - *the next simple selector (after the evaluation of the current one) - *and recursively evaluate it. Must be usually set to TRUE unless you - *know what you are doing. - */ -static enum CRStatus -sel_matches_style_real (EekTheme *a_this, - CRSimpleSel *a_sel, - EekThemeNode *a_node, - gboolean *a_result, - gboolean a_eval_sel_list_from_end, - gboolean a_recurse) -{ - CRSimpleSel *cur_sel = NULL; - EekThemeNode *cur_node = NULL; - GType cur_type; - - *a_result = FALSE; - - if (a_eval_sel_list_from_end) - { - /*go and get the last simple selector of the list */ - for (cur_sel = a_sel; cur_sel && cur_sel->next; cur_sel = cur_sel->next) - ; - } - else - { - cur_sel = a_sel; - } - - cur_node = a_node; - cur_type = eek_theme_node_get_element_type (cur_node); - - while (cur_sel) - { - if (((cur_sel->type_mask & TYPE_SELECTOR) - && (cur_sel->name - && cur_sel->name->stryng - && cur_sel->name->stryng->str) - && - (element_name_matches_type (cur_sel->name->stryng->str, cur_type))) - || (cur_sel->type_mask & UNIVERSAL_SELECTOR)) - { - /* - *this simple selector - *matches the current style node - *Let's see if the preceding - *simple selectors also match - *their style node counterpart. - */ - if (cur_sel->add_sel) - { - if (additional_selector_matches_style (a_this, cur_sel->add_sel, cur_node)) - goto walk_a_step_in_expr; - else - goto done; - } - else - goto walk_a_step_in_expr; - } - if (!(cur_sel->type_mask & TYPE_SELECTOR) - && !(cur_sel->type_mask & UNIVERSAL_SELECTOR)) - { - if (!cur_sel->add_sel) - goto done; - if (additional_selector_matches_style (a_this, cur_sel->add_sel, cur_node)) - goto walk_a_step_in_expr; - else - goto done; - } - else - { - goto done; - } - - walk_a_step_in_expr: - if (a_recurse == FALSE) - { - *a_result = TRUE; - goto done; - } - - /* - *here, depending on the combinator of cur_sel - *choose the axis of the element tree traversal - *and walk one step in the element tree. - */ - if (!cur_sel->prev) - break; - - switch (cur_sel->combinator) - { - case NO_COMBINATOR: - break; - - case COMB_WS: /*descendant selector */ - { - EekThemeNode *n = NULL; - - /* - *walk the element tree upward looking for a parent - *style that matches the preceding selector. - */ - for (n = eek_theme_node_get_parent (a_node); n; n = eek_theme_node_get_parent (n)) - { - enum CRStatus status; - gboolean matches = FALSE; - - status = sel_matches_style_real (a_this, cur_sel->prev, n, &matches, FALSE, TRUE); - - if (status != CR_OK) - goto done; - - if (matches) - { - cur_node = n; - cur_type = eek_theme_node_get_element_type (cur_node); - break; - } - } - - if (!n) - { - /* - *didn't find any ancestor that matches - *the previous simple selector. - */ - goto done; - } - /* - *in this case, the preceding simple sel - *will have been interpreted twice, which - *is a cpu and mem waste ... I need to find - *another way to do this. Anyway, this is - *my first attempt to write this function and - *I am a bit clueless. - */ - break; - } - - case COMB_PLUS: - g_warning ("+ combinators are not supported"); - goto done; - - case COMB_GT: - cur_node = eek_theme_node_get_parent (cur_node); - if (!cur_node) - goto done; - cur_type = eek_theme_node_get_element_type (cur_node); - break; - - default: - goto done; - } - - cur_sel = cur_sel->prev; - } - - /* - *if we reached this point, it means the selector matches - *the style node. - */ - *a_result = TRUE; - -done: - return CR_OK; -} - -static void -add_matched_properties (EekTheme *a_this, - CRStyleSheet *a_nodesheet, - EekThemeNode *a_node, - GPtrArray *props) -{ - CRStatement *cur_stmt = NULL; - CRSelector *sel_list = NULL; - CRSelector *cur_sel = NULL; - gboolean matches = FALSE; - enum CRStatus status = CR_OK; - - /* - *walk through the list of statements and, - *get the selectors list inside the statements that - *contain some, and try to match our style node in these - *selectors lists. - */ - for (cur_stmt = a_nodesheet->statements; cur_stmt; cur_stmt = cur_stmt->next) - { - /* - *initialyze the selector list in which we will - *really perform the search. - */ - sel_list = NULL; - - /* - *get the the damn selector list in - *which we have to look - */ - switch (cur_stmt->type) - { - case RULESET_STMT: - if (cur_stmt->kind.ruleset && cur_stmt->kind.ruleset->sel_list) - { - sel_list = cur_stmt->kind.ruleset->sel_list; - } - break; - - case AT_MEDIA_RULE_STMT: - if (cur_stmt->kind.media_rule - && cur_stmt->kind.media_rule->rulesets - && cur_stmt->kind.media_rule->rulesets->kind.ruleset - && cur_stmt->kind.media_rule->rulesets->kind.ruleset->sel_list) - { - sel_list = cur_stmt->kind.media_rule->rulesets->kind.ruleset->sel_list; - } - break; - - case AT_IMPORT_RULE_STMT: - { - CRAtImportRule *import_rule = cur_stmt->kind.import_rule; - - if (import_rule->sheet == NULL) - { - char *filename = NULL; - - if (import_rule->url->stryng && import_rule->url->stryng->str) - filename = eek_theme_resolve_url (a_this, - a_nodesheet, - import_rule->url->stryng->str); - - if (filename) - import_rule->sheet = parse_stylesheet (filename, NULL); - - if (import_rule->sheet) - { - insert_stylesheet (a_this, filename, import_rule->sheet); - /* refcount of stylesheets starts off at zero, so we don't need to unref! */ - } - else - { - /* Set a marker to avoid repeatedly trying to parse a non-existent or - * broken stylesheet - */ - import_rule->sheet = (CRStyleSheet *) - 1; - } - - if (filename) - g_free (filename); - } - - if (import_rule->sheet != (CRStyleSheet *) - 1) - { - add_matched_properties (a_this, import_rule->sheet, - a_node, props); - } - } - break; - default: - break; - } - - if (!sel_list) - continue; - - /* - *now, we have a comma separated selector list to look in. - *let's walk it and try to match the style node - *on each item of the list. - */ - for (cur_sel = sel_list; cur_sel; cur_sel = cur_sel->next) - { - if (!cur_sel->simple_sel) - continue; - - status = sel_matches_style_real (a_this, cur_sel->simple_sel, a_node, &matches, TRUE, TRUE); - - if (status == CR_OK && matches) - { - CRDeclaration *cur_decl = NULL; - - /* In order to sort the matching properties, we need to compute the - * specificity of the selector that actually matched this - * element. In a non-thread-safe fashion, we store it in the - * ruleset. (Fixing this would mean cut-and-pasting - * cr_simple_sel_compute_specificity(), and have no need for - * thread-safety anyways.) - * - * Once we've sorted the properties, the specificity no longer - * matters and it can be safely overriden. - */ - cr_simple_sel_compute_specificity (cur_sel->simple_sel); - - cur_stmt->specificity = cur_sel->simple_sel->specificity; - - for (cur_decl = cur_stmt->kind.ruleset->decl_list; cur_decl; cur_decl = cur_decl->next) - g_ptr_array_add (props, cur_decl); - } - } - } -} - -#define ORIGIN_AUTHOR_IMPORTANT (ORIGIN_AUTHOR + 1) -#define ORIGIN_USER_IMPORTANT (ORIGIN_AUTHOR + 2) - -static inline int -get_origin (const CRDeclaration * decl) -{ - enum CRStyleOrigin origin = decl->parent_statement->parent_sheet->origin; - - if (decl->important) - { - if (origin == ORIGIN_AUTHOR) - return ORIGIN_AUTHOR_IMPORTANT; - else if (origin == ORIGIN_USER) - return ORIGIN_USER_IMPORTANT; - } - - return origin; -} - -/* Order of comparison is so that higher priority statements compare after - * lower priority statements */ -static int -compare_declarations (gconstpointer a, - gconstpointer b) -{ - /* g_ptr_array_sort() is broooken */ - CRDeclaration *decl_a = *(CRDeclaration **) a; - CRDeclaration *decl_b = *(CRDeclaration **) b; - - int origin_a = get_origin (decl_a); - int origin_b = get_origin (decl_b); - - if (origin_a != origin_b) - return origin_a - origin_b; - - if (decl_a->parent_statement->specificity != decl_b->parent_statement->specificity) - return decl_a->parent_statement->specificity - decl_b->parent_statement->specificity; - - return 0; -} - -GPtrArray * -_eek_theme_get_matched_properties (EekTheme *theme, - EekThemeNode *node) -{ - enum CRStyleOrigin origin = 0; - CRStyleSheet *sheet = NULL; - GPtrArray *props = g_ptr_array_new (); - GSList *iter; - - g_return_val_if_fail (EEK_IS_THEME (theme), NULL); - g_return_val_if_fail (EEK_IS_THEME_NODE (node), NULL); - - for (origin = ORIGIN_UA; origin < NB_ORIGINS; origin++) - { - sheet = cr_cascade_get_sheet (theme->cascade, origin); - if (!sheet) - continue; - - add_matched_properties (theme, sheet, node, props); - } - - for (iter = theme->custom_stylesheets; iter; iter = iter->next) - add_matched_properties (theme, iter->data, node, props); - - /* We count on a stable sort here so that later declarations come - * after earlier declarations */ - g_ptr_array_sort (props, compare_declarations); - - return props; -} - -/* Resolve an url from an url() reference in a stylesheet into an absolute - * local filename, if possible. The resolution here is distinctly lame and - * will fail on many examples. - */ -static char * -eek_theme_resolve_url (EekTheme *theme, - CRStyleSheet *base_stylesheet, - const char *url) -{ - const char *base_filename = NULL; - char *dirname; - char *filename; - - /* Handle absolute file:/ URLs */ - if (g_str_has_prefix (url, "file:") || - g_str_has_prefix (url, "File:") || - g_str_has_prefix (url, "FILE:")) - { - GError *error = NULL; - char *filename; - - filename = g_filename_from_uri (url, NULL, &error); - if (filename == NULL) - { - g_warning ("%s", error->message); - g_error_free (error); - } - - return NULL; - } - - /* Guard against http:/ URLs */ - - if (g_str_has_prefix (url, "http:") || - g_str_has_prefix (url, "Http:") || - g_str_has_prefix (url, "HTTP:")) - { - g_warning ("Http URL '%s' in theme stylesheet is not supported", url); - return NULL; - } - - /* Assume anything else is a relative URL, and "resolve" it - */ - if (url[0] == '/') - return g_strdup (url); - - base_filename = g_hash_table_lookup (theme->filenames_by_stylesheet, base_stylesheet); - - if (base_filename == NULL) - { - g_warning ("Can't get base to resolve url '%s'", url); - return NULL; - } - - dirname = g_path_get_dirname (base_filename); - filename = g_build_filename (dirname, url, NULL); - g_free (dirname); - - return filename; -} diff --git a/eek/eek-theme.h b/eek/eek-theme.h deleted file mode 100644 index 14fba423..00000000 --- a/eek/eek-theme.h +++ /dev/null @@ -1,49 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -#if !defined(__EEK_H_INSIDE__) && !defined(EEK_COMPILATION) -#error "Only can be included directly." -#endif - -#ifndef __EEK_THEME_H__ -#define __EEK_THEME_H__ - -#include - -#include "eek-types.h" - -G_BEGIN_DECLS - -/** - * SECTION:EekTheme - * @short_description: a set of stylesheets - * - * #EekTheme holds a set of stylesheets. (The "cascade" of the name - * Cascading Stylesheets.) An #EekTheme can be set to apply to all the - * keyboard elements. - */ - -typedef struct _EekThemeClass EekThemeClass; - -#define EEK_TYPE_THEME (eek_theme_get_type()) -#define EEK_THEME(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EEK_TYPE_THEME, EekTheme)) -#define EEK_THEME_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EEK_TYPE_THEME, EekThemeClass)) -#define EEK_IS_THEME(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EEK_TYPE_THEME)) -#define EEK_IS_THEME_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EEK_TYPE_THEME)) -#define EEK_THEME_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EEK_TYPE_THEME, EekThemeClass)) - -GType eek_theme_get_type (void) G_GNUC_CONST; - -EekTheme *eek_theme_new (const char *application_stylesheet, - const char *theme_stylesheet, - const char *default_stylesheet); - -gboolean eek_theme_load_stylesheet (EekTheme *theme, - const char *path, - GError **error); - -void eek_theme_unload_stylesheet (EekTheme *theme, - const char *path); - -G_END_DECLS - -#endif /* __EEK_THEME_H__ */