summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlberts Muktupāvels <alberts.muktupavels@gmail.com>2016-01-29 01:08:44 +0200
committerAlberts Muktupāvels <alberts.muktupavels@gmail.com>2016-01-29 01:08:44 +0200
commitde5f3fbd10d3e8b44a0be5723272c0d35bc7ff8b (patch)
tree2980305d3e1e6629db90014b7fa9a40497312ed4
parent624308207a1692a5e13d17c58e2554891bb1b5a9 (diff)
downloadmetacity-de5f3fbd10d3e8b44a0be5723272c0d35bc7ff8b.tar.gz
libmetacity: add meta-draw-spec.[c/h]
-rw-r--r--libmetacity/Makefile.am3
-rw-r--r--libmetacity/meta-draw-spec.c1272
-rw-r--r--libmetacity/meta-draw-spec.h68
-rw-r--r--po/POTFILES.in1
-rw-r--r--src/ui/theme-parser.c120
-rw-r--r--src/ui/theme-private.h110
-rw-r--r--src/ui/theme.c1297
7 files changed, 1466 insertions, 1405 deletions
diff --git a/libmetacity/Makefile.am b/libmetacity/Makefile.am
index 5c2a89b4..54dde93a 100644
--- a/libmetacity/Makefile.am
+++ b/libmetacity/Makefile.am
@@ -12,6 +12,8 @@ libmetacity_la_SOURCES = \
meta-color-private.h \
meta-color-spec.c \
meta-color-spec.h \
+ meta-draw-spec.c \
+ meta-draw-spec.h \
meta-frame-borders.c \
meta-frame-borders.h \
meta-frame-flags.h \
@@ -61,6 +63,7 @@ libmetacity_include_HEADERS = \
meta-button-layout.h \
meta-color.h \
meta-color-spec.h \
+ meta-draw-spec.h \
meta-frame-borders.h \
meta-frame-flags.h \
meta-frame-type.h \
diff --git a/libmetacity/meta-draw-spec.c b/libmetacity/meta-draw-spec.c
new file mode 100644
index 00000000..f9b1a089
--- /dev/null
+++ b/libmetacity/meta-draw-spec.c
@@ -0,0 +1,1272 @@
+/*
+ * Copyright (C) 2001 Havoc Pennington
+ * Copyright (C) 2016 Alberts Muktupāvels
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <glib/gi18n-lib.h>
+#include <stdlib.h>
+
+#include "meta-draw-spec.h"
+#include "meta-theme.h"
+
+typedef enum
+{
+ POS_TOKEN_INT,
+ POS_TOKEN_DOUBLE,
+ POS_TOKEN_OPERATOR,
+ POS_TOKEN_VARIABLE,
+ POS_TOKEN_OPEN_PAREN,
+ POS_TOKEN_CLOSE_PAREN
+} PosTokenType;
+
+typedef enum
+{
+ POS_OP_NONE,
+ POS_OP_ADD,
+ POS_OP_SUBTRACT,
+ POS_OP_MULTIPLY,
+ POS_OP_DIVIDE,
+ POS_OP_MOD,
+ POS_OP_MAX,
+ POS_OP_MIN
+} PosOperatorType;
+
+/**
+ * A token, as output by the tokeniser.
+ *
+ * \ingroup tokenizer
+ */
+typedef struct
+{
+ PosTokenType type;
+
+ union
+ {
+ struct {
+ int val;
+ } i;
+
+ struct {
+ double val;
+ } d;
+
+ struct {
+ PosOperatorType op;
+ } o;
+
+ struct {
+ char *name;
+ GQuark name_quark;
+ } v;
+
+ } d;
+} PosToken;
+
+/**
+ * A computed expression in our simple vector drawing language.
+ * While it appears to take the form of a tree, this is actually
+ * merely a list; concerns such as precedence of operators are
+ * currently recomputed on every recalculation.
+ *
+ * Created by meta_draw_spec_new(), destroyed by meta_draw_spec_free().
+ * pos_eval() fills this with ...FIXME. Are tokens a tree or a list?
+ * \ingroup parser
+ */
+struct _MetaDrawSpec
+{
+ /**
+ * If this spec is constant, this is the value of the constant;
+ * otherwise it is zero.
+ */
+ int value;
+
+ /** A list of tokens in the expression. */
+ PosToken *tokens;
+
+ /** How many tokens are in the tokens list. */
+ int n_tokens;
+
+ /** Does the expression contain any variables? */
+ gboolean constant : 1;
+};
+
+/**
+ * The type of a PosExpr: either integer, double, or an operation.
+ * \ingroup parser
+ */
+typedef enum
+{
+ POS_EXPR_INT,
+ POS_EXPR_DOUBLE,
+ POS_EXPR_OPERATOR
+} PosExprType;
+
+/**
+ * Type and value of an expression in a parsed sequence. We don't
+ * keep expressions in a tree; if this is of type POS_EXPR_OPERATOR,
+ * the arguments of the operator will be in the array positions
+ * immediately preceding and following this operator; they cannot
+ * themselves be operators.
+ *
+ * \bug operator is char; it should really be of PosOperatorType.
+ * \ingroup parser
+ */
+typedef struct
+{
+ PosExprType type;
+ union
+ {
+ double double_val;
+ int int_val;
+ char operator;
+ } d;
+} PosExpr;
+
+/**
+ * Frees an array of tokens. All the tokens and their associated memory
+ * will be freed.
+ *
+ * \param tokens an array of tokens to be freed
+ * \param n_tokens how many tokens are in the array.
+ */
+static void
+free_tokens (PosToken *tokens,
+ int n_tokens)
+{
+ int i;
+
+ /* n_tokens can be 0 since tokens may have been allocated more than
+ * it was initialized
+ */
+
+ for (i = 0; i < n_tokens; i++)
+ if (tokens[i].type == POS_TOKEN_VARIABLE)
+ g_free (tokens[i].d.v.name);
+
+ g_free (tokens);
+}
+
+/**
+ * Tokenises a number in an expression.
+ *
+ * \param p a pointer into a string representing an operation; part of an
+ * expression somewhere, so not null-terminated
+ * \param end_return set to a pointer to the end of the number found; but
+ * not updated if no number was found at all
+ * \param next set to either an integer or a float token
+ * \param[out] err set to the problem if there was a problem
+ * \return TRUE if a valid number was found, FALSE otherwise (and "err" will
+ * have been set)
+ *
+ * \bug The "while (*start)..." part: what's wrong with strchr-ish things?
+ * \bug The name is wrong: it doesn't parse anything.
+ * \ingroup tokenizer
+ */
+static gboolean
+parse_number (const char *p,
+ const char **end_return,
+ PosToken *next,
+ GError **err)
+{
+ const char *start = p;
+ char *end;
+ gboolean is_float;
+ char *num_str;
+
+ while (*p && (*p == '.' || g_ascii_isdigit (*p)))
+ ++p;
+
+ if (p == start)
+ {
+ char buf[7] = { '\0' };
+ buf[g_unichar_to_utf8 (g_utf8_get_char (p), buf)] = '\0';
+ g_set_error (err, META_THEME_ERROR, META_THEME_ERROR_BAD_CHARACTER,
+ _("Coordinate expression contains character '%s' which is not allowed"),
+ buf);
+ return FALSE;
+ }
+
+ *end_return = p;
+
+ /* we need this to exclude floats like "1e6" */
+ num_str = g_strndup (start, p - start);
+ start = num_str;
+ is_float = FALSE;
+ while (*start)
+ {
+ if (*start == '.')
+ is_float = TRUE;
+ ++start;
+ }
+
+ if (is_float)
+ {
+ next->type = POS_TOKEN_DOUBLE;
+ next->d.d.val = g_ascii_strtod (num_str, &end);
+
+ if (end == num_str)
+ {
+ g_set_error (err, META_THEME_ERROR,
+ META_THEME_ERROR_FAILED,
+ _("Coordinate expression contains floating point number '%s' which could not be parsed"),
+ num_str);
+ g_free (num_str);
+ return FALSE;
+ }
+ }
+ else
+ {
+ next->type = POS_TOKEN_INT;
+ next->d.i.val = strtol (num_str, &end, 10);
+ if (end == num_str)
+ {
+ g_set_error (err, META_THEME_ERROR, META_THEME_ERROR_FAILED,
+ _("Coordinate expression contains integer '%s' which could not be parsed"),
+ num_str);
+ g_free (num_str);
+ return FALSE;
+ }
+ }
+
+ g_free (num_str);
+
+ g_assert (next->type == POS_TOKEN_INT || next->type == POS_TOKEN_DOUBLE);
+
+ return TRUE;
+}
+
+/**
+ * Parses a string and returns an operation.
+ *
+ * \param p a pointer into a string representing an operation; part of an
+ * expression somewhere, so not null-terminated
+ * \param len set to the length of the string found. Set to 0 if none is.
+ * \return the operation found. If none was, returns POS_OP_NONE.
+ */
+static PosOperatorType
+op_from_string (const char *p,
+ int *len)
+{
+ *len = 0;
+
+ switch (*p)
+ {
+ case '+':
+ *len = 1;
+ return POS_OP_ADD;
+ case '-':
+ *len = 1;
+ return POS_OP_SUBTRACT;
+ case '*':
+ *len = 1;
+ return POS_OP_MULTIPLY;
+ case '/':
+ *len = 1;
+ return POS_OP_DIVIDE;
+ case '%':
+ *len = 1;
+ return POS_OP_MOD;
+
+ case '`':
+ if (strncmp (p, "`max`", 5) == 0)
+ {
+ *len = 5;
+ return POS_OP_MAX;
+ }
+ else if (strncmp (p, "`min`", 5) == 0)
+ {
+ *len = 5;
+ return POS_OP_MIN;
+ }
+
+ default:
+ break;
+ }
+
+ return POS_OP_NONE;
+}
+
+/**
+ * Tokenises an expression.
+ *
+ * \param expr The expression
+ * \param[out] tokens_p The resulting tokens
+ * \param[out] n_tokens_p The number of resulting tokens
+ * \param[out] err set to the problem if there was a problem
+ *
+ * \return True if the expression was successfully tokenised; false otherwise.
+ *
+ * \ingroup tokenizer
+ */
+static gboolean
+pos_tokenize (const char *expr,
+ PosToken **tokens_p,
+ int *n_tokens_p,
+ GError **err)
+{
+ PosToken *tokens;
+ int n_tokens;
+ int allocated;
+ const char *p;
+
+ *tokens_p = NULL;
+ *n_tokens_p = 0;
+
+ allocated = 3;
+ n_tokens = 0;
+ tokens = g_new (PosToken, allocated);
+
+ p = expr;
+ while (*p)
+ {
+ PosToken *next;
+ int len;
+
+ if (n_tokens == allocated)
+ {
+ allocated *= 2;
+ tokens = g_renew (PosToken, tokens, allocated);
+ }
+
+ next = &tokens[n_tokens];
+
+ switch (*p)
+ {
+ case '*':
+ case '/':
+ case '+':
+ case '-': /* negative numbers aren't allowed so this is easy */
+ case '%':
+ case '`':
+ next->type = POS_TOKEN_OPERATOR;
+ next->d.o.op = op_from_string (p, &len);
+ if (next->d.o.op != POS_OP_NONE)
+ {
+ ++n_tokens;
+ p = p + (len - 1); /* -1 since we ++p later */
+ }
+ else
+ {
+ g_set_error (err, META_THEME_ERROR, META_THEME_ERROR_FAILED,
+ _("Coordinate expression contained unknown operator at the start of this text: \"%s\""),
+ p);
+
+ goto error;
+ }
+ break;
+
+ case '(':
+ next->type = POS_TOKEN_OPEN_PAREN;
+ ++n_tokens;
+ break;
+
+ case ')':
+ next->type = POS_TOKEN_CLOSE_PAREN;
+ ++n_tokens;
+ break;
+
+ case ' ':
+ case '\t':
+ case '\n':
+ break;
+
+ default:
+ if (g_ascii_isalpha (*p) || *p == '_')
+ {
+ /* Assume variable */
+ const char *start = p;
+ while (*p && (g_ascii_isalpha (*p) || *p == '_'))
+ ++p;
+ g_assert (p != start);
+ next->type = POS_TOKEN_VARIABLE;
+ next->d.v.name = g_strndup (start, p - start);
+ ++n_tokens;
+ --p; /* since we ++p again at the end of while loop */
+ }
+ else
+ {
+ /* Assume number */
+ const char *end;
+
+ if (!parse_number (p, &end, next, err))
+ goto error;
+
+ ++n_tokens;
+ p = end - 1; /* -1 since we ++p again at the end of while loop */
+ }
+
+ break;
+ }
+
+ ++p;
+ }
+
+ if (n_tokens == 0)
+ {
+ g_set_error (err, META_THEME_ERROR, META_THEME_ERROR_FAILED,
+ _("Coordinate expression was empty or not understood"));
+
+ goto error;
+ }
+
+ *tokens_p = tokens;
+ *n_tokens_p = n_tokens;
+
+ return TRUE;
+
+ error:
+ g_assert (err == NULL || *err != NULL);
+
+ free_tokens (tokens, n_tokens);
+ return FALSE;
+}
+
+/* To do this we tokenize, replace variable tokens
+ * that are constants, then reassemble. The purpose
+ * here is to optimize expressions so we don't do hash
+ * lookups to eval them. Obviously it's a tradeoff that
+ * slows down theme load times.
+ */
+static gboolean
+replace_constants (MetaThemeMetacity *metacity,
+ PosToken *tokens,
+ int n_tokens,
+ GError **err)
+{
+ int i;
+ double dval;
+ int ival;
+ gboolean is_constant = TRUE;
+
+ /* Loop through tokenized string looking for variables to replace */
+ for (i = 0; i < n_tokens; i++)
+ {
+ PosToken *t = &tokens[i];
+
+ if (t->type == POS_TOKEN_VARIABLE)
+ {
+ if (meta_theme_metacity_lookup_int (metacity, t->d.v.name, &ival))
+ {
+ g_free (t->d.v.name);
+ t->type = POS_TOKEN_INT;
+ t->d.i.val = ival;
+ }
+ else if (meta_theme_metacity_lookup_float (metacity, t->d.v.name, &dval))
+ {
+ g_free (t->d.v.name);
+ t->type = POS_TOKEN_DOUBLE;
+ t->d.d.val = dval;
+ }
+ else
+ {
+ /* If we've found a variable that cannot be replaced then the
+ expression is not a constant expression and we want to
+ replace it with a GQuark */
+
+ t->d.v.name_quark = g_quark_from_string (t->d.v.name);
+ is_constant = FALSE;
+ }
+ }
+ }
+
+ return is_constant;
+}
+
+/**
+ * pos_eval_get_variable:
+ * @token: The token representing a variable
+ * @result: (out): The value of that variable; not set if the token did not
+ * represent a known variable
+ * @env: The environment within which @token should be evaluated
+ * @err: (out): Set to the problem if there was a problem
+ *
+ * There is a predefined set of variables which can appear in an expression.
+ * Here we take a token representing a variable, and return the current value
+ * of that variable in a particular environment.
+ * (The value is always an integer.)
+ *
+ * Returns: %TRUE if we found the variable asked for, %FALSE if we didn't
+ */
+static gboolean
+pos_eval_get_variable (const PosToken *token,
+ int *result,
+ const MetaPositionExprEnv *env,
+ GError **err)
+{
+ GQuark quark;
+
+ quark = token->d.v.name_quark;
+
+ if (quark == g_quark_from_static_string ("width"))
+ {
+ *result = env->rect.width;
+ }
+ else if (quark == g_quark_from_static_string ("height"))
+ {
+ *result = env->rect.height;
+ }
+ else if (env->object_width >= 0 &&
+ quark == g_quark_from_static_string ("object_width"))
+ {
+ *result = env->object_width;
+ }
+ else if (env->object_height >= 0 &&
+ quark == g_quark_from_static_string ("object_height"))
+ {
+ *result = env->object_height;
+ }
+ else if (quark == g_quark_from_static_string ("left_width"))
+ {
+ *result = env->left_width;
+ }
+ else if (quark == g_quark_from_static_string ("right_width"))
+ {
+ *result = env->right_width;
+ }
+ else if (quark == g_quark_from_static_string ("top_height"))
+ {
+ *result = env->top_height;
+ }
+ else if (quark == g_quark_from_static_string ("bottom_height"))
+ {
+ *result = env->bottom_height;
+ }
+ else if (quark == g_quark_from_static_string ("mini_icon_width"))
+ {
+ *result = env->mini_icon_width;
+ }
+ else if (quark == g_quark_from_static_string ("mini_icon_height"))
+ {
+ *result = env->mini_icon_height;
+ }
+ else if (quark == g_quark_from_static_string ("icon_width"))
+ {
+ *result = env->icon_width;
+ }
+ else if (quark == g_quark_from_static_string ("icon_height"))
+ {
+ *result = env->icon_height;
+ }
+ else if (quark == g_quark_from_static_string ("title_width"))
+ {
+ *result = env->title_width;
+ }
+ else if (quark == g_quark_from_static_string ("title_height"))
+ {
+ *result = env->title_height;
+ }
+ else if (quark == g_quark_from_static_string ("frame_x_center"))
+ {
+ *result = env->frame_x_center;
+ }
+ else if (quark == g_quark_from_static_string ("frame_y_center"))
+ {
+ *result = env->frame_y_center;
+ }
+ else
+ {
+ g_set_error (err, META_THEME_ERROR, META_THEME_ERROR_UNKNOWN_VARIABLE,
+ _("Coordinate expression had unknown variable or constant '%s'"),
+ token->d.v.name);
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+do_operation (PosExpr *a,
+ PosExpr *b,
+ PosOperatorType op,
+ GError **err)
+{
+ /* Promote types to double if required */
+ if (a->type == POS_EXPR_DOUBLE ||
+ b->type == POS_EXPR_DOUBLE)
+ {
+ if (a->type != POS_EXPR_DOUBLE)
+ {
+ a->type = POS_EXPR_DOUBLE;
+ a->d.double_val = a->d.int_val;
+ }
+ if (b->type != POS_EXPR_DOUBLE)
+ {
+ b->type = POS_EXPR_DOUBLE;
+ b->d.double_val = b->d.int_val;
+ }
+ }
+
+ g_assert (a->type == b->type);
+
+ if (a->type == POS_EXPR_INT)
+ {
+ switch (op)
+ {
+ case POS_OP_MULTIPLY:
+ a->d.int_val = a->d.int_val * b->d.int_val;
+ break;
+ case POS_OP_DIVIDE:
+ if (b->d.int_val == 0)
+ {
+ g_set_error (err, META_THEME_ERROR,
+ META_THEME_ERROR_DIVIDE_BY_ZERO,
+ _("Coordinate expression results in division by zero"));
+ return FALSE;
+ }
+ a->d.int_val = a->d.int_val / b->d.int_val;
+ break;
+ case POS_OP_MOD:
+ if (b->d.int_val == 0)
+ {
+ g_set_error (err, META_THEME_ERROR,
+ META_THEME_ERROR_DIVIDE_BY_ZERO,
+ _("Coordinate expression results in division by zero"));
+ return FALSE;
+ }
+ a->d.int_val = a->d.int_val % b->d.int_val;
+ break;
+ case POS_OP_ADD:
+ a->d.int_val = a->d.int_val + b->d.int_val;
+ break;
+ case POS_OP_SUBTRACT:
+ a->d.int_val = a->d.int_val - b->d.int_val;
+ break;
+ case POS_OP_MAX:
+ a->d.int_val = MAX (a->d.int_val, b->d.int_val);
+ break;
+ case POS_OP_MIN:
+ a->d.int_val = MIN (a->d.int_val, b->d.int_val);
+ break;
+ case POS_OP_NONE:
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+ }
+ else if (a->type == POS_EXPR_DOUBLE)
+ {
+ switch (op)
+ {
+ case POS_OP_MULTIPLY:
+ a->d.double_val = a->d.double_val * b->d.double_val;
+ break;
+ case POS_OP_DIVIDE:
+ if (b->d.double_val == 0.0)
+ {
+ g_set_error (err, META_THEME_ERROR,
+ META_THEME_ERROR_DIVIDE_BY_ZERO,
+ _("Coordinate expression results in division by zero"));
+ return FALSE;
+ }
+ a->d.double_val = a->d.double_val / b->d.double_val;
+ break;
+ case POS_OP_MOD:
+ g_set_error (err, META_THEME_ERROR,
+ META_THEME_ERROR_MOD_ON_FLOAT,
+ _("Coordinate expression tries to use mod operator on a floating-point number"));
+ return FALSE;
+ case POS_OP_ADD:
+ a->d.double_val = a->d.double_val + b->d.double_val;
+ break;
+ case POS_OP_SUBTRACT:
+ a->d.double_val = a->d.double_val - b->d.double_val;
+ break;
+ case POS_OP_MAX:
+ a->d.double_val = MAX (a->d.double_val, b->d.double_val);
+ break;
+ case POS_OP_MIN:
+ a->d.double_val = MIN (a->d.double_val, b->d.double_val);
+ break;
+ case POS_OP_NONE:
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+ }
+ else
+ g_assert_not_reached ();
+
+ return TRUE;
+}
+
+/**
+ * Represents an operation as a string.
+ *
+ * \param type an operation, such as addition
+ * \return a string, such as "+"
+ */
+static const char*
+op_name (PosOperatorType type)
+{
+ switch (type)
+ {
+ case POS_OP_ADD:
+ return "+";
+ case POS_OP_SUBTRACT:
+ return "-";
+ case POS_OP_MULTIPLY:
+ return "*";
+ case POS_OP_DIVIDE:
+ return "/";
+ case POS_OP_MOD:
+ return "%";
+ case POS_OP_MAX:
+ return "`max`";
+ case POS_OP_MIN:
+ return "`min`";
+ case POS_OP_NONE:
+ break;
+ default:
+ break;
+ }
+
+ return "<unknown>";
+}
+
+static gboolean
+do_operations (PosExpr *exprs,
+ int *n_exprs,
+ int precedence,
+ GError **err)
+{
+ int i;
+
+ i = 1;
+ while (i < *n_exprs)
+ {
+ gboolean compress;
+
+ /* exprs[i-1] first operand
+ * exprs[i] operator
+ * exprs[i+1] second operand
+ *
+ * we replace first operand with result of mul/div/mod,
+ * or skip over operator and second operand if we have
+ * an add/subtract
+ */
+
+ if (exprs[i-1].type == POS_EXPR_OPERATOR)
+ {
+ g_set_error (err, META_THEME_ERROR,
+ META_THEME_ERROR_FAILED,
+ _("Coordinate expression has an operator \"%s\" where an operand was expected"),
+ op_name (exprs[i-1].d.operator));
+ return FALSE;
+ }
+
+ if (exprs[i].type != POS_EXPR_OPERATOR)
+ {
+ g_set_error (err, META_THEME_ERROR,
+ META_THEME_ERROR_FAILED,
+ _("Coordinate expression had an operand where an operator was expected"));
+ return FALSE;
+ }
+
+ if (i == (*n_exprs - 1))
+ {
+ g_set_error (err, META_THEME_ERROR,
+ META_THEME_ERROR_FAILED,
+ _("Coordinate expression ended with an operator instead of an operand"));
+ return FALSE;
+ }
+
+ g_assert ((i+1) < *n_exprs);
+
+ if (exprs[i+1].type == POS_EXPR_OPERATOR)
+ {
+ g_set_error (err, META_THEME_ERROR,
+ META_THEME_ERROR_FAILED,
+ _("Coordinate expression has operator \"%c\" following operator \"%c\" with no operand in between"),
+ exprs[i+1].d.operator,
+ exprs[i].d.operator);
+ return FALSE;
+ }
+
+ compress = FALSE;
+
+ switch (precedence)
+ {
+ case 2:
+ switch (exprs[i].d.operator)
+ {
+ case POS_OP_DIVIDE:
+ case POS_OP_MOD:
+ case POS_OP_MULTIPLY:
+ compress = TRUE;
+ if (!do_operation (&exprs[i-1], &exprs[i+1],
+ exprs[i].d.operator,
+ err))
+ return FALSE;
+ break;
+ default:
+ break;
+ }
+ break;
+ case 1:
+ switch (exprs[i].d.operator)
+ {
+ case POS_OP_ADD:
+ case POS_OP_SUBTRACT:
+ compress = TRUE;
+ if (!do_operation (&exprs[i-1], &exprs[i+1],
+ exprs[i].d.operator,
+ err))
+ return FALSE;
+ break;
+ default:
+ break;
+ }
+ break;
+ /* I have no rationale at all for making these low-precedence */
+ case 0:
+ switch (exprs[i].d.operator)
+ {
+ case POS_OP_MAX:
+ case POS_OP_MIN:
+ compress = TRUE;
+ if (!do_operation (&exprs[i-1], &exprs[i+1],
+ exprs[i].d.operator,
+ err))
+ return FALSE;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (compress)
+ {
+ /* exprs[i-1] first operand (now result)
+ * exprs[i] operator
+ * exprs[i+1] second operand
+ * exprs[i+2] new operator
+ *
+ * we move new operator just after first operand
+ */
+ if ((i+2) < *n_exprs)
+ {
+ g_memmove (&exprs[i], &exprs[i+2],
+ sizeof (PosExpr) * (*n_exprs - i - 2));
+ }
+
+ *n_exprs -= 2;
+ }
+ else
+ {
+ /* Skip operator and next operand */
+ i += 2;
+ }
+ }
+
+ return TRUE;
+}
+
+/**
+ * Evaluates a sequence of tokens within a particular environment context,
+ * and returns the current value. May recur if parantheses are found.
+ *
+ * \param tokens A list of tokens to evaluate.
+ * \param n_tokens How many tokens are in the list.
+ * \param env The environment context in which to evaluate the expression.
+ * \param[out] result The current value of the expression
+ *
+ * \bug Yes, we really do reparse the expression every time it's evaluated.
+ * We should keep the parse tree around all the time and just
+ * run the new values through it.
+ * \ingroup parser
+ */
+static gboolean
+pos_eval_helper (PosToken *tokens,
+ int n_tokens,
+ const MetaPositionExprEnv *env,
+ PosExpr *result,
+ GError **err)
+{
+ /* Lazy-ass hardcoded limit on number of terms in expression */
+#define MAX_EXPRS 32
+ int paren_level;
+ int first_paren;
+ int i;
+ PosExpr exprs[MAX_EXPRS];
+ int n_exprs;
+ int precedence;
+
+ /* Our first goal is to get a list of PosExpr, essentially
+ * substituting variables and handling parentheses.
+ */
+
+ first_paren = 0;
+ paren_level = 0;
+ n_exprs = 0;
+ for (i = 0; i < n_tokens; i++)
+ {
+ PosToken *t = &tokens[i];
+
+ if (n_exprs >= MAX_EXPRS)
+ {
+ g_set_error (err, META_THEME_ERROR, META_THEME_ERROR_FAILED,
+ _("Coordinate expression parser overflowed its buffer."));
+ return FALSE;
+ }
+
+ if (paren_level == 0)
+ {
+ switch (t->type)
+ {
+ case POS_TOKEN_INT:
+ exprs[n_exprs].type = POS_EXPR_INT;
+ exprs[n_exprs].d.int_val = t->d.i.val;
+ ++n_exprs;
+ break;
+
+ case POS_TOKEN_DOUBLE:
+ exprs[n_exprs].type = POS_EXPR_DOUBLE;
+ exprs[n_exprs].d.double_val = t->d.d.val;
+ ++n_exprs;
+ break;
+
+ case POS_TOKEN_OPEN_PAREN:
+ ++paren_level;
+ if (paren_level == 1)
+ first_paren = i;
+ break;
+
+ case POS_TOKEN_CLOSE_PAREN:
+ g_set_error (err, META_THEME_ERROR, META_THEME_ERROR_BAD_PARENS,
+ _("Coordinate expression had a close parenthesis with no open parenthesis"));
+ return FALSE;
+
+ case POS_TOKEN_VARIABLE:
+ exprs[n_exprs].type = POS_EXPR_INT;
+
+ /* FIXME we should just dump all this crap
+ * in a hash, maybe keep width/height out
+ * for optimization purposes
+ */
+ if (!pos_eval_get_variable (t, &exprs[n_exprs].d.int_val, env, err))
+ return FALSE;
+
+ ++n_exprs;
+ break;
+
+ case POS_TOKEN_OPERATOR:
+ exprs[n_exprs].type = POS_EXPR_OPERATOR;
+ exprs[n_exprs].d.operator = t->d.o.op;
+ ++n_exprs;
+ break;
+
+ default:
+ break;
+ }
+ }
+ else
+ {
+ g_assert (paren_level > 0);
+
+ switch (t->type)
+ {
+ case POS_TOKEN_INT:
+ case POS_TOKEN_DOUBLE:
+ case POS_TOKEN_VARIABLE:
+ case POS_TOKEN_OPERATOR:
+ break;
+
+ case POS_TOKEN_OPEN_PAREN:
+ ++paren_level;
+ break;
+
+ case POS_TOKEN_CLOSE_PAREN:
+ if (paren_level == 1)
+ {
+ /* We closed a toplevel paren group, so recurse */
+ if (!pos_eval_helper (&tokens[first_paren+1],
+ i - first_paren - 1,
+ env,
+ &exprs[n_exprs],
+ err))
+ return FALSE;
+
+ ++n_exprs;
+ }
+
+ --paren_level;
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ if (paren_level > 0)
+ {
+ g_set_error (err, META_THEME_ERROR, META_THEME_ERROR_BAD_PARENS,
+ _("Coordinate expression had an open parenthesis with no close parenthesis"));
+ return FALSE;
+ }
+
+ /* Now we have no parens and no vars; so we just do all the multiplies
+ * and divides, then all the add and subtract.
+ */
+ if (n_exprs == 0)
+ {
+ g_set_error (err, META_THEME_ERROR, META_THEME_ERROR_FAILED,
+ _("Coordinate expression doesn't seem to have any operators or operands"));
+ return FALSE;
+ }
+
+ /* precedence 1 ops */
+ precedence = 2;
+ while (precedence >= 0)
+ {
+ if (!do_operations (exprs, &n_exprs, precedence, err))
+ return FALSE;
+ --precedence;
+ }
+
+ g_assert (n_exprs == 1);
+
+ *result = *exprs;
+
+ return TRUE;
+}
+
+/*
+ * expr = int | double | expr * expr | expr / expr |
+ * expr + expr | expr - expr | (expr)
+ *
+ * so very not worth fooling with bison, yet so very painful by hand.
+ */
+/**
+ * Evaluates an expression.
+ *
+ * \param spec The expression to evaluate.
+ * \param env The environment context to evaluate the expression in.
+ * \param[out] val_p The integer value of the expression; if the expression
+ * is of type float, this will be rounded. If we return
+ * FALSE because the expression is invalid, this will be
+ * zero.
+ * \param[out] err The error, if anything went wrong.
+ *
+ * \return True if we evaluated the expression successfully; false otherwise.
+ *
+ * \bug Shouldn't spec be const?
+ * \ingroup parser
+ */
+static gboolean
+pos_eval (MetaDrawSpec *spec,
+ const MetaPositionExprEnv *env,
+ int *val_p,
+ GError **err)
+{
+ PosExpr expr;
+
+ *val_p = 0;
+
+ if (pos_eval_helper (spec->tokens, spec->n_tokens, env, &expr, err))
+ {
+ switch (expr.type)
+ {
+ case POS_EXPR_INT:
+ *val_p = expr.d.int_val;
+ break;
+ case POS_EXPR_DOUBLE:
+ *val_p = expr.d.double_val;
+ break;
+ case POS_EXPR_OPERATOR:
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+/* We always return both X and Y, but only one will be meaningful in
+ * most contexts.
+ */
+static gboolean
+parse_position_expression (MetaDrawSpec *spec,
+ const MetaPositionExprEnv *env,
+ int *x_return,
+ int *y_return,
+ GError **err)
+{
+ /* All positions are in a coordinate system with x, y at the origin.
+ * The expression can have -, +, *, / as operators, floating point
+ * or integer constants, and the variables "width" and "height" and
+ * optionally "object_width" and object_height". Negative numbers
+ * aren't allowed.
+ */
+ int val;
+
+ if (spec->constant)
+ val = spec->value;
+ else
+ {
+ if (pos_eval (spec, env, &spec->value, err) == FALSE)
+ {
+ g_assert (err == NULL || *err != NULL);
+ return FALSE;
+ }
+
+ val = spec->value;
+ }
+
+ if (x_return)
+ *x_return = env->rect.x + val;
+ if (y_return)
+ *y_return = env->rect.y + val;
+
+ return TRUE;
+}
+
+static gboolean
+parse_size_expression (MetaDrawSpec *spec,
+ const MetaPositionExprEnv *env,
+ int *val_return,
+ GError **err)
+{
+ int val;
+
+ if (spec->constant)
+ val = spec->value;
+ else
+ {
+ if (pos_eval (spec, env, &spec->value, err) == FALSE)
+ {
+ g_assert (err == NULL || *err != NULL);
+ return FALSE;
+ }
+
+ val = spec->value;
+ }
+
+ if (val_return)
+ *val_return = MAX (val, 1); /* require that sizes be at least 1x1 */
+
+ return TRUE;
+}
+
+MetaDrawSpec *
+meta_draw_spec_new (MetaThemeMetacity *metacity,
+ const gchar *expr,
+ GError **error)
+{
+ MetaDrawSpec *spec;
+
+ spec = g_slice_new0 (MetaDrawSpec);
+
+ pos_tokenize (expr, &spec->tokens, &spec->n_tokens, NULL);
+
+ spec->constant = replace_constants (metacity, spec->tokens,
+ spec->n_tokens, NULL);
+
+ if (spec->constant)
+ {
+ gboolean result;
+
+ result = pos_eval (spec, NULL, &spec->value, error);
+
+ if (result == FALSE)
+ {
+ meta_draw_spec_free (spec);
+ return NULL;
+ }
+ }
+
+ return spec;
+}
+
+void
+meta_draw_spec_free (MetaDrawSpec *spec)
+{
+ if (!spec)
+ return;
+
+ free_tokens (spec->tokens, spec->n_tokens);
+ g_slice_free (MetaDrawSpec, spec);
+}
+
+gint
+meta_draw_spec_parse_x_position (MetaDrawSpec *spec,
+ const MetaPositionExprEnv *env)
+{
+ int retval;
+ GError *error;
+
+ retval = 0;
+ error = NULL;
+ if (!parse_position_expression (spec, env, &retval, NULL, &error))
+ {
+ g_warning (_("Theme contained an expression that resulted in an error: %s"),
+ error->message);
+
+ g_error_free (error);
+ }
+
+ return retval;
+}
+
+gint
+meta_draw_spec_parse_y_position (MetaDrawSpec *spec,
+ const MetaPositionExprEnv *env)
+{
+ int retval;
+ GError *error;
+
+ retval = 0;
+ error = NULL;
+ if (!parse_position_expression (spec, env, NULL, &retval, &error))
+ {
+ g_warning (_("Theme contained an expression that resulted in an error: %s"),
+ error->message);
+
+ g_error_free (error);
+ }
+
+ return retval;
+}
+
+gint
+meta_draw_spec_parse_size (MetaDrawSpec *spec,
+ const MetaPositionExprEnv *env)
+{
+ int retval;
+ GError *error;
+
+ retval = 0;
+ error = NULL;
+ if (!parse_size_expression (spec, env, &retval, &error))
+ {
+ g_warning (_("Theme contained an expression that resulted in an error: %s"),
+ error->message);
+
+ g_error_free (error);
+ }
+
+ return retval;
+}
diff --git a/libmetacity/meta-draw-spec.h b/libmetacity/meta-draw-spec.h
new file mode 100644
index 00000000..5f7dfddc
--- /dev/null
+++ b/libmetacity/meta-draw-spec.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2001 Havoc Pennington
+ * Copyright (C) 2016 Alberts Muktupāvels
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef META_DRAW_SPEC_H
+#define META_DRAW_SPEC_H
+
+#include <gdk/gdk.h>
+#include <libmetacity/meta-theme-metacity.h>
+
+G_BEGIN_DECLS
+
+typedef struct _MetaDrawSpec MetaDrawSpec;
+typedef struct _MetaPositionExprEnv MetaPositionExprEnv;
+
+struct _MetaPositionExprEnv
+{
+ GdkRectangle rect;
+ /* size of an object being drawn, if it has a natural size */
+ int object_width;
+ int object_height;
+ /* global object sizes, always available */
+ int left_width;
+ int right_width;
+ int top_height;
+ int bottom_height;
+ int title_width;
+ int title_height;
+ int frame_x_center;
+ int frame_y_center;
+ int mini_icon_width;
+ int mini_icon_height;
+ int icon_width;
+ int icon_height;
+};
+
+MetaDrawSpec *meta_draw_spec_new (MetaThemeMetacity *metacity,
+ const char *expr,
+ GError **error);
+
+void meta_draw_spec_free (MetaDrawSpec *spec);
+
+gint meta_draw_spec_parse_x_position (MetaDrawSpec *spec,
+ const MetaPositionExprEnv *env);
+
+gint meta_draw_spec_parse_y_position (MetaDrawSpec *spec,
+ const MetaPositionExprEnv *env);
+
+gint meta_draw_spec_parse_size (MetaDrawSpec *spec,
+ const MetaPositionExprEnv *env);
+
+G_END_DECLS
+
+#endif
diff --git a/po/POTFILES.in b/po/POTFILES.in
index e56b9051..020b48d5 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -1,6 +1,7 @@
# List of source files containing translatable strings.
# Please keep this file sorted alphabetically.
libmetacity/meta-color-spec.c
+libmetacity/meta-draw-spec.c
libmetacity/meta-gradient-spec.c
libmetacity/meta-theme-metacity.c
src/50-metacity-navigation.xml.in
diff --git a/src/ui/theme-parser.c b/src/ui/theme-parser.c
index faab4ddd..48cea2ee 100644
--- a/src/ui/theme-parser.c
+++ b/src/ui/theme-parser.c
@@ -1713,8 +1713,12 @@ parse_draw_op_element (GMarkupParseContext *context,
ParseInfo *info,
GError **error)
{
+ MetaThemeMetacity *metacity;
+
g_return_if_fail (peek_state (info) == STATE_DRAW_OPS);
+ metacity = META_THEME_METACITY (info->theme->impl);
+
if (ELEMENT_IS ("line"))
{
MetaDrawOp *op;
@@ -1771,18 +1775,18 @@ parse_draw_op_element (GMarkupParseContext *context,
op->data.line.color_spec = color_spec;
- op->data.line.x1 = meta_draw_spec_new (info->theme, x1, NULL);
- op->data.line.y1 = meta_draw_spec_new (info->theme, y1, NULL);
+ op->data.line.x1 = meta_draw_spec_new (metacity, x1, NULL);
+ op->data.line.y1 = meta_draw_spec_new (metacity, y1, NULL);
if (strcmp(x1, x2)==0)
op->data.line.x2 = NULL;
else
- op->data.line.x2 = meta_draw_spec_new (info->theme, x2, NULL);
+ op->data.line.x2 = meta_draw_spec_new (metacity, x2, NULL);
if (strcmp(y1, y2)==0)
op->data.line.y2 = NULL;
else
- op->data.line.y2 = meta_draw_spec_new (info->theme, y2, NULL);
+ op->data.line.y2 = meta_draw_spec_new (metacity, y2, NULL);
op->data.line.width = width_val;
op->data.line.dash_on_length = dash_on_val;
@@ -1832,10 +1836,10 @@ parse_draw_op_element (GMarkupParseContext *context,
op = meta_draw_op_new (META_DRAW_RECTANGLE);
op->data.rectangle.color_spec = color_spec;
- op->data.rectangle.x = meta_draw_spec_new (info->theme, x, NULL);
- op->data.rectangle.y = meta_draw_spec_new (info->theme, y, NULL);
- op->data.rectangle.width = meta_draw_spec_new (info->theme, width, NULL);
- op->data.rectangle.height = meta_draw_spec_new (info->theme,
+ op->data.rectangle.x = meta_draw_spec_new (metacity, x, NULL);
+ op->data.rectangle.y = meta_draw_spec_new (metacity, y, NULL);
+ op->data.rectangle.width = meta_draw_spec_new (metacity, width, NULL);
+ op->data.rectangle.height = meta_draw_spec_new (metacity,
height, NULL);
op->data.rectangle.filled = filled_val;
@@ -1954,10 +1958,10 @@ parse_draw_op_element (GMarkupParseContext *context,
op->data.arc.color_spec = color_spec;
- op->data.arc.x = meta_draw_spec_new (info->theme, x, NULL);
- op->data.arc.y = meta_draw_spec_new (info->theme, y, NULL);
- op->data.arc.width = meta_draw_spec_new (info->theme, width, NULL);
- op->data.arc.height = meta_draw_spec_new (info->theme, height, NULL);
+ op->data.arc.x = meta_draw_spec_new (metacity, x, NULL);
+ op->data.arc.y = meta_draw_spec_new (metacity, y, NULL);
+ op->data.arc.width = meta_draw_spec_new (metacity, width, NULL);
+ op->data.arc.height = meta_draw_spec_new (metacity, height, NULL);
op->data.arc.filled = filled_val;
op->data.arc.start_angle = start_angle_val;
@@ -1986,10 +1990,10 @@ parse_draw_op_element (GMarkupParseContext *context,
op = meta_draw_op_new (META_DRAW_CLIP);
- op->data.clip.x = meta_draw_spec_new (info->theme, x, NULL);
- op->data.clip.y = meta_draw_spec_new (info->theme, y, NULL);
- op->data.clip.width = meta_draw_spec_new (info->theme, width, NULL);
- op->data.clip.height = meta_draw_spec_new (info->theme, height, NULL);
+ op->data.clip.x = meta_draw_spec_new (metacity, x, NULL);
+ op->data.clip.y = meta_draw_spec_new (metacity, y, NULL);
+ op->data.clip.width = meta_draw_spec_new (metacity, width, NULL);
+ op->data.clip.height = meta_draw_spec_new (metacity, height, NULL);
g_assert (info->op_list);
@@ -2040,10 +2044,10 @@ parse_draw_op_element (GMarkupParseContext *context,
op->data.tint.color_spec = color_spec;
op->data.tint.alpha_spec = alpha_spec;
- op->data.tint.x = meta_draw_spec_new (info->theme, x, NULL);
- op->data.tint.y = meta_draw_spec_new (info->theme, y, NULL);
- op->data.tint.width = meta_draw_spec_new (info->theme, width, NULL);
- op->data.tint.height = meta_draw_spec_new (info->theme, height, NULL);
+ op->data.tint.x = meta_draw_spec_new (metacity, x, NULL);
+ op->data.tint.y = meta_draw_spec_new (metacity, y, NULL);
+ op->data.tint.width = meta_draw_spec_new (metacity, width, NULL);
+ op->data.tint.height = meta_draw_spec_new (metacity, height, NULL);
g_assert (info->op_list);
@@ -2087,11 +2091,11 @@ parse_draw_op_element (GMarkupParseContext *context,
g_assert (info->op == NULL);
info->op = meta_draw_op_new (META_DRAW_GRADIENT);
- info->op->data.gradient.x = meta_draw_spec_new (info->theme, x, NULL);
- info->op->data.gradient.y = meta_draw_spec_new (info->theme, y, NULL);
- info->op->data.gradient.width = meta_draw_spec_new (info->theme,
+ info->op->data.gradient.x = meta_draw_spec_new (metacity, x, NULL);
+ info->op->data.gradient.y = meta_draw_spec_new (metacity, y, NULL);
+ info->op->data.gradient.width = meta_draw_spec_new (metacity,
width, NULL);
- info->op->data.gradient.height = meta_draw_spec_new (info->theme,
+ info->op->data.gradient.height = meta_draw_spec_new (metacity,
height, NULL);
info->op->data.gradient.gradient_spec = meta_gradient_spec_new (type_val);
@@ -2183,10 +2187,10 @@ parse_draw_op_element (GMarkupParseContext *context,
op->data.image.pixbuf = pixbuf;
op->data.image.colorize_spec = colorize_spec;
- op->data.image.x = meta_draw_spec_new (info->theme, x, NULL);
- op->data.image.y = meta_draw_spec_new (info->theme, y, NULL);
- op->data.image.width = meta_draw_spec_new (info->theme, width, NULL);
- op->data.image.height = meta_draw_spec_new (info->theme, height, NULL);
+ op->data.image.x = meta_draw_spec_new (metacity, x, NULL);
+ op->data.image.y = meta_draw_spec_new (metacity, y, NULL);
+ op->data.image.width = meta_draw_spec_new (metacity, width, NULL);
+ op->data.image.height = meta_draw_spec_new (metacity, height, NULL);
op->data.image.alpha_spec = alpha_spec;
op->data.image.fill_type = fill_type_val;
@@ -2320,10 +2324,10 @@ parse_draw_op_element (GMarkupParseContext *context,
op = meta_draw_op_new (META_DRAW_GTK_ARROW);
- op->data.gtk_arrow.x = meta_draw_spec_new (info->theme, x, NULL);
- op->data.gtk_arrow.y = meta_draw_spec_new (info->theme, y, NULL);
- op->data.gtk_arrow.width = meta_draw_spec_new (info->theme, width, NULL);
- op->data.gtk_arrow.height = meta_draw_spec_new (info->theme,
+ op->data.gtk_arrow.x = meta_draw_spec_new (metacity, x, NULL);
+ op->data.gtk_arrow.y = meta_draw_spec_new (metacity, y, NULL);
+ op->data.gtk_arrow.width = meta_draw_spec_new (metacity, width, NULL);
+ op->data.gtk_arrow.height = meta_draw_spec_new (metacity,
height, NULL);
op->data.gtk_arrow.filled = filled_val;
@@ -2379,10 +2383,10 @@ parse_draw_op_element (GMarkupParseContext *context,
op = meta_draw_op_new (META_DRAW_GTK_BOX);
- op->data.gtk_box.x = meta_draw_spec_new (info->theme, x, NULL);
- op->data.gtk_box.y = meta_draw_spec_new (info->theme, y, NULL);
- op->data.gtk_box.width = meta_draw_spec_new (info->theme, width, NULL);
- op->data.gtk_box.height = meta_draw_spec_new (info->theme, height, NULL);
+ op->data.gtk_box.x = meta_draw_spec_new (metacity, x, NULL);
+ op->data.gtk_box.y = meta_draw_spec_new (metacity, y, NULL);
+ op->data.gtk_box.width = meta_draw_spec_new (metacity, width, NULL);
+ op->data.gtk_box.height = meta_draw_spec_new (metacity, height, NULL);
op->data.gtk_box.state = state_val;
op->data.gtk_box.shadow = shadow_val;
@@ -2420,9 +2424,9 @@ parse_draw_op_element (GMarkupParseContext *context,
op = meta_draw_op_new (META_DRAW_GTK_VLINE);
- op->data.gtk_vline.x = meta_draw_spec_new (info->theme, x, NULL);
- op->data.gtk_vline.y1 = meta_draw_spec_new (info->theme, y1, NULL);
- op->data.gtk_vline.y2 = meta_draw_spec_new (info->theme, y2, NULL);
+ op->data.gtk_vline.x = meta_draw_spec_new (metacity, x, NULL);
+ op->data.gtk_vline.y1 = meta_draw_spec_new (metacity, y1, NULL);
+ op->data.gtk_vline.y2 = meta_draw_spec_new (metacity, y2, NULL);
op->data.gtk_vline.state = state_val;
@@ -2473,10 +2477,10 @@ parse_draw_op_element (GMarkupParseContext *context,
op = meta_draw_op_new (META_DRAW_ICON);
- op->data.icon.x = meta_draw_spec_new (info->theme, x, NULL);
- op->data.icon.y = meta_draw_spec_new (info->theme, y, NULL);
- op->data.icon.width = meta_draw_spec_new (info->theme, width, NULL);
- op->data.icon.height = meta_draw_spec_new (info->theme, height, NULL);
+ op->data.icon.x = meta_draw_spec_new (metacity, x, NULL);
+ op->data.icon.y = meta_draw_spec_new (metacity, y, NULL);
+ op->data.icon.width = meta_draw_spec_new (metacity, width, NULL);
+ op->data.icon.height = meta_draw_spec_new (metacity, height, NULL);
op->data.icon.alpha_spec = alpha_spec;
op->data.icon.fill_type = fill_type_val;
@@ -2525,10 +2529,10 @@ parse_draw_op_element (GMarkupParseContext *context,
op->data.title.color_spec = color_spec;
- op->data.title.x = meta_draw_spec_new (info->theme, x, NULL);
- op->data.title.y = meta_draw_spec_new (info->theme, y, NULL);
+ op->data.title.x = meta_draw_spec_new (metacity, x, NULL);
+ op->data.title.y = meta_draw_spec_new (metacity, y, NULL);
if (ellipsize_width)
- op->data.title.ellipsize_width = meta_draw_spec_new (info->theme, ellipsize_width, NULL);
+ op->data.title.ellipsize_width = meta_draw_spec_new (metacity, ellipsize_width, NULL);
g_assert (info->op_list);
@@ -2585,12 +2589,12 @@ parse_draw_op_element (GMarkupParseContext *context,
meta_draw_op_list_ref (op_list);
op->data.op_list.op_list = op_list;
- op->data.op_list.x = meta_draw_spec_new (info->theme, x ? x : "0", NULL);
- op->data.op_list.y = meta_draw_spec_new (info->theme, y ? y : "0", NULL);
- op->data.op_list.width = meta_draw_spec_new (info->theme,
+ op->data.op_list.x = meta_draw_spec_new (metacity, x ? x : "0", NULL);
+ op->data.op_list.y = meta_draw_spec_new (metacity, y ? y : "0", NULL);
+ op->data.op_list.width = meta_draw_spec_new (metacity,
width ? width : "width",
NULL);
- op->data.op_list.height = meta_draw_spec_new (info->theme,
+ op->data.op_list.height = meta_draw_spec_new (metacity,
height ? height : "height",
NULL);
@@ -2652,22 +2656,22 @@ parse_draw_op_element (GMarkupParseContext *context,
meta_draw_op_list_ref (op_list);
- op->data.tile.x = meta_draw_spec_new (info->theme, x ? x : "0", NULL);
- op->data.tile.y = meta_draw_spec_new (info->theme, y ? y : "0", NULL);
- op->data.tile.width = meta_draw_spec_new (info->theme,
+ op->data.tile.x = meta_draw_spec_new (metacity, x ? x : "0", NULL);
+ op->data.tile.y = meta_draw_spec_new (metacity, y ? y : "0", NULL);
+ op->data.tile.width = meta_draw_spec_new (metacity,
width ? width : "width",
NULL);
- op->data.tile.height = meta_draw_spec_new (info->theme,
+ op->data.tile.height = meta_draw_spec_new (metacity,
height ? height : "height",
NULL);
- op->data.tile.tile_xoffset = meta_draw_spec_new (info->theme,
+ op->data.tile.tile_xoffset = meta_draw_spec_new (metacity,
tile_xoffset ? tile_xoffset : "0",
NULL);
- op->data.tile.tile_yoffset = meta_draw_spec_new (info->theme,
+ op->data.tile.tile_yoffset = meta_draw_spec_new (metacity,
tile_yoffset ? tile_yoffset : "0",
NULL);
- op->data.tile.tile_width = meta_draw_spec_new (info->theme, tile_width, NULL);
- op->data.tile.tile_height = meta_draw_spec_new (info->theme, tile_height, NULL);
+ op->data.tile.tile_width = meta_draw_spec_new (metacity, tile_width, NULL);
+ op->data.tile.tile_height = meta_draw_spec_new (metacity, tile_height, NULL);
op->data.tile.op_list = op_list;
diff --git a/src/ui/theme-private.h b/src/ui/theme-private.h
index c9cdebaf..ebfc6e8a 100644
--- a/src/ui/theme-private.h
+++ b/src/ui/theme-private.h
@@ -19,6 +19,7 @@
#define META_THEME_PRIVATE_H
#include <libmetacity/meta-color-spec.h>
+#include <libmetacity/meta-draw-spec.h>
#include <libmetacity/meta-gradient-spec.h>
#include <libmetacity/meta-theme-impl.h>
@@ -29,11 +30,9 @@ G_BEGIN_DECLS
typedef struct _MetaDrawInfo MetaDrawInfo;
typedef struct _MetaDrawOp MetaDrawOp;
typedef struct _MetaDrawOpList MetaDrawOpList;
-typedef struct _MetaDrawSpec MetaDrawSpec;
typedef struct _MetaFrameLayout MetaFrameLayout;
typedef struct _MetaFrameStyle MetaFrameStyle;
typedef struct _MetaFrameStyleSet MetaFrameStyleSet;
-typedef struct _MetaPositionExprEnv MetaPositionExprEnv;
/**
* A drawing operation in our simple vector drawing language.
@@ -74,59 +73,6 @@ typedef enum
typedef enum
{
- POS_TOKEN_INT,
- POS_TOKEN_DOUBLE,
- POS_TOKEN_OPERATOR,
- POS_TOKEN_VARIABLE,
- POS_TOKEN_OPEN_PAREN,
- POS_TOKEN_CLOSE_PAREN
-} PosTokenType;
-
-typedef enum
-{
- POS_OP_NONE,
- POS_OP_ADD,
- POS_OP_SUBTRACT,
- POS_OP_MULTIPLY,
- POS_OP_DIVIDE,
- POS_OP_MOD,
- POS_OP_MAX,
- POS_OP_MIN
-} PosOperatorType;
-
-/**
- * A token, as output by the tokeniser.
- *
- * \ingroup tokenizer
- */
-typedef struct
-{
- PosTokenType type;
-
- union
- {
- struct {
- int val;
- } i;
-
- struct {
- double val;
- } d;
-
- struct {
- PosOperatorType op;
- } o;
-
- struct {
- char *name;
- GQuark name_quark;
- } v;
-
- } d;
-} PosToken;
-
-typedef enum
-{
/* Listed in the order in which the textures are drawn.
* (though this only matters for overlaps of course.)
* Buttons are drawn after the frame textures.
@@ -334,55 +280,6 @@ struct _MetaDrawInfo
const MetaFrameGeometry *fgeom;
};
-struct _MetaPositionExprEnv
-{
- GdkRectangle rect;
- /* size of an object being drawn, if it has a natural size */
- int object_width;
- int object_height;
- /* global object sizes, always available */
- int left_width;
- int right_width;
- int top_height;
- int bottom_height;
- int title_width;
- int title_height;
- int frame_x_center;
- int frame_y_center;
- int mini_icon_width;
- int mini_icon_height;
- int icon_width;
- int icon_height;
-};
-
-/**
- * A computed expression in our simple vector drawing language.
- * While it appears to take the form of a tree, this is actually
- * merely a list; concerns such as precedence of operators are
- * currently recomputed on every recalculation.
- *
- * Created by meta_draw_spec_new(), destroyed by meta_draw_spec_free().
- * pos_eval() fills this with ...FIXME. Are tokens a tree or a list?
- * \ingroup parser
- */
-struct _MetaDrawSpec
-{
- /**
- * If this spec is constant, this is the value of the constant;
- * otherwise it is zero.
- */
- int value;
-
- /** A list of tokens in the expression. */
- PosToken *tokens;
-
- /** How many tokens are in the tokens list. */
- int n_tokens;
-
- /** Does the expression contain any variables? */
- gboolean constant : 1;
-};
-
/**
* A single drawing operation in our simple vector drawing language.
*/
@@ -665,11 +562,6 @@ void meta_frame_layout_unref (MetaFrameLayout
gboolean meta_frame_layout_validate (const MetaFrameLayout *layout,
GError **error);
-MetaDrawSpec *meta_draw_spec_new (MetaTheme *theme,
- const char *expr,
- GError **error);
-void meta_draw_spec_free (MetaDrawSpec *spec);
-
MetaDrawOp *meta_draw_op_new (MetaDrawType type);
void meta_draw_op_free (MetaDrawOp *op);
diff --git a/src/ui/theme.c b/src/ui/theme.c
index a0de20a5..10598cfa 100644
--- a/src/ui/theme.c
+++ b/src/ui/theme.c
@@ -1159,1185 +1159,6 @@ meta_frame_layout_calc_geometry (MetaFrameLayout *layout,
fgeom->bottom_right_corner_rounded_radius = layout->bottom_right_corner_rounded_radius;
}
-/**
- * Represents an operation as a string.
- *
- * \param type an operation, such as addition
- * \return a string, such as "+"
- */
-static const char*
-op_name (PosOperatorType type)
-{
- switch (type)
- {
- case POS_OP_ADD:
- return "+";
- case POS_OP_SUBTRACT:
- return "-";
- case POS_OP_MULTIPLY:
- return "*";
- case POS_OP_DIVIDE:
- return "/";
- case POS_OP_MOD:
- return "%";
- case POS_OP_MAX:
- return "`max`";
- case POS_OP_MIN:
- return "`min`";
- case POS_OP_NONE:
- break;
- default:
- break;
- }
-
- return "<unknown>";
-}
-
-/**
- * Parses a string and returns an operation.
- *
- * \param p a pointer into a string representing an operation; part of an
- * expression somewhere, so not null-terminated
- * \param len set to the length of the string found. Set to 0 if none is.
- * \return the operation found. If none was, returns POS_OP_NONE.
- */
-static PosOperatorType
-op_from_string (const char *p,
- int *len)
-{
- *len = 0;
-
- switch (*p)
- {
- case '+':
- *len = 1;
- return POS_OP_ADD;
- case '-':
- *len = 1;
- return POS_OP_SUBTRACT;
- case '*':
- *len = 1;
- return POS_OP_MULTIPLY;
- case '/':
- *len = 1;
- return POS_OP_DIVIDE;
- case '%':
- *len = 1;
- return POS_OP_MOD;
-
- case '`':
- if (strncmp (p, "`max`", 5) == 0)
- {
- *len = 5;
- return POS_OP_MAX;
- }
- else if (strncmp (p, "`min`", 5) == 0)
- {
- *len = 5;
- return POS_OP_MIN;
- }
-
- default:
- break;
- }
-
- return POS_OP_NONE;
-}
-
-/**
- * Frees an array of tokens. All the tokens and their associated memory
- * will be freed.
- *
- * \param tokens an array of tokens to be freed
- * \param n_tokens how many tokens are in the array.
- */
-static void
-free_tokens (PosToken *tokens,
- int n_tokens)
-{
- int i;
-
- /* n_tokens can be 0 since tokens may have been allocated more than
- * it was initialized
- */
-
- for (i = 0; i < n_tokens; i++)
- if (tokens[i].type == POS_TOKEN_VARIABLE)
- g_free (tokens[i].d.v.name);
-
- g_free (tokens);
-}
-
-/**
- * Tokenises a number in an expression.
- *
- * \param p a pointer into a string representing an operation; part of an
- * expression somewhere, so not null-terminated
- * \param end_return set to a pointer to the end of the number found; but
- * not updated if no number was found at all
- * \param next set to either an integer or a float token
- * \param[out] err set to the problem if there was a problem
- * \return TRUE if a valid number was found, FALSE otherwise (and "err" will
- * have been set)
- *
- * \bug The "while (*start)..." part: what's wrong with strchr-ish things?
- * \bug The name is wrong: it doesn't parse anything.
- * \ingroup tokenizer
- */
-static gboolean
-parse_number (const char *p,
- const char **end_return,
- PosToken *next,
- GError **err)
-{
- const char *start = p;
- char *end;
- gboolean is_float;
- char *num_str;
-
- while (*p && (*p == '.' || g_ascii_isdigit (*p)))
- ++p;
-
- if (p == start)
- {
- char buf[7] = { '\0' };
- buf[g_unichar_to_utf8 (g_utf8_get_char (p), buf)] = '\0';
- g_set_error (err, META_THEME_ERROR,
- META_THEME_ERROR_BAD_CHARACTER,
- _("Coordinate expression contains character '%s' which is not allowed"),
- buf);
- return FALSE;
- }
-
- *end_return = p;
-
- /* we need this to exclude floats like "1e6" */
- num_str = g_strndup (start, p - start);
- start = num_str;
- is_float = FALSE;
- while (*start)
- {
- if (*start == '.')
- is_float = TRUE;
- ++start;
- }
-
- if (is_float)
- {
- next->type = POS_TOKEN_DOUBLE;
- next->d.d.val = g_ascii_strtod (num_str, &end);
-
- if (end == num_str)
- {
- g_set_error (err, META_THEME_ERROR,
- META_THEME_ERROR_FAILED,
- _("Coordinate expression contains floating point number '%s' which could not be parsed"),
- num_str);
- g_free (num_str);
- return FALSE;
- }
- }
- else
- {
- next->type = POS_TOKEN_INT;
- next->d.i.val = strtol (num_str, &end, 10);
- if (end == num_str)
- {
- g_set_error (err, META_THEME_ERROR,
- META_THEME_ERROR_FAILED,
- _("Coordinate expression contains integer '%s' which could not be parsed"),
- num_str);
- g_free (num_str);
- return FALSE;
- }
- }
-
- g_free (num_str);
-
- g_assert (next->type == POS_TOKEN_INT || next->type == POS_TOKEN_DOUBLE);
-
- return TRUE;
-}
-
-/**
- * Whether a variable can validly appear as part of the name of a variable.
- */
-#define IS_VARIABLE_CHAR(c) (g_ascii_isalpha ((c)) || (c) == '_')
-
-/**
- * Tokenises an expression.
- *
- * \param expr The expression
- * \param[out] tokens_p The resulting tokens
- * \param[out] n_tokens_p The number of resulting tokens
- * \param[out] err set to the problem if there was a problem
- *
- * \return True if the expression was successfully tokenised; false otherwise.
- *
- * \ingroup tokenizer
- */
-static gboolean
-pos_tokenize (const char *expr,
- PosToken **tokens_p,
- int *n_tokens_p,
- GError **err)
-{
- PosToken *tokens;
- int n_tokens;
- int allocated;
- const char *p;
-
- *tokens_p = NULL;
- *n_tokens_p = 0;
-
- allocated = 3;
- n_tokens = 0;
- tokens = g_new (PosToken, allocated);
-
- p = expr;
- while (*p)
- {
- PosToken *next;
- int len;
-
- if (n_tokens == allocated)
- {
- allocated *= 2;
- tokens = g_renew (PosToken, tokens, allocated);
- }
-
- next = &tokens[n_tokens];
-
- switch (*p)
- {
- case '*':
- case '/':
- case '+':
- case '-': /* negative numbers aren't allowed so this is easy */
- case '%':
- case '`':
- next->type = POS_TOKEN_OPERATOR;
- next->d.o.op = op_from_string (p, &len);
- if (next->d.o.op != POS_OP_NONE)
- {
- ++n_tokens;
- p = p + (len - 1); /* -1 since we ++p later */
- }
- else
- {
- g_set_error (err, META_THEME_ERROR,
- META_THEME_ERROR_FAILED,
- _("Coordinate expression contained unknown operator at the start of this text: \"%s\""),
- p);
-
- goto error;
- }
- break;
-
- case '(':
- next->type = POS_TOKEN_OPEN_PAREN;
- ++n_tokens;
- break;
-
- case ')':
- next->type = POS_TOKEN_CLOSE_PAREN;
- ++n_tokens;
- break;
-
- case ' ':
- case '\t':
- case '\n':
- break;
-
- default:
- if (IS_VARIABLE_CHAR (*p))
- {
- /* Assume variable */
- const char *start = p;
- while (*p && IS_VARIABLE_CHAR (*p))
- ++p;
- g_assert (p != start);
- next->type = POS_TOKEN_VARIABLE;
- next->d.v.name = g_strndup (start, p - start);
- ++n_tokens;
- --p; /* since we ++p again at the end of while loop */
- }
- else
- {
- /* Assume number */
- const char *end;
-
- if (!parse_number (p, &end, next, err))
- goto error;
-
- ++n_tokens;
- p = end - 1; /* -1 since we ++p again at the end of while loop */
- }
-
- break;
- }
-
- ++p;
- }
-
- if (n_tokens == 0)
- {
- g_set_error (err, META_THEME_ERROR,
- META_THEME_ERROR_FAILED,
- _("Coordinate expression was empty or not understood"));
-
- goto error;
- }
-
- *tokens_p = tokens;
- *n_tokens_p = n_tokens;
-
- return TRUE;
-
- error:
- g_assert (err == NULL || *err != NULL);
-
- free_tokens (tokens, n_tokens);
- return FALSE;
-}
-
-/**
- * The type of a PosExpr: either integer, double, or an operation.
- * \ingroup parser
- */
-typedef enum
-{
- POS_EXPR_INT,
- POS_EXPR_DOUBLE,
- POS_EXPR_OPERATOR
-} PosExprType;
-
-/**
- * Type and value of an expression in a parsed sequence. We don't
- * keep expressions in a tree; if this is of type POS_EXPR_OPERATOR,
- * the arguments of the operator will be in the array positions
- * immediately preceding and following this operator; they cannot
- * themselves be operators.
- *
- * \bug operator is char; it should really be of PosOperatorType.
- * \ingroup parser
- */
-typedef struct
-{
- PosExprType type;
- union
- {
- double double_val;
- int int_val;
- char operator;
- } d;
-} PosExpr;
-
-static gboolean
-do_operation (PosExpr *a,
- PosExpr *b,
- PosOperatorType op,
- GError **err)
-{
- /* Promote types to double if required */
- if (a->type == POS_EXPR_DOUBLE ||
- b->type == POS_EXPR_DOUBLE)
- {
- if (a->type != POS_EXPR_DOUBLE)
- {
- a->type = POS_EXPR_DOUBLE;
- a->d.double_val = a->d.int_val;
- }
- if (b->type != POS_EXPR_DOUBLE)
- {
- b->type = POS_EXPR_DOUBLE;
- b->d.double_val = b->d.int_val;
- }
- }
-
- g_assert (a->type == b->type);
-
- if (a->type == POS_EXPR_INT)
- {
- switch (op)
- {
- case POS_OP_MULTIPLY:
- a->d.int_val = a->d.int_val * b->d.int_val;
- break;
- case POS_OP_DIVIDE:
- if (b->d.int_val == 0)
- {
- g_set_error (err, META_THEME_ERROR,
- META_THEME_ERROR_DIVIDE_BY_ZERO,
- _("Coordinate expression results in division by zero"));
- return FALSE;
- }
- a->d.int_val = a->d.int_val / b->d.int_val;
- break;
- case POS_OP_MOD:
- if (b->d.int_val == 0)
- {
- g_set_error (err, META_THEME_ERROR,
- META_THEME_ERROR_DIVIDE_BY_ZERO,
- _("Coordinate expression results in division by zero"));
- return FALSE;
- }
- a->d.int_val = a->d.int_val % b->d.int_val;
- break;
- case POS_OP_ADD:
- a->d.int_val = a->d.int_val + b->d.int_val;
- break;
- case POS_OP_SUBTRACT:
- a->d.int_val = a->d.int_val - b->d.int_val;
- break;
- case POS_OP_MAX:
- a->d.int_val = MAX (a->d.int_val, b->d.int_val);
- break;
- case POS_OP_MIN:
- a->d.int_val = MIN (a->d.int_val, b->d.int_val);
- break;
- case POS_OP_NONE:
- default:
- g_assert_not_reached ();
- break;
- }
- }
- else if (a->type == POS_EXPR_DOUBLE)
- {
- switch (op)
- {
- case POS_OP_MULTIPLY:
- a->d.double_val = a->d.double_val * b->d.double_val;
- break;
- case POS_OP_DIVIDE:
- if (b->d.double_val == 0.0)
- {
- g_set_error (err, META_THEME_ERROR,
- META_THEME_ERROR_DIVIDE_BY_ZERO,
- _("Coordinate expression results in division by zero"));
- return FALSE;
- }
- a->d.double_val = a->d.double_val / b->d.double_val;
- break;
- case POS_OP_MOD:
- g_set_error (err, META_THEME_ERROR,
- META_THEME_ERROR_MOD_ON_FLOAT,
- _("Coordinate expression tries to use mod operator on a floating-point number"));
- return FALSE;
- case POS_OP_ADD:
- a->d.double_val = a->d.double_val + b->d.double_val;
- break;
- case POS_OP_SUBTRACT:
- a->d.double_val = a->d.double_val - b->d.double_val;
- break;
- case POS_OP_MAX:
- a->d.double_val = MAX (a->d.double_val, b->d.double_val);
- break;
- case POS_OP_MIN:
- a->d.double_val = MIN (a->d.double_val, b->d.double_val);
- break;
- case POS_OP_NONE:
- default:
- g_assert_not_reached ();
- break;
- }
- }
- else
- g_assert_not_reached ();
-
- return TRUE;
-}
-
-static gboolean
-do_operations (PosExpr *exprs,
- int *n_exprs,
- int precedence,
- GError **err)
-{
- int i;
-
- i = 1;
- while (i < *n_exprs)
- {
- gboolean compress;
-
- /* exprs[i-1] first operand
- * exprs[i] operator
- * exprs[i+1] second operand
- *
- * we replace first operand with result of mul/div/mod,
- * or skip over operator and second operand if we have
- * an add/subtract
- */
-
- if (exprs[i-1].type == POS_EXPR_OPERATOR)
- {
- g_set_error (err, META_THEME_ERROR,
- META_THEME_ERROR_FAILED,
- _("Coordinate expression has an operator \"%s\" where an operand was expected"),
- op_name (exprs[i-1].d.operator));
- return FALSE;
- }
-
- if (exprs[i].type != POS_EXPR_OPERATOR)
- {
- g_set_error (err, META_THEME_ERROR,
- META_THEME_ERROR_FAILED,
- _("Coordinate expression had an operand where an operator was expected"));
- return FALSE;
- }
-
- if (i == (*n_exprs - 1))
- {
- g_set_error (err, META_THEME_ERROR,
- META_THEME_ERROR_FAILED,
- _("Coordinate expression ended with an operator instead of an operand"));
- return FALSE;
- }
-
- g_assert ((i+1) < *n_exprs);
-
- if (exprs[i+1].type == POS_EXPR_OPERATOR)
- {
- g_set_error (err, META_THEME_ERROR,
- META_THEME_ERROR_FAILED,
- _("Coordinate expression has operator \"%c\" following operator \"%c\" with no operand in between"),
- exprs[i+1].d.operator,
- exprs[i].d.operator);
- return FALSE;
- }
-
- compress = FALSE;
-
- switch (precedence)
- {
- case 2:
- switch (exprs[i].d.operator)
- {
- case POS_OP_DIVIDE:
- case POS_OP_MOD:
- case POS_OP_MULTIPLY:
- compress = TRUE;
- if (!do_operation (&exprs[i-1], &exprs[i+1],
- exprs[i].d.operator,
- err))
- return FALSE;
- break;
- default:
- break;
- }
- break;
- case 1:
- switch (exprs[i].d.operator)
- {
- case POS_OP_ADD:
- case POS_OP_SUBTRACT:
- compress = TRUE;
- if (!do_operation (&exprs[i-1], &exprs[i+1],
- exprs[i].d.operator,
- err))
- return FALSE;
- break;
- default:
- break;
- }
- break;
- /* I have no rationale at all for making these low-precedence */
- case 0:
- switch (exprs[i].d.operator)
- {
- case POS_OP_MAX:
- case POS_OP_MIN:
- compress = TRUE;
- if (!do_operation (&exprs[i-1], &exprs[i+1],
- exprs[i].d.operator,
- err))
- return FALSE;
- break;
- default:
- break;
- }
- break;
- default:
- break;
- }
-
- if (compress)
- {
- /* exprs[i-1] first operand (now result)
- * exprs[i] operator
- * exprs[i+1] second operand
- * exprs[i+2] new operator
- *
- * we move new operator just after first operand
- */
- if ((i+2) < *n_exprs)
- {
- g_memmove (&exprs[i], &exprs[i+2],
- sizeof (PosExpr) * (*n_exprs - i - 2));
- }
-
- *n_exprs -= 2;
- }
- else
- {
- /* Skip operator and next operand */
- i += 2;
- }
- }
-
- return TRUE;
-}
-
-/**
- * pos_eval_get_variable:
- * @token: The token representing a variable
- * @result: (out): The value of that variable; not set if the token did not
- * represent a known variable
- * @env: The environment within which @token should be evaluated
- * @err: (out): Set to the problem if there was a problem
- *
- * There is a predefined set of variables which can appear in an expression.
- * Here we take a token representing a variable, and return the current value
- * of that variable in a particular environment.
- * (The value is always an integer.)
- *
- * Returns: %TRUE if we found the variable asked for, %FALSE if we didn't
- */
-static gboolean
-pos_eval_get_variable (const PosToken *token,
- int *result,
- const MetaPositionExprEnv *env,
- GError **err)
-{
- GQuark quark;
-
- quark = token->d.v.name_quark;
-
- if (quark == g_quark_from_static_string ("width"))
- {
- *result = env->rect.width;
- }
- else if (quark == g_quark_from_static_string ("height"))
- {
- *result = env->rect.height;
- }
- else if (env->object_width >= 0 &&
- quark == g_quark_from_static_string ("object_width"))
- {
- *result = env->object_width;
- }
- else if (env->object_height >= 0 &&
- quark == g_quark_from_static_string ("object_height"))
- {
- *result = env->object_height;
- }
- else if (quark == g_quark_from_static_string ("left_width"))
- {
- *result = env->left_width;
- }
- else if (quark == g_quark_from_static_string ("right_width"))
- {
- *result = env->right_width;
- }
- else if (quark == g_quark_from_static_string ("top_height"))
- {
- *result = env->top_height;
- }
- else if (quark == g_quark_from_static_string ("bottom_height"))
- {
- *result = env->bottom_height;
- }
- else if (quark == g_quark_from_static_string ("mini_icon_width"))
- {
- *result = env->mini_icon_width;
- }
- else if (quark == g_quark_from_static_string ("mini_icon_height"))
- {
- *result = env->mini_icon_height;
- }
- else if (quark == g_quark_from_static_string ("icon_width"))
- {
- *result = env->icon_width;
- }
- else if (quark == g_quark_from_static_string ("icon_height"))
- {
- *result = env->icon_height;
- }
- else if (quark == g_quark_from_static_string ("title_width"))
- {
- *result = env->title_width;
- }
- else if (quark == g_quark_from_static_string ("title_height"))
- {
- *result = env->title_height;
- }
- else if (quark == g_quark_from_static_string ("frame_x_center"))
- {
- *result = env->frame_x_center;
- }
- else if (quark == g_quark_from_static_string ("frame_y_center"))
- {
- *result = env->frame_y_center;
- }
- else
- {
- g_set_error (err, META_THEME_ERROR, META_THEME_ERROR_UNKNOWN_VARIABLE,
- _("Coordinate expression had unknown variable or constant '%s'"),
- token->d.v.name);
-
- return FALSE;
- }
-
- return TRUE;
-}
-
-/**
- * Evaluates a sequence of tokens within a particular environment context,
- * and returns the current value. May recur if parantheses are found.
- *
- * \param tokens A list of tokens to evaluate.
- * \param n_tokens How many tokens are in the list.
- * \param env The environment context in which to evaluate the expression.
- * \param[out] result The current value of the expression
- *
- * \bug Yes, we really do reparse the expression every time it's evaluated.
- * We should keep the parse tree around all the time and just
- * run the new values through it.
- * \ingroup parser
- */
-static gboolean
-pos_eval_helper (PosToken *tokens,
- int n_tokens,
- const MetaPositionExprEnv *env,
- PosExpr *result,
- GError **err)
-{
- /* Lazy-ass hardcoded limit on number of terms in expression */
-#define MAX_EXPRS 32
- int paren_level;
- int first_paren;
- int i;
- PosExpr exprs[MAX_EXPRS];
- int n_exprs;
- int precedence;
-
- /* Our first goal is to get a list of PosExpr, essentially
- * substituting variables and handling parentheses.
- */
-
- first_paren = 0;
- paren_level = 0;
- n_exprs = 0;
- for (i = 0; i < n_tokens; i++)
- {
- PosToken *t = &tokens[i];
-
- if (n_exprs >= MAX_EXPRS)
- {
- g_set_error (err, META_THEME_ERROR,
- META_THEME_ERROR_FAILED,
- _("Coordinate expression parser overflowed its buffer."));
- return FALSE;
- }
-
- if (paren_level == 0)
- {
- switch (t->type)
- {
- case POS_TOKEN_INT:
- exprs[n_exprs].type = POS_EXPR_INT;
- exprs[n_exprs].d.int_val = t->d.i.val;
- ++n_exprs;
- break;
-
- case POS_TOKEN_DOUBLE:
- exprs[n_exprs].type = POS_EXPR_DOUBLE;
- exprs[n_exprs].d.double_val = t->d.d.val;
- ++n_exprs;
- break;
-
- case POS_TOKEN_OPEN_PAREN:
- ++paren_level;
- if (paren_level == 1)
- first_paren = i;
- break;
-
- case POS_TOKEN_CLOSE_PAREN:
- g_set_error (err, META_THEME_ERROR,
- META_THEME_ERROR_BAD_PARENS,
- _("Coordinate expression had a close parenthesis with no open parenthesis"));
- return FALSE;
-
- case POS_TOKEN_VARIABLE:
- exprs[n_exprs].type = POS_EXPR_INT;
-
- /* FIXME we should just dump all this crap
- * in a hash, maybe keep width/height out
- * for optimization purposes
- */
- if (!pos_eval_get_variable (t, &exprs[n_exprs].d.int_val, env, err))
- return FALSE;
-
- ++n_exprs;
- break;
-
- case POS_TOKEN_OPERATOR:
- exprs[n_exprs].type = POS_EXPR_OPERATOR;
- exprs[n_exprs].d.operator = t->d.o.op;
- ++n_exprs;
- break;
-
- default:
- break;
- }
- }
- else
- {
- g_assert (paren_level > 0);
-
- switch (t->type)
- {
- case POS_TOKEN_INT:
- case POS_TOKEN_DOUBLE:
- case POS_TOKEN_VARIABLE:
- case POS_TOKEN_OPERATOR:
- break;
-
- case POS_TOKEN_OPEN_PAREN:
- ++paren_level;
- break;
-
- case POS_TOKEN_CLOSE_PAREN:
- if (paren_level == 1)
- {
- /* We closed a toplevel paren group, so recurse */
- if (!pos_eval_helper (&tokens[first_paren+1],
- i - first_paren - 1,
- env,
- &exprs[n_exprs],
- err))
- return FALSE;
-
- ++n_exprs;
- }
-
- --paren_level;
- break;
-
- default:
- break;
- }
- }
- }
-
- if (paren_level > 0)
- {
- g_set_error (err, META_THEME_ERROR,
- META_THEME_ERROR_BAD_PARENS,
- _("Coordinate expression had an open parenthesis with no close parenthesis"));
- return FALSE;
- }
-
- /* Now we have no parens and no vars; so we just do all the multiplies
- * and divides, then all the add and subtract.
- */
- if (n_exprs == 0)
- {
- g_set_error (err, META_THEME_ERROR,
- META_THEME_ERROR_FAILED,
- _("Coordinate expression doesn't seem to have any operators or operands"));
- return FALSE;
- }
-
- /* precedence 1 ops */
- precedence = 2;
- while (precedence >= 0)
- {
- if (!do_operations (exprs, &n_exprs, precedence, err))
- return FALSE;
- --precedence;
- }
-
- g_assert (n_exprs == 1);
-
- *result = *exprs;
-
- return TRUE;
-}
-
-/*
- * expr = int | double | expr * expr | expr / expr |
- * expr + expr | expr - expr | (expr)
- *
- * so very not worth fooling with bison, yet so very painful by hand.
- */
-/**
- * Evaluates an expression.
- *
- * \param spec The expression to evaluate.
- * \param env The environment context to evaluate the expression in.
- * \param[out] val_p The integer value of the expression; if the expression
- * is of type float, this will be rounded. If we return
- * FALSE because the expression is invalid, this will be
- * zero.
- * \param[out] err The error, if anything went wrong.
- *
- * \return True if we evaluated the expression successfully; false otherwise.
- *
- * \bug Shouldn't spec be const?
- * \ingroup parser
- */
-static gboolean
-pos_eval (MetaDrawSpec *spec,
- const MetaPositionExprEnv *env,
- int *val_p,
- GError **err)
-{
- PosExpr expr;
-
- *val_p = 0;
-
- if (pos_eval_helper (spec->tokens, spec->n_tokens, env, &expr, err))
- {
- switch (expr.type)
- {
- case POS_EXPR_INT:
- *val_p = expr.d.int_val;
- break;
- case POS_EXPR_DOUBLE:
- *val_p = expr.d.double_val;
- break;
- case POS_EXPR_OPERATOR:
- default:
- g_assert_not_reached ();
- break;
- }
- return TRUE;
- }
- else
- {
- return FALSE;
- }
-}
-
-/* We always return both X and Y, but only one will be meaningful in
- * most contexts.
- */
-
-static gboolean
-meta_parse_position_expression (MetaDrawSpec *spec,
- const MetaPositionExprEnv *env,
- int *x_return,
- int *y_return,
- GError **err)
-{
- /* All positions are in a coordinate system with x, y at the origin.
- * The expression can have -, +, *, / as operators, floating point
- * or integer constants, and the variables "width" and "height" and
- * optionally "object_width" and object_height". Negative numbers
- * aren't allowed.
- */
- int val;
-
- if (spec->constant)
- val = spec->value;
- else
- {
- if (pos_eval (spec, env, &spec->value, err) == FALSE)
- {
- g_assert (err == NULL || *err != NULL);
- return FALSE;
- }
-
- val = spec->value;
- }
-
- if (x_return)
- *x_return = env->rect.x + val;
- if (y_return)
- *y_return = env->rect.y + val;
-
- return TRUE;
-}
-
-
-static gboolean
-meta_parse_size_expression (MetaDrawSpec *spec,
- const MetaPositionExprEnv *env,
- int *val_return,
- GError **err)
-{
- int val;
-
- if (spec->constant)
- val = spec->value;
- else
- {
- if (pos_eval (spec, env, &spec->value, err) == FALSE)
- {
- g_assert (err == NULL || *err != NULL);
- return FALSE;
- }
-
- val = spec->value;
- }
-
- if (val_return)
- *val_return = MAX (val, 1); /* require that sizes be at least 1x1 */
-
- return TRUE;
-}
-
-/* To do this we tokenize, replace variable tokens
- * that are constants, then reassemble. The purpose
- * here is to optimize expressions so we don't do hash
- * lookups to eval them. Obviously it's a tradeoff that
- * slows down theme load times.
- */
-static gboolean
-meta_theme_replace_constants (MetaTheme *theme,
- PosToken *tokens,
- int n_tokens,
- GError **err)
-{
- int i;
- double dval;
- int ival;
- gboolean is_constant = TRUE;
-
- /* Loop through tokenized string looking for variables to replace */
- for (i = 0; i < n_tokens; i++)
- {
- PosToken *t = &tokens[i];
-
- if (t->type == POS_TOKEN_VARIABLE)
- {
- if (meta_theme_metacity_lookup_int (META_THEME_METACITY (theme->impl),
- t->d.v.name, &ival))
- {
- g_free (t->d.v.name);
- t->type = POS_TOKEN_INT;
- t->d.i.val = ival;
- }
- else if (meta_theme_metacity_lookup_float (META_THEME_METACITY (theme->impl),
- t->d.v.name, &dval))
- {
- g_free (t->d.v.name);
- t->type = POS_TOKEN_DOUBLE;
- t->d.d.val = dval;
- }
- else
- {
- /* If we've found a variable that cannot be replaced then the
- expression is not a constant expression and we want to
- replace it with a GQuark */
-
- t->d.v.name_quark = g_quark_from_string (t->d.v.name);
- is_constant = FALSE;
- }
- }
- }
-
- return is_constant;
-}
-
-static int
-parse_x_position_unchecked (MetaDrawSpec *spec,
- const MetaPositionExprEnv *env)
-{
- int retval;
- GError *error;
-
- retval = 0;
- error = NULL;
- if (!meta_parse_position_expression (spec, env, &retval, NULL, &error))
- {
- meta_warning (_("Theme contained an expression that resulted in an error: %s\n"),
- error->message);
-
- g_error_free (error);
- }
-
- return retval;
-}
-
-static int
-parse_y_position_unchecked (MetaDrawSpec *spec,
- const MetaPositionExprEnv *env)
-{
- int retval;
- GError *error;
-
- retval = 0;
- error = NULL;
- if (!meta_parse_position_expression (spec, env, NULL, &retval, &error))
- {
- meta_warning (_("Theme contained an expression that resulted in an error: %s\n"),
- error->message);
-
- g_error_free (error);
- }
-
- return retval;
-}
-
-static int
-parse_size_unchecked (MetaDrawSpec *spec,
- MetaPositionExprEnv *env)
-{
- int retval;
- GError *error;
-
- retval = 0;
- error = NULL;
- if (!meta_parse_size_expression (spec, env, &retval, &error))
- {
- meta_warning (_("Theme contained an expression that resulted in an error: %s\n"),
- error->message);
-
- g_error_free (error);
- }
-
- return retval;
-}
-
-void
-meta_draw_spec_free (MetaDrawSpec *spec)
-{
- if (!spec) return;
- free_tokens (spec->tokens, spec->n_tokens);
- g_slice_free (MetaDrawSpec, spec);
-}
-
-MetaDrawSpec *
-meta_draw_spec_new (MetaTheme *theme,
- const char *expr,
- GError **error)
-{
- MetaDrawSpec *spec;
-
- spec = g_slice_new0 (MetaDrawSpec);
-
- pos_tokenize (expr, &spec->tokens, &spec->n_tokens, NULL);
-
- spec->constant = meta_theme_replace_constants (theme, spec->tokens,
- spec->n_tokens, NULL);
- if (spec->constant)
- {
- gboolean result;
-
- result = pos_eval (spec, NULL, &spec->value, error);
- if (result == FALSE)
- {
- meta_draw_spec_free (spec);
- return NULL;
- }
- }
-
- return spec;
-}
-
MetaDrawOp*
meta_draw_op_new (MetaDrawType type)
{
@@ -3025,8 +1846,8 @@ meta_draw_op_draw_with_env (const MetaDrawOp *op,
cairo_set_dash (cr, dash_list, 2, 0);
}
- x1 = parse_x_position_unchecked (op->data.line.x1, env);
- y1 = parse_y_position_unchecked (op->data.line.y1, env);
+ x1 = meta_draw_spec_parse_x_position (op->data.line.x1, env);
+ y1 = meta_draw_spec_parse_y_position (op->data.line.y1, env);
if (!op->data.line.x2 &&
!op->data.line.y2 &&
@@ -3038,12 +1859,12 @@ meta_draw_op_draw_with_env (const MetaDrawOp *op,
else
{
if (op->data.line.x2)
- x2 = parse_x_position_unchecked (op->data.line.x2, env);
+ x2 = meta_draw_spec_parse_x_position (op->data.line.x2, env);
else
x2 = x1;
if (op->data.line.y2)
- y2 = parse_y_position_unchecked (op->data.line.y2, env);
+ y2 = meta_draw_spec_parse_y_position (op->data.line.y2, env);
else
y2 = y1;
@@ -3088,10 +1909,10 @@ meta_draw_op_draw_with_env (const MetaDrawOp *op,
meta_color_spec_render (op->data.rectangle.color_spec, style_gtk, &color);
gdk_cairo_set_source_rgba (cr, &color);
- rx = parse_x_position_unchecked (op->data.rectangle.x, env);
- ry = parse_y_position_unchecked (op->data.rectangle.y, env);
- rwidth = parse_size_unchecked (op->data.rectangle.width, env);
- rheight = parse_size_unchecked (op->data.rectangle.height, env);
+ rx = meta_draw_spec_parse_x_position (op->data.rectangle.x, env);
+ ry = meta_draw_spec_parse_y_position (op->data.rectangle.y, env);
+ rwidth = meta_draw_spec_parse_size (op->data.rectangle.width, env);
+ rheight = meta_draw_spec_parse_size (op->data.rectangle.height, env);
/* Filled and stroked rectangles are the other cases
* we pixel-align to X rasterization
@@ -3118,10 +1939,10 @@ meta_draw_op_draw_with_env (const MetaDrawOp *op,
meta_color_spec_render (op->data.arc.color_spec, style_gtk, &color);
gdk_cairo_set_source_rgba (cr, &color);
- rx = parse_x_position_unchecked (op->data.arc.x, env);
- ry = parse_y_position_unchecked (op->data.arc.y, env);
- rwidth = parse_size_unchecked (op->data.arc.width, env);
- rheight = parse_size_unchecked (op->data.arc.height, env);
+ rx = meta_draw_spec_parse_x_position (op->data.arc.x, env);
+ ry = meta_draw_spec_parse_y_position (op->data.arc.y, env);
+ rwidth = meta_draw_spec_parse_size (op->data.arc.width, env);
+ rheight = meta_draw_spec_parse_size (op->data.arc.height, env);
start_angle = op->data.arc.start_angle * (M_PI / 180.)
- (.5 * M_PI); /* start at 12 instead of 3 oclock */
@@ -3161,10 +1982,10 @@ meta_draw_op_draw_with_env (const MetaDrawOp *op,
needs_alpha = meta_alpha_gradient_spec_needs_alpha (op->data.tint.alpha_spec);
- rx = parse_x_position_unchecked (op->data.tint.x, env);
- ry = parse_y_position_unchecked (op->data.tint.y, env);
- rwidth = parse_size_unchecked (op->data.tint.width, env);
- rheight = parse_size_unchecked (op->data.tint.height, env);
+ rx = meta_draw_spec_parse_x_position (op->data.tint.x, env);
+ ry = meta_draw_spec_parse_y_position (op->data.tint.y, env);
+ rwidth = meta_draw_spec_parse_size (op->data.tint.width, env);
+ rheight = meta_draw_spec_parse_size (op->data.tint.height, env);
if (!needs_alpha)
{
@@ -3197,10 +2018,10 @@ meta_draw_op_draw_with_env (const MetaDrawOp *op,
int rx, ry, rwidth, rheight;
GdkPixbuf *pixbuf;
- rx = parse_x_position_unchecked (op->data.gradient.x, env);
- ry = parse_y_position_unchecked (op->data.gradient.y, env);
- rwidth = parse_size_unchecked (op->data.gradient.width, env);
- rheight = parse_size_unchecked (op->data.gradient.height, env);
+ rx = meta_draw_spec_parse_x_position (op->data.gradient.x, env);
+ ry = meta_draw_spec_parse_y_position (op->data.gradient.y, env);
+ rwidth = meta_draw_spec_parse_size (op->data.gradient.width, env);
+ rheight = meta_draw_spec_parse_size (op->data.gradient.height, env);
pixbuf = draw_op_as_pixbuf (op, style_gtk, info,
rwidth, rheight);
@@ -3226,16 +2047,16 @@ meta_draw_op_draw_with_env (const MetaDrawOp *op,
env->object_height = gdk_pixbuf_get_height (op->data.image.pixbuf);
}
- rwidth = parse_size_unchecked (op->data.image.width, env);
- rheight = parse_size_unchecked (op->data.image.height, env);
+ rwidth = meta_draw_spec_parse_size (op->data.image.width, env);
+ rheight = meta_draw_spec_parse_size (op->data.image.height, env);
pixbuf = draw_op_as_pixbuf (op, style_gtk, info,
rwidth, rheight);
if (pixbuf)
{
- rx = parse_x_position_unchecked (op->data.image.x, env);
- ry = parse_y_position_unchecked (op->data.image.y, env);
+ rx = meta_draw_spec_parse_x_position (op->data.image.x, env);
+ ry = meta_draw_spec_parse_y_position (op->data.image.y, env);
gdk_cairo_set_source_pixbuf (cr, pixbuf, rx, ry);
cairo_paint (cr);
@@ -3250,10 +2071,10 @@ meta_draw_op_draw_with_env (const MetaDrawOp *op,
int rx, ry, rwidth, rheight;
double angle = 0, size;
- rx = parse_x_position_unchecked (op->data.gtk_arrow.x, env);
- ry = parse_y_position_unchecked (op->data.gtk_arrow.y, env);
- rwidth = parse_size_unchecked (op->data.gtk_arrow.width, env);
- rheight = parse_size_unchecked (op->data.gtk_arrow.height, env);
+ rx = meta_draw_spec_parse_x_position (op->data.gtk_arrow.x, env);
+ ry = meta_draw_spec_parse_y_position (op->data.gtk_arrow.y, env);
+ rwidth = meta_draw_spec_parse_size (op->data.gtk_arrow.width, env);
+ rheight = meta_draw_spec_parse_size (op->data.gtk_arrow.height, env);
size = MAX(rwidth, rheight);
@@ -3286,10 +2107,10 @@ meta_draw_op_draw_with_env (const MetaDrawOp *op,
{
int rx, ry, rwidth, rheight;
- rx = parse_x_position_unchecked (op->data.gtk_box.x, env);
- ry = parse_y_position_unchecked (op->data.gtk_box.y, env);
- rwidth = parse_size_unchecked (op->data.gtk_box.width, env);
- rheight = parse_size_unchecked (op->data.gtk_box.height, env);
+ rx = meta_draw_spec_parse_x_position (op->data.gtk_box.x, env);
+ ry = meta_draw_spec_parse_y_position (op->data.gtk_box.y, env);
+ rwidth = meta_draw_spec_parse_size (op->data.gtk_box.width, env);
+ rheight = meta_draw_spec_parse_size (op->data.gtk_box.height, env);
gtk_style_context_set_state (style_gtk, op->data.gtk_box.state);
gtk_render_background (style_gtk, cr, rx, ry, rwidth, rheight);
@@ -3301,9 +2122,9 @@ meta_draw_op_draw_with_env (const MetaDrawOp *op,
{
int rx, ry1, ry2;
- rx = parse_x_position_unchecked (op->data.gtk_vline.x, env);
- ry1 = parse_y_position_unchecked (op->data.gtk_vline.y1, env);
- ry2 = parse_y_position_unchecked (op->data.gtk_vline.y2, env);
+ rx = meta_draw_spec_parse_x_position (op->data.gtk_vline.x, env);
+ ry1 = meta_draw_spec_parse_y_position (op->data.gtk_vline.y1, env);
+ ry2 = meta_draw_spec_parse_y_position (op->data.gtk_vline.y2, env);
gtk_style_context_set_state (style_gtk, op->data.gtk_vline.state);
gtk_render_line (style_gtk, cr, rx, ry1, rx, ry2);
@@ -3315,16 +2136,16 @@ meta_draw_op_draw_with_env (const MetaDrawOp *op,
int rx, ry, rwidth, rheight;
GdkPixbuf *pixbuf;
- rwidth = parse_size_unchecked (op->data.icon.width, env);
- rheight = parse_size_unchecked (op->data.icon.height, env);
+ rwidth = meta_draw_spec_parse_size (op->data.icon.width, env);
+ rheight = meta_draw_spec_parse_size (op->data.icon.height, env);
pixbuf = draw_op_as_pixbuf (op, style_gtk, info,
rwidth, rheight);
if (pixbuf)
{
- rx = parse_x_position_unchecked (op->data.icon.x, env);
- ry = parse_y_position_unchecked (op->data.icon.y, env);
+ rx = meta_draw_spec_parse_x_position (op->data.icon.x, env);
+ ry = meta_draw_spec_parse_y_position (op->data.icon.y, env);
gdk_cairo_set_source_pixbuf (cr, pixbuf, rx, ry);
cairo_paint (cr);
@@ -3343,16 +2164,16 @@ meta_draw_op_draw_with_env (const MetaDrawOp *op,
meta_color_spec_render (op->data.title.color_spec, style_gtk, &color);
gdk_cairo_set_source_rgba (cr, &color);
- rx = parse_x_position_unchecked (op->data.title.x, env);
- ry = parse_y_position_unchecked (op->data.title.y, env);
+ rx = meta_draw_spec_parse_x_position (op->data.title.x, env);
+ ry = meta_draw_spec_parse_y_position (op->data.title.y, env);
if (op->data.title.ellipsize_width)
{
int ellipsize_width;
int right_bearing;
- ellipsize_width = parse_x_position_unchecked (op->data.title.ellipsize_width, env);
- /* HACK: parse_x_position_unchecked adds in env->rect.x, subtract out again */
+ ellipsize_width = meta_draw_spec_parse_x_position (op->data.title.ellipsize_width, env);
+ /* HACK: meta_draw_spec_parse_x_position adds in env->rect.x, subtract out again */
ellipsize_width -= env->rect.x;
pango_layout_set_width (info->title_layout, -1);
@@ -3418,10 +2239,10 @@ meta_draw_op_draw_with_env (const MetaDrawOp *op,
{
GdkRectangle d_rect;
- d_rect.x = parse_x_position_unchecked (op->data.op_list.x, env);
- d_rect.y = parse_y_position_unchecked (op->data.op_list.y, env);
- d_rect.width = parse_size_unchecked (op->data.op_list.width, env);
- d_rect.height = parse_size_unchecked (op->data.op_list.height, env);
+ d_rect.x = meta_draw_spec_parse_x_position (op->data.op_list.x, env);
+ d_rect.y = meta_draw_spec_parse_y_position (op->data.op_list.y, env);
+ d_rect.width = meta_draw_spec_parse_size (op->data.op_list.width, env);
+ d_rect.height = meta_draw_spec_parse_size (op->data.op_list.height, env);
meta_draw_op_list_draw_with_style (op->data.op_list.op_list,
style_gtk, cr, info,
@@ -3435,24 +2256,24 @@ meta_draw_op_draw_with_env (const MetaDrawOp *op,
int tile_xoffset, tile_yoffset;
GdkRectangle tile;
- rx = parse_x_position_unchecked (op->data.tile.x, env);
- ry = parse_y_position_unchecked (op->data.tile.y, env);
- rwidth = parse_size_unchecked (op->data.tile.width, env);
- rheight = parse_size_unchecked (op->data.tile.height, env);
+ rx = meta_draw_spec_parse_x_position (op->data.tile.x, env);
+ ry = meta_draw_spec_parse_y_position (op->data.tile.y, env);
+ rwidth = meta_draw_spec_parse_size (op->data.tile.width, env);
+ rheight = meta_draw_spec_parse_size (op->data.tile.height, env);
cairo_save (cr);
cairo_rectangle (cr, rx, ry, rwidth, rheight);
cairo_clip (cr);
- tile_xoffset = parse_x_position_unchecked (op->data.tile.tile_xoffset, env);
- tile_yoffset = parse_y_position_unchecked (op->data.tile.tile_yoffset, env);
+ tile_xoffset = meta_draw_spec_parse_x_position (op->data.tile.tile_xoffset, env);
+ tile_yoffset = meta_draw_spec_parse_y_position (op->data.tile.tile_yoffset, env);
/* tile offset should not include x/y */
tile_xoffset -= rect.x;
tile_yoffset -= rect.y;
- tile.width = parse_size_unchecked (op->data.tile.tile_width, env);
- tile.height = parse_size_unchecked (op->data.tile.tile_height, env);
+ tile.width = meta_draw_spec_parse_size (op->data.tile.tile_width, env);
+ tile.height = meta_draw_spec_parse_size (op->data.tile.tile_height, env);
tile.x = rx - tile_xoffset;
@@ -3562,10 +2383,10 @@ meta_draw_op_list_draw_with_style (const MetaDrawOpList *op_list,
cairo_restore (cr);
cairo_rectangle (cr,
- parse_x_position_unchecked (op->data.clip.x, &env),
- parse_y_position_unchecked (op->data.clip.y, &env),
- parse_size_unchecked (op->data.clip.width, &env),
- parse_size_unchecked (op->data.clip.height, &env));
+ meta_draw_spec_parse_x_position (op->data.clip.x, &env),
+ meta_draw_spec_parse_y_position (op->data.clip.y, &env),
+ meta_draw_spec_parse_size (op->data.clip.width, &env),
+ meta_draw_spec_parse_size (op->data.clip.height, &env));
cairo_clip (cr);
cairo_save (cr);