summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHavoc Pennington <hp@pobox.com>2002-02-07 03:07:56 +0000
committerHavoc Pennington <hp@src.gnome.org>2002-02-07 03:07:56 +0000
commit8ae714eeaef03db0a55c11fc31834c8d65e2ea03 (patch)
treee3bc1027754f8e70bfc06266f798f682802c4342
parent2be2d8ccbe196193d383841e1c990843a35d4f1b (diff)
downloadmetacity-8ae714eeaef03db0a55c11fc31834c8d65e2ea03.tar.gz
metacity-8ae714eeaef03db0a55c11fc31834c8d65e2ea03.tar.bz2
disable custom log handler and fatal mask for now
2002-02-06 Havoc Pennington <hp@pobox.com> * src/main.c (main): disable custom log handler and fatal mask for now * src/theme.c (meta_draw_op_list_draw): Add META_DRAW_CLIP * src/main.c: load theme, monitor current theme setting * src/prefs.c: add "current theme" setting * src/stack.c (meta_stack_free): don't try to free last_root_children_stacked if it doesn't exist * src/themewidget.c: pluggable GtkMisc subclass to use for menu icons * src/screen.c (meta_screen_manage_all_windows): fix signed/unsigned warning * src/frames.c: port to theme system (meta_frames_style_set): chain up * theme-format.txt: new file * configure.in: add more compiler warnings * src/theme.c: add various stuff needed to get theme parser working. Remove the "spacer" concept from FrameLayout object. Add draw op that references a draw op list. * configure.in: require GTK 1.3.13 * src/Makefile.am: add theme-parser.[hc], implement loading a theme * src/theme.c: add "draw title" and "draw window icon" operations (meta_draw_op_draw): put object_width/object_height in expression environment before computing x/y. Handle out-of-memory when creating pixbufs. Assorted other cleanups.
-rw-r--r--ChangeLog42
-rw-r--r--Makefile.am2
-rw-r--r--configure.in46
-rw-r--r--src/Makefile.am10
-rw-r--r--src/common.h2
-rw-r--r--src/core.c59
-rw-r--r--src/core.h4
-rw-r--r--src/display.c31
-rw-r--r--src/display.h1
-rw-r--r--src/frames.c739
-rw-r--r--src/frames.h10
-rw-r--r--src/gradient.c5
-rw-r--r--src/gradient.h3
-rw-r--r--src/main.c37
-rw-r--r--src/menu.c85
-rw-r--r--src/metacity.schemas15
-rw-r--r--src/prefs.c67
-rw-r--r--src/prefs.h3
-rw-r--r--src/screen.c2
-rw-r--r--src/stack.c9
-rw-r--r--src/tabpopup.c2
-rw-r--r--src/theme-parser.c3920
-rw-r--r--src/theme-parser.h30
-rw-r--r--src/theme-viewer.c287
-rw-r--r--src/theme.c3305
-rw-r--r--src/theme.h325
-rw-r--r--src/themes/Atlanta/metacity-theme-1.xml239
-rw-r--r--src/themes/Makefile.am29
-rw-r--r--src/themewidget.c181
-rw-r--r--src/themewidget.h76
-rw-r--r--src/tools/Makefile.am8
-rw-r--r--src/tools/metacity-reload-theme.c56
-rw-r--r--src/ui.c14
-rw-r--r--src/ui.h5
-rw-r--r--src/window.c16
-rw-r--r--theme-format.txt218
36 files changed, 8382 insertions, 1501 deletions
diff --git a/ChangeLog b/ChangeLog
index fa66722..3abe6c7 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,45 @@
+2002-02-06 Havoc Pennington <hp@pobox.com>
+
+ * src/main.c (main): disable custom log handler and fatal mask for
+ now
+
+ * src/theme.c (meta_draw_op_list_draw):
+ Add META_DRAW_CLIP
+
+ * src/main.c: load theme, monitor current theme setting
+
+ * src/prefs.c: add "current theme" setting
+
+ * src/stack.c (meta_stack_free): don't try to free
+ last_root_children_stacked if it doesn't exist
+
+ * src/themewidget.c: pluggable GtkMisc subclass to use
+ for menu icons
+
+ * src/screen.c (meta_screen_manage_all_windows): fix
+ signed/unsigned warning
+
+ * src/frames.c: port to theme system
+ (meta_frames_style_set): chain up
+
+ * theme-format.txt: new file
+
+ * configure.in: add more compiler warnings
+
+ * src/theme.c: add various stuff needed to get theme parser
+ working. Remove the "spacer" concept from FrameLayout object.
+ Add draw op that references a draw op list.
+
+ * configure.in: require GTK 1.3.13
+
+ * src/Makefile.am: add theme-parser.[hc], implement loading a
+ theme
+
+ * src/theme.c: add "draw title" and "draw window icon" operations
+ (meta_draw_op_draw): put object_width/object_height in expression
+ environment before computing x/y. Handle out-of-memory when
+ creating pixbufs. Assorted other cleanups.
+
2002-02-07 Anders Carlsson <andersca@gnu.org>
* src/themes/Crux/metacity-theme-1.xml:
diff --git a/Makefile.am b/Makefile.am
index e7cccc7..d439b10 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,4 +1,4 @@
SUBDIRS=src
-EXTRA_DIST=HACKING
+EXTRA_DIST=HACKING theme-format.txt
diff --git a/configure.in b/configure.in
index 07b33ad..af9444c 100644
--- a/configure.in
+++ b/configure.in
@@ -26,6 +26,46 @@ if test "x$GCC" = "xyes"; then
*) CFLAGS="$CFLAGS -Wall" ;;
esac
+ case " $CFLAGS " in
+ *[\ \ ]-Wshadow[\ \ ]*) ;;
+ *) CFLAGS="$CFLAGS -Wshadow" ;;
+ esac
+
+ case " $CFLAGS " in
+ *[\ \ ]-Wchar-subscripts[\ \ ]*) ;;
+ *) CFLAGS="$CFLAGS -Wchar-subscripts" ;;
+ esac
+
+ case " $CFLAGS " in
+ *[\ \ ]-Wmissing-declarations[\ \ ]*) ;;
+ *) CFLAGS="$CFLAGS -Wmissing-declarations" ;;
+ esac
+
+ case " $CFLAGS " in
+ *[\ \ ]-Wmissing-prototypes[\ \ ]*) ;;
+ *) CFLAGS="$CFLAGS -Wmissing-prototypes" ;;
+ esac
+
+ case " $CFLAGS " in
+ *[\ \ ]-Wnested-externs[\ \ ]*) ;;
+ *) CFLAGS="$CFLAGS -Wnested-externs" ;;
+ esac
+
+ case " $CFLAGS " in
+ *[\ \ ]-Wpointer-arith[\ \ ]*) ;;
+ *) CFLAGS="$CFLAGS -Wpointer-arith" ;;
+ esac
+
+ case " $CFLAGS " in
+ *[\ \ ]-Wcast-align[\ \ ]*) ;;
+ *) CFLAGS="$CFLAGS -Wcast-align" ;;
+ esac
+
+ case " $CFLAGS " in
+ *[\ \ ]-Wsign-compare[\ \ ]*) ;;
+ *) CFLAGS="$CFLAGS -Wsign-compare" ;;
+ esac
+
if test "x$enable_ansi" = "xyes"; then
case " $CFLAGS " in
*[\ \ ]-ansi[\ \ ]*) ;;
@@ -44,8 +84,9 @@ ALL_LINGUAS="da es gl lv ms no pt ru sk sv tr uk"
AM_GLIB_GNU_GETTEXT
## here we get the flags we'll actually use
-PKG_CHECK_MODULES(METACITY, gtk+-2.0 >= 1.3.11 gconf-2.0 >= 1.1.5)
-PKG_CHECK_MODULES(METACITY_RESTART, gtk+-2.0 >= 1.3.11)
+PKG_CHECK_MODULES(METACITY, gtk+-2.0 >= 1.3.13 gconf-2.0 >= 1.1.5)
+PKG_CHECK_MODULES(METACITY_RESTART, gtk+-2.0 >= 1.3.13)
+PKG_CHECK_MODULES(METACITY_RELOAD_THEME, gtk+-2.0 >= 1.3.13)
CFLAGS="$METACITY_CFLAGS $CFLAGS"
@@ -98,4 +139,5 @@ Makefile
src/Makefile
src/wm-tester/Makefile
src/tools/Makefile
+src/themes/Makefile
])
diff --git a/src/Makefile.am b/src/Makefile.am
index 8391041..6ba0497 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,7 +1,7 @@
-SUBDIRS=wm-tester tools
+SUBDIRS=wm-tester tools themes
-INCLUDES=@METACITY_CFLAGS@ -DMETACITY_LIBEXECDIR=\"$(libexecdir)\" -DHOST_ALIAS=\"@HOST_ALIAS@\" -DMETACITY_LOCALEDIR=\"$(datadir)/locale\"
+INCLUDES=@METACITY_CFLAGS@ -DMETACITY_LIBEXECDIR=\"$(libexecdir)\" -DHOST_ALIAS=\"@HOST_ALIAS@\" -DMETACITY_LOCALEDIR=\"$(datadir)/locale\" -DMETACITY_PKGDATADIR=\"$(pkgdatadir)\"
metacity_SOURCES= \
common.h \
@@ -44,6 +44,10 @@ metacity_SOURCES= \
tabpopup.h \
theme.c \
theme.h \
+ theme-parser.c \
+ theme-parser.h \
+ themewidget.c \
+ themewidget.h \
ui.c \
ui.h \
util.c \
@@ -60,6 +64,8 @@ metacity_theme_viewer_SOURCES= \
gradient.h \
theme.c \
theme.h \
+ theme-parser.c \
+ theme-parser.h \
theme-viewer.c \
util.c \
util.h
diff --git a/src/common.h b/src/common.h
index 62369c4..de34df8 100644
--- a/src/common.h
+++ b/src/common.h
@@ -135,7 +135,7 @@ typedef enum
META_FRAME_TYPE_MODAL_DIALOG,
META_FRAME_TYPE_UTILITY,
META_FRAME_TYPE_MENU,
- META_FRAME_TYPE_TOOLBAR,
+ /* META_FRAME_TYPE_TOOLBAR, */
META_FRAME_TYPE_LAST
} MetaFrameType;
diff --git a/src/core.c b/src/core.c
index dddcf68..c490d88 100644
--- a/src/core.c
+++ b/src/core.c
@@ -60,6 +60,49 @@ meta_core_get_frame_flags (Display *xdisplay,
return meta_frame_get_flags (window->frame);
}
+MetaFrameType
+meta_core_get_frame_type (Display *xdisplay,
+ Window frame_xwindow)
+{
+ MetaDisplay *display;
+ MetaWindow *window;
+
+ display = meta_display_for_x_display (xdisplay);
+ window = meta_display_lookup_x_window (display, frame_xwindow);
+
+ if (window == NULL || window->frame == NULL)
+ meta_bug ("No such frame window 0x%lx!\n", frame_xwindow);
+
+ switch (window->type)
+ {
+ case META_WINDOW_NORMAL:
+ return META_FRAME_TYPE_NORMAL;
+ break;
+
+ case META_WINDOW_DIALOG:
+ return META_FRAME_TYPE_DIALOG;
+ break;
+
+ case META_WINDOW_MODAL_DIALOG:
+ return META_FRAME_TYPE_MODAL_DIALOG;
+ break;
+
+ case META_WINDOW_MENU:
+ return META_FRAME_TYPE_MENU;
+ break;
+
+ case META_WINDOW_DESKTOP:
+ case META_WINDOW_DOCK:
+ case META_WINDOW_TOOLBAR:
+ /* No frame */
+ return META_FRAME_TYPE_LAST;
+ break;
+ }
+
+ g_assert_not_reached ();
+ return META_FRAME_TYPE_LAST;
+}
+
GdkPixbuf*
meta_core_get_mini_icon (Display *xdisplay,
Window frame_xwindow)
@@ -76,6 +119,22 @@ meta_core_get_mini_icon (Display *xdisplay,
return window->mini_icon;
}
+GdkPixbuf*
+meta_core_get_icon (Display *xdisplay,
+ Window frame_xwindow)
+{
+ MetaDisplay *display;
+ MetaWindow *window;
+
+ display = meta_display_for_x_display (xdisplay);
+ window = meta_display_lookup_x_window (display, frame_xwindow);
+
+ if (window == NULL || window->frame == NULL)
+ meta_bug ("No such frame window 0x%lx!\n", frame_xwindow);
+
+ return window->icon;
+}
+
void
meta_core_queue_frame_resize (Display *xdisplay,
Window frame_xwindow)
diff --git a/src/core.h b/src/core.h
index b214642..7952e88 100644
--- a/src/core.h
+++ b/src/core.h
@@ -33,9 +33,13 @@ void meta_core_get_client_size (Display *xdisplay,
MetaFrameFlags meta_core_get_frame_flags (Display *xdisplay,
Window frame_xwindow);
+MetaFrameType meta_core_get_frame_type (Display *xdisplay,
+ Window frame_xwindow);
GdkPixbuf* meta_core_get_mini_icon (Display *xdisplay,
Window frame_xwindow);
+GdkPixbuf* meta_core_get_icon (Display *xdisplay,
+ Window frame_xwindow);
void meta_core_queue_frame_resize (Display *xdisplay,
Window frame_xwindow);
diff --git a/src/display.c b/src/display.c
index 45511d5..073232d 100644
--- a/src/display.c
+++ b/src/display.c
@@ -139,9 +139,10 @@ meta_display_open (const char *name)
"_KWM_WIN_ICON",
"_NET_WM_MOVERESIZE",
"_NET_ACTIVE_WINDOW",
- "_METACITY_RESTART_MESSAGE",
+ "_METACITY_RESTART_MESSAGE",
"_NET_WM_STRUT",
- "_WIN_HINTS"
+ "_WIN_HINTS",
+ "_METACITY_RELOAD_THEME_MESSAGE"
};
Atom atoms[G_N_ELEMENTS(atom_names)];
@@ -237,6 +238,7 @@ meta_display_open (const char *name)
display->atom_metacity_restart_message = atoms[44];
display->atom_net_wm_strut = atoms[45];
display->atom_win_hints = atoms[46];
+ display->atom_metacity_reload_theme_message = atoms[47];
/* Offscreen unmapped window used for _NET_SUPPORTING_WM_CHECK,
* created in screen_new
@@ -668,7 +670,7 @@ event_callback (XEvent *event,
/* mark double click events, kind of a hack, oh well. */
if (event->type == ButtonPress)
{
- if (event->xbutton.button == display->last_button_num &&
+ if (((int)event->xbutton.button) == display->last_button_num &&
event->xbutton.window == display->last_button_xwindow &&
event->xbutton.time < (display->last_button_time + display->double_click_time))
{
@@ -716,7 +718,7 @@ event_callback (XEvent *event,
break;
case ButtonPress:
if ((grab_op_is_mouse (display->grab_op) &&
- display->grab_button != event->xbutton.button &&
+ display->grab_button != (int) event->xbutton.button &&
display->grab_window == window) ||
grab_op_is_keyboard (display->grab_op))
{
@@ -1104,6 +1106,13 @@ event_callback (XEvent *event,
meta_verbose ("Received restart request\n");
meta_restart ();
}
+ else if (event->xclient.message_type ==
+ display->atom_metacity_reload_theme_message)
+ {
+ meta_verbose ("Received reload theme request\n");
+ meta_ui_set_current_theme (meta_prefs_get_theme (),
+ TRUE);
+ }
}
}
break;
@@ -1567,15 +1576,15 @@ meta_display_unregister_x_window (MetaDisplay *display,
MetaWorkspace*
meta_display_get_workspace_by_index (MetaDisplay *display,
- int index)
+ int idx)
{
GList *tmp;
/* should be robust, index is maybe from an app */
- if (index < 0)
+ if (idx < 0)
return NULL;
- tmp = g_list_nth (display->workspaces, index);
+ tmp = g_list_nth (display->workspaces, idx);
if (tmp == NULL)
return NULL;
@@ -1586,13 +1595,13 @@ meta_display_get_workspace_by_index (MetaDisplay *display,
MetaWorkspace*
meta_display_get_workspace_by_screen_index (MetaDisplay *display,
MetaScreen *screen,
- int index)
+ int idx)
{
GList *tmp;
int i;
- /* should be robust, index is maybe from an app */
- if (index < 0)
+ /* should be robust, idx is maybe from an app */
+ if (idx < 0)
return NULL;
i = 0;
@@ -1603,7 +1612,7 @@ meta_display_get_workspace_by_screen_index (MetaDisplay *display,
if (w->screen == screen)
{
- if (i == index)
+ if (i == idx)
return w;
else
++i;
diff --git a/src/display.h b/src/display.h
index bf5186c..f1ef424 100644
--- a/src/display.h
+++ b/src/display.h
@@ -106,6 +106,7 @@ struct _MetaDisplay
Atom atom_metacity_restart_message;
Atom atom_net_wm_strut;
Atom atom_win_hints;
+ Atom atom_metacity_reload_theme_message;
/* This is the actual window from focus events,
* not the one we last set
diff --git a/src/frames.c b/src/frames.c
index e2c6f5f..990eb04 100644
--- a/src/frames.c
+++ b/src/frames.c
@@ -128,24 +128,6 @@ meta_frames_get_type (void)
return frames_type;
}
-#define BORDER_PROPERTY(name, blurb, docs) \
- gtk_widget_class_install_style_property (widget_class, \
- g_param_spec_boxed (name, \
- blurb, \
- docs, \
- GTK_TYPE_BORDER, \
- G_PARAM_READABLE))
-
-#define INT_PROPERTY(name, default, blurb, docs) \
- gtk_widget_class_install_style_property (widget_class, \
- g_param_spec_int (name, \
- blurb, \
- docs, \
- 0, \
- G_MAXINT, \
- default, \
- G_PARAM_READABLE))
-
static void
meta_frames_class_init (MetaFramesClass *class)
{
@@ -174,27 +156,6 @@ meta_frames_class_init (MetaFramesClass *class)
widget_class->button_release_event = meta_frames_button_release_event;
widget_class->motion_notify_event = meta_frames_motion_notify_event;
widget_class->leave_notify_event = meta_frames_leave_notify_event;
-
- INT_PROPERTY ("left_width", 6, _("Left edge"), _("Left window edge width"));
- INT_PROPERTY ("right_width", 6, _("Right edge"), _("Right window edge width"));
- INT_PROPERTY ("bottom_height", 7, _("Bottom edge"), _("Bottom window edge height"));
-
- BORDER_PROPERTY ("title_border", _("Title border"), _("Border around title area"));
- BORDER_PROPERTY ("text_border", _("Text border"), _("Border around window title text"));
-
- INT_PROPERTY ("spacer_padding", 3, _("Spacer padding"), _("Padding on either side of spacer"));
- INT_PROPERTY ("spacer_width", 2, _("Spacer width"), _("Width of spacer"));
- INT_PROPERTY ("spacer_height", 11, _("Spacer height"), _("Height of spacer"));
-
- /* same as right_width left_width by default */
- INT_PROPERTY ("right_inset", 6, _("Right inset"), _("Distance of buttons from right edge of frame"));
- INT_PROPERTY ("left_inset", 6, _("Left inset"), _("Distance of menu button from left edge of frame"));
-
- INT_PROPERTY ("button_width", 17, _("Button width"), _("Width of buttons"));
- INT_PROPERTY ("button_height", 17, _("Button height"), _("Height of buttons"));
-
- BORDER_PROPERTY ("button_border", _("Button border"), _("Border around buttons"));
- BORDER_PROPERTY ("inner_button_border", _("Inner button border"), _("Border around the icon inside buttons"));
}
static gint
@@ -222,8 +183,6 @@ meta_frames_init (MetaFrames *frames)
{
GTK_WINDOW (frames)->type = GTK_WINDOW_POPUP;
- frames->layout = meta_frame_layout_new ();
-
frames->frames = g_hash_table_new (unsigned_long_hash, unsigned_long_equal);
frames->tooltip_timeout = 0;
@@ -282,8 +241,6 @@ meta_frames_finalize (GObject *object)
g_assert (g_hash_table_size (frames->frames) == 0);
g_hash_table_destroy (frames->frames);
-
- meta_frame_layout_free (frames->layout);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
@@ -327,107 +284,23 @@ meta_frames_style_set (GtkWidget *widget,
GtkStyle *prev_style)
{
MetaFrames *frames;
- /* left, right, top, bottom */
- static GtkBorder default_title_border = { 3, 4, 4, 3 };
- static GtkBorder default_text_border = { 2, 2, 2, 2 };
- static GtkBorder default_button_border = { 0, 0, 1, 1 };
- static GtkBorder default_inner_button_border = {
- DEFAULT_INNER_BUTTON_BORDER,
- DEFAULT_INNER_BUTTON_BORDER,
- DEFAULT_INNER_BUTTON_BORDER,
- DEFAULT_INNER_BUTTON_BORDER
- };
- GtkBorder *title_border;
- GtkBorder *text_border;
- GtkBorder *button_border;
- GtkBorder *inner_button_border;
- MetaFrameLayout layout;
frames = META_FRAMES (widget);
-
- gtk_widget_style_get (widget,
- "left_width",
- &layout.left_width,
- "right_width",
- &layout.right_width,
- "bottom_height",
- &layout.bottom_height,
- "title_border",
- &title_border,
- "text_border",
- &text_border,
- "spacer_padding",
- &layout.spacer_padding,
- "spacer_width",
- &layout.spacer_width,
- "spacer_height",
- &layout.spacer_height,
- "right_inset",
- &layout.right_inset,
- "left_inset",
- &layout.left_inset,
- "button_width",
- &layout.button_width,
- "button_height",
- &layout.button_height,
- "button_border",
- &button_border,
- "inner_button_border",
- &inner_button_border,
- NULL);
-
- if (title_border)
- layout.title_border = *title_border;
- else
- layout.title_border = default_title_border;
- g_free (title_border);
-
- if (text_border)
- layout.text_border = *text_border;
- else
- layout.text_border = default_text_border;
-
- g_free (text_border);
-
- if (button_border)
- layout.button_border = *button_border;
- else
- layout.button_border = default_button_border;
-
- g_free (button_border);
-
- if (inner_button_border)
- layout.inner_button_border = *inner_button_border;
+ if (GTK_WIDGET_REALIZED (widget))
+ {
+ frames->text_height = meta_gtk_widget_get_text_height (widget);
+ }
else
- layout.inner_button_border = default_inner_button_border;
-
- g_free (inner_button_border);
-
- *(frames->layout) = layout;
-
- {
- PangoFontMetrics *metrics;
- PangoFont *font;
- PangoLanguage *lang;
-
- font = pango_context_load_font (gtk_widget_get_pango_context (widget),
- widget->style->font_desc);
- lang = pango_context_get_language (gtk_widget_get_pango_context (widget));
- metrics = pango_font_get_metrics (font, lang);
-
- g_object_unref (G_OBJECT (font));
-
- frames->text_height =
- PANGO_PIXELS (pango_font_metrics_get_ascent (metrics) +
- pango_font_metrics_get_descent (metrics));
-
- pango_font_metrics_unref (metrics);
- }
-
+ {
+ frames->text_height = 0;
+ }
+
/* Queue a draw/resize on all frames */
g_hash_table_foreach (frames->frames,
queue_recalc_func, frames);
+
+ GTK_WIDGET_CLASS (parent_class)->style_set (widget, prev_style);
}
static void
@@ -437,18 +310,20 @@ meta_frames_calc_geometry (MetaFrames *frames,
{
int width, height;
MetaFrameFlags flags;
+ MetaFrameType type;
meta_core_get_client_size (gdk_display, frame->xwindow,
&width, &height);
flags = meta_core_get_frame_flags (gdk_display, frame->xwindow);
-
- meta_frame_layout_calc_geometry (frames->layout,
- GTK_WIDGET (frames),
- frames->text_height,
- flags,
- width, height,
- fgeom);
+ type = meta_core_get_frame_type (gdk_display, frame->xwindow);
+
+ meta_theme_calc_geometry (meta_theme_get_current (),
+ type,
+ frames->text_height,
+ flags,
+ width, height,
+ fgeom);
}
MetaFrames*
@@ -526,6 +401,8 @@ meta_frames_realize (GtkWidget *widget)
if (GTK_WIDGET_CLASS (parent_class)->realize)
GTK_WIDGET_CLASS (parent_class)->realize (widget);
+
+ frames->text_height = meta_gtk_widget_get_text_height (widget);
}
static void
@@ -537,6 +414,8 @@ meta_frames_unrealize (GtkWidget *widget)
if (GTK_WIDGET_CLASS (parent_class)->unrealize)
GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
+
+ frames->text_height = 0;
}
static MetaUIFrame*
@@ -556,27 +435,31 @@ meta_frames_get_geometry (MetaFrames *frames,
int *top_height, int *bottom_height,
int *left_width, int *right_width)
{
- MetaFrameFlags flags;
+ MetaFrameFlags flags;
MetaUIFrame *frame;
-
+ MetaFrameType type;
+
frame = meta_frames_lookup_window (frames, xwindow);
if (frame == NULL)
meta_bug ("No such frame 0x%lx\n", xwindow);
- flags = meta_core_get_frame_flags (gdk_display, frame->xwindow);
+ flags = meta_core_get_frame_flags (gdk_display, frame->xwindow);
+ type = meta_core_get_frame_type (gdk_display, frame->xwindow);
+ g_return_if_fail (type < META_FRAME_TYPE_LAST);
+
/* We can't get the full geometry, because that depends on
* the client window size and probably we're being called
* by the core move/resize code to decide on the client
* window size
*/
- meta_frame_layout_get_borders (frames->layout,
- GTK_WIDGET (frames),
- frames->text_height,
- flags,
- top_height, bottom_height,
- left_width, right_width);
+ meta_theme_get_frame_borders (meta_theme_get_current (),
+ type,
+ frames->text_height,
+ flags,
+ top_height, bottom_height,
+ left_width, right_width);
}
void
@@ -1082,7 +965,7 @@ meta_frames_button_release_event (GtkWidget *widget,
* frame are handled in the Xlib part of the code, display.c/window.c
*/
if (frame->xwindow == meta_core_get_grab_frame (gdk_display) &&
- event->button == meta_core_get_grab_button (gdk_display))
+ ((int) event->button) == meta_core_get_grab_button (gdk_display))
{
gboolean end_grab;
@@ -1264,228 +1147,6 @@ meta_frames_destroy_event (GtkWidget *widget,
return TRUE;
}
-#define THICK_LINE_WIDTH 3
-static void
-draw_mini_window (MetaFrames *frames,
- GdkDrawable *drawable,
- GdkGC *fg_gc,
- GdkGC *bg_gc,
- gboolean thin_title,
- int x, int y, int width, int height)
-{
- GdkGCValues vals;
-
- gdk_draw_rectangle (drawable,
- bg_gc,
- TRUE,
- x, y, width - 1, height - 1);
-
- gdk_draw_rectangle (drawable,
- fg_gc,
- FALSE,
- x, y, width - 1, height - 1);
-
- vals.line_width = thin_title ? THICK_LINE_WIDTH - 1 : THICK_LINE_WIDTH;
- gdk_gc_set_values (fg_gc,
- &vals,
- GDK_GC_LINE_WIDTH);
-
- gdk_draw_line (drawable,
- fg_gc,
- x, y + 1, x + width, y + 1);
-
- vals.line_width = 0;
- gdk_gc_set_values (fg_gc,
- &vals,
- GDK_GC_LINE_WIDTH);
-}
-
-static void
-draw_control (MetaFrames *frames,
- GdkDrawable *drawable,
- GdkGC *fg_override,
- GdkGC *bg_override,
- MetaFrameControl control,
- int x, int y, int width, int height)
-{
- GtkWidget *widget;
- GdkGCValues vals;
- GdkGC *fg_gc;
- GdkGC *bg_gc;
-
- widget = GTK_WIDGET (frames);
-
- fg_gc = fg_override ? fg_override : widget->style->fg_gc[GTK_STATE_NORMAL];
- bg_gc = bg_override ? bg_override : widget->style->bg_gc[GTK_STATE_NORMAL];
-
- switch (control)
- {
- case META_FRAME_CONTROL_DELETE:
- {
- gdk_draw_line (drawable,
- fg_gc,
- x, y, x + width - 1, y + height - 1);
-
- gdk_draw_line (drawable,
- fg_gc,
- x, y + height - 1, x + width - 1, y);
- }
- break;
-
- case META_FRAME_CONTROL_MAXIMIZE:
- {
- draw_mini_window (frames, drawable, fg_gc, bg_gc, FALSE,
- x, y, width, height);
- }
- break;
-
- case META_FRAME_CONTROL_UNMAXIMIZE:
- {
- int w_delta = width * 0.3;
- int h_delta = height * 0.3;
-
- w_delta = MAX (w_delta, 3);
- h_delta = MAX (h_delta, 3);
-
- draw_mini_window (frames, drawable, fg_gc, bg_gc, TRUE,
- x, y, width - w_delta, height - h_delta);
- draw_mini_window (frames, drawable, fg_gc, bg_gc, TRUE,
- x + w_delta, y + h_delta,
- width - w_delta, height - h_delta);
- }
- break;
-
- case META_FRAME_CONTROL_MINIMIZE:
- {
-
- vals.line_width = THICK_LINE_WIDTH;
- gdk_gc_set_values (fg_gc,
- &vals,
- GDK_GC_LINE_WIDTH);
-
- gdk_draw_line (drawable,
- fg_gc,
- x, y + height - THICK_LINE_WIDTH + 1,
- x + width, y + height - THICK_LINE_WIDTH + 1);
-
- vals.line_width = 0;
- gdk_gc_set_values (fg_gc,
- &vals,
- GDK_GC_LINE_WIDTH);
- }
- break;
-
- default:
- break;
- }
-}
-#undef THICK_LINE_WIDTH
-
-void
-meta_frames_get_pixmap_for_control (MetaFrames *frames,
- MetaFrameControl control,
- GdkPixmap **pixmapp,
- GdkBitmap **maskp)
-{
- int w, h;
- GdkPixmap *pix;
- GdkBitmap *mask;
- GtkWidget *widget;
- GdkGC *mgc, *mgc_bg;
- GdkColor color;
-
- widget = GTK_WIDGET (frames);
-
- gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &w, &h);
-
- w -= DEFAULT_INNER_BUTTON_BORDER * 2;
- h -= DEFAULT_INNER_BUTTON_BORDER * 2;
-
- /* avoid crashing on bizarre icon sizes */
- if (w < 1)
- w = 1;
- if (h < 1)
- h = 1;
-
- pix = gdk_pixmap_new (NULL, w, h, gtk_widget_get_visual (widget)->depth);
- mask = gdk_pixmap_new (NULL, w, h, 1);
-
- mgc = gdk_gc_new (mask);
- mgc_bg = gdk_gc_new (mask);
-
- color.pixel = 0;
- gdk_gc_set_foreground (mgc_bg, &color);
- color.pixel = 1;
- gdk_gc_set_foreground (mgc, &color);
-
- gdk_draw_rectangle (mask, mgc_bg, TRUE, 0, 0, -1, -1);
-
- draw_control (frames, mask, mgc, mgc_bg, control, 0, 0, w, h);
-
- gdk_gc_unref (mgc);
- gdk_gc_unref (mgc_bg);
-
- draw_control (frames, pix, NULL, NULL, control, 0, 0, w, h);
-
- *pixmapp = pix;
- *maskp = mask;
-}
-
-static void
-draw_control_bg (MetaFrames *frames,
- MetaUIFrame *frame,
- GdkDrawable *drawable,
- MetaFrameControl control,
- MetaFrameGeometry *fgeom)
-{
- GdkRectangle *rect;
- GtkWidget *widget;
- gboolean draw = FALSE;
- Window grab_frame;
-
- widget = GTK_WIDGET (frames);
-
- grab_frame = meta_core_get_grab_frame (gdk_display);
-
- if (frame->xwindow == grab_frame)
- {
- switch (meta_core_get_grab_op (gdk_display))
- {
- case META_GRAB_OP_CLICKING_MENU:
- draw = control == META_FRAME_CONTROL_MENU;
- break;
- case META_GRAB_OP_CLICKING_DELETE:
- draw = control == META_FRAME_CONTROL_DELETE;
- break;
- case META_GRAB_OP_CLICKING_MAXIMIZE:
- draw = control == META_FRAME_CONTROL_MAXIMIZE;
- break;
- case META_GRAB_OP_CLICKING_UNMAXIMIZE:
- draw = control == META_FRAME_CONTROL_UNMAXIMIZE;
- break;
- case META_GRAB_OP_CLICKING_MINIMIZE:
- draw = control == META_FRAME_CONTROL_MINIMIZE;
- break;
- default:
- break;
- }
- }
-
- if (draw)
- {
- rect = control_rect (control, fgeom);
-
- if (rect == NULL)
- return;
-
- gtk_paint_box (widget->style, drawable,
- GTK_STATE_ACTIVE,
- GTK_SHADOW_IN, NULL,
- widget, "button",
- rect->x, rect->y, rect->width, rect->height);
- }
-}
-
static gboolean
meta_frames_expose_event (GtkWidget *widget,
GdkEventExpose *event)
@@ -1518,285 +1179,77 @@ meta_frames_paint_to_drawable (MetaFrames *frames,
GdkRectangle *area)
{
GtkWidget *widget;
- MetaFrameGeometry fgeom;
MetaFrameFlags flags;
- int width, height;
- GtkBorder inner;
-
- widget = GTK_WIDGET (frames);
-
- meta_frames_calc_geometry (frames, frame, &fgeom);
- flags = meta_core_get_frame_flags (gdk_display, frame->xwindow);
- width = fgeom.width;
- height = fgeom.height;
+ MetaFrameType type;
+ GdkPixbuf *mini_icon;
+ GdkPixbuf *icon;
+ int w, h;
+ MetaButtonState button_states[META_BUTTON_TYPE_LAST];
+ Window grab_frame;
+ int i;
- /* Black line around outside to give definition */
- gdk_draw_rectangle (drawable,
- widget->style->black_gc,
- FALSE,
- 0, 0, width - 1, height - 1);
-
- /* Light GC on top/left edges */
- gdk_draw_line (drawable,
- widget->style->light_gc[GTK_STATE_NORMAL],
- 1, 1,
- 1, height - 2);
- gdk_draw_line (drawable,
- widget->style->light_gc[GTK_STATE_NORMAL],
- 1, 1,
- width - 2, 1);
- /* Dark on bottom/right */
- gdk_draw_line (drawable,
- widget->style->dark_gc[GTK_STATE_NORMAL],
- width - 2, 1,
- width - 2, height - 2);
- gdk_draw_line (drawable,
- widget->style->dark_gc[GTK_STATE_NORMAL],
- 1, height - 2,
- width - 2, height - 2);
-
- if (flags & META_FRAME_HAS_FOCUS)
- {
- /* Black line around inside while we have focus */
-
- gdk_draw_rectangle (drawable,
- widget->style->black_gc,
- FALSE,
- fgeom.left_width - 1,
- fgeom.top_height - 1,
- width - fgeom.right_width - fgeom.left_width + 1,
- height - fgeom.bottom_height - fgeom.top_height + 1);
- }
+ widget = GTK_WIDGET (frames);
- if (area->y < fgeom.top_height &&
- fgeom.title_rect.width > 0 && fgeom.title_rect.height > 0)
+ /* note, prelight not implemented yet */
+ i = 0;
+ while (i < META_BUTTON_TYPE_LAST)
{
- GdkRectangle clip;
- GdkGC *layout_gc;
+ button_states[i] = META_BUTTON_STATE_NORMAL;
- clip = fgeom.title_rect;
- clip.x += frames->layout->text_border.left;
- clip.width -= frames->layout->text_border.left +
- frames->layout->text_border.right;
-
- layout_gc = widget->style->fg_gc[GTK_STATE_NORMAL];
- if (flags & META_FRAME_HAS_FOCUS)
- {
- GdkPixbuf *gradient;
- GdkColor selected_faded;
- const GdkColor *bg = &widget->style->bg[GTK_STATE_NORMAL];
-
- /* alpha blend selection color into normal color */
-#define ALPHA 25000
- selected_faded = widget->style->bg[GTK_STATE_SELECTED];
- selected_faded.red = selected_faded.red + (((bg->red - selected_faded.red) * ALPHA + 32768) >> 16);
- selected_faded.green = selected_faded.green + (((bg->green - selected_faded.green) * ALPHA + 32768) >> 16);
- selected_faded.blue = selected_faded.blue + (((bg->blue - selected_faded.blue) * ALPHA + 32768) >> 16);
-
- layout_gc = widget->style->fg_gc[GTK_STATE_SELECTED];
-
- gradient = meta_gradient_create_simple (fgeom.title_rect.width,
- fgeom.title_rect.height,
- &selected_faded,
- &widget->style->bg[GTK_STATE_SELECTED],
- META_GRADIENT_DIAGONAL);
-
- if (gradient != NULL)
- {
- gdk_pixbuf_render_to_drawable (gradient,
- drawable,
- widget->style->bg_gc[GTK_STATE_SELECTED],
- 0, 0,
- fgeom.title_rect.x,
- fgeom.title_rect.y,
- fgeom.title_rect.width,
- fgeom.title_rect.height,
- GDK_RGB_DITHER_MAX,
- 0, 0);
-
- g_object_unref (G_OBJECT (gradient));
- }
- else
- {
- /* Fallback to plain selection color */
- gdk_draw_rectangle (drawable,
- widget->style->bg_gc[GTK_STATE_SELECTED],
- TRUE,
- fgeom.title_rect.x,
- fgeom.title_rect.y,
- fgeom.title_rect.width,
- fgeom.title_rect.height);
- }
- }
-
- if (frame->layout)
- {
- PangoRectangle layout_rect;
- int x, y, icon_x, icon_y;
- GdkPixbuf *icon;
- int icon_w, icon_h;
- int area_w, area_h;
-
-#define ICON_TEXT_SPACING 2
-
- icon = meta_core_get_mini_icon (gdk_display,
- frame->xwindow);
-
- icon_w = gdk_pixbuf_get_width (icon);
- icon_h = gdk_pixbuf_get_height (icon);
-
- pango_layout_get_pixel_extents (frame->layout,
- NULL,
- &layout_rect);
-
- /* corner of whole title area */
- x = fgeom.title_rect.x + frames->layout->text_border.left;
- y = fgeom.title_rect.y + frames->layout->text_border.top;
-
- area_w = fgeom.title_rect.width -
- frames->layout->text_border.left -
- frames->layout->text_border.right;
-
- area_h = fgeom.title_rect.height -
- frames->layout->text_border.top -
- frames->layout->text_border.bottom;
-
- /* center icon vertically */
- icon_y = y + MAX ((area_h - icon_h) / 2, 0);
- /* center text vertically */
- y = y + MAX ((area_h - layout_rect.height) / 2, 0);
-
- /* Center icon + text combo */
- icon_x = x + MAX ((area_w - layout_rect.width - icon_w - ICON_TEXT_SPACING) / 2, 0);
- x = icon_x + icon_w + ICON_TEXT_SPACING;
-
- gdk_gc_set_clip_rectangle (layout_gc, &clip);
-
- {
- /* grumble, render_to_drawable_alpha does not accept a clip
- * mask, so we have to go through some BS
- */
- GdkRectangle pixbuf_rect;
- GdkRectangle draw_rect;
-
- pixbuf_rect.x = icon_x;
- pixbuf_rect.y = icon_y;
- pixbuf_rect.width = icon_w;
- pixbuf_rect.height = icon_h;
-
- if (gdk_rectangle_intersect (&clip, &pixbuf_rect, &draw_rect))
- {
- gdk_pixbuf_render_to_drawable_alpha (icon,
- drawable,
- draw_rect.x - pixbuf_rect.x,
- draw_rect.y - pixbuf_rect.y,
- draw_rect.x, draw_rect.y,
- draw_rect.width,
- draw_rect.height,
- GDK_PIXBUF_ALPHA_FULL,
- 128,
- GDK_RGB_DITHER_NORMAL,
- 0, 0);
- }
- }
-
- gdk_draw_layout (drawable,
- layout_gc,
- x, y,
- frame->layout);
- gdk_gc_set_clip_rectangle (layout_gc, NULL);
- }
+ ++i;
}
-
- inner = frames->layout->inner_button_border;
- if (fgeom.close_rect.width > 0 && fgeom.close_rect.height > 0)
- {
- draw_control_bg (frames, frame, drawable,
- META_FRAME_CONTROL_DELETE, &fgeom);
-
- draw_control (frames, drawable,
- NULL, NULL,
- META_FRAME_CONTROL_DELETE,
- fgeom.close_rect.x + inner.left,
- fgeom.close_rect.y + inner.top,
- fgeom.close_rect.width - inner.right - inner.left,
- fgeom.close_rect.height - inner.bottom - inner.top);
- }
-
- if (fgeom.max_rect.width > 0 && fgeom.max_rect.height > 0)
- {
- MetaFrameControl ctrl;
-
- if (flags & META_FRAME_MAXIMIZED)
- ctrl = META_FRAME_CONTROL_UNMAXIMIZE;
- else
- ctrl = META_FRAME_CONTROL_MAXIMIZE;
-
- draw_control_bg (frames, frame, drawable, ctrl, &fgeom);
-
- draw_control (frames, drawable,
- NULL, NULL,
- ctrl,
- fgeom.max_rect.x + inner.left,
- fgeom.max_rect.y + inner.top,
- fgeom.max_rect.width - inner.left - inner.right,
- fgeom.max_rect.height - inner.top - inner.bottom);
- }
+ grab_frame = meta_core_get_grab_frame (gdk_display);
- if (fgeom.min_rect.width > 0 && fgeom.min_rect.height > 0)
+ if (frame->xwindow == grab_frame)
{
- draw_control_bg (frames, frame, drawable,
- META_FRAME_CONTROL_MINIMIZE, &fgeom);
-
- draw_control (frames, drawable,
- NULL, NULL,
- META_FRAME_CONTROL_MINIMIZE,
- fgeom.min_rect.x + inner.left,
- fgeom.min_rect.y + inner.top,
- fgeom.min_rect.width - inner.left - inner.right,
- fgeom.min_rect.height - inner.top - inner.bottom);
+ switch (meta_core_get_grab_op (gdk_display))
+ {
+ case META_GRAB_OP_CLICKING_MENU:
+ button_states[META_BUTTON_TYPE_MENU] =
+ META_BUTTON_STATE_PRESSED;
+ break;
+ case META_GRAB_OP_CLICKING_DELETE:
+ button_states[META_BUTTON_TYPE_CLOSE] =
+ META_BUTTON_STATE_PRESSED;
+ break;
+ case META_GRAB_OP_CLICKING_MAXIMIZE:
+ button_states[META_BUTTON_TYPE_MAXIMIZE] =
+ META_BUTTON_STATE_PRESSED;
+ break;
+ case META_GRAB_OP_CLICKING_UNMAXIMIZE:
+ button_states[META_BUTTON_TYPE_MAXIMIZE] =
+ META_BUTTON_STATE_PRESSED;
+ break;
+ case META_GRAB_OP_CLICKING_MINIMIZE:
+ button_states[META_BUTTON_TYPE_MINIMIZE] =
+ META_BUTTON_STATE_PRESSED;
+ break;
+ default:
+ break;
+ }
}
- if (fgeom.spacer_rect.width > 0 && fgeom.spacer_rect.height > 0)
- {
- gtk_paint_vline (widget->style,
- drawable,
- GTK_STATE_NORMAL,
- area,
- widget,
- "metacity_frame_spacer",
- fgeom.spacer_rect.y,
- fgeom.spacer_rect.y + fgeom.spacer_rect.height,
- fgeom.spacer_rect.x + fgeom.spacer_rect.width / 2);
- }
+ flags = meta_core_get_frame_flags (gdk_display, frame->xwindow);
+ type = meta_core_get_frame_type (gdk_display, frame->xwindow);
+ mini_icon = meta_core_get_mini_icon (gdk_display, frame->xwindow);
+ icon = meta_core_get_icon (gdk_display, frame->xwindow);
- if (fgeom.menu_rect.width > 0 && fgeom.menu_rect.height > 0)
- {
- int x, y;
-#define ARROW_WIDTH 7
-#define ARROW_HEIGHT 5
-
- draw_control_bg (frames, frame,
- drawable,
- META_FRAME_CONTROL_MENU, &fgeom);
-
- x = fgeom.menu_rect.x;
- y = fgeom.menu_rect.y;
- x += (fgeom.menu_rect.width - ARROW_WIDTH) / 2;
- y += (fgeom.menu_rect.height - ARROW_HEIGHT) / 2;
-
- gtk_paint_arrow (widget->style,
- drawable,
- GTK_STATE_NORMAL,
- GTK_SHADOW_OUT,
- area,
- widget,
- "metacity_menu_button",
- GTK_ARROW_DOWN,
- TRUE,
- x, y, ARROW_WIDTH, ARROW_HEIGHT);
- }
+ meta_core_get_client_size (gdk_display, frame->xwindow,
+ &w, &h);
+
+ meta_theme_draw_frame (meta_theme_get_current (),
+ widget,
+ drawable,
+ area,
+ 0, 0,
+ type,
+ flags,
+ w, h,
+ frame->layout,
+ frames->text_height,
+ button_states,
+ mini_icon, icon);
}
static gboolean
diff --git a/src/frames.h b/src/frames.h
index 9979432..e756ec5 100644
--- a/src/frames.h
+++ b/src/frames.h
@@ -74,10 +74,7 @@ struct _MetaUIFrame
struct _MetaFrames
{
GtkWindow parent_instance;
-
- /* If we did a widget per frame, we wouldn't want to cache this. */
- MetaFrameLayout *layout;
-
+
int text_height;
GHashTable *frames;
@@ -121,11 +118,6 @@ void meta_frames_unflicker_bg (MetaFrames *frames,
void meta_frames_queue_draw (MetaFrames *frames,
Window xwindow);
-void meta_frames_get_pixmap_for_control (MetaFrames *frames,
- MetaFrameControl control,
- GdkPixmap **pixmap,
- GdkBitmap **mask);
-
void meta_frames_notify_menu_hide (MetaFrames *frames);
Window meta_frames_get_moving_frame (MetaFrames *frames);
diff --git a/src/gradient.c b/src/gradient.c
index 7be76d9..ee1f921 100644
--- a/src/gradient.c
+++ b/src/gradient.c
@@ -103,6 +103,8 @@ meta_gradient_create_simple (int width,
case META_GRADIENT_DIAGONAL:
return meta_gradient_create_diagonal (width, height,
from, to);
+ case META_GRADIENT_LAST:
+ break;
}
g_assert_not_reached ();
return NULL;
@@ -126,6 +128,9 @@ meta_gradient_create_multi (int width,
return meta_gradient_create_multi_vertical (width, height, colors, n_colors);
case META_GRADIENT_DIAGONAL:
return meta_gradient_create_multi_diagonal (width, height, colors, n_colors);
+ case META_GRADIENT_LAST:
+ g_assert_not_reached ();
+ break;
}
}
else if (n_colors > 1)
diff --git a/src/gradient.h b/src/gradient.h
index 785a1a8..206c6e4 100644
--- a/src/gradient.h
+++ b/src/gradient.h
@@ -29,7 +29,8 @@ typedef enum
{
META_GRADIENT_VERTICAL,
META_GRADIENT_HORIZONTAL,
- META_GRADIENT_DIAGONAL
+ META_GRADIENT_DIAGONAL,
+ META_GRADIENT_LAST
} MetaGradientType;
GdkPixbuf* meta_gradient_create_simple (int width,
diff --git a/src/main.c b/src/main.c
index 87d2d8c..4e30b1c 100644
--- a/src/main.c
+++ b/src/main.c
@@ -43,6 +43,9 @@ static MetaExitCode meta_exit_code = META_EXIT_SUCCESS;
static GMainLoop *meta_main_loop = NULL;
static gboolean meta_restart_after_quit = FALSE;
+static void prefs_changed_callback (MetaPreference pref,
+ gpointer data);
+
static void
log_handler (const gchar *log_domain,
GLogLevelFlags log_level,
@@ -175,12 +178,14 @@ main (int argc, char **argv)
/* Load prefs */
meta_prefs_init ();
+ meta_prefs_add_listener (prefs_changed_callback, NULL);
meta_ui_init (&argc, &argv);
/* must be after UI init so we can override GDK handlers */
meta_errors_init ();
+#if 0
g_log_set_handler (NULL,
G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION,
log_handler, NULL);
@@ -205,6 +210,22 @@ main (int argc, char **argv)
if (meta_is_debugging ())
g_log_set_always_fatal (G_LOG_LEVEL_MASK);
+#endif
+
+ meta_ui_set_current_theme (meta_prefs_get_theme (), FALSE);
+
+ /* Try some panic stuff, this is lame but we really
+ * don't want users to lose their WM :-/
+ */
+ if (!meta_ui_have_a_theme ())
+ meta_ui_set_current_theme ("Atlanta", FALSE);
+
+ if (!meta_ui_have_a_theme ())
+ meta_ui_set_current_theme ("Crux", FALSE);
+
+ if (!meta_ui_have_a_theme ())
+ meta_fatal (_("Could not find a theme! Be sure %s exits and contains the usual themes."),
+ METACITY_PKGDATADIR"/themes");
/* Connect to SM as late as possible - but before managing display,
* or we might try to manage a window before we have the session
@@ -280,3 +301,19 @@ meta_restart (void)
meta_restart_after_quit = TRUE;
meta_quit (META_EXIT_SUCCESS);
}
+
+static void
+prefs_changed_callback (MetaPreference pref,
+ gpointer data)
+{
+ switch (pref)
+ {
+ case META_PREF_THEME:
+ meta_ui_set_current_theme (meta_prefs_get_theme (), FALSE);
+ break;
+
+ default:
+ /* handled elsewhere or otherwise */
+ break;
+ }
+}
diff --git a/src/menu.c b/src/menu.c
index 708274f..a803c0c 100644
--- a/src/menu.c
+++ b/src/menu.c
@@ -24,6 +24,7 @@
#include "main.h"
#include "util.h"
#include "core.h"
+#include "themewidget.h"
typedef struct _MenuItem MenuItem;
typedef struct _MenuData MenuData;
@@ -117,6 +118,41 @@ activate_cb (GtkWidget *menuitem, gpointer data)
/* menu may now be freed */
}
+static void
+menu_icon_size_func (MetaArea *area,
+ int *width,
+ int *height,
+ void *user_data)
+{
+ gtk_icon_size_lookup (GTK_ICON_SIZE_MENU,
+ width, height);
+}
+
+static void
+menu_icon_expose_func (MetaArea *area,
+ GdkEventExpose *event,
+ int x_offset,
+ int y_offset,
+ void *user_data)
+{
+ int width, height;
+ MetaMenuIconType type;
+
+ type = GPOINTER_TO_INT (user_data);
+
+ gtk_icon_size_lookup (GTK_ICON_SIZE_MENU,
+ &width, &height);
+
+ meta_theme_draw_menu_icon (meta_theme_get_current (),
+ GTK_WIDGET (area),
+ GTK_WIDGET (area)->window,
+ &event->area,
+ x_offset, y_offset,
+ width, height,
+ type);
+}
+
+
MetaWindowMenu*
meta_window_menu_new (MetaFrames *frames,
MetaMenuOp ops,
@@ -141,7 +177,7 @@ meta_window_menu_new (MetaFrames *frames,
menu->menu = gtk_menu_new ();
i = 0;
- while (i < G_N_ELEMENTS (menuitems))
+ while (i < (int) G_N_ELEMENTS (menuitems))
{
if (ops & menuitems[i].op || menuitems[i].op == 0)
{
@@ -155,48 +191,49 @@ meta_window_menu_new (MetaFrames *frames,
else
{
GtkWidget *image;
- GdkPixmap *pix;
- GdkBitmap *mask;
image = NULL;
- pix = NULL;
- mask = NULL;
switch (menuitems[i].op)
{
case META_MENU_OP_MAXIMIZE:
- meta_frames_get_pixmap_for_control (frames,
- META_FRAME_CONTROL_MAXIMIZE,
- &pix, &mask);
+ image = meta_area_new ();
+ meta_area_setup (META_AREA (image),
+ menu_icon_size_func,
+ menu_icon_expose_func,
+ GINT_TO_POINTER (META_MENU_ICON_TYPE_MAXIMIZE),
+ NULL);
break;
case META_MENU_OP_UNMAXIMIZE:
- meta_frames_get_pixmap_for_control (frames,
- META_FRAME_CONTROL_UNMAXIMIZE,
- &pix, &mask);
+ image = meta_area_new ();
+ meta_area_setup (META_AREA (image),
+ menu_icon_size_func,
+ menu_icon_expose_func,
+ GINT_TO_POINTER (META_MENU_ICON_TYPE_UNMAXIMIZE),
+ NULL);
break;
case META_MENU_OP_MINIMIZE:
- meta_frames_get_pixmap_for_control (frames,
- META_FRAME_CONTROL_MINIMIZE,
- &pix, &mask);
+ image = meta_area_new ();
+ meta_area_setup (META_AREA (image),
+ menu_icon_size_func,
+ menu_icon_expose_func,
+ GINT_TO_POINTER (META_MENU_ICON_TYPE_MINIMIZE),
+ NULL);
break;
case META_MENU_OP_DELETE:
- meta_frames_get_pixmap_for_control (frames,
- META_FRAME_CONTROL_DELETE,
- &pix, &mask);
+ image = meta_area_new ();
+ meta_area_setup (META_AREA (image),
+ menu_icon_size_func,
+ menu_icon_expose_func,
+ GINT_TO_POINTER (META_MENU_ICON_TYPE_CLOSE),
+ NULL);
break;
default:
break;
}
-
- if (pix)
- {
- image = gtk_image_new_from_pixmap (pix, mask);
- g_object_unref (G_OBJECT (pix));
- g_object_unref (G_OBJECT (mask));
- }
if (image == NULL &&
menuitems[i].stock_id)
diff --git a/src/metacity.schemas b/src/metacity.schemas
index 4f6fb4b..e8aa7b3 100644
--- a/src/metacity.schemas
+++ b/src/metacity.schemas
@@ -23,6 +23,21 @@
</schema>
<schema>
+ <key>/schemas/apps/metacity/general/theme</key>
+ <applyto>/apps/metacity/general/theme</applyto>
+ <owner>metacity</owner>
+ <type>string</type>
+ <default>Atlanta</default>
+ <locale name="C">
+ <short>Current theme</short>
+ <long>
+ The theme determines the appearance of window borders,
+ titlebar, and so forth.
+ </long>
+ </locale>
+ </schema>
+
+ <schema>
<key>/schemas/apps/metacity/general/titlebar_uses_desktop_font</key>
<applyto>/apps/metacity/general/titlebar_uses_desktop_font</applyto>
<owner>metacity</owner>
diff --git a/src/prefs.c b/src/prefs.c
index 79a2d1a..a5fe823 100644
--- a/src/prefs.c
+++ b/src/prefs.c
@@ -29,6 +29,7 @@
* notify listener and of course in the .schemas file
*/
#define KEY_FOCUS_MODE "/apps/metacity/general/focus_mode"
+#define KEY_THEME "/apps/metacity/general/theme"
#define KEY_USE_DESKTOP_FONT "/apps/metacity/general/titlebar_uses_desktop_font"
#define KEY_TITLEBAR_FONT "/apps/metacity/general/titlebar_font"
#define KEY_TITLEBAR_FONT_SIZE "/apps/metacity/general/titlebar_font_size"
@@ -43,6 +44,7 @@ static gboolean use_desktop_font = TRUE;
static PangoFontDescription *titlebar_font = NULL;
static int titlebar_font_size = 0;
static MetaFocusMode focus_mode = META_FOCUS_MODE_CLICK;
+static char* current_theme = NULL;
static int num_workspaces = 4;
static gboolean application_based = FALSE;
@@ -50,6 +52,7 @@ static gboolean update_use_desktop_font (gboolean value);
static gboolean update_titlebar_font (const char *value);
static gboolean update_titlebar_font_size (int value);
static gboolean update_focus_mode (const char *value);
+static gboolean update_theme (const char *value);
static gboolean update_num_workspaces (int value);
static gboolean update_application_based (gboolean value);
@@ -212,6 +215,12 @@ meta_prefs_init (void)
update_focus_mode (str_val);
g_free (str_val);
+ str_val = gconf_client_get_string (client, KEY_THEME,
+ &err);
+ cleanup_error (&err);
+ update_theme (str_val);
+ g_free (str_val);
+
/* If the keys aren't set in the database, we use essentially
* bogus values instead of any kind of default. This is
* just lazy. But they keys ought to be set, anyhow.
@@ -279,6 +288,22 @@ change_notify (GConfClient *client,
if (update_focus_mode (str))
queue_changed (META_PREF_FOCUS_MODE);
}
+ if (strcmp (key, KEY_THEME) == 0)
+ {
+ const char *str;
+
+ if (value && value->type != GCONF_VALUE_STRING)
+ {
+ meta_warning (_("GConf key \"%s\" is set to an invalid type\n"),
+ KEY_THEME);
+ goto out;
+ }
+
+ str = value ? gconf_value_get_string (value) : NULL;
+
+ if (update_focus_mode (str))
+ queue_changed (META_PREF_THEME);
+ }
else if (strcmp (key, KEY_TITLEBAR_FONT) == 0)
{
const char *str;
@@ -394,12 +419,50 @@ update_focus_mode (const char *value)
return (old_mode != focus_mode);
}
+static gboolean
+update_theme (const char *value)
+{
+ const char *old_theme;
+ gboolean changed;
+
+ old_theme = current_theme;
+
+ if (value != NULL && *value)
+ {
+ current_theme = g_strdup (value);
+ }
+
+ changed = TRUE;
+ if ((old_theme && current_theme &&
+ strcmp (old_theme, current_theme) == 0) ||
+ (old_theme == NULL && current_theme == NULL))
+ changed = FALSE;
+
+ if (old_theme != current_theme)
+ g_free (old_theme);
+
+ if (current_theme == NULL)
+ {
+ /* Fallback crackrock */
+ current_theme = g_strdup ("Atlanta");
+ changed = TRUE;
+ }
+
+ return changed;
+}
+
MetaFocusMode
meta_prefs_get_focus_mode (void)
{
return focus_mode;
}
+const char*
+meta_prefs_get_theme (void)
+{
+ return current_theme;
+}
+
static gboolean
update_use_desktop_font (gboolean value)
{
@@ -528,6 +591,10 @@ meta_preference_to_string (MetaPreference pref)
return "FOCUS_MODE";
break;
+ case META_PREF_THEME:
+ return "THEME";
+ break;
+
case META_PREF_TITLEBAR_FONT:
return "TITLEBAR_FONT";
break;
diff --git a/src/prefs.h b/src/prefs.h
index 3abdd36..617d730 100644
--- a/src/prefs.h
+++ b/src/prefs.h
@@ -29,11 +29,11 @@
typedef enum
{
META_PREF_FOCUS_MODE,
+ META_PREF_THEME,
META_PREF_TITLEBAR_FONT,
META_PREF_TITLEBAR_FONT_SIZE,
META_PREF_NUM_WORKSPACES,
META_PREF_APPLICATION_BASED
-
} MetaPreference;
typedef void (* MetaPrefsChangedFunc) (MetaPreference pref,
@@ -48,6 +48,7 @@ void meta_prefs_init (void);
const char* meta_preference_to_string (MetaPreference pref);
MetaFocusMode meta_prefs_get_focus_mode (void);
+const char* meta_prefs_get_theme (void);
/* returns NULL if GTK default should be used */
const PangoFontDescription* meta_prefs_get_titlebar_font (void);
/* returns 0 if default should be used */
diff --git a/src/screen.c b/src/screen.c
index 01e4b8f..719d753 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -277,7 +277,7 @@ meta_screen_manage_all_windows (MetaScreen *screen)
{
Window ignored1, ignored2;
Window *children;
- unsigned int n_children;
+ int n_children;
int i;
/* Must grab server to avoid obvious race condition */
diff --git a/src/stack.c b/src/stack.c
index e9672a8..c78a62b 100644
--- a/src/stack.c
+++ b/src/stack.c
@@ -98,7 +98,8 @@ meta_stack_free (MetaStack *stack)
g_list_free (stack->pending);
- g_array_free (stack->last_root_children_stacked, TRUE);
+ if (stack->last_root_children_stacked)
+ g_array_free (stack->last_root_children_stacked, TRUE);
g_free (stack);
}
@@ -940,7 +941,7 @@ find_tab_forward (MetaStack *stack,
/* start may be -1 to find any tab window at all */
i = start + 1;
- while (i < stack->windows->len)
+ while (i < (int) stack->windows->len)
{
MetaWindow *window;
@@ -1045,7 +1046,7 @@ meta_stack_get_tab_next (MetaStack *stack,
if (window != NULL)
{
i = 0;
- while (i < stack->windows->len)
+ while (i < (int) stack->windows->len)
{
Window w;
@@ -1083,7 +1084,7 @@ meta_stack_get_tab_list (MetaStack *stack,
list = NULL;
i = 0;
- while (i < stack->windows->len)
+ while (i < (int) stack->windows->len)
{
MetaWindow *window;
diff --git a/src/tabpopup.c b/src/tabpopup.c
index 08c4ac7..ddd7553 100644
--- a/src/tabpopup.c
+++ b/src/tabpopup.c
@@ -22,8 +22,8 @@
#include "util.h"
#include "core.h"
#include "tabpopup.h"
-#include <math.h>
#include <gtk/gtk.h>
+#include <math.h>
#define OUTSIDE_SELECT_RECT 2
#define INSIDE_SELECT_RECT 2
diff --git a/src/theme-parser.c b/src/theme-parser.c
new file mode 100644
index 0000000..dfeed94
--- /dev/null
+++ b/src/theme-parser.c
@@ -0,0 +1,3920 @@
+/* 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,
+ /* 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 */
+ /* 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
+} 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 */
+ 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 */
+ MetaMenuIconType menu_icon_type; /* type of menu icon being parsed */
+ GtkStateType menu_icon_state; /* state of menu icon 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);
+
+ if (n_attrs == MAX_ATTRS)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Element <%s> has more than %d attributes, can't possibly be valid"),
+ element_name, MAX_ATTRS);
+ retval = FALSE;
+ goto out;
+ }
+
+ 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**);
+ }
+
+ out:
+ 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 out2;
+ }
+
+ *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 out2;
+ }
+
+ ++i;
+ }
+
+ out2:
+ 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,
+ GError **error)
+{
+ char *end;
+ long l;
+
+ *val = 0;
+
+ end = NULL;
+
+ 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_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,
+ double *val,
+ GMarkupParseContext *context,
+ GError **error)
+{
+ if (!parse_double (str, val, context, error))
+ return FALSE;
+
+ if (*val < (0.0 - 1e6) || *val > (1.0 + 1e6))
+ {
+ 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"),
+ *val);
+ return FALSE;
+ }
+
+ 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;
+ double dval;
+
+ 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;
+ }
+
+ if (strchr (value, '.'))
+ {
+ dval = 0.0;
+ if (!parse_double (value, &dval, context, error))
+ return;
+
+ if (!meta_theme_define_float_constant (info->theme,
+ name,
+ dval,
+ error))
+ {
+ add_context_to_error (error, context);
+ return;
+ }
+ }
+ else
+ {
+ ival = 0;
+ if (!parse_positive_integer (value, &ival, context, error))
+ return;
+
+ if (!meta_theme_define_int_constant (info->theme,
+ name,
+ ival,
+ 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;
+ MetaFrameLayout *parent_layout;
+
+ 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 \"name\" attribute on <%s> element"),
+ element_name);
+ 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 ();
+
+ 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 \"name\" attribute on <%s> element"),
+ 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;
+ MetaFrameStyle *parent_style;
+ MetaFrameLayout *layout;
+
+ if (!locate_attributes (context, element_name, attribute_names, attribute_values,
+ error,
+ "name", &name, "parent", &parent,
+ "geometry", &geometry,
+ NULL))
+ return;
+
+ if (name == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"name\" attribute on <%s> element"),
+ 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;
+
+ 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 \"name\" attribute on <%s> element"),
+ 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 \"type\" attribute on <%s> element"),
+ element_name);
+ return;
+ }
+
+ if (style_set_name == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"style_set\" attribute on <%s> element"),
+ 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"))
+ {
+ 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->menu_icon_type = meta_menu_icon_type_from_string (function);
+ if (info->menu_icon_type == META_BUTTON_TYPE_LAST)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Unknown function \"%s\" for menu icon"),
+ function);
+ return;
+ }
+
+ info->menu_icon_state = meta_gtk_state_from_string (state);
+ if (((int) info->menu_icon_state) == -1)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Unknown state \"%s\" for menu icon"),
+ state);
+ return;
+ }
+
+ if (info->theme->menu_icons[info->menu_icon_type][info->menu_icon_state] != NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Theme already has a menu icon 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_MENU_ICON);
+ }
+ else
+ {
+ set_error (error, context,
+ G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Element <%s> is not allowed below <metacity_theme>"),
+ element_name);
+ }
+}
+
+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 <info>"),
+ element_name);
+ }
+}
+
+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, 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;
+ else if (strcmp (name, "button_height") == 0)
+ info->layout->button_height = val;
+ else
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Distance \"%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, error))
+ return;
+
+ bottom_val = 0;
+ if (!parse_positive_integer (bottom, &bottom_val, context, error))
+ return;
+
+ left_val = 0;
+ if (!parse_positive_integer (left, &left_val, context, error))
+ return;
+
+ right_val = 0;
+ if (!parse_positive_integer (right, &right_val, context, 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
+ {
+ set_error (error, context,
+ G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Element <%s> is not allowed below <frame_geometry>"),
+ element_name);
+ }
+}
+
+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.x = 0;
+ env.y = 0;
+ env.width = 0;
+ env.height = 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 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 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, error))
+ return;
+
+ dash_off_val = 0;
+ if (dash_off_length &&
+ !parse_positive_integer (dash_off_length, &dash_off_val, context, error))
+ return;
+
+ width_val = 0;
+ if (width &&
+ !parse_positive_integer (width, &width_val, context, error))
+ return;
+
+ /* Check last so we don't have to free it when other
+ * stuff fails
+ */
+ color_spec = meta_color_spec_new_from_string (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 = meta_color_spec_new_from_string (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;
+ 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,
+ 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 (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 (!parse_angle (start_angle, &start_angle_val, context, error))
+ return;
+
+ 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 = meta_color_spec_new_from_string (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;
+ double alpha_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,
+ "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;
+
+ if (!parse_alpha (alpha, &alpha_val, context, error))
+ return;
+
+ /* Check last so we don't have to free it when other
+ * stuff fails
+ */
+ color_spec = meta_color_spec_new_from_string (color, error);
+ if (color_spec == NULL)
+ {
+ 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.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);
+ op->data.tint.alpha = alpha_val;
+
+ 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;
+ double alpha_val;
+ 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;
+
+ alpha_val = 1.0;
+ if (alpha && !parse_alpha (alpha, &alpha_val, 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;
+ }
+
+ 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 = alpha_val;
+
+ 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;
+ double alpha_val;
+ GdkPixbuf *pixbuf;
+
+ if (!locate_attributes (context, element_name, attribute_names, attribute_values,
+ error,
+ "x", &x, "y", &y,
+ "width", &width, "height", &height,
+ "alpha", &alpha, "filename", &filename,
+ 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;
+
+ alpha_val = 1.0;
+ if (alpha && !parse_alpha (alpha, &alpha_val, context, error))
+ return;
+
+ /* Check last so we don't have to free it when other
+ * stuff fails
+ */
+ pixbuf = meta_theme_load_image (info->theme, filename, error);
+
+ if (pixbuf == NULL)
+ {
+ add_context_to_error (error, context);
+ return;
+ }
+
+ op = meta_draw_op_new (META_DRAW_IMAGE);
+
+ op->data.image.pixbuf = pixbuf;
+ 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 = alpha_val;
+
+ 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;
+ double alpha_val;
+
+ if (!locate_attributes (context, element_name, attribute_names, attribute_values,
+ error,
+ "x", &x, "y", &y,
+ "width", &width, "height", &height,
+ "alpha", &alpha,
+ 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;
+
+ alpha_val = 1.0;
+ if (alpha && !parse_alpha (alpha, &alpha_val, 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 = alpha_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 = meta_color_spec_new_from_string (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 \"name\" attribute on element <%s>"), 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
+ {
+ set_error (error, context,
+ G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Element <%s> is not allowed below <draw_ops>"),
+ element_name);
+ }
+}
+
+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 = meta_color_spec_new_from_string (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 <gradient>"),
+ element_name);
+ }
+}
+
+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);
+ 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;
+ }
+
+ 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 <frame_style>"),
+ element_name);
+ }
+}
+
+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;
+ }
+
+ if (frame_state == 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;
+ }
+ }
+ else
+ {
+ 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;
+ }
+
+ 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_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->shaded_styles[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 <frame_style_set>"),
+ element_name);
+ }
+}
+
+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 <piece>"),
+ element_name);
+ }
+}
+
+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 <piece>"),
+ element_name);
+ }
+}
+
+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 <piece>"),
+ element_name);
+ }
+}
+
+
+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);
+
+ 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:
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Element <%s> is not allowed inside a distance/border 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:
+ 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 <color> element"),
+ element_name);
+ 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 <frame> element"),
+ element_name);
+ break;
+ case STATE_WINDOW:
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Element <%s> is not allowed inside a <window> element"),
+ element_name);
+ 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_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_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,
+ 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)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No draw_ops provided for menu icon"));
+ }
+ else
+ {
+ g_assert (info->theme->menu_icons[info->menu_icon_type][info->menu_icon_state] == NULL);
+ info->theme->menu_icons[info->menu_icon_type][info->menu_icon_state] =
+ 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;
+ }
+}
+
+#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_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_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;
+ }
+}
+
+/* We change the filename when we break the format,
+ * so themes can work with various metacity versions
+ */
+#define THEME_FILENAME "metacity-theme-1.xml"
+
+MetaTheme*
+meta_theme_load (const char *theme_name,
+ GError **err)
+{
+ GMarkupParseContext *context;
+ GError *error;
+ ParseInfo info;
+ char *text;
+ int length;
+ char *theme_file;
+ char *theme_dir;
+ MetaTheme *retval;
+
+ text = NULL;
+ length = 0;
+ retval = NULL;
+
+ theme_dir = NULL;
+ theme_file = NULL;
+
+ if (meta_is_debugging ())
+ {
+ 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_verbose ("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;
+ }
+ }
+
+ /* We try in current dir, then home dir, then system dir for themes */
+ if (text == NULL)
+ {
+ theme_dir = g_build_filename ("./", 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_verbose ("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 (g_get_home_dir (),
+ ".metacity/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_verbose ("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_PKGDATADIR,
+ "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_warning (_("Failed to read theme from file %s: %s\n"),
+ theme_file, error->message);
+ g_propagate_error (err, error);
+ g_free (theme_file);
+ g_free (theme_dir);
+ return FALSE; /* all fallbacks failed */
+ }
+ }
+
+ g_assert (text);
+
+ meta_verbose ("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;
+
+ 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;
+
+ g_markup_parse_context_free (context);
+
+ goto out;
+
+ out:
+
+ g_free (text);
+
+ 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;
+}
diff --git a/src/theme-parser.h b/src/theme-parser.h
new file mode 100644
index 0000000..2ba198e
--- /dev/null
+++ b/src/theme-parser.h
@@ -0,0 +1,30 @@
+/* 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 "theme.h"
+
+#ifndef META_THEME_PARSER_H
+#define META_THEME_PARSER_H
+
+MetaTheme* meta_theme_load (const char *theme_name,
+ GError **err);
+
+#endif
diff --git a/src/theme-viewe