90359a5346
* common.h: Added "above" to the list of flags a frame can have, so that we know when to mark it as always on top. Added six grab ops, one to do and one to undo each of the three new titlebar buttons (shade, above, stick). Added six new button functions, similarly. (#96229) * frame.c (meta_frame_get_flags): If a frame has the WM_STATE_ABOVE X attribute, set META_FRAME_ABOVE in its flags. * frames.c (meta_frames_apply_shapes): Allow variable amounts of rounding. (#113162) * frames.c (show_tip_now, meta_frames_paint_to_drawable, control_rect, get_control): extend handling of existing buttons to the 3*2 new kinds of button. (#96229) * frames.c (meta_frames_button_press_event): translate clicks on the 3*2 new kinds of button to the new grab ops. (#96229) * frames.c (meta_frames_button_release_event): implement the various actions for the 3*2 new kinds of button. (#96229) * frames.c (meta_frames_update_prelit_control, meta_frames_motion_notify_event): extend existing motion notifications for buttons to the 3*2 new kinds of button. (#96229) * frames.c (meta_frames_set_window_background): handle specified background colours and alpha transparency. (#151261) * frames.h (MetaFrameControl): New control types for the 3*2 new kinds of button. (#96229) * iconcache.[ch] (meta_read_icons): use theme's fallback icons if a window has no icon; use metacity's fallback icons only if the theme does not provide any. (#11363) * iconcache.[ch] (meta_invalidate_default_icons (new function)): clear icon cache on windows using default icons, and update them. (#11363) * main.c (main): added \n to error message. * prefs.c (button_function_from_string): extend for 3 new button types. (#96229) * prefs.c (button_opposite_function (new function)): return a button function's inverse (shade -> unshade, etc) (#96229) * prefs.c (update_button_layout): allocate space for a button's inverse, if it has one. (#96229) * theme-parser.c (ParseState): add state for fallback icons (#11363) * theme-parser.c (ParseInfo): add format_version; remove menu_icon_* (#114305) * theme-parser.c (parse_positive_integer): add lookup for integer constants (#331356) * theme-parser.c (parse_rounding (new function)): parse window rounding amount (#113162) * theme-parser.c (parse_alpha): don't set error if the number can't be parsed since it'll already be set; change tolerance in comparison from 1e6 to 1e-6 * theme-parser.c (parse_color (new function)): parse colour, including possible constant lookup. * theme-parser.c (parse_toplevel_element): allow defining of various new kinds of constant; allow hide_buttons (#121639) and more detailed rounding attributes on <frame_geometry> (#113162); allow background and alpha attributes on <frame_style>; (#151261) remove support for <menu_icon> except as stub; (#114305) add support for loading stock images (#113465); add support for <fallback>. (#11363)) * theme-parser.c (parse_draw_op_element): add from and to attribute for arcs. (#121603) * theme-parser.c (parse_style_element): add check for theme version supporting a button function. (#96229) * theme-parser.c (parse_style_set_element): add ability for shaded windows to be resizable (#114304) * theme-parser.c (meta_theme_load): add theme versioning routine. * theme.c ( meta_frame_layout_get_borders): return rectangles for the new 3*2 kinds of button, except where they're inapplicable. (#96229) * theme.c (meta_frame_layout_calc_geometry): don't format buttons on windows with no buttons (#121639); strip the 3*2 new kinds of button correctly (#96229); allow variable amounts of rounding (#113162). * theme.c (meta_frame_style_new): set alpha to 255 by default. (#151261) * theme.c (meta_frame_style_unref): free colour spec if allocated. (#151261) * theme.c (meta_frame_style_validate): it's only an error not to include a button if that button is valid in the current theme. (#96229) * theme.c (button_rect): return rectangles for the new 3*2 kinds of button. (#96229) * theme.c (meta_frame_style_set_unref): free differently resizable shaded styles. (#114304) * theme.c (get_style): look up differently resizable styles for shaded windows. (#114304) * theme.c (free_menu_ops (removed function), get_menu_icon (removed function), meta_theme_draw_menu_icon (removed function), meta_menu_icon_type_from_string (removed function), meta_menu_icon_type_to_string (removed function), meta_theme_free, meta_theme_validate): removed menu icon code. (#114305) * theme.c (meta_theme_load_image): add size_of_theme_icons parameter. (#113465) * theme.c (meta_theme_define_color_constant (new function), meta_theme_lookup_color_constant (new function)): allow definition of colour constants. (#129747) * theme.c (meta_button_type_from_string, meta_button_type_to_string): add the 3*2 new kinds of button. (#96229) * theme.c (meta_theme_earliest_version_with_button (new function)): return the theme version each button was introduced in. (#96229) * theme.h ( MetaFrameLayout): add "hide_buttons" flag (#121639) and corner radiuses. (#113162) * theme.h (MetaFrameGeometry): add rectangles for the 3*2 new buttons. (#96229) * theme.h (MetaButtonType): the 3*2 new buttons. (#96229) * theme.h (MetaFrameStyle): add window_background_color and window_background_alpha so that we can specify background on a <frame_style>. (#151261) * theme.h (MetaFrameStyleSet): shaded_styles gets resize dimension. (#114304) * theme.h (MetaTheme): added format_version, color_constants hash, (#129747) fallback_icon and fallback_mini_icon, (#11363) and removed menu_icons. (#114305) * theme.h (META_THEME_ALLOWS (new macro)): return whether a theme supports a given feature. Also, several macros representing new features in v2. * ui.c (meta_ui_set_current_theme)): also invalidate default icons. (#11363) * window.[ch] (meta_window_update_icon_now)): became non-static. (#11363)
4636 lines
143 KiB
C
4636 lines
143 KiB
C
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
|
|
/* Metacity theme parsing */
|
|
|
|
/*
|
|
* Copyright (C) 2001 Havoc Pennington
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
* 02111-1307, USA.
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include "theme-parser.h"
|
|
#include "util.h"
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
typedef enum
|
|
{
|
|
STATE_START,
|
|
STATE_THEME,
|
|
/* info section */
|
|
STATE_INFO,
|
|
STATE_NAME,
|
|
STATE_AUTHOR,
|
|
STATE_COPYRIGHT,
|
|
STATE_DATE,
|
|
STATE_DESCRIPTION,
|
|
/* constants */
|
|
STATE_CONSTANT,
|
|
/* geometry */
|
|
STATE_FRAME_GEOMETRY,
|
|
STATE_DISTANCE,
|
|
STATE_BORDER,
|
|
STATE_ASPECT_RATIO,
|
|
/* draw ops */
|
|
STATE_DRAW_OPS,
|
|
STATE_LINE,
|
|
STATE_RECTANGLE,
|
|
STATE_ARC,
|
|
STATE_CLIP,
|
|
STATE_TINT,
|
|
STATE_GRADIENT,
|
|
STATE_IMAGE,
|
|
STATE_GTK_ARROW,
|
|
STATE_GTK_BOX,
|
|
STATE_GTK_VLINE,
|
|
STATE_ICON,
|
|
STATE_TITLE,
|
|
STATE_INCLUDE, /* include another draw op list */
|
|
STATE_TILE, /* tile another draw op list */
|
|
/* sub-parts of gradient */
|
|
STATE_COLOR,
|
|
/* frame style */
|
|
STATE_FRAME_STYLE,
|
|
STATE_PIECE,
|
|
STATE_BUTTON,
|
|
/* style set */
|
|
STATE_FRAME_STYLE_SET,
|
|
STATE_FRAME,
|
|
/* assigning style sets to windows */
|
|
STATE_WINDOW,
|
|
/* and menu icons */
|
|
STATE_MENU_ICON,
|
|
/* fallback icons */
|
|
STATE_FALLBACK
|
|
} ParseState;
|
|
|
|
typedef struct
|
|
{
|
|
GSList *states;
|
|
|
|
const char *theme_name; /* name of theme (directory it's in) */
|
|
char *theme_file; /* theme filename */
|
|
char *theme_dir; /* dir the theme is inside */
|
|
MetaTheme *theme; /* theme being parsed */
|
|
guint format_version; /* version of format of theme file */
|
|
char *name; /* name of named thing being parsed */
|
|
MetaFrameLayout *layout; /* layout being parsed if any */
|
|
MetaDrawOpList *op_list; /* op list being parsed if any */
|
|
MetaDrawOp *op; /* op being parsed if any */
|
|
MetaFrameStyle *style; /* frame style being parsed if any */
|
|
MetaFrameStyleSet *style_set; /* frame style set being parsed if any */
|
|
MetaFramePiece piece; /* position of piece being parsed */
|
|
MetaButtonType button_type; /* type of button/menuitem being parsed */
|
|
MetaButtonState button_state; /* state of button being parsed */
|
|
} ParseInfo;
|
|
|
|
static void set_error (GError **err,
|
|
GMarkupParseContext *context,
|
|
int error_domain,
|
|
int error_code,
|
|
const char *format,
|
|
...) G_GNUC_PRINTF (5, 6);
|
|
|
|
static void add_context_to_error (GError **err,
|
|
GMarkupParseContext *context);
|
|
|
|
static void parse_info_init (ParseInfo *info);
|
|
static void parse_info_free (ParseInfo *info);
|
|
|
|
static void push_state (ParseInfo *info,
|
|
ParseState state);
|
|
static void pop_state (ParseInfo *info);
|
|
static ParseState peek_state (ParseInfo *info);
|
|
|
|
|
|
static void parse_toplevel_element (GMarkupParseContext *context,
|
|
const gchar *element_name,
|
|
const gchar **attribute_names,
|
|
const gchar **attribute_values,
|
|
ParseInfo *info,
|
|
GError **error);
|
|
static void parse_info_element (GMarkupParseContext *context,
|
|
const gchar *element_name,
|
|
const gchar **attribute_names,
|
|
const gchar **attribute_values,
|
|
ParseInfo *info,
|
|
GError **error);
|
|
static void parse_geometry_element (GMarkupParseContext *context,
|
|
const gchar *element_name,
|
|
const gchar **attribute_names,
|
|
const gchar **attribute_values,
|
|
ParseInfo *info,
|
|
GError **error);
|
|
static void parse_draw_op_element (GMarkupParseContext *context,
|
|
const gchar *element_name,
|
|
const gchar **attribute_names,
|
|
const gchar **attribute_values,
|
|
ParseInfo *info,
|
|
GError **error);
|
|
static void parse_gradient_element (GMarkupParseContext *context,
|
|
const gchar *element_name,
|
|
const gchar **attribute_names,
|
|
const gchar **attribute_values,
|
|
ParseInfo *info,
|
|
GError **error);
|
|
static void parse_style_element (GMarkupParseContext *context,
|
|
const gchar *element_name,
|
|
const gchar **attribute_names,
|
|
const gchar **attribute_values,
|
|
ParseInfo *info,
|
|
GError **error);
|
|
static void parse_style_set_element (GMarkupParseContext *context,
|
|
const gchar *element_name,
|
|
const gchar **attribute_names,
|
|
const gchar **attribute_values,
|
|
ParseInfo *info,
|
|
GError **error);
|
|
|
|
static void parse_piece_element (GMarkupParseContext *context,
|
|
const gchar *element_name,
|
|
const gchar **attribute_names,
|
|
const gchar **attribute_values,
|
|
ParseInfo *info,
|
|
GError **error);
|
|
|
|
static void parse_button_element (GMarkupParseContext *context,
|
|
const gchar *element_name,
|
|
const gchar **attribute_names,
|
|
const gchar **attribute_values,
|
|
ParseInfo *info,
|
|
GError **error);
|
|
|
|
static void parse_menu_icon_element (GMarkupParseContext *context,
|
|
const gchar *element_name,
|
|
const gchar **attribute_names,
|
|
const gchar **attribute_values,
|
|
ParseInfo *info,
|
|
GError **error);
|
|
|
|
static void start_element_handler (GMarkupParseContext *context,
|
|
const gchar *element_name,
|
|
const gchar **attribute_names,
|
|
const gchar **attribute_values,
|
|
gpointer user_data,
|
|
GError **error);
|
|
static void end_element_handler (GMarkupParseContext *context,
|
|
const gchar *element_name,
|
|
gpointer user_data,
|
|
GError **error);
|
|
static void text_handler (GMarkupParseContext *context,
|
|
const gchar *text,
|
|
gsize text_len,
|
|
gpointer user_data,
|
|
GError **error);
|
|
|
|
static GMarkupParser metacity_theme_parser = {
|
|
start_element_handler,
|
|
end_element_handler,
|
|
text_handler,
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
static void
|
|
set_error (GError **err,
|
|
GMarkupParseContext *context,
|
|
int error_domain,
|
|
int error_code,
|
|
const char *format,
|
|
...)
|
|
{
|
|
int line, ch;
|
|
va_list args;
|
|
char *str;
|
|
|
|
g_markup_parse_context_get_position (context, &line, &ch);
|
|
|
|
va_start (args, format);
|
|
str = g_strdup_vprintf (format, args);
|
|
va_end (args);
|
|
|
|
g_set_error (err, error_domain, error_code,
|
|
_("Line %d character %d: %s"),
|
|
line, ch, str);
|
|
|
|
g_free (str);
|
|
}
|
|
|
|
static void
|
|
add_context_to_error (GError **err,
|
|
GMarkupParseContext *context)
|
|
{
|
|
int line, ch;
|
|
char *str;
|
|
|
|
if (err == NULL || *err == NULL)
|
|
return;
|
|
|
|
g_markup_parse_context_get_position (context, &line, &ch);
|
|
|
|
str = g_strdup_printf (_("Line %d character %d: %s"),
|
|
line, ch, (*err)->message);
|
|
g_free ((*err)->message);
|
|
(*err)->message = str;
|
|
}
|
|
|
|
static void
|
|
parse_info_init (ParseInfo *info)
|
|
{
|
|
info->theme_file = NULL;
|
|
info->states = g_slist_prepend (NULL, GINT_TO_POINTER (STATE_START));
|
|
info->theme = NULL;
|
|
info->name = NULL;
|
|
info->layout = NULL;
|
|
info->op_list = NULL;
|
|
info->op = NULL;
|
|
info->style = NULL;
|
|
info->style_set = NULL;
|
|
info->piece = META_FRAME_PIECE_LAST;
|
|
info->button_type = META_BUTTON_TYPE_LAST;
|
|
info->button_state = META_BUTTON_STATE_LAST;
|
|
}
|
|
|
|
static void
|
|
parse_info_free (ParseInfo *info)
|
|
{
|
|
g_free (info->theme_file);
|
|
g_free (info->theme_dir);
|
|
|
|
g_slist_free (info->states);
|
|
|
|
if (info->theme)
|
|
meta_theme_free (info->theme);
|
|
|
|
if (info->layout)
|
|
meta_frame_layout_unref (info->layout);
|
|
|
|
if (info->op_list)
|
|
meta_draw_op_list_unref (info->op_list);
|
|
|
|
if (info->op)
|
|
meta_draw_op_free (info->op);
|
|
|
|
if (info->style)
|
|
meta_frame_style_unref (info->style);
|
|
|
|
if (info->style_set)
|
|
meta_frame_style_set_unref (info->style_set);
|
|
}
|
|
|
|
static void
|
|
push_state (ParseInfo *info,
|
|
ParseState state)
|
|
{
|
|
info->states = g_slist_prepend (info->states, GINT_TO_POINTER (state));
|
|
}
|
|
|
|
static void
|
|
pop_state (ParseInfo *info)
|
|
{
|
|
g_return_if_fail (info->states != NULL);
|
|
|
|
info->states = g_slist_remove (info->states, info->states->data);
|
|
}
|
|
|
|
static ParseState
|
|
peek_state (ParseInfo *info)
|
|
{
|
|
g_return_val_if_fail (info->states != NULL, STATE_START);
|
|
|
|
return GPOINTER_TO_INT (info->states->data);
|
|
}
|
|
|
|
#define ELEMENT_IS(name) (strcmp (element_name, (name)) == 0)
|
|
|
|
typedef struct
|
|
{
|
|
const char *name;
|
|
const char **retloc;
|
|
} LocateAttr;
|
|
|
|
static gboolean
|
|
locate_attributes (GMarkupParseContext *context,
|
|
const char *element_name,
|
|
const char **attribute_names,
|
|
const char **attribute_values,
|
|
GError **error,
|
|
const char *first_attribute_name,
|
|
const char **first_attribute_retloc,
|
|
...)
|
|
{
|
|
va_list args;
|
|
const char *name;
|
|
const char **retloc;
|
|
int n_attrs;
|
|
#define MAX_ATTRS 24
|
|
LocateAttr attrs[MAX_ATTRS];
|
|
gboolean retval;
|
|
int i;
|
|
|
|
g_return_val_if_fail (first_attribute_name != NULL, FALSE);
|
|
g_return_val_if_fail (first_attribute_retloc != NULL, FALSE);
|
|
|
|
retval = TRUE;
|
|
|
|
n_attrs = 1;
|
|
attrs[0].name = first_attribute_name;
|
|
attrs[0].retloc = first_attribute_retloc;
|
|
*first_attribute_retloc = NULL;
|
|
|
|
va_start (args, first_attribute_retloc);
|
|
|
|
name = va_arg (args, const char*);
|
|
retloc = va_arg (args, const char**);
|
|
|
|
while (name != NULL)
|
|
{
|
|
g_return_val_if_fail (retloc != NULL, FALSE);
|
|
|
|
g_assert (n_attrs < MAX_ATTRS);
|
|
|
|
attrs[n_attrs].name = name;
|
|
attrs[n_attrs].retloc = retloc;
|
|
n_attrs += 1;
|
|
*retloc = NULL;
|
|
|
|
name = va_arg (args, const char*);
|
|
retloc = va_arg (args, const char**);
|
|
}
|
|
|
|
va_end (args);
|
|
|
|
if (!retval)
|
|
return retval;
|
|
|
|
i = 0;
|
|
while (attribute_names[i])
|
|
{
|
|
int j;
|
|
gboolean found;
|
|
|
|
found = FALSE;
|
|
j = 0;
|
|
while (j < n_attrs)
|
|
{
|
|
if (strcmp (attrs[j].name, attribute_names[i]) == 0)
|
|
{
|
|
retloc = attrs[j].retloc;
|
|
|
|
if (*retloc != NULL)
|
|
{
|
|
set_error (error, context,
|
|
G_MARKUP_ERROR,
|
|
G_MARKUP_ERROR_PARSE,
|
|
_("Attribute \"%s\" repeated twice on the same <%s> element"),
|
|
attrs[j].name, element_name);
|
|
retval = FALSE;
|
|
goto out;
|
|
}
|
|
|
|
*retloc = attribute_values[i];
|
|
found = TRUE;
|
|
}
|
|
|
|
++j;
|
|
}
|
|
|
|
if (!found)
|
|
{
|
|
set_error (error, context,
|
|
G_MARKUP_ERROR,
|
|
G_MARKUP_ERROR_PARSE,
|
|
_("Attribute \"%s\" is invalid on <%s> element in this context"),
|
|
attribute_names[i], element_name);
|
|
retval = FALSE;
|
|
goto out;
|
|
}
|
|
|
|
++i;
|
|
}
|
|
|
|
out:
|
|
return retval;
|
|
}
|
|
|
|
static gboolean
|
|
check_no_attributes (GMarkupParseContext *context,
|
|
const char *element_name,
|
|
const char **attribute_names,
|
|
const char **attribute_values,
|
|
GError **error)
|
|
{
|
|
if (attribute_names[0] != NULL)
|
|
{
|
|
set_error (error, context,
|
|
G_MARKUP_ERROR,
|
|
G_MARKUP_ERROR_PARSE,
|
|
_("Attribute \"%s\" is invalid on <%s> element in this context"),
|
|
attribute_names[0], element_name);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#define MAX_REASONABLE 4096
|
|
static gboolean
|
|
parse_positive_integer (const char *str,
|
|
int *val,
|
|
GMarkupParseContext *context,
|
|
MetaTheme *theme,
|
|
GError **error)
|
|
{
|
|
char *end;
|
|
long l;
|
|
int j;
|
|
|
|
*val = 0;
|
|
|
|
end = NULL;
|
|
|
|
/* Is str a constant? */
|
|
|
|
if (META_THEME_ALLOWS (theme, META_THEME_UBIQUITOUS_CONSTANTS) &&
|
|
meta_theme_lookup_int_constant (theme, str, &j))
|
|
{
|
|
/* Yes. */
|
|
l = j;
|
|
}
|
|
else
|
|
{
|
|
/* No. Let's try parsing it instead. */
|
|
|
|
l = strtol (str, &end, 10);
|
|
|
|
if (end == NULL || end == str)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR,
|
|
G_MARKUP_ERROR_PARSE,
|
|
_("Could not parse \"%s\" as an integer"),
|
|
str);
|
|
return FALSE;
|
|
}
|
|
|
|
if (*end != '\0')
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR,
|
|
G_MARKUP_ERROR_PARSE,
|
|
_("Did not understand trailing characters \"%s\" in string \"%s\""),
|
|
end, str);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (l < 0)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR,
|
|
G_MARKUP_ERROR_PARSE,
|
|
_("Integer %ld must be positive"), l);
|
|
return FALSE;
|
|
}
|
|
|
|
if (l > MAX_REASONABLE)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR,
|
|
G_MARKUP_ERROR_PARSE,
|
|
_("Integer %ld is too large, current max is %d"),
|
|
l, MAX_REASONABLE);
|
|
return FALSE;
|
|
}
|
|
|
|
*val = (int) l;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
parse_double (const char *str,
|
|
double *val,
|
|
GMarkupParseContext *context,
|
|
GError **error)
|
|
{
|
|
char *end;
|
|
|
|
*val = 0;
|
|
|
|
end = NULL;
|
|
|
|
*val = g_ascii_strtod (str, &end);
|
|
|
|
if (end == NULL || end == str)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR,
|
|
G_MARKUP_ERROR_PARSE,
|
|
_("Could not parse \"%s\" as a floating point number"),
|
|
str);
|
|
return FALSE;
|
|
}
|
|
|
|
if (*end != '\0')
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR,
|
|
G_MARKUP_ERROR_PARSE,
|
|
_("Did not understand trailing characters \"%s\" in string \"%s\""),
|
|
end, str);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
parse_boolean (const char *str,
|
|
gboolean *val,
|
|
GMarkupParseContext *context,
|
|
GError **error)
|
|
{
|
|
if (strcmp ("true", str) == 0)
|
|
*val = TRUE;
|
|
else if (strcmp ("false", str) == 0)
|
|
*val = FALSE;
|
|
else
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR,
|
|
G_MARKUP_ERROR_PARSE,
|
|
_("Boolean values must be \"true\" or \"false\" not \"%s\""),
|
|
str);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
parse_rounding (const char *str,
|
|
guint *val,
|
|
GMarkupParseContext *context,
|
|
MetaTheme *theme,
|
|
GError **error)
|
|
{
|
|
if (strcmp ("true", str) == 0)
|
|
*val = 5; /* historical "true" value */
|
|
else if (strcmp ("false", str) == 0)
|
|
*val = 0;
|
|
else
|
|
{
|
|
int tmp;
|
|
gboolean result;
|
|
if (!META_THEME_ALLOWS (theme, META_THEME_VARIED_ROUND_CORNERS))
|
|
{
|
|
/* Not known in this version, so bail. */
|
|
set_error (error, context, G_MARKUP_ERROR,
|
|
G_MARKUP_ERROR_PARSE,
|
|
_("Boolean values must be \"true\" or \"false\" not \"%s\""),
|
|
str);
|
|
return FALSE;
|
|
}
|
|
|
|
result = parse_positive_integer (str, &tmp, context, theme, error);
|
|
|
|
*val = tmp;
|
|
|
|
return result;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
parse_angle (const char *str,
|
|
double *val,
|
|
GMarkupParseContext *context,
|
|
GError **error)
|
|
{
|
|
if (!parse_double (str, val, context, error))
|
|
return FALSE;
|
|
|
|
if (*val < (0.0 - 1e6) || *val > (360.0 + 1e6))
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Angle must be between 0.0 and 360.0, was %g\n"),
|
|
*val);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
parse_alpha (const char *str,
|
|
MetaAlphaGradientSpec **spec_ret,
|
|
GMarkupParseContext *context,
|
|
GError **error)
|
|
{
|
|
char **split;
|
|
int i;
|
|
int n_alphas;
|
|
MetaAlphaGradientSpec *spec;
|
|
|
|
*spec_ret = NULL;
|
|
|
|
split = g_strsplit (str, ":", -1);
|
|
|
|
i = 0;
|
|
while (split[i])
|
|
++i;
|
|
|
|
if (i == 0)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Could not parse \"%s\" as a floating point number"),
|
|
str);
|
|
|
|
g_strfreev (split);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
n_alphas = i;
|
|
|
|
/* FIXME allow specifying horizontal/vertical/diagonal in theme format,
|
|
* once we implement vertical/diagonal in gradient.c
|
|
*/
|
|
spec = meta_alpha_gradient_spec_new (META_GRADIENT_HORIZONTAL,
|
|
n_alphas);
|
|
|
|
i = 0;
|
|
while (i < n_alphas)
|
|
{
|
|
double v;
|
|
|
|
if (!parse_double (split[i], &v, context, error))
|
|
{
|
|
/* clear up, but don't set error: it was set by parse_double */
|
|
g_strfreev (split);
|
|
meta_alpha_gradient_spec_free (spec);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
if (v < (0.0 - 1e-6) || v > (1.0 + 1e-6))
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Alpha must be between 0.0 (invisible) and 1.0 (fully opaque), was %g\n"),
|
|
v);
|
|
|
|
g_strfreev (split);
|
|
meta_alpha_gradient_spec_free (spec);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
spec->alphas[i] = (unsigned char) (v * 255);
|
|
|
|
++i;
|
|
}
|
|
|
|
g_strfreev (split);
|
|
|
|
*spec_ret = spec;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static MetaColorSpec*
|
|
parse_color (MetaTheme *theme,
|
|
const char *str,
|
|
GError **err)
|
|
{
|
|
char* referent;
|
|
|
|
if (META_THEME_ALLOWS (theme, META_THEME_COLOR_CONSTANTS) &&
|
|
meta_theme_lookup_color_constant (theme, str, &referent))
|
|
{
|
|
if (referent)
|
|
return meta_color_spec_new_from_string (referent, err);
|
|
|
|
/* no need to free referent: it's a pointer into the actual hash table */
|
|
}
|
|
|
|
return meta_color_spec_new_from_string (str, err);
|
|
}
|
|
|
|
static gboolean
|
|
parse_title_scale (const char *str,
|
|
double *val,
|
|
GMarkupParseContext *context,
|
|
GError **error)
|
|
{
|
|
double factor;
|
|
|
|
if (strcmp (str, "xx-small") == 0)
|
|
factor = PANGO_SCALE_XX_SMALL;
|
|
else if (strcmp (str, "x-small") == 0)
|
|
factor = PANGO_SCALE_X_SMALL;
|
|
else if (strcmp (str, "small") == 0)
|
|
factor = PANGO_SCALE_SMALL;
|
|
else if (strcmp (str, "medium") == 0)
|
|
factor = PANGO_SCALE_MEDIUM;
|
|
else if (strcmp (str, "large") == 0)
|
|
factor = PANGO_SCALE_LARGE;
|
|
else if (strcmp (str, "x-large") == 0)
|
|
factor = PANGO_SCALE_X_LARGE;
|
|
else if (strcmp (str, "xx-large") == 0)
|
|
factor = PANGO_SCALE_XX_LARGE;
|
|
else
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Invalid title scale \"%s\" (must be one of xx-small,x-small,small,medium,large,x-large,xx-large)\n"),
|
|
str);
|
|
return FALSE;
|
|
}
|
|
|
|
*val = factor;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
parse_toplevel_element (GMarkupParseContext *context,
|
|
const gchar *element_name,
|
|
const gchar **attribute_names,
|
|
const gchar **attribute_values,
|
|
ParseInfo *info,
|
|
GError **error)
|
|
{
|
|
g_return_if_fail (peek_state (info) == STATE_THEME);
|
|
|
|
if (ELEMENT_IS ("info"))
|
|
{
|
|
if (!check_no_attributes (context, element_name,
|
|
attribute_names, attribute_values,
|
|
error))
|
|
return;
|
|
|
|
push_state (info, STATE_INFO);
|
|
}
|
|
else if (ELEMENT_IS ("constant"))
|
|
{
|
|
const char *name;
|
|
const char *value;
|
|
int ival = 0;
|
|
double dval = 0.0;
|
|
|
|
if (!locate_attributes (context, element_name, attribute_names, attribute_values,
|
|
error,
|
|
"name", &name, "value", &value,
|
|
NULL))
|
|
return;
|
|
|
|
if (name == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"%s\" attribute on <%s> element"),
|
|
"name", element_name);
|
|
return;
|
|
}
|
|
|
|
if (value == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"%s\" attribute on <%s> element"),
|
|
"value", element_name);
|
|
return;
|
|
}
|
|
|
|
if (strchr (value, '.') && parse_double (value, &dval, context, error))
|
|
{
|
|
g_clear_error (error);
|
|
|
|
if (!meta_theme_define_float_constant (info->theme,
|
|
name,
|
|
dval,
|
|
error))
|
|
{
|
|
add_context_to_error (error, context);
|
|
return;
|
|
}
|
|
}
|
|
else if (parse_positive_integer (value, &ival, context, info->theme, error))
|
|
{
|
|
g_clear_error (error);
|
|
|
|
if (!meta_theme_define_int_constant (info->theme,
|
|
name,
|
|
ival,
|
|
error))
|
|
{
|
|
add_context_to_error (error, context);
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
g_clear_error (error);
|
|
|
|
if (!meta_theme_define_color_constant (info->theme,
|
|
name,
|
|
value,
|
|
error))
|
|
{
|
|
add_context_to_error (error, context);
|
|
return;
|
|
}
|
|
}
|
|
|
|
push_state (info, STATE_CONSTANT);
|
|
}
|
|
else if (ELEMENT_IS ("frame_geometry"))
|
|
{
|
|
const char *name = NULL;
|
|
const char *parent = NULL;
|
|
const char *has_title = NULL;
|
|
const char *title_scale = NULL;
|
|
const char *rounded_top_left = NULL;
|
|
const char *rounded_top_right = NULL;
|
|
const char *rounded_bottom_left = NULL;
|
|
const char *rounded_bottom_right = NULL;
|
|
const char *hide_buttons = NULL;
|
|
gboolean has_title_val;
|
|
guint rounded_top_left_val;
|
|
guint rounded_top_right_val;
|
|
guint rounded_bottom_left_val;
|
|
guint rounded_bottom_right_val;
|
|
gboolean hide_buttons_val;
|
|
double title_scale_val;
|
|
MetaFrameLayout *parent_layout;
|
|
|
|
if (!locate_attributes (context, element_name, attribute_names, attribute_values,
|
|
error,
|
|
"name", &name, "parent", &parent,
|
|
"has_title", &has_title, "title_scale", &title_scale,
|
|
"rounded_top_left", &rounded_top_left,
|
|
"rounded_top_right", &rounded_top_right,
|
|
"rounded_bottom_left", &rounded_bottom_left,
|
|
"rounded_bottom_right", &rounded_bottom_right,
|
|
"hide_buttons", &hide_buttons,
|
|
NULL))
|
|
return;
|
|
|
|
if (name == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"%s\" attribute on <%s> element"),
|
|
"name", element_name);
|
|
return;
|
|
}
|
|
|
|
has_title_val = TRUE;
|
|
if (has_title && !parse_boolean (has_title, &has_title_val, context, error))
|
|
return;
|
|
|
|
hide_buttons_val = FALSE;
|
|
if (hide_buttons && !parse_boolean (hide_buttons, &hide_buttons_val, context, error))
|
|
return;
|
|
|
|
rounded_top_left_val = 0;
|
|
rounded_top_right_val = 0;
|
|
rounded_bottom_left_val = 0;
|
|
rounded_bottom_right_val = 0;
|
|
|
|
if (rounded_top_left && !parse_rounding (rounded_top_left, &rounded_top_left_val, context, info->theme, error))
|
|
return;
|
|
if (rounded_top_right && !parse_rounding (rounded_top_right, &rounded_top_right_val, context, info->theme, error))
|
|
return;
|
|
if (rounded_bottom_left && !parse_rounding (rounded_bottom_left, &rounded_bottom_left_val, context, info->theme, error))
|
|
return;
|
|
if (rounded_bottom_right && !parse_rounding (rounded_bottom_right, &rounded_bottom_right_val, context, info->theme, error))
|
|
return;
|
|
|
|
title_scale_val = 1.0;
|
|
if (title_scale && !parse_title_scale (title_scale, &title_scale_val, context, error))
|
|
return;
|
|
|
|
if (meta_theme_lookup_layout (info->theme, name))
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("<%s> name \"%s\" used a second time"),
|
|
element_name, name);
|
|
return;
|
|
}
|
|
|
|
parent_layout = NULL;
|
|
if (parent)
|
|
{
|
|
parent_layout = meta_theme_lookup_layout (info->theme, parent);
|
|
if (parent_layout == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("<%s> parent \"%s\" has not been defined"),
|
|
element_name, parent);
|
|
return;
|
|
}
|
|
}
|
|
|
|
g_assert (info->layout == NULL);
|
|
|
|
if (parent_layout)
|
|
info->layout = meta_frame_layout_copy (parent_layout);
|
|
else
|
|
info->layout = meta_frame_layout_new ();
|
|
|
|
if (has_title) /* only if explicit, otherwise inherit */
|
|
info->layout->has_title = has_title_val;
|
|
|
|
if (META_THEME_ALLOWS (info->theme, META_THEME_HIDDEN_BUTTONS) && hide_buttons_val)
|
|
info->layout->hide_buttons = hide_buttons_val;
|
|
|
|
if (title_scale)
|
|
info->layout->title_scale = title_scale_val;
|
|
|
|
if (rounded_top_left)
|
|
info->layout->top_left_corner_rounded_radius = rounded_top_left_val;
|
|
|
|
if (rounded_top_right)
|
|
info->layout->top_right_corner_rounded_radius = rounded_top_right_val;
|
|
|
|
if (rounded_bottom_left)
|
|
info->layout->bottom_left_corner_rounded_radius = rounded_bottom_left_val;
|
|
|
|
if (rounded_bottom_right)
|
|
info->layout->bottom_right_corner_rounded_radius = rounded_bottom_right_val;
|
|
|
|
meta_theme_insert_layout (info->theme, name, info->layout);
|
|
|
|
push_state (info, STATE_FRAME_GEOMETRY);
|
|
}
|
|
else if (ELEMENT_IS ("draw_ops"))
|
|
{
|
|
const char *name = NULL;
|
|
|
|
if (!locate_attributes (context, element_name, attribute_names, attribute_values,
|
|
error,
|
|
"name", &name,
|
|
NULL))
|
|
return;
|
|
|
|
if (name == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"%s\" attribute on <%s> element"),
|
|
"name", element_name);
|
|
return;
|
|
}
|
|
|
|
if (meta_theme_lookup_draw_op_list (info->theme, name))
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("<%s> name \"%s\" used a second time"),
|
|
element_name, name);
|
|
return;
|
|
}
|
|
|
|
g_assert (info->op_list == NULL);
|
|
info->op_list = meta_draw_op_list_new (2);
|
|
|
|
meta_theme_insert_draw_op_list (info->theme, name, info->op_list);
|
|
|
|
push_state (info, STATE_DRAW_OPS);
|
|
}
|
|
else if (ELEMENT_IS ("frame_style"))
|
|
{
|
|
const char *name = NULL;
|
|
const char *parent = NULL;
|
|
const char *geometry = NULL;
|
|
const char *background = NULL;
|
|
const char *alpha = NULL;
|
|
MetaFrameStyle *parent_style;
|
|
MetaFrameLayout *layout;
|
|
|
|
if (!locate_attributes (context, element_name, attribute_names, attribute_values,
|
|
error,
|
|
"name", &name, "parent", &parent,
|
|
"geometry", &geometry,
|
|
"background", &background,
|
|
"alpha", &alpha,
|
|
NULL))
|
|
return;
|
|
|
|
if (name == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"%s\" attribute on <%s> element"),
|
|
"name", element_name);
|
|
return;
|
|
}
|
|
|
|
if (meta_theme_lookup_style (info->theme, name))
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("<%s> name \"%s\" used a second time"),
|
|
element_name, name);
|
|
return;
|
|
}
|
|
|
|
parent_style = NULL;
|
|
if (parent)
|
|
{
|
|
parent_style = meta_theme_lookup_style (info->theme, parent);
|
|
if (parent_style == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("<%s> parent \"%s\" has not been defined"),
|
|
element_name, parent);
|
|
return;
|
|
}
|
|
}
|
|
|
|
layout = NULL;
|
|
if (geometry)
|
|
{
|
|
layout = meta_theme_lookup_layout (info->theme, geometry);
|
|
if (layout == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("<%s> geometry \"%s\" has not been defined"),
|
|
element_name, geometry);
|
|
return;
|
|
}
|
|
}
|
|
else if (parent_style)
|
|
{
|
|
layout = parent_style->layout;
|
|
}
|
|
|
|
if (layout == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("<%s> must specify either a geometry or a parent that has a geometry"),
|
|
element_name);
|
|
return;
|
|
}
|
|
|
|
g_assert (info->style == NULL);
|
|
|
|
info->style = meta_frame_style_new (parent_style);
|
|
g_assert (info->style->layout == NULL);
|
|
meta_frame_layout_ref (layout);
|
|
info->style->layout = layout;
|
|
|
|
if (background != NULL && META_THEME_ALLOWS (info->theme, META_THEME_FRAME_BACKGROUNDS))
|
|
{
|
|
info->style->window_background_color = meta_color_spec_new_from_string (background, error);
|
|
if (!info->style->window_background_color)
|
|
return;
|
|
|
|
if (alpha != NULL)
|
|
{
|
|
|
|
gboolean success;
|
|
MetaAlphaGradientSpec *alpha_vector;
|
|
|
|
g_clear_error (error);
|
|
/* fortunately, we already have a routine to parse alpha values,
|
|
* though it produces a vector of them, which is a superset of
|
|
* what we want.
|
|
*/
|
|
success = parse_alpha (alpha, &alpha_vector, context, error);
|
|
if (!success)
|
|
return;
|
|
|
|
/* alpha_vector->alphas must contain at least one element */
|
|
info->style->window_background_alpha = alpha_vector->alphas[0];
|
|
|
|
meta_alpha_gradient_spec_free (alpha_vector);
|
|
}
|
|
}
|
|
else if (alpha != NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("You must specify a background for an alpha value to be meaningful"));
|
|
return;
|
|
}
|
|
|
|
meta_theme_insert_style (info->theme, name, info->style);
|
|
|
|
push_state (info, STATE_FRAME_STYLE);
|
|
}
|
|
else if (ELEMENT_IS ("frame_style_set"))
|
|
{
|
|
const char *name = NULL;
|
|
const char *parent = NULL;
|
|
MetaFrameStyleSet *parent_set;
|
|
|
|
if (!locate_attributes (context, element_name, attribute_names, attribute_values,
|
|
error,
|
|
"name", &name, "parent", &parent,
|
|
NULL))
|
|
return;
|
|
|
|
if (name == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"%s\" attribute on <%s> element"),
|
|
"name", element_name);
|
|
return;
|
|
}
|
|
|
|
if (meta_theme_lookup_style_set (info->theme, name))
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("<%s> name \"%s\" used a second time"),
|
|
element_name, name);
|
|
return;
|
|
}
|
|
|
|
parent_set = NULL;
|
|
if (parent)
|
|
{
|
|
parent_set = meta_theme_lookup_style_set (info->theme, parent);
|
|
if (parent_set == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("<%s> parent \"%s\" has not been defined"),
|
|
element_name, parent);
|
|
return;
|
|
}
|
|
}
|
|
|
|
g_assert (info->style_set == NULL);
|
|
|
|
info->style_set = meta_frame_style_set_new (parent_set);
|
|
|
|
meta_theme_insert_style_set (info->theme, name, info->style_set);
|
|
|
|
push_state (info, STATE_FRAME_STYLE_SET);
|
|
}
|
|
else if (ELEMENT_IS ("window"))
|
|
{
|
|
const char *type_name = NULL;
|
|
const char *style_set_name = NULL;
|
|
MetaFrameStyleSet *style_set;
|
|
MetaFrameType type;
|
|
|
|
if (!locate_attributes (context, element_name, attribute_names, attribute_values,
|
|
error,
|
|
"type", &type_name, "style_set", &style_set_name,
|
|
NULL))
|
|
return;
|
|
|
|
if (type_name == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"%s\" attribute on <%s> element"),
|
|
"type", element_name);
|
|
return;
|
|
}
|
|
|
|
if (style_set_name == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"%s\" attribute on <%s> element"),
|
|
"style_set", element_name);
|
|
return;
|
|
}
|
|
|
|
type = meta_frame_type_from_string (type_name);
|
|
|
|
if (type == META_FRAME_TYPE_LAST)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Unknown type \"%s\" on <%s> element"),
|
|
type_name, element_name);
|
|
return;
|
|
}
|
|
|
|
style_set = meta_theme_lookup_style_set (info->theme,
|
|
style_set_name);
|
|
|
|
if (style_set == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Unknown style_set \"%s\" on <%s> element"),
|
|
style_set_name, element_name);
|
|
return;
|
|
}
|
|
|
|
if (info->theme->style_sets_by_type[type] != NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Window type \"%s\" has already been assigned a style set"),
|
|
type_name);
|
|
return;
|
|
}
|
|
|
|
meta_frame_style_set_ref (style_set);
|
|
info->theme->style_sets_by_type[type] = style_set;
|
|
|
|
push_state (info, STATE_WINDOW);
|
|
}
|
|
else if (ELEMENT_IS ("menu_icon"))
|
|
{
|
|
/* Not supported any more, but we have to parse it if they include it,
|
|
* for backwards compatibility.
|
|
*/
|
|
g_assert (info->op_list == NULL);
|
|
|
|
push_state (info, STATE_MENU_ICON);
|
|
}
|
|
else if (ELEMENT_IS ("fallback"))
|
|
{
|
|
const char *icon = NULL;
|
|
const char *mini_icon = NULL;
|
|
|
|
if (!locate_attributes (context, element_name, attribute_names, attribute_values,
|
|
error,
|
|
"icon", &icon,
|
|
"mini_icon", &mini_icon,
|
|
NULL))
|
|
return;
|
|
|
|
if (icon)
|
|
{
|
|
if (info->theme->fallback_icon != NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Theme already has a fallback icon"));
|
|
return;
|
|
}
|
|
|
|
info->theme->fallback_icon = meta_theme_load_image(info->theme, icon, 64, error);
|
|
}
|
|
|
|
if (mini_icon)
|
|
{
|
|
if (info->theme->fallback_mini_icon != NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Theme already has a fallback mini_icon"));
|
|
return;
|
|
}
|
|
|
|
info->theme->fallback_mini_icon = meta_theme_load_image(info->theme, mini_icon, 16, error);
|
|
}
|
|
|
|
push_state (info, STATE_FALLBACK);
|
|
}
|
|
else
|
|
{
|
|
set_error (error, context,
|
|
G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Element <%s> is not allowed below <%s>"),
|
|
element_name, "metacity_theme");
|
|
}
|
|
}
|
|
|
|
static void
|
|
parse_info_element (GMarkupParseContext *context,
|
|
const gchar *element_name,
|
|
const gchar **attribute_names,
|
|
const gchar **attribute_values,
|
|
ParseInfo *info,
|
|
GError **error)
|
|
{
|
|
g_return_if_fail (peek_state (info) == STATE_INFO);
|
|
|
|
if (ELEMENT_IS ("name"))
|
|
{
|
|
if (!check_no_attributes (context, element_name,
|
|
attribute_names, attribute_values,
|
|
error))
|
|
return;
|
|
|
|
push_state (info, STATE_NAME);
|
|
}
|
|
else if (ELEMENT_IS ("author"))
|
|
{
|
|
if (!check_no_attributes (context, element_name,
|
|
attribute_names, attribute_values,
|
|
error))
|
|
return;
|
|
|
|
push_state (info, STATE_AUTHOR);
|
|
}
|
|
else if (ELEMENT_IS ("copyright"))
|
|
{
|
|
if (!check_no_attributes (context, element_name,
|
|
attribute_names, attribute_values,
|
|
error))
|
|
return;
|
|
|
|
push_state (info, STATE_COPYRIGHT);
|
|
}
|
|
else if (ELEMENT_IS ("description"))
|
|
{
|
|
if (!check_no_attributes (context, element_name,
|
|
attribute_names, attribute_values,
|
|
error))
|
|
return;
|
|
|
|
push_state (info, STATE_DESCRIPTION);
|
|
}
|
|
else if (ELEMENT_IS ("date"))
|
|
{
|
|
if (!check_no_attributes (context, element_name,
|
|
attribute_names, attribute_values,
|
|
error))
|
|
return;
|
|
|
|
push_state (info, STATE_DATE);
|
|
}
|
|
else
|
|
{
|
|
set_error (error, context,
|
|
G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Element <%s> is not allowed below <%s>"),
|
|
element_name, "info");
|
|
}
|
|
}
|
|
|
|
static void
|
|
parse_distance (GMarkupParseContext *context,
|
|
const gchar *element_name,
|
|
const gchar **attribute_names,
|
|
const gchar **attribute_values,
|
|
ParseInfo *info,
|
|
GError **error)
|
|
{
|
|
const char *name;
|
|
const char *value;
|
|
int val;
|
|
|
|
if (!locate_attributes (context, element_name, attribute_names, attribute_values,
|
|
error,
|
|
"name", &name, "value", &value,
|
|
NULL))
|
|
return;
|
|
|
|
if (name == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"name\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (value == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"value\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
val = 0;
|
|
if (!parse_positive_integer (value, &val, context, info->theme, error))
|
|
return;
|
|
|
|
g_assert (val >= 0); /* yeah, "non-negative" not "positive" get over it */
|
|
g_assert (info->layout);
|
|
|
|
if (strcmp (name, "left_width") == 0)
|
|
info->layout->left_width = val;
|
|
else if (strcmp (name, "right_width") == 0)
|
|
info->layout->right_width = val;
|
|
else if (strcmp (name, "bottom_height") == 0)
|
|
info->layout->bottom_height = val;
|
|
else if (strcmp (name, "title_vertical_pad") == 0)
|
|
info->layout->title_vertical_pad = val;
|
|
else if (strcmp (name, "right_titlebar_edge") == 0)
|
|
info->layout->right_titlebar_edge = val;
|
|
else if (strcmp (name, "left_titlebar_edge") == 0)
|
|
info->layout->left_titlebar_edge = val;
|
|
else if (strcmp (name, "button_width") == 0)
|
|
{
|
|
info->layout->button_width = val;
|
|
|
|
if (!(info->layout->button_sizing == META_BUTTON_SIZING_LAST ||
|
|
info->layout->button_sizing == META_BUTTON_SIZING_FIXED))
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Cannot specify both button_width/button_height and aspect ratio for buttons"));
|
|
return;
|
|
}
|
|
|
|
info->layout->button_sizing = META_BUTTON_SIZING_FIXED;
|
|
}
|
|
else if (strcmp (name, "button_height") == 0)
|
|
{
|
|
info->layout->button_height = val;
|
|
|
|
if (!(info->layout->button_sizing == META_BUTTON_SIZING_LAST ||
|
|
info->layout->button_sizing == META_BUTTON_SIZING_FIXED))
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Cannot specify both button_width/button_height and aspect ratio for buttons"));
|
|
return;
|
|
}
|
|
|
|
info->layout->button_sizing = META_BUTTON_SIZING_FIXED;
|
|
}
|
|
else
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Distance \"%s\" is unknown"), name);
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void
|
|
parse_aspect_ratio (GMarkupParseContext *context,
|
|
const gchar *element_name,
|
|
const gchar **attribute_names,
|
|
const gchar **attribute_values,
|
|
ParseInfo *info,
|
|
GError **error)
|
|
{
|
|
const char *name;
|
|
const char *value;
|
|
double val;
|
|
|
|
if (!locate_attributes (context, element_name, attribute_names, attribute_values,
|
|
error,
|
|
"name", &name, "value", &value,
|
|
NULL))
|
|
return;
|
|
|
|
if (name == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"name\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (value == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"value\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
val = 0;
|
|
if (!parse_double (value, &val, context, error))
|
|
return;
|
|
|
|
g_assert (info->layout);
|
|
|
|
if (strcmp (name, "button") == 0)
|
|
{
|
|
info->layout->button_aspect = val;
|
|
|
|
if (info->layout->button_sizing != META_BUTTON_SIZING_LAST)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Cannot specify both button_width/button_height and aspect ratio for buttons"));
|
|
return;
|
|
}
|
|
|
|
info->layout->button_sizing = META_BUTTON_SIZING_ASPECT;
|
|
}
|
|
else
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Aspect ratio \"%s\" is unknown"), name);
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void
|
|
parse_border (GMarkupParseContext *context,
|
|
const gchar *element_name,
|
|
const gchar **attribute_names,
|
|
const gchar **attribute_values,
|
|
ParseInfo *info,
|
|
GError **error)
|
|
{
|
|
const char *name;
|
|
const char *top;
|
|
const char *bottom;
|
|
const char *left;
|
|
const char *right;
|
|
int top_val;
|
|
int bottom_val;
|
|
int left_val;
|
|
int right_val;
|
|
GtkBorder *border;
|
|
|
|
if (!locate_attributes (context, element_name, attribute_names, attribute_values,
|
|
error,
|
|
"name", &name,
|
|
"top", &top,
|
|
"bottom", &bottom,
|
|
"left", &left,
|
|
"right", &right,
|
|
NULL))
|
|
return;
|
|
|
|
if (name == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"name\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (top == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"top\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (bottom == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"bottom\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (left == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"left\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (right == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"right\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
top_val = 0;
|
|
if (!parse_positive_integer (top, &top_val, context, info->theme, error))
|
|
return;
|
|
|
|
bottom_val = 0;
|
|
if (!parse_positive_integer (bottom, &bottom_val, context, info->theme, error))
|
|
return;
|
|
|
|
left_val = 0;
|
|
if (!parse_positive_integer (left, &left_val, context, info->theme, error))
|
|
return;
|
|
|
|
right_val = 0;
|
|
if (!parse_positive_integer (right, &right_val, context, info->theme, error))
|
|
return;
|
|
|
|
g_assert (info->layout);
|
|
|
|
border = NULL;
|
|
|
|
if (strcmp (name, "title_border") == 0)
|
|
border = &info->layout->title_border;
|
|
else if (strcmp (name, "button_border") == 0)
|
|
border = &info->layout->button_border;
|
|
|
|
if (border == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Border \"%s\" is unknown"), name);
|
|
return;
|
|
}
|
|
|
|
border->top = top_val;
|
|
border->bottom = bottom_val;
|
|
border->left = left_val;
|
|
border->right = right_val;
|
|
}
|
|
|
|
static void
|
|
parse_geometry_element (GMarkupParseContext *context,
|
|
const gchar *element_name,
|
|
const gchar **attribute_names,
|
|
const gchar **attribute_values,
|
|
ParseInfo *info,
|
|
GError **error)
|
|
{
|
|
g_return_if_fail (peek_state (info) == STATE_FRAME_GEOMETRY);
|
|
|
|
if (ELEMENT_IS ("distance"))
|
|
{
|
|
parse_distance (context, element_name,
|
|
attribute_names, attribute_values,
|
|
info, error);
|
|
push_state (info, STATE_DISTANCE);
|
|
}
|
|
else if (ELEMENT_IS ("border"))
|
|
{
|
|
parse_border (context, element_name,
|
|
attribute_names, attribute_values,
|
|
info, error);
|
|
push_state (info, STATE_BORDER);
|
|
}
|
|
else if (ELEMENT_IS ("aspect_ratio"))
|
|
{
|
|
parse_aspect_ratio (context, element_name,
|
|
attribute_names, attribute_values,
|
|
info, error);
|
|
|
|
push_state (info, STATE_ASPECT_RATIO);
|
|
}
|
|
else
|
|
{
|
|
set_error (error, context,
|
|
G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Element <%s> is not allowed below <%s>"),
|
|
element_name, "frame_geometry");
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
check_expression (const char *expr,
|
|
gboolean has_object,
|
|
MetaTheme *theme,
|
|
GMarkupParseContext *context,
|
|
GError **error)
|
|
{
|
|
MetaPositionExprEnv env;
|
|
int x, y;
|
|
|
|
/* We set it all to 0 to try and catch divide-by-zero screwups.
|
|
* it's possible we should instead guarantee that widths and heights
|
|
* are at least 1.
|
|
*/
|
|
|
|
env.rect = meta_rect (0, 0, 0, 0);
|
|
if (has_object)
|
|
{
|
|
env.object_width = 0;
|
|
env.object_height = 0;
|
|
}
|
|
else
|
|
{
|
|
env.object_width = -1;
|
|
env.object_height = -1;
|
|
}
|
|
|
|
env.left_width = 0;
|
|
env.right_width = 0;
|
|
env.top_height = 0;
|
|
env.bottom_height = 0;
|
|
env.title_width = 0;
|
|
env.title_height = 0;
|
|
|
|
env.icon_width = 0;
|
|
env.icon_height = 0;
|
|
env.mini_icon_width = 0;
|
|
env.mini_icon_height = 0;
|
|
env.theme = theme;
|
|
|
|
if (!meta_parse_position_expression (expr,
|
|
&env,
|
|
&x, &y,
|
|
error))
|
|
{
|
|
add_context_to_error (error, context);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static char*
|
|
optimize_expression (MetaTheme *theme,
|
|
const char *expr)
|
|
{
|
|
/* We aren't expecting an error here, since we already
|
|
* did check_expression
|
|
*/
|
|
return meta_theme_replace_constants (theme, expr, NULL);
|
|
}
|
|
|
|
static void
|
|
parse_draw_op_element (GMarkupParseContext *context,
|
|
const gchar *element_name,
|
|
const gchar **attribute_names,
|
|
const gchar **attribute_values,
|
|
ParseInfo *info,
|
|
GError **error)
|
|
{
|
|
g_return_if_fail (peek_state (info) == STATE_DRAW_OPS);
|
|
|
|
if (ELEMENT_IS ("line"))
|
|
{
|
|
MetaDrawOp *op;
|
|
const char *color;
|
|
const char *x1;
|
|
const char *y1;
|
|
const char *x2;
|
|
const char *y2;
|
|
const char *dash_on_length;
|
|
const char *dash_off_length;
|
|
const char *width;
|
|
MetaColorSpec *color_spec;
|
|
int dash_on_val;
|
|
int dash_off_val;
|
|
int width_val;
|
|
|
|
if (!locate_attributes (context, element_name, attribute_names, attribute_values,
|
|
error,
|
|
"color", &color,
|
|
"x1", &x1, "y1", &y1,
|
|
"x2", &x2, "y2", &y2,
|
|
"dash_on_length", &dash_on_length,
|
|
"dash_off_length", &dash_off_length,
|
|
"width", &width,
|
|
NULL))
|
|
return;
|
|
|
|
if (color == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"color\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (x1 == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"x1\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (y1 == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"y1\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (x2 == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"x2\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (y2 == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"y2\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (!check_expression (x1, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
if (!check_expression (y1, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
if (!check_expression (x2, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
if (!check_expression (y2, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
dash_on_val = 0;
|
|
if (dash_on_length &&
|
|
!parse_positive_integer (dash_on_length, &dash_on_val, context, info->theme, error))
|
|
return;
|
|
|
|
dash_off_val = 0;
|
|
if (dash_off_length &&
|
|
!parse_positive_integer (dash_off_length, &dash_off_val, context, info->theme, error))
|
|
return;
|
|
|
|
width_val = 0;
|
|
if (width &&
|
|
!parse_positive_integer (width, &width_val, context, info->theme, error))
|
|
return;
|
|
|
|
/* Check last so we don't have to free it when other
|
|
* stuff fails
|
|
*/
|
|
color_spec = parse_color (info->theme, color, error);
|
|
if (color_spec == NULL)
|
|
{
|
|
add_context_to_error (error, context);
|
|
return;
|
|
}
|
|
|
|
op = meta_draw_op_new (META_DRAW_LINE);
|
|
|
|
op->data.line.color_spec = color_spec;
|
|
op->data.line.x1 = optimize_expression (info->theme, x1);
|
|
op->data.line.y1 = optimize_expression (info->theme, y1);
|
|
op->data.line.x2 = optimize_expression (info->theme, x2);
|
|
op->data.line.y2 = optimize_expression (info->theme, y2);
|
|
op->data.line.width = width_val;
|
|
op->data.line.dash_on_length = dash_on_val;
|
|
op->data.line.dash_off_length = dash_off_val;
|
|
|
|
g_assert (info->op_list);
|
|
|
|
meta_draw_op_list_append (info->op_list, op);
|
|
|
|
push_state (info, STATE_LINE);
|
|
}
|
|
else if (ELEMENT_IS ("rectangle"))
|
|
{
|
|
MetaDrawOp *op;
|
|
const char *color;
|
|
const char *x;
|
|
const char *y;
|
|
const char *width;
|
|
const char *height;
|
|
const char *filled;
|
|
gboolean filled_val;
|
|
MetaColorSpec *color_spec;
|
|
|
|
if (!locate_attributes (context, element_name, attribute_names, attribute_values,
|
|
error,
|
|
"color", &color,
|
|
"x", &x, "y", &y,
|
|
"width", &width, "height", &height,
|
|
"filled", &filled,
|
|
NULL))
|
|
return;
|
|
|
|
if (color == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"color\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (x == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"x\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (y == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"y\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (width == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"width\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (height == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"height\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (!check_expression (x, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
if (!check_expression (y, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
if (!check_expression (width, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
if (!check_expression (height, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
filled_val = FALSE;
|
|
if (filled && !parse_boolean (filled, &filled_val, context, error))
|
|
return;
|
|
|
|
/* Check last so we don't have to free it when other
|
|
* stuff fails
|
|
*/
|
|
color_spec = parse_color (info->theme, color, error);
|
|
if (color_spec == NULL)
|
|
{
|
|
add_context_to_error (error, context);
|
|
return;
|
|
}
|
|
|
|
op = meta_draw_op_new (META_DRAW_RECTANGLE);
|
|
|
|
op->data.rectangle.color_spec = color_spec;
|
|
op->data.rectangle.x = optimize_expression (info->theme, x);
|
|
op->data.rectangle.y = optimize_expression (info->theme, y);
|
|
op->data.rectangle.width = optimize_expression (info->theme, width);
|
|
op->data.rectangle.height = optimize_expression (info->theme, height);
|
|
op->data.rectangle.filled = filled_val;
|
|
|
|
g_assert (info->op_list);
|
|
|
|
meta_draw_op_list_append (info->op_list, op);
|
|
|
|
push_state (info, STATE_RECTANGLE);
|
|
}
|
|
else if (ELEMENT_IS ("arc"))
|
|
{
|
|
MetaDrawOp *op;
|
|
const char *color;
|
|
const char *x;
|
|
const char *y;
|
|
const char *width;
|
|
const char *height;
|
|
const char *filled;
|
|
const char *start_angle;
|
|
const char *extent_angle;
|
|
const char *from;
|
|
const char *to;
|
|
gboolean filled_val;
|
|
double start_angle_val;
|
|
double extent_angle_val;
|
|
MetaColorSpec *color_spec;
|
|
|
|
if (!locate_attributes (context, element_name, attribute_names, attribute_values,
|
|
error,
|
|
"color", &color,
|
|
"x", &x, "y", &y,
|
|
"width", &width, "height", &height,
|
|
"filled", &filled,
|
|
"start_angle", &start_angle,
|
|
"extent_angle", &extent_angle,
|
|
"from", &from,
|
|
"to", &to,
|
|
NULL))
|
|
return;
|
|
|
|
if (color == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"color\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (x == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"x\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (y == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"y\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (width == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"width\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (height == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"height\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (META_THEME_ALLOWS (info->theme, META_THEME_DEGREES_IN_ARCS) )
|
|
{
|
|
if (start_angle == NULL && from == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"start_angle\" or \"from\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (extent_angle == NULL && to == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"extent_angle\" or \"to\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (start_angle == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"start_angle\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (extent_angle == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"extent_angle\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
if (!check_expression (x, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
if (!check_expression (y, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
if (!check_expression (width, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
if (!check_expression (height, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
if (start_angle == NULL)
|
|
{
|
|
if (!parse_angle (from, &start_angle_val, context, error))
|
|
return;
|
|
|
|
start_angle_val = (180-start_angle_val)/360.0;
|
|
}
|
|
else
|
|
{
|
|
if (!parse_angle (start_angle, &start_angle_val, context, error))
|
|
return;
|
|
}
|
|
|
|
if (extent_angle == NULL)
|
|
{
|
|
if (!parse_angle (to, &extent_angle_val, context, error))
|
|
return;
|
|
|
|
extent_angle_val = ((180-extent_angle_val)/360.0) - start_angle_val;
|
|
}
|
|
else
|
|
{
|
|
if (!parse_angle (extent_angle, &extent_angle_val, context, error))
|
|
return;
|
|
}
|
|
|
|
filled_val = FALSE;
|
|
if (filled && !parse_boolean (filled, &filled_val, context, error))
|
|
return;
|
|
|
|
/* Check last so we don't have to free it when other
|
|
* stuff fails
|
|
*/
|
|
color_spec = parse_color (info->theme, color, error);
|
|
if (color_spec == NULL)
|
|
{
|
|
add_context_to_error (error, context);
|
|
return;
|
|
}
|
|
|
|
op = meta_draw_op_new (META_DRAW_ARC);
|
|
|
|
op->data.arc.color_spec = color_spec;
|
|
op->data.arc.x = optimize_expression (info->theme, x);
|
|
op->data.arc.y = optimize_expression (info->theme, y);
|
|
op->data.arc.width = optimize_expression (info->theme, width);
|
|
op->data.arc.height = optimize_expression (info->theme, height);
|
|
op->data.arc.filled = filled_val;
|
|
op->data.arc.start_angle = start_angle_val;
|
|
op->data.arc.extent_angle = extent_angle_val;
|
|
|
|
g_assert (info->op_list);
|
|
|
|
meta_draw_op_list_append (info->op_list, op);
|
|
|
|
push_state (info, STATE_ARC);
|
|
}
|
|
else if (ELEMENT_IS ("clip"))
|
|
{
|
|
MetaDrawOp *op;
|
|
const char *x;
|
|
const char *y;
|
|
const char *width;
|
|
const char *height;
|
|
|
|
if (!locate_attributes (context, element_name, attribute_names, attribute_values,
|
|
error,
|
|
"x", &x, "y", &y,
|
|
"width", &width, "height", &height,
|
|
NULL))
|
|
return;
|
|
|
|
if (x == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"x\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (y == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"y\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (width == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"width\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (height == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"height\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (!check_expression (x, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
if (!check_expression (y, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
if (!check_expression (width, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
if (!check_expression (height, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
op = meta_draw_op_new (META_DRAW_CLIP);
|
|
|
|
op->data.clip.x = optimize_expression (info->theme, x);
|
|
op->data.clip.y = optimize_expression (info->theme, y);
|
|
op->data.clip.width = optimize_expression (info->theme, width);
|
|
op->data.clip.height = optimize_expression (info->theme, height);
|
|
|
|
g_assert (info->op_list);
|
|
|
|
meta_draw_op_list_append (info->op_list, op);
|
|
|
|
push_state (info, STATE_CLIP);
|
|
}
|
|
else if (ELEMENT_IS ("tint"))
|
|
{
|
|
MetaDrawOp *op;
|
|
const char *color;
|
|
const char *x;
|
|
const char *y;
|
|
const char *width;
|
|
const char *height;
|
|
const char *alpha;
|
|
MetaAlphaGradientSpec *alpha_spec;
|
|
MetaColorSpec *color_spec;
|
|
|
|
if (!locate_attributes (context, element_name, attribute_names, attribute_values,
|
|
error,
|
|
"color", &color,
|
|
"x", &x, "y", &y,
|
|
"width", &width, "height", &height,
|
|
"alpha", &alpha,
|
|
NULL))
|
|
return;
|
|
|
|
if (color == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"color\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (x == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"x\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (y == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"y\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (width == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"width\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (height == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"height\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (alpha == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"alpha\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (!check_expression (x, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
if (!check_expression (y, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
if (!check_expression (width, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
if (!check_expression (height, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
alpha_spec = NULL;
|
|
if (!parse_alpha (alpha, &alpha_spec, context, error))
|
|
return;
|
|
|
|
/* Check last so we don't have to free it when other
|
|
* stuff fails
|
|
*/
|
|
color_spec = parse_color (info->theme, color, error);
|
|
if (color_spec == NULL)
|
|
{
|
|
if (alpha_spec)
|
|
meta_alpha_gradient_spec_free (alpha_spec);
|
|
|
|
add_context_to_error (error, context);
|
|
return;
|
|
}
|
|
|
|
op = meta_draw_op_new (META_DRAW_TINT);
|
|
|
|
op->data.tint.color_spec = color_spec;
|
|
op->data.tint.alpha_spec = alpha_spec;
|
|
op->data.tint.x = optimize_expression (info->theme, x);
|
|
op->data.tint.y = optimize_expression (info->theme, y);
|
|
op->data.tint.width = optimize_expression (info->theme, width);
|
|
op->data.tint.height = optimize_expression (info->theme, height);
|
|
|
|
g_assert (info->op_list);
|
|
|
|
meta_draw_op_list_append (info->op_list, op);
|
|
|
|
push_state (info, STATE_TINT);
|
|
}
|
|
else if (ELEMENT_IS ("gradient"))
|
|
{
|
|
const char *x;
|
|
const char *y;
|
|
const char *width;
|
|
const char *height;
|
|
const char *type;
|
|
const char *alpha;
|
|
MetaAlphaGradientSpec *alpha_spec;
|
|
MetaGradientType type_val;
|
|
|
|
if (!locate_attributes (context, element_name, attribute_names, attribute_values,
|
|
error,
|
|
"type", &type,
|
|
"x", &x, "y", &y,
|
|
"width", &width, "height", &height,
|
|
"alpha", &alpha,
|
|
NULL))
|
|
return;
|
|
|
|
if (type == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"type\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (x == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"x\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (y == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"y\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (width == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"width\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (height == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"height\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (!check_expression (x, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
if (!check_expression (y, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
if (!check_expression (width, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
if (!check_expression (height, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
type_val = meta_gradient_type_from_string (type);
|
|
if (type_val == META_GRADIENT_LAST)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Did not understand value \"%s\" for type of gradient"),
|
|
type);
|
|
return;
|
|
}
|
|
|
|
alpha_spec = NULL;
|
|
if (alpha && !parse_alpha (alpha, &alpha_spec, context, error))
|
|
return;
|
|
|
|
g_assert (info->op == NULL);
|
|
info->op = meta_draw_op_new (META_DRAW_GRADIENT);
|
|
|
|
info->op->data.gradient.x = optimize_expression (info->theme, x);
|
|
info->op->data.gradient.y = optimize_expression (info->theme, y);
|
|
info->op->data.gradient.width = optimize_expression (info->theme, width);
|
|
info->op->data.gradient.height = optimize_expression (info->theme, height);
|
|
|
|
info->op->data.gradient.gradient_spec = meta_gradient_spec_new (type_val);
|
|
|
|
info->op->data.gradient.alpha_spec = alpha_spec;
|
|
|
|
push_state (info, STATE_GRADIENT);
|
|
|
|
/* op gets appended on close tag */
|
|
}
|
|
else if (ELEMENT_IS ("image"))
|
|
{
|
|
MetaDrawOp *op;
|
|
const char *filename;
|
|
const char *x;
|
|
const char *y;
|
|
const char *width;
|
|
const char *height;
|
|
const char *alpha;
|
|
const char *colorize;
|
|
const char *fill_type;
|
|
MetaAlphaGradientSpec *alpha_spec;
|
|
GdkPixbuf *pixbuf;
|
|
MetaColorSpec *colorize_spec = NULL;
|
|
MetaImageFillType fill_type_val;
|
|
int h, w, c;
|
|
int pixbuf_width, pixbuf_height, pixbuf_n_channels, pixbuf_rowstride;
|
|
guchar *pixbuf_pixels;
|
|
|
|
if (!locate_attributes (context, element_name, attribute_names, attribute_values,
|
|
error,
|
|
"x", &x, "y", &y,
|
|
"width", &width, "height", &height,
|
|
"alpha", &alpha, "filename", &filename,
|
|
"colorize", &colorize,
|
|
"fill_type", &fill_type,
|
|
NULL))
|
|
return;
|
|
|
|
if (x == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"x\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (y == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"y\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (width == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"width\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (height == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"height\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (filename == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"filename\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (!check_expression (x, TRUE, info->theme, context, error))
|
|
return;
|
|
|
|
if (!check_expression (y, TRUE, info->theme, context, error))
|
|
return;
|
|
|
|
if (!check_expression (width, TRUE, info->theme, context, error))
|
|
return;
|
|
|
|
if (!check_expression (height, TRUE, info->theme, context, error))
|
|
return;
|
|
|
|
fill_type_val = META_IMAGE_FILL_SCALE;
|
|
if (fill_type)
|
|
{
|
|
fill_type_val = meta_image_fill_type_from_string (fill_type);
|
|
|
|
if (((int) fill_type_val) == -1)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR,
|
|
G_MARKUP_ERROR_PARSE,
|
|
_("Did not understand fill type \"%s\" for <%s> element"),
|
|
fill_type, element_name);
|
|
}
|
|
}
|
|
|
|
/* Check last so we don't have to free it when other
|
|
* stuff fails.
|
|
*
|
|
* If it's a theme image, ask for it at 64px, which is
|
|
* the largest possible. We scale it anyway.
|
|
*/
|
|
pixbuf = meta_theme_load_image (info->theme, filename, 64, error);
|
|
|
|
if (pixbuf == NULL)
|
|
{
|
|
add_context_to_error (error, context);
|
|
return;
|
|
}
|
|
|
|
if (colorize)
|
|
{
|
|
colorize_spec = parse_color (info->theme, colorize, error);
|
|
|
|
if (colorize_spec == NULL)
|
|
{
|
|
add_context_to_error (error, context);
|
|
g_object_unref (G_OBJECT (pixbuf));
|
|
return;
|
|
}
|
|
}
|
|
|
|
alpha_spec = NULL;
|
|
if (alpha && !parse_alpha (alpha, &alpha_spec, context, error))
|
|
{
|
|
g_object_unref (G_OBJECT (pixbuf));
|
|
return;
|
|
}
|
|
|
|
op = meta_draw_op_new (META_DRAW_IMAGE);
|
|
|
|
op->data.image.pixbuf = pixbuf;
|
|
op->data.image.colorize_spec = colorize_spec;
|
|
op->data.image.x = optimize_expression (info->theme, x);
|
|
op->data.image.y = optimize_expression (info->theme, y);
|
|
op->data.image.width = optimize_expression (info->theme, width);
|
|
op->data.image.height = optimize_expression (info->theme, height);
|
|
op->data.image.alpha_spec = alpha_spec;
|
|
op->data.image.fill_type = fill_type_val;
|
|
|
|
/* Check for vertical & horizontal stripes */
|
|
pixbuf_n_channels = gdk_pixbuf_get_n_channels(pixbuf);
|
|
pixbuf_width = gdk_pixbuf_get_width(pixbuf);
|
|
pixbuf_height = gdk_pixbuf_get_height(pixbuf);
|
|
pixbuf_rowstride = gdk_pixbuf_get_rowstride(pixbuf);
|
|
pixbuf_pixels = gdk_pixbuf_get_pixels(pixbuf);
|
|
|
|
/* Check for horizontal stripes */
|
|
for (h = 0; h < pixbuf_height; h++)
|
|
{
|
|
for (w = 1; w < pixbuf_width; w++)
|
|
{
|
|
for (c = 0; c < pixbuf_n_channels; c++)
|
|
{
|
|
if (pixbuf_pixels[(h * pixbuf_rowstride) + c] !=
|
|
pixbuf_pixels[(h * pixbuf_rowstride) + w + c])
|
|
break;
|
|
}
|
|
if (c < pixbuf_n_channels)
|
|
break;
|
|
}
|
|
if (w < pixbuf_width)
|
|
break;
|
|
}
|
|
|
|
if (h >= pixbuf_height)
|
|
{
|
|
op->data.image.horizontal_stripes = TRUE;
|
|
}
|
|
else
|
|
{
|
|
op->data.image.horizontal_stripes = FALSE;
|
|
}
|
|
|
|
/* Check for vertical stripes */
|
|
for (w = 0; w < pixbuf_width; w++)
|
|
{
|
|
for (h = 1; h < pixbuf_height; h++)
|
|
{
|
|
for (c = 0; c < pixbuf_n_channels; c++)
|
|
{
|
|
if (pixbuf_pixels[w + c] !=
|
|
pixbuf_pixels[(h * pixbuf_rowstride) + w + c])
|
|
break;
|
|
}
|
|
if (c < pixbuf_n_channels)
|
|
break;
|
|
}
|
|
if (h < pixbuf_height)
|
|
break;
|
|
}
|
|
|
|
if (w >= pixbuf_width)
|
|
{
|
|
op->data.image.vertical_stripes = TRUE;
|
|
}
|
|
else
|
|
{
|
|
op->data.image.vertical_stripes = FALSE;
|
|
}
|
|
|
|
g_assert (info->op_list);
|
|
|
|
meta_draw_op_list_append (info->op_list, op);
|
|
|
|
push_state (info, STATE_IMAGE);
|
|
}
|
|
else if (ELEMENT_IS ("gtk_arrow"))
|
|
{
|
|
MetaDrawOp *op;
|
|
const char *state;
|
|
const char *shadow;
|
|
const char *arrow;
|
|
const char *x;
|
|
const char *y;
|
|
const char *width;
|
|
const char *height;
|
|
const char *filled;
|
|
gboolean filled_val;
|
|
GtkStateType state_val;
|
|
GtkShadowType shadow_val;
|
|
GtkArrowType arrow_val;
|
|
|
|
if (!locate_attributes (context, element_name, attribute_names, attribute_values,
|
|
error,
|
|
"state", &state,
|
|
"shadow", &shadow,
|
|
"arrow", &arrow,
|
|
"x", &x, "y", &y,
|
|
"width", &width, "height", &height,
|
|
"filled", &filled,
|
|
NULL))
|
|
return;
|
|
|
|
if (state == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"state\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (shadow == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"shadow\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (arrow == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"arrow\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (x == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"x\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (y == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"y\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (width == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"width\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (height == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"height\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (!check_expression (x, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
if (!check_expression (y, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
if (!check_expression (width, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
if (!check_expression (height, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
filled_val = TRUE;
|
|
if (filled && !parse_boolean (filled, &filled_val, context, error))
|
|
return;
|
|
|
|
state_val = meta_gtk_state_from_string (state);
|
|
if (((int) state_val) == -1)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR,
|
|
G_MARKUP_ERROR_PARSE,
|
|
_("Did not understand state \"%s\" for <%s> element"),
|
|
state, element_name);
|
|
return;
|
|
}
|
|
|
|
shadow_val = meta_gtk_shadow_from_string (shadow);
|
|
if (((int) shadow_val) == -1)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR,
|
|
G_MARKUP_ERROR_PARSE,
|
|
_("Did not understand shadow \"%s\" for <%s> element"),
|
|
shadow, element_name);
|
|
return;
|
|
}
|
|
|
|
arrow_val = meta_gtk_arrow_from_string (arrow);
|
|
if (((int) arrow_val) == -1)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR,
|
|
G_MARKUP_ERROR_PARSE,
|
|
_("Did not understand arrow \"%s\" for <%s> element"),
|
|
arrow, element_name);
|
|
return;
|
|
}
|
|
|
|
op = meta_draw_op_new (META_DRAW_GTK_ARROW);
|
|
|
|
op->data.gtk_arrow.x = optimize_expression (info->theme, x);
|
|
op->data.gtk_arrow.y = optimize_expression (info->theme, y);
|
|
op->data.gtk_arrow.width = optimize_expression (info->theme, width);
|
|
op->data.gtk_arrow.height = optimize_expression (info->theme, height);
|
|
op->data.gtk_arrow.filled = filled_val;
|
|
op->data.gtk_arrow.state = state_val;
|
|
op->data.gtk_arrow.shadow = shadow_val;
|
|
op->data.gtk_arrow.arrow = arrow_val;
|
|
|
|
g_assert (info->op_list);
|
|
|
|
meta_draw_op_list_append (info->op_list, op);
|
|
|
|
push_state (info, STATE_GTK_ARROW);
|
|
}
|
|
else if (ELEMENT_IS ("gtk_box"))
|
|
{
|
|
MetaDrawOp *op;
|
|
const char *state;
|
|
const char *shadow;
|
|
const char *x;
|
|
const char *y;
|
|
const char *width;
|
|
const char *height;
|
|
GtkStateType state_val;
|
|
GtkShadowType shadow_val;
|
|
|
|
if (!locate_attributes (context, element_name, attribute_names, attribute_values,
|
|
error,
|
|
"state", &state,
|
|
"shadow", &shadow,
|
|
"x", &x, "y", &y,
|
|
"width", &width, "height", &height,
|
|
NULL))
|
|
return;
|
|
|
|
if (state == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"state\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (shadow == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"shadow\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (x == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"x\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (y == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"y\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (width == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"width\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (height == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"height\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (!check_expression (x, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
if (!check_expression (y, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
if (!check_expression (width, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
if (!check_expression (height, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
state_val = meta_gtk_state_from_string (state);
|
|
if (((int) state_val) == -1)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR,
|
|
G_MARKUP_ERROR_PARSE,
|
|
_("Did not understand state \"%s\" for <%s> element"),
|
|
state, element_name);
|
|
return;
|
|
}
|
|
|
|
shadow_val = meta_gtk_shadow_from_string (shadow);
|
|
if (((int) shadow_val) == -1)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR,
|
|
G_MARKUP_ERROR_PARSE,
|
|
_("Did not understand shadow \"%s\" for <%s> element"),
|
|
shadow, element_name);
|
|
return;
|
|
}
|
|
|
|
op = meta_draw_op_new (META_DRAW_GTK_BOX);
|
|
|
|
op->data.gtk_box.x = optimize_expression (info->theme, x);
|
|
op->data.gtk_box.y = optimize_expression (info->theme, y);
|
|
op->data.gtk_box.width = optimize_expression (info->theme, width);
|
|
op->data.gtk_box.height = optimize_expression (info->theme, height);
|
|
op->data.gtk_box.state = state_val;
|
|
op->data.gtk_box.shadow = shadow_val;
|
|
|
|
g_assert (info->op_list);
|
|
|
|
meta_draw_op_list_append (info->op_list, op);
|
|
|
|
push_state (info, STATE_GTK_BOX);
|
|
}
|
|
else if (ELEMENT_IS ("gtk_vline"))
|
|
{
|
|
MetaDrawOp *op;
|
|
const char *state;
|
|
const char *x;
|
|
const char *y1;
|
|
const char *y2;
|
|
GtkStateType state_val;
|
|
|
|
if (!locate_attributes (context, element_name, attribute_names, attribute_values,
|
|
error,
|
|
"state", &state,
|
|
"x", &x, "y1", &y1, "y2", &y2,
|
|
NULL))
|
|
return;
|
|
|
|
if (state == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"state\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (x == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"x\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (y1 == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"y1\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (y2 == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"y2\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (!check_expression (x, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
if (!check_expression (y1, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
if (!check_expression (y2, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
state_val = meta_gtk_state_from_string (state);
|
|
if (((int) state_val) == -1)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR,
|
|
G_MARKUP_ERROR_PARSE,
|
|
_("Did not understand state \"%s\" for <%s> element"),
|
|
state, element_name);
|
|
return;
|
|
}
|
|
|
|
op = meta_draw_op_new (META_DRAW_GTK_VLINE);
|
|
|
|
op->data.gtk_vline.x = optimize_expression (info->theme, x);
|
|
op->data.gtk_vline.y1 = optimize_expression (info->theme, y1);
|
|
op->data.gtk_vline.y2 = optimize_expression (info->theme, y2);
|
|
op->data.gtk_vline.state = state_val;
|
|
|
|
g_assert (info->op_list);
|
|
|
|
meta_draw_op_list_append (info->op_list, op);
|
|
|
|
push_state (info, STATE_GTK_VLINE);
|
|
}
|
|
else if (ELEMENT_IS ("icon"))
|
|
{
|
|
MetaDrawOp *op;
|
|
const char *x;
|
|
const char *y;
|
|
const char *width;
|
|
const char *height;
|
|
const char *alpha;
|
|
const char *fill_type;
|
|
MetaAlphaGradientSpec *alpha_spec;
|
|
MetaImageFillType fill_type_val;
|
|
|
|
if (!locate_attributes (context, element_name, attribute_names, attribute_values,
|
|
error,
|
|
"x", &x, "y", &y,
|
|
"width", &width, "height", &height,
|
|
"alpha", &alpha,
|
|
"fill_type", &fill_type,
|
|
NULL))
|
|
return;
|
|
|
|
if (x == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"x\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (y == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"y\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (width == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"width\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (height == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"height\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (!check_expression (x, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
if (!check_expression (y, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
if (!check_expression (width, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
if (!check_expression (height, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
fill_type_val = META_IMAGE_FILL_SCALE;
|
|
if (fill_type)
|
|
{
|
|
fill_type_val = meta_image_fill_type_from_string (fill_type);
|
|
|
|
if (((int) fill_type_val) == -1)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR,
|
|
G_MARKUP_ERROR_PARSE,
|
|
_("Did not understand fill type \"%s\" for <%s> element"),
|
|
fill_type, element_name);
|
|
}
|
|
}
|
|
|
|
alpha_spec = NULL;
|
|
if (alpha && !parse_alpha (alpha, &alpha_spec, context, error))
|
|
return;
|
|
|
|
op = meta_draw_op_new (META_DRAW_ICON);
|
|
|
|
op->data.icon.x = optimize_expression (info->theme, x);
|
|
op->data.icon.y = optimize_expression (info->theme, y);
|
|
op->data.icon.width = optimize_expression (info->theme, width);
|
|
op->data.icon.height = optimize_expression (info->theme, height);
|
|
op->data.icon.alpha_spec = alpha_spec;
|
|
op->data.icon.fill_type = fill_type_val;
|
|
|
|
g_assert (info->op_list);
|
|
|
|
meta_draw_op_list_append (info->op_list, op);
|
|
|
|
push_state (info, STATE_ICON);
|
|
}
|
|
else if (ELEMENT_IS ("title"))
|
|
{
|
|
MetaDrawOp *op;
|
|
const char *color;
|
|
const char *x;
|
|
const char *y;
|
|
MetaColorSpec *color_spec;
|
|
|
|
if (!locate_attributes (context, element_name, attribute_names, attribute_values,
|
|
error,
|
|
"color", &color,
|
|
"x", &x, "y", &y,
|
|
NULL))
|
|
return;
|
|
|
|
if (color == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"color\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (x == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"x\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (y == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"y\" attribute on element <%s>"), element_name);
|
|
return;
|
|
}
|
|
|
|
if (!check_expression (x, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
if (!check_expression (y, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
/* Check last so we don't have to free it when other
|
|
* stuff fails
|
|
*/
|
|
color_spec = parse_color (info->theme, color, error);
|
|
if (color_spec == NULL)
|
|
{
|
|
add_context_to_error (error, context);
|
|
return;
|
|
}
|
|
|
|
op = meta_draw_op_new (META_DRAW_TITLE);
|
|
|
|
op->data.title.color_spec = color_spec;
|
|
op->data.title.x = optimize_expression (info->theme, x);
|
|
op->data.title.y = optimize_expression (info->theme, y);
|
|
|
|
g_assert (info->op_list);
|
|
|
|
meta_draw_op_list_append (info->op_list, op);
|
|
|
|
push_state (info, STATE_TITLE);
|
|
}
|
|
else if (ELEMENT_IS ("include"))
|
|
{
|
|
MetaDrawOp *op;
|
|
const char *name;
|
|
const char *x;
|
|
const char *y;
|
|
const char *width;
|
|
const char *height;
|
|
MetaDrawOpList *op_list;
|
|
|
|
if (!locate_attributes (context, element_name, attribute_names, attribute_values,
|
|
error,
|
|
"x", &x, "y", &y,
|
|
"width", &width, "height", &height,
|
|
"name", &name,
|
|
NULL))
|
|
return;
|
|
|
|
if (name == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"%s\" attribute on <%s> element"), "name", element_name);
|
|
return;
|
|
}
|
|
|
|
/* x/y/width/height default to 0,0,width,height - should
|
|
* probably do this for all the draw ops
|
|
*/
|
|
|
|
if (x && !check_expression (x, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
if (y && !check_expression (y, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
if (width && !check_expression (width, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
if (height && !check_expression (height, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
op_list = meta_theme_lookup_draw_op_list (info->theme,
|
|
name);
|
|
if (op_list == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR,
|
|
G_MARKUP_ERROR_PARSE,
|
|
_("No <draw_ops> called \"%s\" has been defined"),
|
|
name);
|
|
return;
|
|
}
|
|
|
|
g_assert (info->op_list);
|
|
|
|
if (op_list == info->op_list ||
|
|
meta_draw_op_list_contains (op_list, info->op_list))
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR,
|
|
G_MARKUP_ERROR_PARSE,
|
|
_("Including draw_ops \"%s\" here would create a circular reference"),
|
|
name);
|
|
return;
|
|
}
|
|
|
|
op = meta_draw_op_new (META_DRAW_OP_LIST);
|
|
|
|
meta_draw_op_list_ref (op_list);
|
|
op->data.op_list.op_list = op_list;
|
|
op->data.op_list.x = x ? optimize_expression (info->theme, x) :
|
|
g_strdup ("0");
|
|
op->data.op_list.y = y ? optimize_expression (info->theme, y) :
|
|
g_strdup ("0");
|
|
op->data.op_list.width = width ? optimize_expression (info->theme, width) :
|
|
g_strdup ("width");
|
|
op->data.op_list.height = height ? optimize_expression (info->theme, height) :
|
|
g_strdup ("height");
|
|
|
|
meta_draw_op_list_append (info->op_list, op);
|
|
|
|
push_state (info, STATE_INCLUDE);
|
|
}
|
|
else if (ELEMENT_IS ("tile"))
|
|
{
|
|
MetaDrawOp *op;
|
|
const char *name;
|
|
const char *x;
|
|
const char *y;
|
|
const char *width;
|
|
const char *height;
|
|
const char *tile_xoffset;
|
|
const char *tile_yoffset;
|
|
const char *tile_width;
|
|
const char *tile_height;
|
|
MetaDrawOpList *op_list;
|
|
|
|
if (!locate_attributes (context, element_name, attribute_names, attribute_values,
|
|
error,
|
|
"x", &x, "y", &y,
|
|
"width", &width, "height", &height,
|
|
"name", &name,
|
|
"tile_xoffset", &tile_xoffset,
|
|
"tile_yoffset", &tile_yoffset,
|
|
"tile_width", &tile_width,
|
|
"tile_height", &tile_height,
|
|
NULL))
|
|
return;
|
|
|
|
if (name == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"%s\" attribute on <%s> element"), "name", element_name);
|
|
return;
|
|
}
|
|
|
|
if (tile_width == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"%s\" attribute on <%s> element"), "tile_width", element_name);
|
|
return;
|
|
}
|
|
|
|
if (tile_height == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"%s\" attribute on <%s> element"), "tile_height", element_name);
|
|
return;
|
|
}
|
|
|
|
/* These default to 0 */
|
|
if (tile_xoffset && !check_expression (tile_xoffset, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
if (tile_yoffset && !check_expression (tile_xoffset, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
/* x/y/width/height default to 0,0,width,height - should
|
|
* probably do this for all the draw ops
|
|
*/
|
|
|
|
if (x && !check_expression (x, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
if (y && !check_expression (y, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
if (width && !check_expression (width, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
if (height && !check_expression (height, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
if (!check_expression (tile_width, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
if (!check_expression (tile_height, FALSE, info->theme, context, error))
|
|
return;
|
|
|
|
op_list = meta_theme_lookup_draw_op_list (info->theme,
|
|
name);
|
|
if (op_list == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR,
|
|
G_MARKUP_ERROR_PARSE,
|
|
_("No <draw_ops> called \"%s\" has been defined"),
|
|
name);
|
|
return;
|
|
}
|
|
|
|
g_assert (info->op_list);
|
|
|
|
if (op_list == info->op_list ||
|
|
meta_draw_op_list_contains (op_list, info->op_list))
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR,
|
|
G_MARKUP_ERROR_PARSE,
|
|
_("Including draw_ops \"%s\" here would create a circular reference"),
|
|
name);
|
|
return;
|
|
}
|
|
|
|
op = meta_draw_op_new (META_DRAW_TILE);
|
|
|
|
meta_draw_op_list_ref (op_list);
|
|
op->data.tile.op_list = op_list;
|
|
op->data.tile.x = x ? optimize_expression (info->theme, x) :
|
|
g_strdup ("0");
|
|
op->data.tile.y = y ? optimize_expression (info->theme, y) :
|
|
g_strdup ("0");
|
|
op->data.tile.width = width ? optimize_expression (info->theme, width) :
|
|
g_strdup ("width");
|
|
op->data.tile.height = height ? optimize_expression (info->theme, height) :
|
|
g_strdup ("height");
|
|
op->data.tile.tile_xoffset = tile_xoffset ?
|
|
optimize_expression (info->theme, tile_xoffset) :
|
|
g_strdup ("0");
|
|
op->data.tile.tile_yoffset = tile_yoffset ?
|
|
optimize_expression (info->theme, tile_yoffset) :
|
|
g_strdup ("0");
|
|
op->data.tile.tile_width = optimize_expression (info->theme, tile_width);
|
|
op->data.tile.tile_height = optimize_expression (info->theme, tile_height);
|
|
|
|
meta_draw_op_list_append (info->op_list, op);
|
|
|
|
push_state (info, STATE_TILE);
|
|
}
|
|
else
|
|
{
|
|
set_error (error, context,
|
|
G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Element <%s> is not allowed below <%s>"),
|
|
element_name, "draw_ops");
|
|
}
|
|
}
|
|
|
|
static void
|
|
parse_gradient_element (GMarkupParseContext *context,
|
|
const gchar *element_name,
|
|
const gchar **attribute_names,
|
|
const gchar **attribute_values,
|
|
ParseInfo *info,
|
|
GError **error)
|
|
{
|
|
g_return_if_fail (peek_state (info) == STATE_GRADIENT);
|
|
|
|
if (ELEMENT_IS ("color"))
|
|
{
|
|
const char *value = NULL;
|
|
MetaColorSpec *color_spec;
|
|
|
|
if (!locate_attributes (context, element_name, attribute_names, attribute_values,
|
|
error,
|
|
"value", &value,
|
|
NULL))
|
|
return;
|
|
|
|
if (value == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"value\" attribute on <%s> element"),
|
|
element_name);
|
|
return;
|
|
}
|
|
|
|
color_spec = parse_color (info->theme, value, error);
|
|
if (color_spec == NULL)
|
|
{
|
|
add_context_to_error (error, context);
|
|
return;
|
|
}
|
|
|
|
g_assert (info->op);
|
|
g_assert (info->op->type == META_DRAW_GRADIENT);
|
|
g_assert (info->op->data.gradient.gradient_spec != NULL);
|
|
info->op->data.gradient.gradient_spec->color_specs =
|
|
g_slist_append (info->op->data.gradient.gradient_spec->color_specs,
|
|
color_spec);
|
|
|
|
push_state (info, STATE_COLOR);
|
|
}
|
|
else
|
|
{
|
|
set_error (error, context,
|
|
G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Element <%s> is not allowed below <%s>"),
|
|
element_name, "gradient");
|
|
}
|
|
}
|
|
|
|
static void
|
|
parse_style_element (GMarkupParseContext *context,
|
|
const gchar *element_name,
|
|
const gchar **attribute_names,
|
|
const gchar **attribute_values,
|
|
ParseInfo *info,
|
|
GError **error)
|
|
{
|
|
g_return_if_fail (peek_state (info) == STATE_FRAME_STYLE);
|
|
|
|
g_assert (info->style);
|
|
|
|
if (ELEMENT_IS ("piece"))
|
|
{
|
|
const char *position = NULL;
|
|
const char *draw_ops = NULL;
|
|
|
|
if (!locate_attributes (context, element_name, attribute_names, attribute_values,
|
|
error,
|
|
"position", &position,
|
|
"draw_ops", &draw_ops,
|
|
NULL))
|
|
return;
|
|
|
|
if (position == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"position\" attribute on <%s> element"),
|
|
element_name);
|
|
return;
|
|
}
|
|
|
|
info->piece = meta_frame_piece_from_string (position);
|
|
if (info->piece == META_FRAME_PIECE_LAST)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Unknown position \"%s\" for frame piece"),
|
|
position);
|
|
return;
|
|
}
|
|
|
|
if (info->style->pieces[info->piece] != NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Frame style already has a piece at position %s"),
|
|
position);
|
|
return;
|
|
}
|
|
|
|
g_assert (info->op_list == NULL);
|
|
|
|
if (draw_ops)
|
|
{
|
|
MetaDrawOpList *op_list;
|
|
|
|
op_list = meta_theme_lookup_draw_op_list (info->theme,
|
|
draw_ops);
|
|
|
|
if (op_list == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No <draw_ops> with the name \"%s\" has been defined"),
|
|
draw_ops);
|
|
return;
|
|
}
|
|
|
|
meta_draw_op_list_ref (op_list);
|
|
info->op_list = op_list;
|
|
}
|
|
|
|
push_state (info, STATE_PIECE);
|
|
}
|
|
else if (ELEMENT_IS ("button"))
|
|
{
|
|
const char *function = NULL;
|
|
const char *state = NULL;
|
|
const char *draw_ops = NULL;
|
|
|
|
if (!locate_attributes (context, element_name, attribute_names, attribute_values,
|
|
error,
|
|
"function", &function,
|
|
"state", &state,
|
|
"draw_ops", &draw_ops,
|
|
NULL))
|
|
return;
|
|
|
|
if (function == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"function\" attribute on <%s> element"),
|
|
element_name);
|
|
return;
|
|
}
|
|
|
|
if (state == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"state\" attribute on <%s> element"),
|
|
element_name);
|
|
return;
|
|
}
|
|
|
|
info->button_type = meta_button_type_from_string (function, info->theme);
|
|
if (info->button_type == META_BUTTON_TYPE_LAST)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Unknown function \"%s\" for button"),
|
|
function);
|
|
return;
|
|
}
|
|
|
|
if (meta_theme_earliest_version_with_button (info->button_type) >
|
|
info->theme->format_version)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Button function \"%s\" does not exist in this version (%d, need %d)"),
|
|
function,
|
|
info->theme->format_version,
|
|
meta_theme_earliest_version_with_button (info->button_type)
|
|
);
|
|
return;
|
|
}
|
|
|
|
info->button_state = meta_button_state_from_string (state);
|
|
if (info->button_state == META_BUTTON_STATE_LAST)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Unknown state \"%s\" for button"),
|
|
state);
|
|
return;
|
|
}
|
|
|
|
if (info->style->buttons[info->button_type][info->button_state] != NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Frame style already has a button for function %s state %s"),
|
|
function, state);
|
|
return;
|
|
}
|
|
|
|
g_assert (info->op_list == NULL);
|
|
|
|
if (draw_ops)
|
|
{
|
|
MetaDrawOpList *op_list;
|
|
|
|
op_list = meta_theme_lookup_draw_op_list (info->theme,
|
|
draw_ops);
|
|
|
|
if (op_list == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No <draw_ops> with the name \"%s\" has been defined"),
|
|
draw_ops);
|
|
return;
|
|
}
|
|
|
|
meta_draw_op_list_ref (op_list);
|
|
info->op_list = op_list;
|
|
}
|
|
|
|
push_state (info, STATE_BUTTON);
|
|
}
|
|
else
|
|
{
|
|
set_error (error, context,
|
|
G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Element <%s> is not allowed below <%s>"),
|
|
element_name, "frame_style");
|
|
}
|
|
}
|
|
|
|
static void
|
|
parse_style_set_element (GMarkupParseContext *context,
|
|
const gchar *element_name,
|
|
const gchar **attribute_names,
|
|
const gchar **attribute_values,
|
|
ParseInfo *info,
|
|
GError **error)
|
|
{
|
|
g_return_if_fail (peek_state (info) == STATE_FRAME_STYLE_SET);
|
|
|
|
if (ELEMENT_IS ("frame"))
|
|
{
|
|
const char *focus = NULL;
|
|
const char *state = NULL;
|
|
const char *resize = NULL;
|
|
const char *style = NULL;
|
|
MetaFrameFocus frame_focus;
|
|
MetaFrameState frame_state;
|
|
MetaFrameResize frame_resize;
|
|
MetaFrameStyle *frame_style;
|
|
|
|
if (!locate_attributes (context, element_name, attribute_names, attribute_values,
|
|
error,
|
|
"focus", &focus,
|
|
"state", &state,
|
|
"resize", &resize,
|
|
"style", &style,
|
|
NULL))
|
|
return;
|
|
|
|
if (focus == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"focus\" attribute on <%s> element"),
|
|
element_name);
|
|
return;
|
|
}
|
|
|
|
if (state == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"state\" attribute on <%s> element"),
|
|
element_name);
|
|
return;
|
|
}
|
|
|
|
if (style == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"style\" attribute on <%s> element"),
|
|
element_name);
|
|
return;
|
|
}
|
|
|
|
frame_focus = meta_frame_focus_from_string (focus);
|
|
if (frame_focus == META_FRAME_FOCUS_LAST)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("\"%s\" is not a valid value for focus attribute"),
|
|
focus);
|
|
return;
|
|
}
|
|
|
|
frame_state = meta_frame_state_from_string (state);
|
|
if (frame_state == META_FRAME_STATE_LAST)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("\"%s\" is not a valid value for state attribute"),
|
|
focus);
|
|
return;
|
|
}
|
|
|
|
frame_style = meta_theme_lookup_style (info->theme, style);
|
|
|
|
if (frame_style == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("A style called \"%s\" has not been defined"),
|
|
style);
|
|
return;
|
|
}
|
|
|
|
switch (frame_state)
|
|
{
|
|
case META_FRAME_STATE_NORMAL:
|
|
if (resize == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No \"resize\" attribute on <%s> element"),
|
|
element_name);
|
|
return;
|
|
}
|
|
|
|
|
|
frame_resize = meta_frame_resize_from_string (resize);
|
|
if (frame_resize == META_FRAME_RESIZE_LAST)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("\"%s\" is not a valid value for resize attribute"),
|
|
focus);
|
|
return;
|
|
}
|
|
|
|
break;
|
|
|
|
case META_FRAME_STATE_SHADED:
|
|
if (META_THEME_ALLOWS (info->theme, META_THEME_UNRESIZABLE_SHADED_STYLES))
|
|
{
|
|
if (resize == NULL)
|
|
/* In state="normal" we would complain here. But instead we accept
|
|
* not having a resize attribute and default to resize="both", since
|
|
* that most closely mimics what we did in v1, and thus people can
|
|
* upgrade a theme to v2 without as much hassle.
|
|
*/
|
|
frame_resize = META_FRAME_RESIZE_BOTH;
|
|
else
|
|
{
|
|
frame_resize = meta_frame_resize_from_string (resize);
|
|
if (frame_resize == META_FRAME_RESIZE_LAST)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("\"%s\" is not a valid value for resize attribute"),
|
|
focus);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
else /* v1 theme */
|
|
{
|
|
if (resize != NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Should not have \"resize\" attribute on <%s> element for maximized/shaded states"),
|
|
element_name);
|
|
return;
|
|
}
|
|
|
|
/* resize="both" is equivalent to the old behaviour */
|
|
frame_resize = META_FRAME_RESIZE_BOTH;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
if (resize != NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Should not have \"resize\" attribute on <%s> element for maximized states"),
|
|
element_name);
|
|
return;
|
|
}
|
|
|
|
frame_resize = META_FRAME_RESIZE_LAST;
|
|
}
|
|
|
|
switch (frame_state)
|
|
{
|
|
case META_FRAME_STATE_NORMAL:
|
|
if (info->style_set->normal_styles[frame_resize][frame_focus])
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Style has already been specified for state %s resize %s focus %s"),
|
|
state, resize, focus);
|
|
return;
|
|
}
|
|
meta_frame_style_ref (frame_style);
|
|
info->style_set->normal_styles[frame_resize][frame_focus] = frame_style;
|
|
break;
|
|
case META_FRAME_STATE_MAXIMIZED:
|
|
if (info->style_set->maximized_styles[frame_focus])
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Style has already been specified for state %s focus %s"),
|
|
state, focus);
|
|
return;
|
|
}
|
|
meta_frame_style_ref (frame_style);
|
|
info->style_set->maximized_styles[frame_focus] = frame_style;
|
|
break;
|
|
case META_FRAME_STATE_SHADED:
|
|
if (info->style_set->shaded_styles[frame_resize][frame_focus])
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Style has already been specified for state %s resize %s focus %s"),
|
|
state, resize, focus);
|
|
return;
|
|
}
|
|
meta_frame_style_ref (frame_style);
|
|
info->style_set->shaded_styles[frame_resize][frame_focus] = frame_style;
|
|
break;
|
|
case META_FRAME_STATE_MAXIMIZED_AND_SHADED:
|
|
if (info->style_set->maximized_and_shaded_styles[frame_focus])
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Style has already been specified for state %s focus %s"),
|
|
state, focus);
|
|
return;
|
|
}
|
|
meta_frame_style_ref (frame_style);
|
|
info->style_set->maximized_and_shaded_styles[frame_focus] = frame_style;
|
|
break;
|
|
case META_FRAME_STATE_LAST:
|
|
g_assert_not_reached ();
|
|
break;
|
|
}
|
|
|
|
push_state (info, STATE_FRAME);
|
|
}
|
|
else
|
|
{
|
|
set_error (error, context,
|
|
G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Element <%s> is not allowed below <%s>"),
|
|
element_name, "frame_style_set");
|
|
}
|
|
}
|
|
|
|
static void
|
|
parse_piece_element (GMarkupParseContext *context,
|
|
const gchar *element_name,
|
|
const gchar **attribute_names,
|
|
const gchar **attribute_values,
|
|
ParseInfo *info,
|
|
GError **error)
|
|
{
|
|
g_return_if_fail (peek_state (info) == STATE_PIECE);
|
|
|
|
if (ELEMENT_IS ("draw_ops"))
|
|
{
|
|
if (info->op_list)
|
|
{
|
|
set_error (error, context,
|
|
G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Can't have a two draw_ops for a <piece> element (theme specified a draw_ops attribute and also a <draw_ops> element, or specified two elements)"));
|
|
return;
|
|
}
|
|
|
|
if (!check_no_attributes (context, element_name, attribute_names, attribute_values,
|
|
error))
|
|
return;
|
|
|
|
g_assert (info->op_list == NULL);
|
|
info->op_list = meta_draw_op_list_new (2);
|
|
|
|
push_state (info, STATE_DRAW_OPS);
|
|
}
|
|
else
|
|
{
|
|
set_error (error, context,
|
|
G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Element <%s> is not allowed below <%s>"),
|
|
element_name, "piece");
|
|
}
|
|
}
|
|
|
|
static void
|
|
parse_button_element (GMarkupParseContext *context,
|
|
const gchar *element_name,
|
|
const gchar **attribute_names,
|
|
const gchar **attribute_values,
|
|
ParseInfo *info,
|
|
GError **error)
|
|
{
|
|
g_return_if_fail (peek_state (info) == STATE_BUTTON);
|
|
|
|
if (ELEMENT_IS ("draw_ops"))
|
|
{
|
|
if (info->op_list)
|
|
{
|
|
set_error (error, context,
|
|
G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Can't have a two draw_ops for a <button> element (theme specified a draw_ops attribute and also a <draw_ops> element, or specified two elements)"));
|
|
return;
|
|
}
|
|
|
|
if (!check_no_attributes (context, element_name, attribute_names, attribute_values,
|
|
error))
|
|
return;
|
|
|
|
g_assert (info->op_list == NULL);
|
|
info->op_list = meta_draw_op_list_new (2);
|
|
|
|
push_state (info, STATE_DRAW_OPS);
|
|
}
|
|
else
|
|
{
|
|
set_error (error, context,
|
|
G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Element <%s> is not allowed below <%s>"),
|
|
element_name, "button");
|
|
}
|
|
}
|
|
|
|
static void
|
|
parse_menu_icon_element (GMarkupParseContext *context,
|
|
const gchar *element_name,
|
|
const gchar **attribute_names,
|
|
const gchar **attribute_values,
|
|
ParseInfo *info,
|
|
GError **error)
|
|
{
|
|
g_return_if_fail (peek_state (info) == STATE_MENU_ICON);
|
|
|
|
if (ELEMENT_IS ("draw_ops"))
|
|
{
|
|
if (info->op_list)
|
|
{
|
|
set_error (error, context,
|
|
G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Can't have a two draw_ops for a <menu_icon> element (theme specified a draw_ops attribute and also a <draw_ops> element, or specified two elements)"));
|
|
return;
|
|
}
|
|
|
|
if (!check_no_attributes (context, element_name, attribute_names, attribute_values,
|
|
error))
|
|
return;
|
|
|
|
g_assert (info->op_list == NULL);
|
|
info->op_list = meta_draw_op_list_new (2);
|
|
|
|
push_state (info, STATE_DRAW_OPS);
|
|
}
|
|
else
|
|
{
|
|
set_error (error, context,
|
|
G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Element <%s> is not allowed below <%s>"),
|
|
element_name, "menu_icon");
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
start_element_handler (GMarkupParseContext *context,
|
|
const gchar *element_name,
|
|
const gchar **attribute_names,
|
|
const gchar **attribute_values,
|
|
gpointer user_data,
|
|
GError **error)
|
|
{
|
|
ParseInfo *info = user_data;
|
|
|
|
switch (peek_state (info))
|
|
{
|
|
case STATE_START:
|
|
if (strcmp (element_name, "metacity_theme") == 0)
|
|
{
|
|
info->theme = meta_theme_new ();
|
|
info->theme->name = g_strdup (info->theme_name);
|
|
info->theme->filename = g_strdup (info->theme_file);
|
|
info->theme->dirname = g_strdup (info->theme_dir);
|
|
info->theme->format_version = info->format_version;
|
|
|
|
push_state (info, STATE_THEME);
|
|
}
|
|
else
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Outermost element in theme must be <metacity_theme> not <%s>"),
|
|
element_name);
|
|
break;
|
|
|
|
case STATE_THEME:
|
|
parse_toplevel_element (context, element_name,
|
|
attribute_names, attribute_values,
|
|
info, error);
|
|
break;
|
|
case STATE_INFO:
|
|
parse_info_element (context, element_name,
|
|
attribute_names, attribute_values,
|
|
info, error);
|
|
break;
|
|
case STATE_NAME:
|
|
case STATE_AUTHOR:
|
|
case STATE_COPYRIGHT:
|
|
case STATE_DATE:
|
|
case STATE_DESCRIPTION:
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Element <%s> is not allowed inside a name/author/date/description element"),
|
|
element_name);
|
|
break;
|
|
case STATE_CONSTANT:
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Element <%s> is not allowed inside a <constant> element"),
|
|
element_name);
|
|
break;
|
|
case STATE_FRAME_GEOMETRY:
|
|
parse_geometry_element (context, element_name,
|
|
attribute_names, attribute_values,
|
|
info, error);
|
|
break;
|
|
case STATE_DISTANCE:
|
|
case STATE_BORDER:
|
|
case STATE_ASPECT_RATIO:
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Element <%s> is not allowed inside a distance/border/aspect_ratio element"),
|
|
element_name);
|
|
break;
|
|
case STATE_DRAW_OPS:
|
|
parse_draw_op_element (context, element_name,
|
|
attribute_names, attribute_values,
|
|
info, error);
|
|
break;
|
|
case STATE_LINE:
|
|
case STATE_RECTANGLE:
|
|
case STATE_ARC:
|
|
case STATE_CLIP:
|
|
case STATE_TINT:
|
|
case STATE_IMAGE:
|
|
case STATE_GTK_ARROW:
|
|
case STATE_GTK_BOX:
|
|
case STATE_GTK_VLINE:
|
|
case STATE_ICON:
|
|
case STATE_TITLE:
|
|
case STATE_INCLUDE:
|
|
case STATE_TILE:
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Element <%s> is not allowed inside a draw operation element"),
|
|
element_name);
|
|
break;
|
|
case STATE_GRADIENT:
|
|
parse_gradient_element (context, element_name,
|
|
attribute_names, attribute_values,
|
|
info, error);
|
|
break;
|
|
case STATE_COLOR:
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Element <%s> is not allowed inside a <%s> element"),
|
|
element_name, "color");
|
|
break;
|
|
case STATE_FRAME_STYLE:
|
|
parse_style_element (context, element_name,
|
|
attribute_names, attribute_values,
|
|
info, error);
|
|
break;
|
|
case STATE_PIECE:
|
|
parse_piece_element (context, element_name,
|
|
attribute_names, attribute_values,
|
|
info, error);
|
|
break;
|
|
case STATE_BUTTON:
|
|
parse_button_element (context, element_name,
|
|
attribute_names, attribute_values,
|
|
info, error);
|
|
break;
|
|
case STATE_MENU_ICON:
|
|
parse_menu_icon_element (context, element_name,
|
|
attribute_names, attribute_values,
|
|
info, error);
|
|
break;
|
|
case STATE_FRAME_STYLE_SET:
|
|
parse_style_set_element (context, element_name,
|
|
attribute_names, attribute_values,
|
|
info, error);
|
|
break;
|
|
case STATE_FRAME:
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Element <%s> is not allowed inside a <%s> element"),
|
|
element_name, "frame");
|
|
break;
|
|
case STATE_WINDOW:
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Element <%s> is not allowed inside a <%s> element"),
|
|
element_name, "window");
|
|
break;
|
|
case STATE_FALLBACK:
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Element <%s> is not allowed inside a <%s> element"),
|
|
element_name, "fallback");
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
end_element_handler (GMarkupParseContext *context,
|
|
const gchar *element_name,
|
|
gpointer user_data,
|
|
GError **error)
|
|
{
|
|
ParseInfo *info = user_data;
|
|
|
|
switch (peek_state (info))
|
|
{
|
|
case STATE_START:
|
|
break;
|
|
case STATE_THEME:
|
|
g_assert (info->theme);
|
|
|
|
if (!meta_theme_validate (info->theme, error))
|
|
{
|
|
add_context_to_error (error, context);
|
|
meta_theme_free (info->theme);
|
|
info->theme = NULL;
|
|
}
|
|
|
|
pop_state (info);
|
|
g_assert (peek_state (info) == STATE_START);
|
|
break;
|
|
case STATE_INFO:
|
|
pop_state (info);
|
|
g_assert (peek_state (info) == STATE_THEME);
|
|
break;
|
|
case STATE_NAME:
|
|
pop_state (info);
|
|
g_assert (peek_state (info) == STATE_INFO);
|
|
break;
|
|
case STATE_AUTHOR:
|
|
pop_state (info);
|
|
g_assert (peek_state (info) == STATE_INFO);
|
|
break;
|
|
case STATE_COPYRIGHT:
|
|
pop_state (info);
|
|
g_assert (peek_state (info) == STATE_INFO);
|
|
break;
|
|
case STATE_DATE:
|
|
pop_state (info);
|
|
g_assert (peek_state (info) == STATE_INFO);
|
|
break;
|
|
case STATE_DESCRIPTION:
|
|
pop_state (info);
|
|
g_assert (peek_state (info) == STATE_INFO);
|
|
break;
|
|
case STATE_CONSTANT:
|
|
pop_state (info);
|
|
g_assert (peek_state (info) == STATE_THEME);
|
|
break;
|
|
case STATE_FRAME_GEOMETRY:
|
|
g_assert (info->layout);
|
|
|
|
if (!meta_frame_layout_validate (info->layout,
|
|
error))
|
|
{
|
|
add_context_to_error (error, context);
|
|
}
|
|
|
|
/* layout will already be stored in the theme under
|
|
* its name
|
|
*/
|
|
meta_frame_layout_unref (info->layout);
|
|
info->layout = NULL;
|
|
pop_state (info);
|
|
g_assert (peek_state (info) == STATE_THEME);
|
|
break;
|
|
case STATE_DISTANCE:
|
|
pop_state (info);
|
|
g_assert (peek_state (info) == STATE_FRAME_GEOMETRY);
|
|
break;
|
|
case STATE_BORDER:
|
|
pop_state (info);
|
|
g_assert (peek_state (info) == STATE_FRAME_GEOMETRY);
|
|
break;
|
|
case STATE_ASPECT_RATIO:
|
|
pop_state (info);
|
|
g_assert (peek_state (info) == STATE_FRAME_GEOMETRY);
|
|
break;
|
|
case STATE_DRAW_OPS:
|
|
{
|
|
g_assert (info->op_list);
|
|
|
|
if (!meta_draw_op_list_validate (info->op_list,
|
|
error))
|
|
{
|
|
add_context_to_error (error, context);
|
|
meta_draw_op_list_unref (info->op_list);
|
|
info->op_list = NULL;
|
|
}
|
|
|
|
pop_state (info);
|
|
|
|
switch (peek_state (info))
|
|
{
|
|
case STATE_BUTTON:
|
|
case STATE_PIECE:
|
|
case STATE_MENU_ICON:
|
|
/* Leave info->op_list to be picked up
|
|
* when these elements are closed
|
|
*/
|
|
g_assert (info->op_list);
|
|
break;
|
|
case STATE_THEME:
|
|
g_assert (info->op_list);
|
|
meta_draw_op_list_unref (info->op_list);
|
|
info->op_list = NULL;
|
|
break;
|
|
default:
|
|
/* Op list can't occur in other contexts */
|
|
g_assert_not_reached ();
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case STATE_LINE:
|
|
pop_state (info);
|
|
g_assert (peek_state (info) == STATE_DRAW_OPS);
|
|
break;
|
|
case STATE_RECTANGLE:
|
|
pop_state (info);
|
|
g_assert (peek_state (info) == STATE_DRAW_OPS);
|
|
break;
|
|
case STATE_ARC:
|
|
pop_state (info);
|
|
g_assert (peek_state (info) == STATE_DRAW_OPS);
|
|
break;
|
|
case STATE_CLIP:
|
|
pop_state (info);
|
|
g_assert (peek_state (info) == STATE_DRAW_OPS);
|
|
break;
|
|
case STATE_TINT:
|
|
pop_state (info);
|
|
g_assert (peek_state (info) == STATE_DRAW_OPS);
|
|
break;
|
|
case STATE_GRADIENT:
|
|
g_assert (info->op);
|
|
g_assert (info->op->type == META_DRAW_GRADIENT);
|
|
if (!meta_gradient_spec_validate (info->op->data.gradient.gradient_spec,
|
|
error))
|
|
{
|
|
add_context_to_error (error, context);
|
|
meta_draw_op_free (info->op);
|
|
info->op = NULL;
|
|
}
|
|
else
|
|
{
|
|
g_assert (info->op_list);
|
|
meta_draw_op_list_append (info->op_list, info->op);
|
|
info->op = NULL;
|
|
}
|
|
pop_state (info);
|
|
g_assert (peek_state (info) == STATE_DRAW_OPS);
|
|
break;
|
|
case STATE_IMAGE:
|
|
pop_state (info);
|
|
g_assert (peek_state (info) == STATE_DRAW_OPS);
|
|
break;
|
|
case STATE_GTK_ARROW:
|
|
pop_state (info);
|
|
g_assert (peek_state (info) == STATE_DRAW_OPS);
|
|
break;
|
|
case STATE_GTK_BOX:
|
|
pop_state (info);
|
|
g_assert (peek_state (info) == STATE_DRAW_OPS);
|
|
break;
|
|
case STATE_GTK_VLINE:
|
|
pop_state (info);
|
|
g_assert (peek_state (info) == STATE_DRAW_OPS);
|
|
break;
|
|
case STATE_ICON:
|
|
pop_state (info);
|
|
g_assert (peek_state (info) == STATE_DRAW_OPS);
|
|
break;
|
|
case STATE_TITLE:
|
|
pop_state (info);
|
|
g_assert (peek_state (info) == STATE_DRAW_OPS);
|
|
break;
|
|
case STATE_INCLUDE:
|
|
pop_state (info);
|
|
g_assert (peek_state (info) == STATE_DRAW_OPS);
|
|
break;
|
|
case STATE_TILE:
|
|
pop_state (info);
|
|
g_assert (peek_state (info) == STATE_DRAW_OPS);
|
|
break;
|
|
case STATE_COLOR:
|
|
pop_state (info);
|
|
g_assert (peek_state (info) == STATE_GRADIENT);
|
|
break;
|
|
case STATE_FRAME_STYLE:
|
|
g_assert (info->style);
|
|
|
|
if (!meta_frame_style_validate (info->style,
|
|
info->theme->format_version,
|
|
error))
|
|
{
|
|
add_context_to_error (error, context);
|
|
}
|
|
|
|
/* Frame style is in the theme hash table and a ref
|
|
* is held there
|
|
*/
|
|
meta_frame_style_unref (info->style);
|
|
info->style = NULL;
|
|
pop_state (info);
|
|
g_assert (peek_state (info) == STATE_THEME);
|
|
break;
|
|
case STATE_PIECE:
|
|
g_assert (info->style);
|
|
if (info->op_list == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No draw_ops provided for frame piece"));
|
|
}
|
|
else
|
|
{
|
|
info->style->pieces[info->piece] = info->op_list;
|
|
info->op_list = NULL;
|
|
}
|
|
pop_state (info);
|
|
g_assert (peek_state (info) == STATE_FRAME_STYLE);
|
|
break;
|
|
case STATE_BUTTON:
|
|
g_assert (info->style);
|
|
if (info->op_list == NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("No draw_ops provided for button"));
|
|
}
|
|
else
|
|
{
|
|
info->style->buttons[info->button_type][info->button_state] =
|
|
info->op_list;
|
|
info->op_list = NULL;
|
|
}
|
|
pop_state (info);
|
|
break;
|
|
case STATE_MENU_ICON:
|
|
g_assert (info->theme);
|
|
if (info->op_list != NULL)
|
|
{
|
|
meta_draw_op_list_unref (info->op_list);
|
|
info->op_list = NULL;
|
|
}
|
|
pop_state (info);
|
|
g_assert (peek_state (info) == STATE_THEME);
|
|
break;
|
|
case STATE_FRAME_STYLE_SET:
|
|
g_assert (info->style_set);
|
|
|
|
if (!meta_frame_style_set_validate (info->style_set,
|
|
error))
|
|
{
|
|
add_context_to_error (error, context);
|
|
}
|
|
|
|
/* Style set is in the theme hash table and a reference
|
|
* is held there.
|
|
*/
|
|
meta_frame_style_set_unref (info->style_set);
|
|
info->style_set = NULL;
|
|
pop_state (info);
|
|
g_assert (peek_state (info) == STATE_THEME);
|
|
break;
|
|
case STATE_FRAME:
|
|
pop_state (info);
|
|
g_assert (peek_state (info) == STATE_FRAME_STYLE_SET);
|
|
break;
|
|
case STATE_WINDOW:
|
|
pop_state (info);
|
|
g_assert (peek_state (info) == STATE_THEME);
|
|
break;
|
|
case STATE_FALLBACK:
|
|
pop_state (info);
|
|
g_assert (peek_state (info) == STATE_THEME);
|
|
break;
|
|
}
|
|
}
|
|
|
|
#define NO_TEXT(element_name) set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, _("No text is allowed inside element <%s>"), element_name)
|
|
|
|
static gboolean
|
|
all_whitespace (const char *text,
|
|
int text_len)
|
|
{
|
|
const char *p;
|
|
const char *end;
|
|
|
|
p = text;
|
|
end = text + text_len;
|
|
|
|
while (p != end)
|
|
{
|
|
if (!g_ascii_isspace (*p))
|
|
return FALSE;
|
|
|
|
p = g_utf8_next_char (p);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
text_handler (GMarkupParseContext *context,
|
|
const gchar *text,
|
|
gsize text_len,
|
|
gpointer user_data,
|
|
GError **error)
|
|
{
|
|
ParseInfo *info = user_data;
|
|
|
|
if (all_whitespace (text, text_len))
|
|
return;
|
|
|
|
/* FIXME http://bugzilla.gnome.org/show_bug.cgi?id=70448 would
|
|
* allow a nice cleanup here.
|
|
*/
|
|
|
|
switch (peek_state (info))
|
|
{
|
|
case STATE_START:
|
|
g_assert_not_reached (); /* gmarkup shouldn't do this */
|
|
break;
|
|
case STATE_THEME:
|
|
NO_TEXT ("metacity_theme");
|
|
break;
|
|
case STATE_INFO:
|
|
NO_TEXT ("info");
|
|
break;
|
|
case STATE_NAME:
|
|
if (info->theme->readable_name != NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR,
|
|
G_MARKUP_ERROR_PARSE,
|
|
_("<name> specified twice for this theme"));
|
|
return;
|
|
}
|
|
|
|
info->theme->readable_name = g_strndup (text, text_len);
|
|
break;
|
|
case STATE_AUTHOR:
|
|
if (info->theme->author != NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR,
|
|
G_MARKUP_ERROR_PARSE,
|
|
_("<author> specified twice for this theme"));
|
|
return;
|
|
}
|
|
|
|
info->theme->author = g_strndup (text, text_len);
|
|
break;
|
|
case STATE_COPYRIGHT:
|
|
if (info->theme->copyright != NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR,
|
|
G_MARKUP_ERROR_PARSE,
|
|
_("<copyright> specified twice for this theme"));
|
|
return;
|
|
}
|
|
|
|
info->theme->copyright = g_strndup (text, text_len);
|
|
break;
|
|
case STATE_DATE:
|
|
if (info->theme->date != NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR,
|
|
G_MARKUP_ERROR_PARSE,
|
|
_("<date> specified twice for this theme"));
|
|
return;
|
|
}
|
|
|
|
info->theme->date = g_strndup (text, text_len);
|
|
break;
|
|
case STATE_DESCRIPTION:
|
|
if (info->theme->description != NULL)
|
|
{
|
|
set_error (error, context, G_MARKUP_ERROR,
|
|
G_MARKUP_ERROR_PARSE,
|
|
_("<description> specified twice for this theme"));
|
|
return;
|
|
}
|
|
|
|
info->theme->description = g_strndup (text, text_len);
|
|
break;
|
|
case STATE_CONSTANT:
|
|
NO_TEXT ("constant");
|
|
break;
|
|
case STATE_FRAME_GEOMETRY:
|
|
NO_TEXT ("frame_geometry");
|
|
break;
|
|
case STATE_DISTANCE:
|
|
NO_TEXT ("distance");
|
|
break;
|
|
case STATE_BORDER:
|
|
NO_TEXT ("border");
|
|
break;
|
|
case STATE_ASPECT_RATIO:
|
|
NO_TEXT ("aspect_ratio");
|
|
break;
|
|
case STATE_DRAW_OPS:
|
|
NO_TEXT ("draw_ops");
|
|
break;
|
|
case STATE_LINE:
|
|
NO_TEXT ("line");
|
|
break;
|
|
case STATE_RECTANGLE:
|
|
NO_TEXT ("rectangle");
|
|
break;
|
|
case STATE_ARC:
|
|
NO_TEXT ("arc");
|
|
break;
|
|
case STATE_CLIP:
|
|
NO_TEXT ("clip");
|
|
break;
|
|
case STATE_TINT:
|
|
NO_TEXT ("tint");
|
|
break;
|
|
case STATE_GRADIENT:
|
|
NO_TEXT ("gradient");
|
|
break;
|
|
case STATE_IMAGE:
|
|
NO_TEXT ("image");
|
|
break;
|
|
case STATE_GTK_ARROW:
|
|
NO_TEXT ("gtk_arrow");
|
|
break;
|
|
case STATE_GTK_BOX:
|
|
NO_TEXT ("gtk_box");
|
|
break;
|
|
case STATE_GTK_VLINE:
|
|
NO_TEXT ("gtk_vline");
|
|
break;
|
|
case STATE_ICON:
|
|
NO_TEXT ("icon");
|
|
break;
|
|
case STATE_TITLE:
|
|
NO_TEXT ("title");
|
|
break;
|
|
case STATE_INCLUDE:
|
|
NO_TEXT ("include");
|
|
break;
|
|
case STATE_TILE:
|
|
NO_TEXT ("tile");
|
|
break;
|
|
case STATE_COLOR:
|
|
NO_TEXT ("color");
|
|
break;
|
|
case STATE_FRAME_STYLE:
|
|
NO_TEXT ("frame_style");
|
|
break;
|
|
case STATE_PIECE:
|
|
NO_TEXT ("piece");
|
|
break;
|
|
case STATE_BUTTON:
|
|
NO_TEXT ("button");
|
|
break;
|
|
case STATE_MENU_ICON:
|
|
NO_TEXT ("menu_icon");
|
|
break;
|
|
case STATE_FRAME_STYLE_SET:
|
|
NO_TEXT ("frame_style_set");
|
|
break;
|
|
case STATE_FRAME:
|
|
NO_TEXT ("frame");
|
|
break;
|
|
case STATE_WINDOW:
|
|
NO_TEXT ("window");
|
|
break;
|
|
case STATE_FALLBACK:
|
|
NO_TEXT ("fallback");
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* We were intending to put the version number
|
|
* in the subdirectory name, but we ended up
|
|
* using the filename instead. The "-1" survives
|
|
* as a fossil.
|
|
*/
|
|
#define THEME_SUBDIR "metacity-1"
|
|
|
|
/* Highest version of the theme format to
|
|
* look out for.
|
|
*/
|
|
#define THEME_VERSION 2
|
|
|
|
#define METACITY_THEME_FILENAME_FORMAT "metacity-theme-%d.xml"
|
|
|
|
MetaTheme*
|
|
meta_theme_load (const char *theme_name,
|
|
GError **err)
|
|
{
|
|
GMarkupParseContext *context;
|
|
GError *error;
|
|
ParseInfo info;
|
|
char *text;
|
|
gsize length;
|
|
char *theme_file;
|
|
char *theme_dir;
|
|
MetaTheme *retval;
|
|
guint version;
|
|
|
|
text = NULL;
|
|
length = 0;
|
|
retval = NULL;
|
|
context = NULL;
|
|
|
|
theme_dir = NULL;
|
|
theme_file = NULL;
|
|
|
|
if (meta_is_debugging ())
|
|
{
|
|
gchar *theme_filename = g_strdup_printf (METACITY_THEME_FILENAME_FORMAT,
|
|
THEME_VERSION);
|
|
|
|
/* Try in themes in our source tree */
|
|
theme_dir = g_build_filename ("./themes", theme_name, NULL);
|
|
|
|
theme_file = g_build_filename (theme_dir,
|
|
theme_filename,
|
|
NULL);
|
|
|
|
error = NULL;
|
|
if (!g_file_get_contents (theme_file,
|
|
&text,
|
|
&length,
|
|
&error))
|
|
{
|
|
meta_topic (META_DEBUG_THEMES, "Failed to read theme from file %s: %s\n",
|
|
theme_file, error->message);
|
|
g_error_free (error);
|
|
g_free (theme_dir);
|
|
g_free (theme_file);
|
|
theme_file = NULL;
|
|
}
|
|
version = THEME_VERSION;
|
|
|
|
g_free (theme_filename);
|
|
}
|
|
|
|
/* We try all supported versions from current to oldest */
|
|
for (version = THEME_VERSION; (version > 0) && (text == NULL); version--)
|
|
{
|
|
gchar *theme_filename = g_strdup_printf (METACITY_THEME_FILENAME_FORMAT,
|
|
version);
|
|
|
|
/* We try in home dir, then system dir for themes */
|
|
|
|
theme_dir = g_build_filename (g_get_home_dir (),
|
|
".themes",
|
|
theme_name,
|
|
THEME_SUBDIR,
|
|
NULL);
|
|
|
|
theme_file = g_build_filename (theme_dir,
|
|
theme_filename,
|
|
NULL);
|
|
|
|
error = NULL;
|
|
if (!g_file_get_contents (theme_file,
|
|
&text,
|
|
&length,
|
|
&error))
|
|
{
|
|
meta_topic (META_DEBUG_THEMES, "Failed to read theme from file %s: %s\n",
|
|
theme_file, error->message);
|
|
g_error_free (error);
|
|
g_free (theme_dir);
|
|
g_free (theme_file);
|
|
theme_file = NULL;
|
|
}
|
|
|
|
if (text == NULL)
|
|
{
|
|
theme_dir = g_build_filename (METACITY_DATADIR,
|
|
"themes",
|
|
theme_name,
|
|
THEME_SUBDIR,
|
|
NULL);
|
|
|
|
theme_file = g_build_filename (theme_dir,
|
|
theme_filename,
|
|
NULL);
|
|
|
|
error = NULL;
|
|
if (!g_file_get_contents (theme_file,
|
|
&text,
|
|
&length,
|
|
&error))
|
|
{
|
|
meta_topic (META_DEBUG_THEMES, "Failed to read theme from file %s: %s\n",
|
|
theme_file, error->message);
|
|
g_error_free (error);
|
|
g_free (theme_dir);
|
|
g_free (theme_file);
|
|
theme_file = NULL;
|
|
}
|
|
}
|
|
|
|
g_free (theme_filename);
|
|
}
|
|
|
|
if (text == NULL)
|
|
{
|
|
g_set_error (err, META_THEME_ERROR, META_THEME_ERROR_FAILED,
|
|
_("Failed to find a valid file for theme %s\n"),
|
|
theme_name);
|
|
|
|
return NULL; /* all fallbacks failed */
|
|
}
|
|
|
|
meta_topic (META_DEBUG_THEMES, "Parsing theme file %s\n", theme_file);
|
|
|
|
|
|
parse_info_init (&info);
|
|
info.theme_name = theme_name;
|
|
|
|
/* pass ownership to info so we free it with the info */
|
|
info.theme_file = theme_file;
|
|
info.theme_dir = theme_dir;
|
|
|
|
info.format_version = version + 1;
|
|
|
|
context = g_markup_parse_context_new (&metacity_theme_parser,
|
|
0, &info, NULL);
|
|
|
|
error = NULL;
|
|
if (!g_markup_parse_context_parse (context,
|
|
text,
|
|
length,
|
|
&error))
|
|
goto out;
|
|
|
|
error = NULL;
|
|
if (!g_markup_parse_context_end_parse (context, &error))
|
|
goto out;
|
|
|
|
goto out;
|
|
|
|
out:
|
|
|
|
if (context)
|
|
g_markup_parse_context_free (context);
|
|
g_free (text);
|
|
|
|
info.theme->format_version = info.format_version;
|
|
|
|
if (error)
|
|
{
|
|
g_propagate_error (err, error);
|
|
}
|
|
else if (info.theme)
|
|
{
|
|
/* Steal theme from info */
|
|
retval = info.theme;
|
|
info.theme = NULL;
|
|
}
|
|
else
|
|
{
|
|
g_set_error (err, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
|
|
_("Theme file %s did not contain a root <metacity_theme> element"),
|
|
info.theme_file);
|
|
}
|
|
|
|
parse_info_free (&info);
|
|
|
|
return retval;
|
|
}
|