From 8ae714eeaef03db0a55c11fc31834c8d65e2ea03 Mon Sep 17 00:00:00 2001 From: Havoc Pennington Date: Thu, 7 Feb 2002 03:07:56 +0000 Subject: [PATCH] disable custom log handler and fatal mask for now 2002-02-06 Havoc Pennington * 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. --- ChangeLog | 42 + Makefile.am | 2 +- configure.in | 46 +- src/Makefile.am | 10 +- src/common.h | 2 +- src/core.c | 59 + src/core.h | 4 + src/display.c | 31 +- src/display.h | 1 + src/frames.c | 755 +---- src/frames.h | 10 +- src/gradient.c | 5 + src/gradient.h | 3 +- src/main.c | 37 + src/menu.c | 85 +- src/metacity.schemas | 15 + src/prefs.c | 67 + src/prefs.h | 3 +- src/screen.c | 2 +- src/stack.c | 9 +- src/tabpopup.c | 2 +- src/theme-parser.c | 3920 +++++++++++++++++++++++ src/theme-parser.h | 30 + src/theme-viewer.c | 285 +- src/theme.c | 3313 +++++++++++++++---- src/theme.h | 329 +- src/themes/Atlanta/metacity-theme-1.xml | 239 ++ src/themes/Makefile.am | 29 + src/themewidget.c | 181 ++ src/themewidget.h | 76 + src/tools/Makefile.am | 8 +- src/tools/metacity-reload-theme.c | 56 + src/ui.c | 14 + src/ui.h | 5 + src/window.c | 16 +- theme-format.txt | 218 ++ 36 files changed, 8395 insertions(+), 1514 deletions(-) create mode 100644 src/theme-parser.c create mode 100644 src/theme-parser.h create mode 100644 src/themes/Atlanta/metacity-theme-1.xml create mode 100644 src/themes/Makefile.am create mode 100644 src/themewidget.c create mode 100644 src/themewidget.h create mode 100644 src/tools/metacity-reload-theme.c create mode 100644 theme-format.txt diff --git a/ChangeLog b/ChangeLog index fa667221b..3abe6c7b6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,45 @@ +2002-02-06 Havoc Pennington + + * 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 * src/themes/Crux/metacity-theme-1.xml: diff --git a/Makefile.am b/Makefile.am index e7cccc70a..d439b1041 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 07b33adfe..af9444c08 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 8391041a2..6ba049799 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 62369c40f..de34df8a1 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 dddcf68f0..c490d88ce 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 b2146424e..7952e88ec 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 45511d549..073232d42 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 bf5186c6e..f1ef42404 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 e2c6f5fa3..990eb04bc 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); + + if (GTK_WIDGET_REALIZED (widget)) + { + frames->text_height = meta_gtk_widget_get_text_height (widget); + } + else + { + frames->text_height = 0; + } - 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; - 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); - } - /* 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; - + MetaFrameType type; + GdkPixbuf *mini_icon; + GdkPixbuf *icon; + int w, h; + MetaButtonState button_states[META_BUTTON_TYPE_LAST]; + Window grab_frame; + int i; + widget = GTK_WIDGET (frames); + + /* note, prelight not implemented yet */ + i = 0; + while (i < META_BUTTON_TYPE_LAST) + { + button_states[i] = META_BUTTON_STATE_NORMAL; + + ++i; + } + + 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: + 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; + } + } - meta_frames_calc_geometry (frames, frame, &fgeom); flags = meta_core_get_frame_flags (gdk_display, frame->xwindow); - width = fgeom.width; - height = fgeom.height; + 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); + + meta_core_get_client_size (gdk_display, frame->xwindow, + &w, &h); - /* 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); - } - - if (area->y < fgeom.top_height && - fgeom.title_rect.width > 0 && fgeom.title_rect.height > 0) - { - GdkRectangle clip; - GdkGC *layout_gc; - - 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); - } - } - - 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); - } - - if (fgeom.min_rect.width > 0 && fgeom.min_rect.height > 0) - { - 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); - } - - 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); - } - - 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_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 997943263..e756ec5bc 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 7be76d9b1..ee1f9215e 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 785a1a862..206c6e4e3 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 87d2d8c8c..4e30b1cf6 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 708274f3e..a803c0c81 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 4f6fb4b63..e8aa7b349 100644 --- a/src/metacity.schemas +++ b/src/metacity.schemas @@ -22,6 +22,21 @@ + + /schemas/apps/metacity/general/theme + /apps/metacity/general/theme + metacity + string + Atlanta + + Current theme + + The theme determines the appearance of window borders, + titlebar, and so forth. + + + + /schemas/apps/metacity/general/titlebar_uses_desktop_font /apps/metacity/general/titlebar_uses_desktop_font diff --git a/src/prefs.c b/src/prefs.c index 79a2d1a01..a5fe823e0 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 3abdd36c9..617d73074 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 01e4b8fd8..719d75333 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 e9672a85f..c78a62b4b 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 08c4ac7ff..ddd7553f3 100644 --- a/src/tabpopup.c +++ b/src/tabpopup.c @@ -22,8 +22,8 @@ #include "util.h" #include "core.h" #include "tabpopup.h" -#include #include +#include #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 000000000..dfeed94ac --- /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 +#include "theme-parser.h" +#include "util.h" +#include +#include + +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 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 "), + 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 "), + 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 "), + 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 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 "), + 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 "), + 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 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 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 "), + 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 "), + 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 element (theme specified a draw_ops attribute and also a 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 "), + 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