summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog42
-rw-r--r--Makefile.am2
-rw-r--r--configure.in46
-rw-r--r--src/Makefile.am10
-rw-r--r--src/common.h2
-rw-r--r--src/core.c59
-rw-r--r--src/core.h4
-rw-r--r--src/display.c31
-rw-r--r--src/display.h1
-rw-r--r--src/frames.c739
-rw-r--r--src/frames.h10
-rw-r--r--src/gradient.c5
-rw-r--r--src/gradient.h3
-rw-r--r--src/main.c37
-rw-r--r--src/menu.c85
-rw-r--r--src/metacity.schemas15
-rw-r--r--src/prefs.c67
-rw-r--r--src/prefs.h3
-rw-r--r--src/screen.c2
-rw-r--r--src/stack.c9
-rw-r--r--src/tabpopup.c2
-rw-r--r--src/theme-parser.c3920
-rw-r--r--src/theme-parser.h30
-rw-r--r--src/theme-viewer.c287
-rw-r--r--src/theme.c3305
-rw-r--r--src/theme.h325
-rw-r--r--src/themes/Atlanta/metacity-theme-1.xml239
-rw-r--r--src/themes/Makefile.am29
-rw-r--r--src/themewidget.c181
-rw-r--r--src/themewidget.h76
-rw-r--r--src/tools/Makefile.am8
-rw-r--r--src/tools/metacity-reload-theme.c56
-rw-r--r--src/ui.c14
-rw-r--r--src/ui.h5
-rw-r--r--src/window.c16
-rw-r--r--theme-format.txt218
36 files changed, 8382 insertions, 1501 deletions
diff --git a/ChangeLog b/ChangeLog
index fa667221..3abe6c7b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,45 @@
+2002-02-06 Havoc Pennington <hp@pobox.com>
+
+ * src/main.c (main): disable custom log handler and fatal mask for
+ now
+
+ * src/theme.c (meta_draw_op_list_draw):
+ Add META_DRAW_CLIP
+
+ * src/main.c: load theme, monitor current theme setting
+
+ * src/prefs.c: add "current theme" setting
+
+ * src/stack.c (meta_stack_free): don't try to free
+ last_root_children_stacked if it doesn't exist
+
+ * src/themewidget.c: pluggable GtkMisc subclass to use
+ for menu icons
+
+ * src/screen.c (meta_screen_manage_all_windows): fix
+ signed/unsigned warning
+
+ * src/frames.c: port to theme system
+ (meta_frames_style_set): chain up
+
+ * theme-format.txt: new file
+
+ * configure.in: add more compiler warnings
+
+ * src/theme.c: add various stuff needed to get theme parser
+ working. Remove the "spacer" concept from FrameLayout object.
+ Add draw op that references a draw op list.
+
+ * configure.in: require GTK 1.3.13
+
+ * src/Makefile.am: add theme-parser.[hc], implement loading a
+ theme
+
+ * src/theme.c: add "draw title" and "draw window icon" operations
+ (meta_draw_op_draw): put object_width/object_height in expression
+ environment before computing x/y. Handle out-of-memory when
+ creating pixbufs. Assorted other cleanups.
+
2002-02-07 Anders Carlsson <andersca@gnu.org>
* src/themes/Crux/metacity-theme-1.xml:
diff --git a/Makefile.am b/Makefile.am
index e7cccc70..d439b104 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 07b33adf..af9444c0 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 8391041a..6ba04979 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 62369c40..de34df8a 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 dddcf68f..c490d88c 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 b2146424..7952e88e 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 45511d54..073232d4 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 bf5186c6..f1ef4240 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 e2c6f5fa..990eb04b 100644
--- a/src/frames.c
+++ b/src/frames.c
@@ -128,24 +128,6 @@ meta_frames_get_type (void)
return frames_type;
}
-#define BORDER_PROPERTY(name, blurb, docs) \
- gtk_widget_class_install_style_property (widget_class, \
- g_param_spec_boxed (name, \
- blurb, \
- docs, \
- GTK_TYPE_BORDER, \
- G_PARAM_READABLE))
-
-#define INT_PROPERTY(name, default, blurb, docs) \
- gtk_widget_class_install_style_property (widget_class, \
- g_param_spec_int (name, \
- blurb, \
- docs, \
- 0, \
- G_MAXINT, \
- default, \
- G_PARAM_READABLE))
-
static void
meta_frames_class_init (MetaFramesClass *class)
{
@@ -174,27 +156,6 @@ meta_frames_class_init (MetaFramesClass *class)
widget_class->button_release_event = meta_frames_button_release_event;
widget_class->motion_notify_event = meta_frames_motion_notify_event;
widget_class->leave_notify_event = meta_frames_leave_notify_event;
-
- INT_PROPERTY ("left_width", 6, _("Left edge"), _("Left window edge width"));
- INT_PROPERTY ("right_width", 6, _("Right edge"), _("Right window edge width"));
- INT_PROPERTY ("bottom_height", 7, _("Bottom edge"), _("Bottom window edge height"));
-
- BORDER_PROPERTY ("title_border", _("Title border"), _("Border around title area"));
- BORDER_PROPERTY ("text_border", _("Text border"), _("Border around window title text"));
-
- INT_PROPERTY ("spacer_padding", 3, _("Spacer padding"), _("Padding on either side of spacer"));
- INT_PROPERTY ("spacer_width", 2, _("Spacer width"), _("Width of spacer"));
- INT_PROPERTY ("spacer_height", 11, _("Spacer height"), _("Height of spacer"));
-
- /* same as right_width left_width by default */
- INT_PROPERTY ("right_inset", 6, _("Right inset"), _("Distance of buttons from right edge of frame"));
- INT_PROPERTY ("left_inset", 6, _("Left inset"), _("Distance of menu button from left edge of frame"));
-
- INT_PROPERTY ("button_width", 17, _("Button width"), _("Width of buttons"));
- INT_PROPERTY ("button_height", 17, _("Button height"), _("Height of buttons"));
-
- BORDER_PROPERTY ("button_border", _("Button border"), _("Border around buttons"));
- BORDER_PROPERTY ("inner_button_border", _("Inner button border"), _("Border around the icon inside buttons"));
}
static gint
@@ -222,8 +183,6 @@ meta_frames_init (MetaFrames *frames)
{
GTK_WINDOW (frames)->type = GTK_WINDOW_POPUP;
- frames->layout = meta_frame_layout_new ();
-
frames->frames = g_hash_table_new (unsigned_long_hash, unsigned_long_equal);
frames->tooltip_timeout = 0;
@@ -282,8 +241,6 @@ meta_frames_finalize (GObject *object)
g_assert (g_hash_table_size (frames->frames) == 0);
g_hash_table_destroy (frames->frames);
-
- meta_frame_layout_free (frames->layout);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
@@ -327,107 +284,23 @@ meta_frames_style_set (GtkWidget *widget,
GtkStyle *prev_style)
{
MetaFrames *frames;
- /* left, right, top, bottom */
- static GtkBorder default_title_border = { 3, 4, 4, 3 };
- static GtkBorder default_text_border = { 2, 2, 2, 2 };
- static GtkBorder default_button_border = { 0, 0, 1, 1 };
- static GtkBorder default_inner_button_border = {
- DEFAULT_INNER_BUTTON_BORDER,
- DEFAULT_INNER_BUTTON_BORDER,
- DEFAULT_INNER_BUTTON_BORDER,
- DEFAULT_INNER_BUTTON_BORDER
- };
- GtkBorder *title_border;
- GtkBorder *text_border;
- GtkBorder *button_border;
- GtkBorder *inner_button_border;
- MetaFrameLayout layout;
frames = META_FRAMES (widget);
-
- gtk_widget_style_get (widget,
- "left_width",
- &layout.left_width,
- "right_width",
- &layout.right_width,
- "bottom_height",
- &layout.bottom_height,
- "title_border",
- &title_border,
- "text_border",
- &text_border,
- "spacer_padding",
- &layout.spacer_padding,
- "spacer_width",
- &layout.spacer_width,
- "spacer_height",
- &layout.spacer_height,
- "right_inset",
- &layout.right_inset,
- "left_inset",
- &layout.left_inset,
- "button_width",
- &layout.button_width,
- "button_height",
- &layout.button_height,
- "button_border",
- &button_border,
- "inner_button_border",
- &inner_button_border,
- NULL);
-
- if (title_border)
- layout.title_border = *title_border;
- else
- layout.title_border = default_title_border;
- g_free (title_border);
-
- if (text_border)
- layout.text_border = *text_border;
- else
- layout.text_border = default_text_border;
-
- g_free (text_border);
-
- if (button_border)
- layout.button_border = *button_border;
- else
- layout.button_border = default_button_border;
-
- g_free (button_border);
-
- if (inner_button_border)
- layout.inner_button_border = *inner_button_border;
+ if (GTK_WIDGET_REALIZED (widget))
+ {
+ frames->text_height = meta_gtk_widget_get_text_height (widget);
+ }
else
- layout.inner_button_border = default_inner_button_border;
-
- g_free (inner_button_border);
-
- *(frames->layout) = layout;
-
- {
- PangoFontMetrics *metrics;
- PangoFont *font;
- PangoLanguage *lang;
-
- font = pango_context_load_font (gtk_widget_get_pango_context (widget),
- widget->style->font_desc);
- lang = pango_context_get_language (gtk_widget_get_pango_context (widget));
- metrics = pango_font_get_metrics (font, lang);
-
- g_object_unref (G_OBJECT (font));
-
- frames->text_height =
- PANGO_PIXELS (pango_font_metrics_get_ascent (metrics) +
- pango_font_metrics_get_descent (metrics));
-
- pango_font_metrics_unref (metrics);
- }
-
+ {
+ frames->text_height = 0;
+ }
+
/* Queue a draw/resize on all frames */
g_hash_table_foreach (frames->frames,
queue_recalc_func, frames);
+
+ GTK_WIDGET_CLASS (parent_class)->style_set (widget, prev_style);
}
static void
@@ -437,18 +310,20 @@ meta_frames_calc_geometry (MetaFrames *frames,
{
int width, height;
MetaFrameFlags flags;
+ MetaFrameType type;
meta_core_get_client_size (gdk_display, frame->xwindow,
&width, &height);
flags = meta_core_get_frame_flags (gdk_display, frame->xwindow);
-
- meta_frame_layout_calc_geometry (frames->layout,
- GTK_WIDGET (frames),
- frames->text_height,
- flags,
- width, height,
- fgeom);
+ type = meta_core_get_frame_type (gdk_display, frame->xwindow);
+
+ meta_theme_calc_geometry (meta_theme_get_current (),
+ type,
+ frames->text_height,
+ flags,
+ width, height,
+ fgeom);
}
MetaFrames*
@@ -526,6 +401,8 @@ meta_frames_realize (GtkWidget *widget)
if (GTK_WIDGET_CLASS (parent_class)->realize)
GTK_WIDGET_CLASS (parent_class)->realize (widget);
+
+ frames->text_height = meta_gtk_widget_get_text_height (widget);
}
static void
@@ -537,6 +414,8 @@ meta_frames_unrealize (GtkWidget *widget)
if (GTK_WIDGET_CLASS (parent_class)->unrealize)
GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
+
+ frames->text_height = 0;
}
static MetaUIFrame*
@@ -556,27 +435,31 @@ meta_frames_get_geometry (MetaFrames *frames,
int *top_height, int *bottom_height,
int *left_width, int *right_width)
{
- MetaFrameFlags flags;
+ MetaFrameFlags flags;
MetaUIFrame *frame;
-
+ MetaFrameType type;
+
frame = meta_frames_lookup_window (frames, xwindow);
if (frame == NULL)
meta_bug ("No such frame 0x%lx\n", xwindow);
- flags = meta_core_get_frame_flags (gdk_display, frame->xwindow);
+ flags = meta_core_get_frame_flags (gdk_display, frame->xwindow);
+ type = meta_core_get_frame_type (gdk_display, frame->xwindow);
+ g_return_if_fail (type < META_FRAME_TYPE_LAST);
+
/* We can't get the full geometry, because that depends on
* the client window size and probably we're being called
* by the core move/resize code to decide on the client
* window size
*/
- meta_frame_layout_get_borders (frames->layout,
- GTK_WIDGET (frames),
- frames->text_height,
- flags,
- top_height, bottom_height,
- left_width, right_width);
+ meta_theme_get_frame_borders (meta_theme_get_current (),
+ type,
+ frames->text_height,
+ flags,
+ top_height, bottom_height,
+ left_width, right_width);
}
void
@@ -1082,7 +965,7 @@ meta_frames_button_release_event (GtkWidget *widget,
* frame are handled in the Xlib part of the code, display.c/window.c
*/
if (frame->xwindow == meta_core_get_grab_frame (gdk_display) &&
- event->button == meta_core_get_grab_button (gdk_display))
+ ((int) event->button) == meta_core_get_grab_button (gdk_display))
{
gboolean end_grab;
@@ -1264,228 +1147,6 @@ meta_frames_destroy_event (GtkWidget *widget,
return TRUE;
}
-#define THICK_LINE_WIDTH 3
-static void
-draw_mini_window (MetaFrames *frames,
- GdkDrawable *drawable,
- GdkGC *fg_gc,
- GdkGC *bg_gc,
- gboolean thin_title,
- int x, int y, int width, int height)
-{
- GdkGCValues vals;
-
- gdk_draw_rectangle (drawable,
- bg_gc,
- TRUE,
- x, y, width - 1, height - 1);
-
- gdk_draw_rectangle (drawable,
- fg_gc,
- FALSE,
- x, y, width - 1, height - 1);
-
- vals.line_width = thin_title ? THICK_LINE_WIDTH - 1 : THICK_LINE_WIDTH;
- gdk_gc_set_values (fg_gc,
- &vals,
- GDK_GC_LINE_WIDTH);
-
- gdk_draw_line (drawable,
- fg_gc,
- x, y + 1, x + width, y + 1);
-
- vals.line_width = 0;
- gdk_gc_set_values (fg_gc,
- &vals,
- GDK_GC_LINE_WIDTH);
-}
-
-static void
-draw_control (MetaFrames *frames,
- GdkDrawable *drawable,
- GdkGC *fg_override,
- GdkGC *bg_override,
- MetaFrameControl control,
- int x, int y, int width, int height)
-{
- GtkWidget *widget;
- GdkGCValues vals;
- GdkGC *fg_gc;
- GdkGC *bg_gc;
-
- widget = GTK_WIDGET (frames);
-
- fg_gc = fg_override ? fg_override : widget->style->fg_gc[GTK_STATE_NORMAL];
- bg_gc = bg_override ? bg_override : widget->style->bg_gc[GTK_STATE_NORMAL];
-
- switch (control)
- {
- case META_FRAME_CONTROL_DELETE:
- {
- gdk_draw_line (drawable,
- fg_gc,
- x, y, x + width - 1, y + height - 1);
-
- gdk_draw_line (drawable,
- fg_gc,
- x, y + height - 1, x + width - 1, y);
- }
- break;
-
- case META_FRAME_CONTROL_MAXIMIZE:
- {
- draw_mini_window (frames, drawable, fg_gc, bg_gc, FALSE,
- x, y, width, height);
- }
- break;
-
- case META_FRAME_CONTROL_UNMAXIMIZE:
- {
- int w_delta = width * 0.3;
- int h_delta = height * 0.3;
-
- w_delta = MAX (w_delta, 3);
- h_delta = MAX (h_delta, 3);
-
- draw_mini_window (frames, drawable, fg_gc, bg_gc, TRUE,
- x, y, width - w_delta, height - h_delta);
- draw_mini_window (frames, drawable, fg_gc, bg_gc, TRUE,
- x + w_delta, y + h_delta,
- width - w_delta, height - h_delta);
- }
- break;
-
- case META_FRAME_CONTROL_MINIMIZE:
- {
-
- vals.line_width = THICK_LINE_WIDTH;
- gdk_gc_set_values (fg_gc,
- &vals,
- GDK_GC_LINE_WIDTH);
-
- gdk_draw_line (drawable,
- fg_gc,
- x, y + height - THICK_LINE_WIDTH + 1,
- x + width, y + height - THICK_LINE_WIDTH + 1);
-
- vals.line_width = 0;
- gdk_gc_set_values (fg_gc,
- &vals,
- GDK_GC_LINE_WIDTH);
- }
- break;
-
- default:
- break;
- }
-}
-#undef THICK_LINE_WIDTH
-
-void
-meta_frames_get_pixmap_for_control (MetaFrames *frames,
- MetaFrameControl control,
- GdkPixmap **pixmapp,
- GdkBitmap **maskp)
-{
- int w, h;
- GdkPixmap *pix;
- GdkBitmap *mask;
- GtkWidget *widget;
- GdkGC *mgc, *mgc_bg;
- GdkColor color;
-
- widget = GTK_WIDGET (frames);
-
- gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &w, &h);
-
- w -= DEFAULT_INNER_BUTTON_BORDER * 2;
- h -= DEFAULT_INNER_BUTTON_BORDER * 2;
-
- /* avoid crashing on bizarre icon sizes */
- if (w < 1)
- w = 1;
- if (h < 1)
- h = 1;
-
- pix = gdk_pixmap_new (NULL, w, h, gtk_widget_get_visual (widget)->depth);
- mask = gdk_pixmap_new (NULL, w, h, 1);
-
- mgc = gdk_gc_new (mask);
- mgc_bg = gdk_gc_new (mask);
-
- color.pixel = 0;
- gdk_gc_set_foreground (mgc_bg, &color);
- color.pixel = 1;
- gdk_gc_set_foreground (mgc, &color);
-
- gdk_draw_rectangle (mask, mgc_bg, TRUE, 0, 0, -1, -1);
-
- draw_control (frames, mask, mgc, mgc_bg, control, 0, 0, w, h);
-
- gdk_gc_unref (mgc);
- gdk_gc_unref (mgc_bg);
-
- draw_control (frames, pix, NULL, NULL, control, 0, 0, w, h);
-
- *pixmapp = pix;
- *maskp = mask;
-}
-
-static void
-draw_control_bg (MetaFrames *frames,
- MetaUIFrame *frame,
- GdkDrawable *drawable,
- MetaFrameControl control,
- MetaFrameGeometry *fgeom)
-{
- GdkRectangle *rect;
- GtkWidget *widget;
- gboolean draw = FALSE;
- Window grab_frame;
-
- widget = GTK_WIDGET (frames);
-
- grab_frame = meta_core_get_grab_frame (gdk_display);
-
- if (frame->xwindow == grab_frame)
- {
- switch (meta_core_get_grab_op (gdk_display))
- {
- case META_GRAB_OP_CLICKING_MENU:
- draw = control == META_FRAME_CONTROL_MENU;
- break;
- case META_GRAB_OP_CLICKING_DELETE:
- draw = control == META_FRAME_CONTROL_DELETE;
- break;
- case META_GRAB_OP_CLICKING_MAXIMIZE:
- draw = control == META_FRAME_CONTROL_MAXIMIZE;
- break;
- case META_GRAB_OP_CLICKING_UNMAXIMIZE:
- draw = control == META_FRAME_CONTROL_UNMAXIMIZE;
- break;
- case META_GRAB_OP_CLICKING_MINIMIZE:
- draw = control == META_FRAME_CONTROL_MINIMIZE;
- break;
- default:
- break;
- }
- }
-
- if (draw)
- {
- rect = control_rect (control, fgeom);
-
- if (rect == NULL)
- return;
-
- gtk_paint_box (widget->style, drawable,
- GTK_STATE_ACTIVE,
- GTK_SHADOW_IN, NULL,
- widget, "button",
- rect->x, rect->y, rect->width, rect->height);
- }
-}
-
static gboolean
meta_frames_expose_event (GtkWidget *widget,
GdkEventExpose *event)
@@ -1518,285 +1179,77 @@ meta_frames_paint_to_drawable (MetaFrames *frames,
GdkRectangle *area)
{
GtkWidget *widget;
- MetaFrameGeometry fgeom;
MetaFrameFlags flags;
- int width, height;
- GtkBorder inner;
-
- widget = GTK_WIDGET (frames);
-
- meta_frames_calc_geometry (frames, frame, &fgeom);
- flags = meta_core_get_frame_flags (gdk_display, frame->xwindow);
- width = fgeom.width;
- height = fgeom.height;
+ MetaFrameType type;
+ GdkPixbuf *mini_icon;
+ GdkPixbuf *icon;
+ int w, h;
+ MetaButtonState button_states[META_BUTTON_TYPE_LAST];
+ Window grab_frame;
+ int i;
- /* Black line around outside to give definition */
- gdk_draw_rectangle (drawable,
- widget->style->black_gc,
- FALSE,
- 0, 0, width - 1, height - 1);
-
- /* Light GC on top/left edges */
- gdk_draw_line (drawable,
- widget->style->light_gc[GTK_STATE_NORMAL],
- 1, 1,
- 1, height - 2);
- gdk_draw_line (drawable,
- widget->style->light_gc[GTK_STATE_NORMAL],
- 1, 1,
- width - 2, 1);
- /* Dark on bottom/right */
- gdk_draw_line (drawable,
- widget->style->dark_gc[GTK_STATE_NORMAL],
- width - 2, 1,
- width - 2, height - 2);
- gdk_draw_line (drawable,
- widget->style->dark_gc[GTK_STATE_NORMAL],
- 1, height - 2,
- width - 2, height - 2);
-
- if (flags & META_FRAME_HAS_FOCUS)
- {
- /* Black line around inside while we have focus */
-
- gdk_draw_rectangle (drawable,
- widget->style->black_gc,
- FALSE,
- fgeom.left_width - 1,
- fgeom.top_height - 1,
- width - fgeom.right_width - fgeom.left_width + 1,
- height - fgeom.bottom_height - fgeom.top_height + 1);
- }
+ widget = GTK_WIDGET (frames);
- if (area->y < fgeom.top_height &&
- fgeom.title_rect.width > 0 && fgeom.title_rect.height > 0)
+ /* note, prelight not implemented yet */
+ i = 0;
+ while (i < META_BUTTON_TYPE_LAST)
{
- GdkRectangle clip;
- GdkGC *layout_gc;
+ button_states[i] = META_BUTTON_STATE_NORMAL;
- clip = fgeom.title_rect;
- clip.x += frames->layout->text_border.left;
- clip.width -= frames->layout->text_border.left +
- frames->layout->text_border.right;
-
- layout_gc = widget->style->fg_gc[GTK_STATE_NORMAL];
- if (flags & META_FRAME_HAS_FOCUS)
- {
- GdkPixbuf *gradient;
- GdkColor selected_faded;
- const GdkColor *bg = &widget->style->bg[GTK_STATE_NORMAL];
-
- /* alpha blend selection color into normal color */
-#define ALPHA 25000
- selected_faded = widget->style->bg[GTK_STATE_SELECTED];
- selected_faded.red = selected_faded.red + (((bg->red - selected_faded.red) * ALPHA + 32768) >> 16);
- selected_faded.green = selected_faded.green + (((bg->green - selected_faded.green) * ALPHA + 32768) >> 16);
- selected_faded.blue = selected_faded.blue + (((bg->blue - selected_faded.blue) * ALPHA + 32768) >> 16);
-
- layout_gc = widget->style->fg_gc[GTK_STATE_SELECTED];
-
- gradient = meta_gradient_create_simple (fgeom.title_rect.width,
- fgeom.title_rect.height,
- &selected_faded,
- &widget->style->bg[GTK_STATE_SELECTED],
- META_GRADIENT_DIAGONAL);
-
- if (gradient != NULL)
- {
- gdk_pixbuf_render_to_drawable (gradient,
- drawable,
- widget->style->bg_gc[GTK_STATE_SELECTED],
- 0, 0,
- fgeom.title_rect.x,
- fgeom.title_rect.y,
- fgeom.title_rect.width,
- fgeom.title_rect.height,
- GDK_RGB_DITHER_MAX,
- 0, 0);
-
- g_object_unref (G_OBJECT (gradient));
- }
- else
- {
- /* Fallback to plain selection color */
- gdk_draw_rectangle (drawable,
- widget->style->bg_gc[GTK_STATE_SELECTED],
- TRUE,
- fgeom.title_rect.x,
- fgeom.title_rect.y,
- fgeom.title_rect.width,
- fgeom.title_rect.height);
- }
- }
-
- if (frame->layout)
- {
- PangoRectangle layout_rect;
- int x, y, icon_x, icon_y;
- GdkPixbuf *icon;
- int icon_w, icon_h;
- int area_w, area_h;
-
-#define ICON_TEXT_SPACING 2
-
- icon = meta_core_get_mini_icon (gdk_display,
- frame->xwindow);
-
- icon_w = gdk_pixbuf_get_width (icon);
- icon_h = gdk_pixbuf_get_height (icon);
-
- pango_layout_get_pixel_extents (frame->layout,
- NULL,
- &layout_rect);
-
- /* corner of whole title area */
- x = fgeom.title_rect.x + frames->layout->text_border.left;
- y = fgeom.title_rect.y + frames->layout->text_border.top;
-
- area_w = fgeom.title_rect.width -
- frames->layout->text_border.left -
- frames->layout->text_border.right;
-
- area_h = fgeom.title_rect.height -
- frames->layout->text_border.top -
- frames->layout->text_border.bottom;
-
- /* center icon vertically */
- icon_y = y + MAX ((area_h - icon_h) / 2, 0);
- /* center text vertically */
- y = y + MAX ((area_h - layout_rect.height) / 2, 0);
-
- /* Center icon + text combo */
- icon_x = x + MAX ((area_w - layout_rect.width - icon_w - ICON_TEXT_SPACING) / 2, 0);
- x = icon_x + icon_w + ICON_TEXT_SPACING;
-
- gdk_gc_set_clip_rectangle (layout_gc, &clip);
-
- {
- /* grumble, render_to_drawable_alpha does not accept a clip
- * mask, so we have to go through some BS
- */
- GdkRectangle pixbuf_rect;
- GdkRectangle draw_rect;
-
- pixbuf_rect.x = icon_x;
- pixbuf_rect.y = icon_y;
- pixbuf_rect.width = icon_w;
- pixbuf_rect.height = icon_h;
-
- if (gdk_rectangle_intersect (&clip, &pixbuf_rect, &draw_rect))
- {
- gdk_pixbuf_render_to_drawable_alpha (icon,
- drawable,
- draw_rect.x - pixbuf_rect.x,
- draw_rect.y - pixbuf_rect.y,
- draw_rect.x, draw_rect.y,
- draw_rect.width,
- draw_rect.height,
- GDK_PIXBUF_ALPHA_FULL,
- 128,
- GDK_RGB_DITHER_NORMAL,
- 0, 0);
- }
- }
-
- gdk_draw_layout (drawable,
- layout_gc,
- x, y,
- frame->layout);
- gdk_gc_set_clip_rectangle (layout_gc, NULL);
- }
+ ++i;
}
-
- inner = frames->layout->inner_button_border;
- if (fgeom.close_rect.width > 0 && fgeom.close_rect.height > 0)
- {
- draw_control_bg (frames, frame, drawable,
- META_FRAME_CONTROL_DELETE, &fgeom);
-
- draw_control (frames, drawable,
- NULL, NULL,
- META_FRAME_CONTROL_DELETE,
- fgeom.close_rect.x + inner.left,
- fgeom.close_rect.y + inner.top,
- fgeom.close_rect.width - inner.right - inner.left,
- fgeom.close_rect.height - inner.bottom - inner.top);
- }
-
- if (fgeom.max_rect.width > 0 && fgeom.max_rect.height > 0)
- {
- MetaFrameControl ctrl;
-
- if (flags & META_FRAME_MAXIMIZED)
- ctrl = META_FRAME_CONTROL_UNMAXIMIZE;
- else
- ctrl = META_FRAME_CONTROL_MAXIMIZE;
-
- draw_control_bg (frames, frame, drawable, ctrl, &fgeom);
-
- draw_control (frames, drawable,
- NULL, NULL,
- ctrl,
- fgeom.max_rect.x + inner.left,
- fgeom.max_rect.y + inner.top,
- fgeom.max_rect.width - inner.left - inner.right,
- fgeom.max_rect.height - inner.top - inner.bottom);
- }
+ grab_frame = meta_core_get_grab_frame (gdk_display);
- if (fgeom.min_rect.width > 0 && fgeom.min_rect.height > 0)
+ if (frame->xwindow == grab_frame)
{
- draw_control_bg (frames, frame, drawable,
- META_FRAME_CONTROL_MINIMIZE, &fgeom);
-
- draw_control (frames, drawable,
- NULL, NULL,
- META_FRAME_CONTROL_MINIMIZE,
- fgeom.min_rect.x + inner.left,
- fgeom.min_rect.y + inner.top,
- fgeom.min_rect.width - inner.left - inner.right,
- fgeom.min_rect.height - inner.top - inner.bottom);
+ switch (meta_core_get_grab_op (gdk_display))
+ {
+ case META_GRAB_OP_CLICKING_MENU:
+ button_states[META_BUTTON_TYPE_MENU] =
+ META_BUTTON_STATE_PRESSED;
+ break;
+ case META_GRAB_OP_CLICKING_DELETE:
+ button_states[META_BUTTON_TYPE_CLOSE] =
+ META_BUTTON_STATE_PRESSED;
+ break;
+ case META_GRAB_OP_CLICKING_MAXIMIZE:
+ button_states[META_BUTTON_TYPE_MAXIMIZE] =
+ META_BUTTON_STATE_PRESSED;
+ break;
+ case META_GRAB_OP_CLICKING_UNMAXIMIZE:
+ button_states[META_BUTTON_TYPE_MAXIMIZE] =
+ META_BUTTON_STATE_PRESSED;
+ break;
+ case META_GRAB_OP_CLICKING_MINIMIZE:
+ button_states[META_BUTTON_TYPE_MINIMIZE] =
+ META_BUTTON_STATE_PRESSED;
+ break;
+ default:
+ break;
+ }
}
- if (fgeom.spacer_rect.width > 0 && fgeom.spacer_rect.height > 0)
- {
- gtk_paint_vline (widget->style,
- drawable,
- GTK_STATE_NORMAL,
- area,
- widget,
- "metacity_frame_spacer",
- fgeom.spacer_rect.y,
- fgeom.spacer_rect.y + fgeom.spacer_rect.height,
- fgeom.spacer_rect.x + fgeom.spacer_rect.width / 2);
- }
+ flags = meta_core_get_frame_flags (gdk_display, frame->xwindow);
+ type = meta_core_get_frame_type (gdk_display, frame->xwindow);
+ mini_icon = meta_core_get_mini_icon (gdk_display, frame->xwindow);
+ icon = meta_core_get_icon (gdk_display, frame->xwindow);
- if (fgeom.menu_rect.width > 0 && fgeom.menu_rect.height > 0)
- {
- int x, y;
-#define ARROW_WIDTH 7
-#define ARROW_HEIGHT 5
-
- draw_control_bg (frames, frame,
- drawable,
- META_FRAME_CONTROL_MENU, &fgeom);
-
- x = fgeom.menu_rect.x;
- y = fgeom.menu_rect.y;
- x += (fgeom.menu_rect.width - ARROW_WIDTH) / 2;
- y += (fgeom.menu_rect.height - ARROW_HEIGHT) / 2;
-
- gtk_paint_arrow (widget->style,
- drawable,
- GTK_STATE_NORMAL,
- GTK_SHADOW_OUT,
- area,
- widget,
- "metacity_menu_button",
- GTK_ARROW_DOWN,
- TRUE,
- x, y, ARROW_WIDTH, ARROW_HEIGHT);
- }
+ meta_core_get_client_size (gdk_display, frame->xwindow,
+ &w, &h);
+
+ meta_theme_draw_frame (meta_theme_get_current (),
+ widget,
+ drawable,
+ area,
+ 0, 0,
+ type,
+ flags,
+ w, h,
+ frame->layout,
+ frames->text_height,
+ button_states,
+ mini_icon, icon);
}
static gboolean
diff --git a/src/frames.h b/src/frames.h
index 99794326..e756ec5b 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 7be76d9b..ee1f9215 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 785a1a86..206c6e4e 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 87d2d8c8..4e30b1cf 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 708274f3..a803c0c8 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 4f6fb4b6..e8aa7b34 100644
--- a/src/metacity.schemas
+++ b/src/metacity.schemas
@@ -23,6 +23,21 @@
</schema>
<schema>
+ <key>/schemas/apps/metacity/general/theme</key>
+ <applyto>/apps/metacity/general/theme</applyto>
+ <owner>metacity</owner>
+ <type>string</type>
+ <default>Atlanta</default>
+ <locale name="C">
+ <short>Current theme</short>
+ <long>
+ The theme determines the appearance of window borders,
+ titlebar, and so forth.
+ </long>
+ </locale>
+ </schema>
+
+ <schema>
<key>/schemas/apps/metacity/general/titlebar_uses_desktop_font</key>
<applyto>/apps/metacity/general/titlebar_uses_desktop_font</applyto>
<owner>metacity</owner>
diff --git a/src/prefs.c b/src/prefs.c
index 79a2d1a0..a5fe823e 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 3abdd36c..617d7307 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 01e4b8fd..719d7533 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 e9672a85..c78a62b4 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 08c4ac7f..ddd7553f 100644
--- a/src/tabpopup.c
+++ b/src/tabpopup.c
@@ -22,8 +22,8 @@
#include "util.h"
#include "core.h"
#include "tabpopup.h"
-#include <math.h>
#include <gtk/gtk.h>
+#include <math.h>
#define OUTSIDE_SELECT_RECT 2
#define INSIDE_SELECT_RECT 2
diff --git a/src/theme-parser.c b/src/theme-parser.c
new file mode 100644
index 00000000..dfeed94a
--- /dev/null
+++ b/src/theme-parser.c
@@ -0,0 +1,3920 @@
+/* Metacity theme parsing */
+
+/*
+ * Copyright (C) 2001 Havoc Pennington
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <config.h>
+#include "theme-parser.h"
+#include "util.h"
+#include <string.h>
+#include <stdlib.h>
+
+typedef enum
+{
+ STATE_START,
+ STATE_THEME,
+ /* info section */
+ STATE_INFO,
+ STATE_NAME,
+ STATE_AUTHOR,
+ STATE_COPYRIGHT,
+ STATE_DATE,
+ STATE_DESCRIPTION,
+ /* constants */
+ STATE_CONSTANT,
+ /* geometry */
+ STATE_FRAME_GEOMETRY,
+ STATE_DISTANCE,
+ STATE_BORDER,
+ /* draw ops */
+ STATE_DRAW_OPS,
+ STATE_LINE,
+ STATE_RECTANGLE,
+ STATE_ARC,
+ STATE_CLIP,
+ STATE_TINT,
+ STATE_GRADIENT,
+ STATE_IMAGE,
+ STATE_GTK_ARROW,
+ STATE_GTK_BOX,
+ STATE_GTK_VLINE,
+ STATE_ICON,
+ STATE_TITLE,
+ STATE_INCLUDE, /* include another draw op list */
+ /* sub-parts of gradient */
+ STATE_COLOR,
+ /* frame style */
+ STATE_FRAME_STYLE,
+ STATE_PIECE,
+ STATE_BUTTON,
+ /* style set */
+ STATE_FRAME_STYLE_SET,
+ STATE_FRAME,
+ /* assigning style sets to windows */
+ STATE_WINDOW,
+ /* and menu icons */
+ STATE_MENU_ICON
+} ParseState;
+
+typedef struct
+{
+ GSList *states;
+
+ const char *theme_name; /* name of theme (directory it's in) */
+ char *theme_file; /* theme filename */
+ char *theme_dir; /* dir the theme is inside */
+ MetaTheme *theme; /* theme being parsed */
+ char *name; /* name of named thing being parsed */
+ MetaFrameLayout *layout; /* layout being parsed if any */
+ MetaDrawOpList *op_list; /* op list being parsed if any */
+ MetaDrawOp *op; /* op being parsed if any */
+ MetaFrameStyle *style; /* frame style being parsed if any */
+ MetaFrameStyleSet *style_set; /* frame style set being parsed if any */
+ MetaFramePiece piece; /* position of piece being parsed */
+ MetaButtonType button_type; /* type of button/menuitem being parsed */
+ MetaButtonState button_state; /* state of button being parsed */
+ MetaMenuIconType menu_icon_type; /* type of menu icon being parsed */
+ GtkStateType menu_icon_state; /* state of menu icon being parsed */
+} ParseInfo;
+
+static void set_error (GError **err,
+ GMarkupParseContext *context,
+ int error_domain,
+ int error_code,
+ const char *format,
+ ...) G_GNUC_PRINTF (5, 6);
+
+static void add_context_to_error (GError **err,
+ GMarkupParseContext *context);
+
+static void parse_info_init (ParseInfo *info);
+static void parse_info_free (ParseInfo *info);
+
+static void push_state (ParseInfo *info,
+ ParseState state);
+static void pop_state (ParseInfo *info);
+static ParseState peek_state (ParseInfo *info);
+
+
+static void parse_toplevel_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ ParseInfo *info,
+ GError **error);
+static void parse_info_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ ParseInfo *info,
+ GError **error);
+static void parse_geometry_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ ParseInfo *info,
+ GError **error);
+static void parse_draw_op_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ ParseInfo *info,
+ GError **error);
+static void parse_gradient_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ ParseInfo *info,
+ GError **error);
+static void parse_style_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ ParseInfo *info,
+ GError **error);
+static void parse_style_set_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ ParseInfo *info,
+ GError **error);
+
+static void parse_piece_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ ParseInfo *info,
+ GError **error);
+
+static void parse_button_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ ParseInfo *info,
+ GError **error);
+
+static void parse_menu_icon_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ ParseInfo *info,
+ GError **error);
+
+static void start_element_handler (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ gpointer user_data,
+ GError **error);
+static void end_element_handler (GMarkupParseContext *context,
+ const gchar *element_name,
+ gpointer user_data,
+ GError **error);
+static void text_handler (GMarkupParseContext *context,
+ const gchar *text,
+ gsize text_len,
+ gpointer user_data,
+ GError **error);
+
+static GMarkupParser metacity_theme_parser = {
+ start_element_handler,
+ end_element_handler,
+ text_handler,
+ NULL,
+ NULL
+};
+
+static void
+set_error (GError **err,
+ GMarkupParseContext *context,
+ int error_domain,
+ int error_code,
+ const char *format,
+ ...)
+{
+ int line, ch;
+ va_list args;
+ char *str;
+
+ g_markup_parse_context_get_position (context, &line, &ch);
+
+ va_start (args, format);
+ str = g_strdup_vprintf (format, args);
+ va_end (args);
+
+ g_set_error (err, error_domain, error_code,
+ _("Line %d character %d: %s"),
+ line, ch, str);
+
+ g_free (str);
+}
+
+static void
+add_context_to_error (GError **err,
+ GMarkupParseContext *context)
+{
+ int line, ch;
+ char *str;
+
+ if (err == NULL || *err == NULL)
+ return;
+
+ g_markup_parse_context_get_position (context, &line, &ch);
+
+ str = g_strdup_printf (_("Line %d character %d: %s"),
+ line, ch, (*err)->message);
+ g_free ((*err)->message);
+ (*err)->message = str;
+}
+
+static void
+parse_info_init (ParseInfo *info)
+{
+ info->theme_file = NULL;
+ info->states = g_slist_prepend (NULL, GINT_TO_POINTER (STATE_START));
+ info->theme = NULL;
+ info->name = NULL;
+ info->layout = NULL;
+ info->op_list = NULL;
+ info->op = NULL;
+ info->style = NULL;
+ info->style_set = NULL;
+ info->piece = META_FRAME_PIECE_LAST;
+ info->button_type = META_BUTTON_TYPE_LAST;
+ info->button_state = META_BUTTON_STATE_LAST;
+}
+
+static void
+parse_info_free (ParseInfo *info)
+{
+ g_free (info->theme_file);
+ g_free (info->theme_dir);
+
+ g_slist_free (info->states);
+
+ if (info->theme)
+ meta_theme_free (info->theme);
+
+ if (info->layout)
+ meta_frame_layout_unref (info->layout);
+
+ if (info->op_list)
+ meta_draw_op_list_unref (info->op_list);
+
+ if (info->op)
+ meta_draw_op_free (info->op);
+
+ if (info->style)
+ meta_frame_style_unref (info->style);
+
+ if (info->style_set)
+ meta_frame_style_set_unref (info->style_set);
+}
+
+static void
+push_state (ParseInfo *info,
+ ParseState state)
+{
+ info->states = g_slist_prepend (info->states, GINT_TO_POINTER (state));
+}
+
+static void
+pop_state (ParseInfo *info)
+{
+ g_return_if_fail (info->states != NULL);
+
+ info->states = g_slist_remove (info->states, info->states->data);
+}
+
+static ParseState
+peek_state (ParseInfo *info)
+{
+ g_return_val_if_fail (info->states != NULL, STATE_START);
+
+ return GPOINTER_TO_INT (info->states->data);
+}
+
+#define ELEMENT_IS(name) (strcmp (element_name, (name)) == 0)
+
+typedef struct
+{
+ const char *name;
+ const char **retloc;
+} LocateAttr;
+
+static gboolean
+locate_attributes (GMarkupParseContext *context,
+ const char *element_name,
+ const char **attribute_names,
+ const char **attribute_values,
+ GError **error,
+ const char *first_attribute_name,
+ const char **first_attribute_retloc,
+ ...)
+{
+ va_list args;
+ const char *name;
+ const char **retloc;
+ int n_attrs;
+#define MAX_ATTRS 24
+ LocateAttr attrs[MAX_ATTRS];
+ gboolean retval;
+ int i;
+
+ g_return_val_if_fail (first_attribute_name != NULL, FALSE);
+ g_return_val_if_fail (first_attribute_retloc != NULL, FALSE);
+
+ retval = TRUE;
+
+ n_attrs = 1;
+ attrs[0].name = first_attribute_name;
+ attrs[0].retloc = first_attribute_retloc;
+ *first_attribute_retloc = NULL;
+
+ va_start (args, first_attribute_retloc);
+
+ name = va_arg (args, const char*);
+ retloc = va_arg (args, const char**);
+
+ while (name != NULL)
+ {
+ g_return_val_if_fail (retloc != NULL, FALSE);
+
+ if (n_attrs == MAX_ATTRS)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Element <%s> has more than %d attributes, can't possibly be valid"),
+ element_name, MAX_ATTRS);
+ retval = FALSE;
+ goto out;
+ }
+
+ attrs[n_attrs].name = name;
+ attrs[n_attrs].retloc = retloc;
+ n_attrs += 1;
+ *retloc = NULL;
+
+ name = va_arg (args, const char*);
+ retloc = va_arg (args, const char**);
+ }
+
+ out:
+ va_end (args);
+
+ if (!retval)
+ return retval;
+
+ i = 0;
+ while (attribute_names[i])
+ {
+ int j;
+ gboolean found;
+
+ found = FALSE;
+ j = 0;
+ while (j < n_attrs)
+ {
+ if (strcmp (attrs[j].name, attribute_names[i]) == 0)
+ {
+ retloc = attrs[j].retloc;
+
+ if (*retloc != NULL)
+ {
+ set_error (error, context,
+ G_MARKUP_ERROR,
+ G_MARKUP_ERROR_PARSE,
+ _("Attribute \"%s\" repeated twice on the same <%s> element"),
+ attrs[j].name, element_name);
+ retval = FALSE;
+ goto out2;
+ }
+
+ *retloc = attribute_values[i];
+ found = TRUE;
+ }
+
+ ++j;
+ }
+
+ if (!found)
+ {
+ set_error (error, context,
+ G_MARKUP_ERROR,
+ G_MARKUP_ERROR_PARSE,
+ _("Attribute \"%s\" is invalid on <%s> element in this context"),
+ attribute_names[i], element_name);
+ retval = FALSE;
+ goto out2;
+ }
+
+ ++i;
+ }
+
+ out2:
+ return retval;
+}
+
+static gboolean
+check_no_attributes (GMarkupParseContext *context,
+ const char *element_name,
+ const char **attribute_names,
+ const char **attribute_values,
+ GError **error)
+{
+ if (attribute_names[0] != NULL)
+ {
+ set_error (error, context,
+ G_MARKUP_ERROR,
+ G_MARKUP_ERROR_PARSE,
+ _("Attribute \"%s\" is invalid on <%s> element in this context"),
+ attribute_names[0], element_name);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+#define MAX_REASONABLE 4096
+static gboolean
+parse_positive_integer (const char *str,
+ int *val,
+ GMarkupParseContext *context,
+ GError **error)
+{
+ char *end;
+ long l;
+
+ *val = 0;
+
+ end = NULL;
+
+ l = strtol (str, &end, 10);
+
+ if (end == NULL || end == str)
+ {
+ set_error (error, context, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_PARSE,
+ _("Could not parse \"%s\" as an integer"),
+ str);
+ return FALSE;
+ }
+
+ if (*end != '\0')
+ {
+ set_error (error, context, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_PARSE,
+ _("Did not understand trailing characters \"%s\" in string \"%s\""),
+ end, str);
+ return FALSE;
+ }
+
+ if (l < 0)
+ {
+ set_error (error, context, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_PARSE,
+ _("Integer %ld must be positive"), l);
+ return FALSE;
+ }
+
+ if (l > MAX_REASONABLE)
+ {
+ set_error (error, context, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_PARSE,
+ _("Integer %ld is too large, current max is %d"),
+ l, MAX_REASONABLE);
+ return FALSE;
+ }
+
+ *val = (int) l;
+
+ return TRUE;
+}
+
+static gboolean
+parse_double (const char *str,
+ double *val,
+ GMarkupParseContext *context,
+ GError **error)
+{
+ char *end;
+
+ *val = 0;
+
+ end = NULL;
+
+ *val = g_ascii_strtod (str, &end);
+
+ if (end == NULL || end == str)
+ {
+ set_error (error, context, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_PARSE,
+ _("Could not parse \"%s\" as a floating point number"),
+ str);
+ return FALSE;
+ }
+
+ if (*end != '\0')
+ {
+ set_error (error, context, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_PARSE,
+ _("Did not understand trailing characters \"%s\" in string \"%s\""),
+ end, str);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+parse_angle (const char *str,
+ double *val,
+ GMarkupParseContext *context,
+ GError **error)
+{
+ if (!parse_double (str, val, context, error))
+ return FALSE;
+
+ if (*val < (0.0 - 1e6) || *val > (360.0 + 1e6))
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Angle must be between 0.0 and 360.0, was %g\n"),
+ *val);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+parse_alpha (const char *str,
+ double *val,
+ GMarkupParseContext *context,
+ GError **error)
+{
+ if (!parse_double (str, val, context, error))
+ return FALSE;
+
+ if (*val < (0.0 - 1e6) || *val > (1.0 + 1e6))
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Alpha must be between 0.0 (invisible) and 1.0 (fully opaque), was %g\n"),
+ *val);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+parse_toplevel_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ ParseInfo *info,
+ GError **error)
+{
+ g_return_if_fail (peek_state (info) == STATE_THEME);
+
+ if (ELEMENT_IS ("info"))
+ {
+ if (!check_no_attributes (context, element_name,
+ attribute_names, attribute_values,
+ error))
+ return;
+
+ push_state (info, STATE_INFO);
+ }
+ else if (ELEMENT_IS ("constant"))
+ {
+ const char *name;
+ const char *value;
+ int ival;
+ double dval;
+
+ if (!locate_attributes (context, element_name, attribute_names, attribute_values,
+ error,
+ "name", &name, "value", &value,
+ NULL))
+ return;
+
+ if (name == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"name\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (value == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"value\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (strchr (value, '.'))
+ {
+ dval = 0.0;
+ if (!parse_double (value, &dval, context, error))
+ return;
+
+ if (!meta_theme_define_float_constant (info->theme,
+ name,
+ dval,
+ error))
+ {
+ add_context_to_error (error, context);
+ return;
+ }
+ }
+ else
+ {
+ ival = 0;
+ if (!parse_positive_integer (value, &ival, context, error))
+ return;
+
+ if (!meta_theme_define_int_constant (info->theme,
+ name,
+ ival,
+ error))
+ {
+ add_context_to_error (error, context);
+ return;
+ }
+ }
+
+ push_state (info, STATE_CONSTANT);
+ }
+ else if (ELEMENT_IS ("frame_geometry"))
+ {
+ const char *name = NULL;
+ const char *parent = NULL;
+ MetaFrameLayout *parent_layout;
+
+ if (!locate_attributes (context, element_name, attribute_names, attribute_values,
+ error,
+ "name", &name, "parent", &parent,
+ NULL))
+ return;
+
+ if (name == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"name\" attribute on <%s> element"),
+ element_name);
+ return;
+ }
+
+ if (meta_theme_lookup_layout (info->theme, name))
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("<%s> name \"%s\" used a second time"),
+ element_name, name);
+ return;
+ }
+
+ parent_layout = NULL;
+ if (parent)
+ {
+ parent_layout = meta_theme_lookup_layout (info->theme, parent);
+ if (parent_layout == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("<%s> parent \"%s\" has not been defined"),
+ element_name, parent);
+ return;
+ }
+ }
+
+ g_assert (info->layout == NULL);
+
+ if (parent_layout)
+ info->layout = meta_frame_layout_copy (parent_layout);
+ else
+ info->layout = meta_frame_layout_new ();
+
+ meta_theme_insert_layout (info->theme, name, info->layout);
+
+ push_state (info, STATE_FRAME_GEOMETRY);
+ }
+ else if (ELEMENT_IS ("draw_ops"))
+ {
+ const char *name = NULL;
+
+ if (!locate_attributes (context, element_name, attribute_names, attribute_values,
+ error,
+ "name", &name,
+ NULL))
+ return;
+
+ if (name == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"name\" attribute on <%s> element"),
+ element_name);
+ return;
+ }
+
+ if (meta_theme_lookup_draw_op_list (info->theme, name))
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("<%s> name \"%s\" used a second time"),
+ element_name, name);
+ return;
+ }
+
+ g_assert (info->op_list == NULL);
+ info->op_list = meta_draw_op_list_new (2);
+
+ meta_theme_insert_draw_op_list (info->theme, name, info->op_list);
+
+ push_state (info, STATE_DRAW_OPS);
+ }
+ else if (ELEMENT_IS ("frame_style"))
+ {
+ const char *name = NULL;
+ const char *parent = NULL;
+ const char *geometry = NULL;
+ MetaFrameStyle *parent_style;
+ MetaFrameLayout *layout;
+
+ if (!locate_attributes (context, element_name, attribute_names, attribute_values,
+ error,
+ "name", &name, "parent", &parent,
+ "geometry", &geometry,
+ NULL))
+ return;
+
+ if (name == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"name\" attribute on <%s> element"),
+ element_name);
+ return;
+ }
+
+ if (meta_theme_lookup_style (info->theme, name))
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("<%s> name \"%s\" used a second time"),
+ element_name, name);
+ return;
+ }
+
+ parent_style = NULL;
+ if (parent)
+ {
+ parent_style = meta_theme_lookup_style (info->theme, parent);
+ if (parent_style == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("<%s> parent \"%s\" has not been defined"),
+ element_name, parent);
+ return;
+ }
+ }
+
+ layout = NULL;
+ if (geometry)
+ {
+ layout = meta_theme_lookup_layout (info->theme, geometry);
+ if (layout == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("<%s> geometry \"%s\" has not been defined"),
+ element_name, geometry);
+ return;
+ }
+ }
+ else if (parent_style)
+ {
+ layout = parent_style->layout;
+ }
+
+ if (layout == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("<%s> must specify either a geometry or a parent that has a geometry"),
+ element_name);
+ return;
+ }
+
+ g_assert (info->style == NULL);
+
+ info->style = meta_frame_style_new (parent_style);
+ g_assert (info->style->layout == NULL);
+ meta_frame_layout_ref (layout);
+ info->style->layout = layout;
+
+ meta_theme_insert_style (info->theme, name, info->style);
+
+ push_state (info, STATE_FRAME_STYLE);
+ }
+ else if (ELEMENT_IS ("frame_style_set"))
+ {
+ const char *name = NULL;
+ const char *parent = NULL;
+ MetaFrameStyleSet *parent_set;
+
+ if (!locate_attributes (context, element_name, attribute_names, attribute_values,
+ error,
+ "name", &name, "parent", &parent,
+ NULL))
+ return;
+
+ if (name == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"name\" attribute on <%s> element"),
+ element_name);
+ return;
+ }
+
+ if (meta_theme_lookup_style_set (info->theme, name))
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("<%s> name \"%s\" used a second time"),
+ element_name, name);
+ return;
+ }
+
+ parent_set = NULL;
+ if (parent)
+ {
+ parent_set = meta_theme_lookup_style_set (info->theme, parent);
+ if (parent_set == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("<%s> parent \"%s\" has not been defined"),
+ element_name, parent);
+ return;
+ }
+ }
+
+ g_assert (info->style_set == NULL);
+
+ info->style_set = meta_frame_style_set_new (parent_set);
+
+ meta_theme_insert_style_set (info->theme, name, info->style_set);
+
+ push_state (info, STATE_FRAME_STYLE_SET);
+ }
+ else if (ELEMENT_IS ("window"))
+ {
+ const char *type_name = NULL;
+ const char *style_set_name = NULL;
+ MetaFrameStyleSet *style_set;
+ MetaFrameType type;
+
+ if (!locate_attributes (context, element_name, attribute_names, attribute_values,
+ error,
+ "type", &type_name, "style_set", &style_set_name,
+ NULL))
+ return;
+
+ if (type_name == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"type\" attribute on <%s> element"),
+ element_name);
+ return;
+ }
+
+ if (style_set_name == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"style_set\" attribute on <%s> element"),
+ element_name);
+ return;
+ }
+
+ type = meta_frame_type_from_string (type_name);
+
+ if (type == META_FRAME_TYPE_LAST)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Unknown type \"%s\" on <%s> element"),
+ type_name, element_name);
+ return;
+ }
+
+ style_set = meta_theme_lookup_style_set (info->theme,
+ style_set_name);
+
+ if (style_set == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Unknown style_set \"%s\" on <%s> element"),
+ style_set_name, element_name);
+ return;
+ }
+
+ if (info->theme->style_sets_by_type[type] != NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Window type \"%s\" has already been assigned a style set"),
+ type_name);
+ return;
+ }
+
+ meta_frame_style_set_ref (style_set);
+ info->theme->style_sets_by_type[type] = style_set;
+
+ push_state (info, STATE_WINDOW);
+ }
+ else if (ELEMENT_IS ("menu_icon"))
+ {
+ const char *function = NULL;
+ const char *state = NULL;
+ const char *draw_ops = NULL;
+
+ if (!locate_attributes (context, element_name, attribute_names, attribute_values,
+ error,
+ "function", &function,
+ "state", &state,
+ "draw_ops", &draw_ops,
+ NULL))
+ return;
+
+ if (function == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"function\" attribute on <%s> element"),
+ element_name);
+ return;
+ }
+
+ if (state == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"state\" attribute on <%s> element"),
+ element_name);
+ return;
+ }
+
+ info->menu_icon_type = meta_menu_icon_type_from_string (function);
+ if (info->menu_icon_type == META_BUTTON_TYPE_LAST)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Unknown function \"%s\" for menu icon"),
+ function);
+ return;
+ }
+
+ info->menu_icon_state = meta_gtk_state_from_string (state);
+ if (((int) info->menu_icon_state) == -1)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Unknown state \"%s\" for menu icon"),
+ state);
+ return;
+ }
+
+ if (info->theme->menu_icons[info->menu_icon_type][info->menu_icon_state] != NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Theme already has a menu icon for function %s state %s"),
+ function, state);
+ return;
+ }
+
+ g_assert (info->op_list == NULL);
+
+ if (draw_ops)
+ {
+ MetaDrawOpList *op_list;
+
+ op_list = meta_theme_lookup_draw_op_list (info->theme,
+ draw_ops);
+
+ if (op_list == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No <draw_ops> with the name \"%s\" has been defined"),
+ draw_ops);
+ return;
+ }
+
+ meta_draw_op_list_ref (op_list);
+ info->op_list = op_list;
+ }
+
+ push_state (info, STATE_MENU_ICON);
+ }
+ else
+ {
+ set_error (error, context,
+ G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Element <%s> is not allowed below <metacity_theme>"),
+ element_name);
+ }
+}
+
+static void
+parse_info_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ ParseInfo *info,
+ GError **error)
+{
+ g_return_if_fail (peek_state (info) == STATE_INFO);
+
+ if (ELEMENT_IS ("name"))
+ {
+ if (!check_no_attributes (context, element_name,
+ attribute_names, attribute_values,
+ error))
+ return;
+
+ push_state (info, STATE_NAME);
+ }
+ else if (ELEMENT_IS ("author"))
+ {
+ if (!check_no_attributes (context, element_name,
+ attribute_names, attribute_values,
+ error))
+ return;
+
+ push_state (info, STATE_AUTHOR);
+ }
+ else if (ELEMENT_IS ("copyright"))
+ {
+ if (!check_no_attributes (context, element_name,
+ attribute_names, attribute_values,
+ error))
+ return;
+
+ push_state (info, STATE_COPYRIGHT);
+ }
+ else if (ELEMENT_IS ("description"))
+ {
+ if (!check_no_attributes (context, element_name,
+ attribute_names, attribute_values,
+ error))
+ return;
+
+ push_state (info, STATE_DESCRIPTION);
+ }
+ else if (ELEMENT_IS ("date"))
+ {
+ if (!check_no_attributes (context, element_name,
+ attribute_names, attribute_values,
+ error))
+ return;
+
+ push_state (info, STATE_DATE);
+ }
+ else
+ {
+ set_error (error, context,
+ G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Element <%s> is not allowed below <info>"),
+ element_name);
+ }
+}
+
+static void
+parse_distance (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ ParseInfo *info,
+ GError **error)
+{
+ const char *name;
+ const char *value;
+ int val;
+
+ if (!locate_attributes (context, element_name, attribute_names, attribute_values,
+ error,
+ "name", &name, "value", &value,
+ NULL))
+ return;
+
+ if (name == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"name\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (value == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"value\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ val = 0;
+ if (!parse_positive_integer (value, &val, context, error))
+ return;
+
+ g_assert (val >= 0); /* yeah, "non-negative" not "positive" get over it */
+ g_assert (info->layout);
+
+ if (strcmp (name, "left_width") == 0)
+ info->layout->left_width = val;
+ else if (strcmp (name, "right_width") == 0)
+ info->layout->right_width = val;
+ else if (strcmp (name, "bottom_height") == 0)
+ info->layout->bottom_height = val;
+ else if (strcmp (name, "title_vertical_pad") == 0)
+ info->layout->title_vertical_pad = val;
+ else if (strcmp (name, "right_titlebar_edge") == 0)
+ info->layout->right_titlebar_edge = val;
+ else if (strcmp (name, "left_titlebar_edge") == 0)
+ info->layout->left_titlebar_edge = val;
+ else if (strcmp (name, "button_width") == 0)
+ info->layout->button_width = val;
+ else if (strcmp (name, "button_height") == 0)
+ info->layout->button_height = val;
+ else
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Distance \"%s\" is unknown"), name);
+ return;
+ }
+}
+
+static void
+parse_border (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ ParseInfo *info,
+ GError **error)
+{
+ const char *name;
+ const char *top;
+ const char *bottom;
+ const char *left;
+ const char *right;
+ int top_val;
+ int bottom_val;
+ int left_val;
+ int right_val;
+ GtkBorder *border;
+
+ if (!locate_attributes (context, element_name, attribute_names, attribute_values,
+ error,
+ "name", &name,
+ "top", &top,
+ "bottom", &bottom,
+ "left", &left,
+ "right", &right,
+ NULL))
+ return;
+
+ if (name == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"name\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (top == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"top\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (bottom == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"bottom\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (left == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"left\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (right == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"right\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ top_val = 0;
+ if (!parse_positive_integer (top, &top_val, context, error))
+ return;
+
+ bottom_val = 0;
+ if (!parse_positive_integer (bottom, &bottom_val, context, error))
+ return;
+
+ left_val = 0;
+ if (!parse_positive_integer (left, &left_val, context, error))
+ return;
+
+ right_val = 0;
+ if (!parse_positive_integer (right, &right_val, context, error))
+ return;
+
+ g_assert (info->layout);
+
+ border = NULL;
+
+ if (strcmp (name, "title_border") == 0)
+ border = &info->layout->title_border;
+ else if (strcmp (name, "button_border") == 0)
+ border = &info->layout->button_border;
+
+ if (border == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Border \"%s\" is unknown"), name);
+ return;
+ }
+
+ border->top = top_val;
+ border->bottom = bottom_val;
+ border->left = left_val;
+ border->right = right_val;
+}
+
+static void
+parse_geometry_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ ParseInfo *info,
+ GError **error)
+{
+ g_return_if_fail (peek_state (info) == STATE_FRAME_GEOMETRY);
+
+ if (ELEMENT_IS ("distance"))
+ {
+ parse_distance (context, element_name,
+ attribute_names, attribute_values,
+ info, error);
+ push_state (info, STATE_DISTANCE);
+ }
+ else if (ELEMENT_IS ("border"))
+ {
+ parse_border (context, element_name,
+ attribute_names, attribute_values,
+ info, error);
+ push_state (info, STATE_BORDER);
+ }
+ else
+ {
+ set_error (error, context,
+ G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Element <%s> is not allowed below <frame_geometry>"),
+ element_name);
+ }
+}
+
+static gboolean
+check_expression (const char *expr,
+ gboolean has_object,
+ MetaTheme *theme,
+ GMarkupParseContext *context,
+ GError **error)
+{
+ MetaPositionExprEnv env;
+ int x, y;
+
+ /* We set it all to 0 to try and catch divide-by-zero screwups.
+ * it's possible we should instead guarantee that widths and heights
+ * are at least 1.
+ */
+
+ env.x = 0;
+ env.y = 0;
+ env.width = 0;
+ env.height = 0;
+ if (has_object)
+ {
+ env.object_width = 0;
+ env.object_height = 0;
+ }
+ else
+ {
+ env.object_width = -1;
+ env.object_height = -1;
+ }
+
+ env.left_width = 0;
+ env.right_width = 0;
+ env.top_height = 0;
+ env.bottom_height = 0;
+ env.title_width = 0;
+ env.title_height = 0;
+
+ env.icon_width = 0;
+ env.icon_height = 0;
+ env.mini_icon_width = 0;
+ env.mini_icon_height = 0;
+ env.theme = theme;
+
+ if (!meta_parse_position_expression (expr,
+ &env,
+ &x, &y,
+ error))
+ {
+ add_context_to_error (error, context);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static char*
+optimize_expression (MetaTheme *theme,
+ const char *expr)
+{
+ /* We aren't expecting an error here, since we already
+ * did check_expression
+ */
+ return meta_theme_replace_constants (theme, expr, NULL);
+}
+
+static gboolean
+parse_boolean (const char *str,
+ gboolean *val,
+ GMarkupParseContext *context,
+ GError **error)
+{
+ if (strcmp ("true", str) == 0)
+ *val = TRUE;
+ else if (strcmp ("false", str) == 0)
+ *val = FALSE;
+ else
+ {
+ set_error (error, context, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_PARSE,
+ _("Boolean values must be \"true\" or \"false\" not \"%s\""),
+ str);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+parse_draw_op_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ ParseInfo *info,
+ GError **error)
+{
+ g_return_if_fail (peek_state (info) == STATE_DRAW_OPS);
+
+ if (ELEMENT_IS ("line"))
+ {
+ MetaDrawOp *op;
+ const char *color;
+ const char *x1;
+ const char *y1;
+ const char *x2;
+ const char *y2;
+ const char *dash_on_length;
+ const char *dash_off_length;
+ const char *width;
+ MetaColorSpec *color_spec;
+ int dash_on_val;
+ int dash_off_val;
+ int width_val;
+
+ if (!locate_attributes (context, element_name, attribute_names, attribute_values,
+ error,
+ "color", &color,
+ "x1", &x1, "y1", &y1,
+ "x2", &x2, "y2", &y2,
+ "dash_on_length", &dash_on_length,
+ "dash_off_length", &dash_off_length,
+ "width", &width,
+ NULL))
+ return;
+
+ if (color == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"color\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (x1 == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"x1\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (y1 == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"y1\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (x2 == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"x2\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (y2 == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"y2\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (!check_expression (x1, FALSE, info->theme, context, error))
+ return;
+
+ if (!check_expression (y1, FALSE, info->theme, context, error))
+ return;
+
+ if (!check_expression (x2, FALSE, info->theme, context, error))
+ return;
+
+ if (!check_expression (y2, FALSE, info->theme, context, error))
+ return;
+
+ dash_on_val = 0;
+ if (dash_on_length &&
+ !parse_positive_integer (dash_on_length, &dash_on_val, context, error))
+ return;
+
+ dash_off_val = 0;
+ if (dash_off_length &&
+ !parse_positive_integer (dash_off_length, &dash_off_val, context, error))
+ return;
+
+ width_val = 0;
+ if (width &&
+ !parse_positive_integer (width, &width_val, context, error))
+ return;
+
+ /* Check last so we don't have to free it when other
+ * stuff fails
+ */
+ color_spec = meta_color_spec_new_from_string (color, error);
+ if (color_spec == NULL)
+ {
+ add_context_to_error (error, context);
+ return;
+ }
+
+ op = meta_draw_op_new (META_DRAW_LINE);
+
+ op->data.line.color_spec = color_spec;
+ op->data.line.x1 = optimize_expression (info->theme, x1);
+ op->data.line.y1 = optimize_expression (info->theme, y1);
+ op->data.line.x2 = optimize_expression (info->theme, x2);
+ op->data.line.y2 = optimize_expression (info->theme, y2);
+ op->data.line.width = width_val;
+ op->data.line.dash_on_length = dash_on_val;
+ op->data.line.dash_off_length = dash_off_val;
+
+ g_assert (info->op_list);
+
+ meta_draw_op_list_append (info->op_list, op);
+
+ push_state (info, STATE_LINE);
+ }
+ else if (ELEMENT_IS ("rectangle"))
+ {
+ MetaDrawOp *op;
+ const char *color;
+ const char *x;
+ const char *y;
+ const char *width;
+ const char *height;
+ const char *filled;
+ gboolean filled_val;
+ MetaColorSpec *color_spec;
+
+ if (!locate_attributes (context, element_name, attribute_names, attribute_values,
+ error,
+ "color", &color,
+ "x", &x, "y", &y,
+ "width", &width, "height", &height,
+ "filled", &filled,
+ NULL))
+ return;
+
+ if (color == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"color\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (x == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"x\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (y == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"y\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (width == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"width\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (height == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"height\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (!check_expression (x, FALSE, info->theme, context, error))
+ return;
+
+ if (!check_expression (y, FALSE, info->theme, context, error))
+ return;
+
+ if (!check_expression (width, FALSE, info->theme, context, error))
+ return;
+
+ if (!check_expression (height, FALSE, info->theme, context, error))
+ return;
+
+ filled_val = FALSE;
+ if (filled && !parse_boolean (filled, &filled_val, context, error))
+ return;
+
+ /* Check last so we don't have to free it when other
+ * stuff fails
+ */
+ color_spec = meta_color_spec_new_from_string (color, error);
+ if (color_spec == NULL)
+ {
+ add_context_to_error (error, context);
+ return;
+ }
+
+ op = meta_draw_op_new (META_DRAW_RECTANGLE);
+
+ op->data.rectangle.color_spec = color_spec;
+ op->data.rectangle.x = optimize_expression (info->theme, x);
+ op->data.rectangle.y = optimize_expression (info->theme, y);
+ op->data.rectangle.width = optimize_expression (info->theme, width);
+ op->data.rectangle.height = optimize_expression (info->theme, height);
+ op->data.rectangle.filled = filled_val;
+
+ g_assert (info->op_list);
+
+ meta_draw_op_list_append (info->op_list, op);
+
+ push_state (info, STATE_RECTANGLE);
+ }
+ else if (ELEMENT_IS ("arc"))
+ {
+ MetaDrawOp *op;
+ const char *color;
+ const char *x;
+ const char *y;
+ const char *width;
+ const char *height;
+ const char *filled;
+ const char *start_angle;
+ const char *extent_angle;
+ gboolean filled_val;
+ double start_angle_val;
+ double extent_angle_val;
+ MetaColorSpec *color_spec;
+
+ if (!locate_attributes (context, element_name, attribute_names, attribute_values,
+ error,
+ "color", &color,
+ "x", &x, "y", &y,
+ "width", &width, "height", &height,
+ "filled", &filled,
+ "start_angle", &start_angle,
+ "extent_angle", &extent_angle,
+ NULL))
+ return;
+
+ if (color == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"color\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (x == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"x\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (y == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"y\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (width == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"width\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (height == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"height\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (start_angle == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"start_angle\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (extent_angle == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"extent_angle\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (!check_expression (x, FALSE, info->theme, context, error))
+ return;
+
+ if (!check_expression (y, FALSE, info->theme, context, error))
+ return;
+
+ if (!check_expression (width, FALSE, info->theme, context, error))
+ return;
+
+ if (!check_expression (height, FALSE, info->theme, context, error))
+ return;
+
+ if (!parse_angle (start_angle, &start_angle_val, context, error))
+ return;
+
+ if (!parse_angle (extent_angle, &extent_angle_val, context, error))
+ return;
+
+ filled_val = FALSE;
+ if (filled && !parse_boolean (filled, &filled_val, context, error))
+ return;
+
+ /* Check last so we don't have to free it when other
+ * stuff fails
+ */
+ color_spec = meta_color_spec_new_from_string (color, error);
+ if (color_spec == NULL)
+ {
+ add_context_to_error (error, context);
+ return;
+ }
+
+ op = meta_draw_op_new (META_DRAW_ARC);
+
+ op->data.arc.color_spec = color_spec;
+ op->data.arc.x = optimize_expression (info->theme, x);
+ op->data.arc.y = optimize_expression (info->theme, y);
+ op->data.arc.width = optimize_expression (info->theme, width);
+ op->data.arc.height = optimize_expression (info->theme, height);
+ op->data.arc.filled = filled_val;
+ op->data.arc.start_angle = start_angle_val;
+ op->data.arc.extent_angle = extent_angle_val;
+
+ g_assert (info->op_list);
+
+ meta_draw_op_list_append (info->op_list, op);
+
+ push_state (info, STATE_ARC);
+ }
+ else if (ELEMENT_IS ("clip"))
+ {
+ MetaDrawOp *op;
+ const char *x;
+ const char *y;
+ const char *width;
+ const char *height;
+
+ if (!locate_attributes (context, element_name, attribute_names, attribute_values,
+ error,
+ "x", &x, "y", &y,
+ "width", &width, "height", &height,
+ NULL))
+ return;
+
+ if (x == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"x\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (y == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"y\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (width == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"width\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (height == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"height\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (!check_expression (x, FALSE, info->theme, context, error))
+ return;
+
+ if (!check_expression (y, FALSE, info->theme, context, error))
+ return;
+
+ if (!check_expression (width, FALSE, info->theme, context, error))
+ return;
+
+ if (!check_expression (height, FALSE, info->theme, context, error))
+ return;
+
+ op = meta_draw_op_new (META_DRAW_CLIP);
+
+ op->data.clip.x = optimize_expression (info->theme, x);
+ op->data.clip.y = optimize_expression (info->theme, y);
+ op->data.clip.width = optimize_expression (info->theme, width);
+ op->data.clip.height = optimize_expression (info->theme, height);
+
+ g_assert (info->op_list);
+
+ meta_draw_op_list_append (info->op_list, op);
+
+ push_state (info, STATE_CLIP);
+ }
+ else if (ELEMENT_IS ("tint"))
+ {
+ MetaDrawOp *op;
+ const char *color;
+ const char *x;
+ const char *y;
+ const char *width;
+ const char *height;
+ const char *alpha;
+ double alpha_val;
+ MetaColorSpec *color_spec;
+
+ if (!locate_attributes (context, element_name, attribute_names, attribute_values,
+ error,
+ "color", &color,
+ "x", &x, "y", &y,
+ "width", &width, "height", &height,
+ "alpha", &alpha,
+ NULL))
+ return;
+
+ if (color == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"color\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (x == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"x\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (y == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"y\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (width == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"width\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (height == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"height\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (alpha == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"alpha\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (!check_expression (x, FALSE, info->theme, context, error))
+ return;
+
+ if (!check_expression (y, FALSE, info->theme, context, error))
+ return;
+
+ if (!check_expression (width, FALSE, info->theme, context, error))
+ return;
+
+ if (!check_expression (height, FALSE, info->theme, context, error))
+ return;
+
+ if (!parse_alpha (alpha, &alpha_val, context, error))
+ return;
+
+ /* Check last so we don't have to free it when other
+ * stuff fails
+ */
+ color_spec = meta_color_spec_new_from_string (color, error);
+ if (color_spec == NULL)
+ {
+ add_context_to_error (error, context);
+ return;
+ }
+
+ op = meta_draw_op_new (META_DRAW_TINT);
+
+ op->data.tint.color_spec = color_spec;
+ op->data.tint.x = optimize_expression (info->theme, x);
+ op->data.tint.y = optimize_expression (info->theme, y);
+ op->data.tint.width = optimize_expression (info->theme, width);
+ op->data.tint.height = optimize_expression (info->theme, height);
+ op->data.tint.alpha = alpha_val;
+
+ g_assert (info->op_list);
+
+ meta_draw_op_list_append (info->op_list, op);
+
+ push_state (info, STATE_TINT);
+ }
+ else if (ELEMENT_IS ("gradient"))
+ {
+ const char *x;
+ const char *y;
+ const char *width;
+ const char *height;
+ const char *type;
+ const char *alpha;
+ double alpha_val;
+ MetaGradientType type_val;
+
+ if (!locate_attributes (context, element_name, attribute_names, attribute_values,
+ error,
+ "type", &type,
+ "x", &x, "y", &y,
+ "width", &width, "height", &height,
+ "alpha", &alpha,
+ NULL))
+ return;
+
+ if (type == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"type\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (x == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"x\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (y == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"y\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (width == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"width\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (height == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"height\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (!check_expression (x, FALSE, info->theme, context, error))
+ return;
+
+ if (!check_expression (y, FALSE, info->theme, context, error))
+ return;
+
+ if (!check_expression (width, FALSE, info->theme, context, error))
+ return;
+
+ if (!check_expression (height, FALSE, info->theme, context, error))
+ return;
+
+ alpha_val = 1.0;
+ if (alpha && !parse_alpha (alpha, &alpha_val, context, error))
+ return;
+
+ type_val = meta_gradient_type_from_string (type);
+ if (type_val == META_GRADIENT_LAST)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Did not understand value \"%s\" for type of gradient"),
+ type);
+ return;
+ }
+
+ g_assert (info->op == NULL);
+ info->op = meta_draw_op_new (META_DRAW_GRADIENT);
+
+ info->op->data.gradient.x = optimize_expression (info->theme, x);
+ info->op->data.gradient.y = optimize_expression (info->theme, y);
+ info->op->data.gradient.width = optimize_expression (info->theme, width);
+ info->op->data.gradient.height = optimize_expression (info->theme, height);
+
+ info->op->data.gradient.gradient_spec =
+ meta_gradient_spec_new (type_val);
+
+ info->op->data.gradient.alpha = alpha_val;
+
+ push_state (info, STATE_GRADIENT);
+
+ /* op gets appended on close tag */
+ }
+ else if (ELEMENT_IS ("image"))
+ {
+ MetaDrawOp *op;
+ const char *filename;
+ const char *x;
+ const char *y;
+ const char *width;
+ const char *height;
+ const char *alpha;
+ double alpha_val;
+ GdkPixbuf *pixbuf;
+
+ if (!locate_attributes (context, element_name, attribute_names, attribute_values,
+ error,
+ "x", &x, "y", &y,
+ "width", &width, "height", &height,
+ "alpha", &alpha, "filename", &filename,
+ NULL))
+ return;
+
+ if (x == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"x\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (y == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"y\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (width == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"width\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (height == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"height\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (filename == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"filename\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (!check_expression (x, TRUE, info->theme, context, error))
+ return;
+
+ if (!check_expression (y, TRUE, info->theme, context, error))
+ return;
+
+ if (!check_expression (width, TRUE, info->theme, context, error))
+ return;
+
+ if (!check_expression (height, TRUE, info->theme, context, error))
+ return;
+
+ alpha_val = 1.0;
+ if (alpha && !parse_alpha (alpha, &alpha_val, context, error))
+ return;
+
+ /* Check last so we don't have to free it when other
+ * stuff fails
+ */
+ pixbuf = meta_theme_load_image (info->theme, filename, error);
+
+ if (pixbuf == NULL)
+ {
+ add_context_to_error (error, context);
+ return;
+ }
+
+ op = meta_draw_op_new (META_DRAW_IMAGE);
+
+ op->data.image.pixbuf = pixbuf;
+ op->data.image.x = optimize_expression (info->theme, x);
+ op->data.image.y = optimize_expression (info->theme, y);
+ op->data.image.width = optimize_expression (info->theme, width);
+ op->data.image.height = optimize_expression (info->theme, height);
+ op->data.image.alpha = alpha_val;
+
+ g_assert (info->op_list);
+
+ meta_draw_op_list_append (info->op_list, op);
+
+ push_state (info, STATE_IMAGE);
+ }
+ else if (ELEMENT_IS ("gtk_arrow"))
+ {
+ MetaDrawOp *op;
+ const char *state;
+ const char *shadow;
+ const char *arrow;
+ const char *x;
+ const char *y;
+ const char *width;
+ const char *height;
+ const char *filled;
+ gboolean filled_val;
+ GtkStateType state_val;
+ GtkShadowType shadow_val;
+ GtkArrowType arrow_val;
+
+ if (!locate_attributes (context, element_name, attribute_names, attribute_values,
+ error,
+ "state", &state,
+ "shadow", &shadow,
+ "arrow", &arrow,
+ "x", &x, "y", &y,
+ "width", &width, "height", &height,
+ "filled", &filled,
+ NULL))
+ return;
+
+ if (state == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"state\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (shadow == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"shadow\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (arrow == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"arrow\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (x == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"x\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (y == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"y\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (width == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"width\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (height == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"height\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (!check_expression (x, FALSE, info->theme, context, error))
+ return;
+
+ if (!check_expression (y, FALSE, info->theme, context, error))
+ return;
+
+ if (!check_expression (width, FALSE, info->theme, context, error))
+ return;
+
+ if (!check_expression (height, FALSE, info->theme, context, error))
+ return;
+
+ filled_val = TRUE;
+ if (filled && !parse_boolean (filled, &filled_val, context, error))
+ return;
+
+ state_val = meta_gtk_state_from_string (state);
+ if (((int) state_val) == -1)
+ {
+ set_error (error, context, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_PARSE,
+ _("Did not understand state \"%s\" for <%s> element"),
+ state, element_name);
+ return;
+ }
+
+ shadow_val = meta_gtk_shadow_from_string (shadow);
+ if (((int) shadow_val) == -1)
+ {
+ set_error (error, context, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_PARSE,
+ _("Did not understand shadow \"%s\" for <%s> element"),
+ shadow, element_name);
+ return;
+ }
+
+ arrow_val = meta_gtk_arrow_from_string (arrow);
+ if (((int) arrow_val) == -1)
+ {
+ set_error (error, context, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_PARSE,
+ _("Did not understand arrow \"%s\" for <%s> element"),
+ arrow, element_name);
+ return;
+ }
+
+ op = meta_draw_op_new (META_DRAW_GTK_ARROW);
+
+ op->data.gtk_arrow.x = optimize_expression (info->theme, x);
+ op->data.gtk_arrow.y = optimize_expression (info->theme, y);
+ op->data.gtk_arrow.width = optimize_expression (info->theme, width);
+ op->data.gtk_arrow.height = optimize_expression (info->theme, height);
+ op->data.gtk_arrow.filled = filled_val;
+ op->data.gtk_arrow.state = state_val;
+ op->data.gtk_arrow.shadow = shadow_val;
+ op->data.gtk_arrow.arrow = arrow_val;
+
+ g_assert (info->op_list);
+
+ meta_draw_op_list_append (info->op_list, op);
+
+ push_state (info, STATE_GTK_ARROW);
+ }
+ else if (ELEMENT_IS ("gtk_box"))
+ {
+ MetaDrawOp *op;
+ const char *state;
+ const char *shadow;
+ const char *x;
+ const char *y;
+ const char *width;
+ const char *height;
+ GtkStateType state_val;
+ GtkShadowType shadow_val;
+
+ if (!locate_attributes (context, element_name, attribute_names, attribute_values,
+ error,
+ "state", &state,
+ "shadow", &shadow,
+ "x", &x, "y", &y,
+ "width", &width, "height", &height,
+ NULL))
+ return;
+
+ if (state == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"state\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (shadow == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"shadow\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (x == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"x\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (y == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"y\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (width == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"width\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (height == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"height\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (!check_expression (x, FALSE, info->theme, context, error))
+ return;
+
+ if (!check_expression (y, FALSE, info->theme, context, error))
+ return;
+
+ if (!check_expression (width, FALSE, info->theme, context, error))
+ return;
+
+ if (!check_expression (height, FALSE, info->theme, context, error))
+ return;
+
+ state_val = meta_gtk_state_from_string (state);
+ if (((int) state_val) == -1)
+ {
+ set_error (error, context, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_PARSE,
+ _("Did not understand state \"%s\" for <%s> element"),
+ state, element_name);
+ return;
+ }
+
+ shadow_val = meta_gtk_shadow_from_string (shadow);
+ if (((int) shadow_val) == -1)
+ {
+ set_error (error, context, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_PARSE,
+ _("Did not understand shadow \"%s\" for <%s> element"),
+ shadow, element_name);
+ return;
+ }
+
+ op = meta_draw_op_new (META_DRAW_GTK_BOX);
+
+ op->data.gtk_box.x = optimize_expression (info->theme, x);
+ op->data.gtk_box.y = optimize_expression (info->theme, y);
+ op->data.gtk_box.width = optimize_expression (info->theme, width);
+ op->data.gtk_box.height = optimize_expression (info->theme, height);
+ op->data.gtk_box.state = state_val;
+ op->data.gtk_box.shadow = shadow_val;
+
+ g_assert (info->op_list);
+
+ meta_draw_op_list_append (info->op_list, op);
+
+ push_state (info, STATE_GTK_BOX);
+ }
+ else if (ELEMENT_IS ("gtk_vline"))
+ {
+ MetaDrawOp *op;
+ const char *state;
+ const char *x;
+ const char *y1;
+ const char *y2;
+ GtkStateType state_val;
+
+ if (!locate_attributes (context, element_name, attribute_names, attribute_values,
+ error,
+ "state", &state,
+ "x", &x, "y1", &y1, "y2", &y2,
+ NULL))
+ return;
+
+ if (state == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"state\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (x == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"x\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (y1 == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"y1\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (y2 == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"y2\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (!check_expression (x, FALSE, info->theme, context, error))
+ return;
+
+ if (!check_expression (y1, FALSE, info->theme, context, error))
+ return;
+
+ if (!check_expression (y2, FALSE, info->theme, context, error))
+ return;
+
+ state_val = meta_gtk_state_from_string (state);
+ if (((int) state_val) == -1)
+ {
+ set_error (error, context, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_PARSE,
+ _("Did not understand state \"%s\" for <%s> element"),
+ state, element_name);
+ return;
+ }
+
+ op = meta_draw_op_new (META_DRAW_GTK_VLINE);
+
+ op->data.gtk_vline.x = optimize_expression (info->theme, x);
+ op->data.gtk_vline.y1 = optimize_expression (info->theme, y1);
+ op->data.gtk_vline.y2 = optimize_expression (info->theme, y2);
+ op->data.gtk_vline.state = state_val;
+
+ g_assert (info->op_list);
+
+ meta_draw_op_list_append (info->op_list, op);
+
+ push_state (info, STATE_GTK_VLINE);
+ }
+ else if (ELEMENT_IS ("icon"))
+ {
+ MetaDrawOp *op;
+ const char *x;
+ const char *y;
+ const char *width;
+ const char *height;
+ const char *alpha;
+ double alpha_val;
+
+ if (!locate_attributes (context, element_name, attribute_names, attribute_values,
+ error,
+ "x", &x, "y", &y,
+ "width", &width, "height", &height,
+ "alpha", &alpha,
+ NULL))
+ return;
+
+ if (x == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"x\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (y == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"y\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (width == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"width\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (height == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"height\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (!check_expression (x, FALSE, info->theme, context, error))
+ return;
+
+ if (!check_expression (y, FALSE, info->theme, context, error))
+ return;
+
+ if (!check_expression (width, FALSE, info->theme, context, error))
+ return;
+
+ if (!check_expression (height, FALSE, info->theme, context, error))
+ return;
+
+ alpha_val = 1.0;
+ if (alpha && !parse_alpha (alpha, &alpha_val, context, error))
+ return;
+
+ op = meta_draw_op_new (META_DRAW_ICON);
+
+ op->data.icon.x = optimize_expression (info->theme, x);
+ op->data.icon.y = optimize_expression (info->theme, y);
+ op->data.icon.width = optimize_expression (info->theme, width);
+ op->data.icon.height = optimize_expression (info->theme, height);
+ op->data.icon.alpha = alpha_val;
+
+ g_assert (info->op_list);
+
+ meta_draw_op_list_append (info->op_list, op);
+
+ push_state (info, STATE_ICON);
+ }
+ else if (ELEMENT_IS ("title"))
+ {
+ MetaDrawOp *op;
+ const char *color;
+ const char *x;
+ const char *y;
+ MetaColorSpec *color_spec;
+
+ if (!locate_attributes (context, element_name, attribute_names, attribute_values,
+ error,
+ "color", &color,
+ "x", &x, "y", &y,
+ NULL))
+ return;
+
+ if (color == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"color\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (x == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"x\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (y == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"y\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ if (!check_expression (x, FALSE, info->theme, context, error))
+ return;
+
+ if (!check_expression (y, FALSE, info->theme, context, error))
+ return;
+
+ /* Check last so we don't have to free it when other
+ * stuff fails
+ */
+ color_spec = meta_color_spec_new_from_string (color, error);
+ if (color_spec == NULL)
+ {
+ add_context_to_error (error, context);
+ return;
+ }
+
+ op = meta_draw_op_new (META_DRAW_TITLE);
+
+ op->data.title.color_spec = color_spec;
+ op->data.title.x = optimize_expression (info->theme, x);
+ op->data.title.y = optimize_expression (info->theme, y);
+
+ g_assert (info->op_list);
+
+ meta_draw_op_list_append (info->op_list, op);
+
+ push_state (info, STATE_TITLE);
+ }
+ else if (ELEMENT_IS ("include"))
+ {
+ MetaDrawOp *op;
+ const char *name;
+ const char *x;
+ const char *y;
+ const char *width;
+ const char *height;
+ MetaDrawOpList *op_list;
+
+ if (!locate_attributes (context, element_name, attribute_names, attribute_values,
+ error,
+ "x", &x, "y", &y,
+ "width", &width, "height", &height,
+ "name", &name,
+ NULL))
+ return;
+
+ if (name == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"name\" attribute on element <%s>"), element_name);
+ return;
+ }
+
+ /* x/y/width/height default to 0,0,width,height - should
+ * probably do this for all the draw ops
+ */
+
+ if (x && !check_expression (x, FALSE, info->theme, context, error))
+ return;
+
+ if (y && !check_expression (y, FALSE, info->theme, context, error))
+ return;
+
+ if (width && !check_expression (width, FALSE, info->theme, context, error))
+ return;
+
+ if (height && !check_expression (height, FALSE, info->theme, context, error))
+ return;
+
+ op_list = meta_theme_lookup_draw_op_list (info->theme,
+ name);
+ if (op_list == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_PARSE,
+ _("No <draw_ops> called \"%s\" has been defined"),
+ name);
+ return;
+ }
+
+ g_assert (info->op_list);
+
+ if (op_list == info->op_list ||
+ meta_draw_op_list_contains (op_list, info->op_list))
+ {
+ set_error (error, context, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_PARSE,
+ _("Including draw_ops \"%s\" here would create a circular reference"),
+ name);
+ return;
+ }
+
+ op = meta_draw_op_new (META_DRAW_OP_LIST);
+
+ meta_draw_op_list_ref (op_list);
+ op->data.op_list.op_list = op_list;
+ op->data.op_list.x = x ? optimize_expression (info->theme, x) :
+ g_strdup ("0");
+ op->data.op_list.y = y ? optimize_expression (info->theme, y) :
+ g_strdup ("0");
+ op->data.op_list.width = width ? optimize_expression (info->theme, width) :
+ g_strdup ("width");
+ op->data.op_list.height = height ? optimize_expression (info->theme, height) :
+ g_strdup ("height");
+
+ meta_draw_op_list_append (info->op_list, op);
+
+ push_state (info, STATE_INCLUDE);
+ }
+ else
+ {
+ set_error (error, context,
+ G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Element <%s> is not allowed below <draw_ops>"),
+ element_name);
+ }
+}
+
+static void
+parse_gradient_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ ParseInfo *info,
+ GError **error)
+{
+ g_return_if_fail (peek_state (info) == STATE_GRADIENT);
+
+ if (ELEMENT_IS ("color"))
+ {
+ const char *value = NULL;
+ MetaColorSpec *color_spec;
+
+ if (!locate_attributes (context, element_name, attribute_names, attribute_values,
+ error,
+ "value", &value,
+ NULL))
+ return;
+
+ if (value == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"value\" attribute on <%s> element"),
+ element_name);
+ return;
+ }
+
+ color_spec = meta_color_spec_new_from_string (value, error);
+ if (color_spec == NULL)
+ {
+ add_context_to_error (error, context);
+ return;
+ }
+
+ g_assert (info->op);
+ g_assert (info->op->type == META_DRAW_GRADIENT);
+ g_assert (info->op->data.gradient.gradient_spec != NULL);
+ info->op->data.gradient.gradient_spec->color_specs =
+ g_slist_append (info->op->data.gradient.gradient_spec->color_specs,
+ color_spec);
+
+ push_state (info, STATE_COLOR);
+ }
+ else
+ {
+ set_error (error, context,
+ G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Element <%s> is not allowed below <gradient>"),
+ element_name);
+ }
+}
+
+static void
+parse_style_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ ParseInfo *info,
+ GError **error)
+{
+ g_return_if_fail (peek_state (info) == STATE_FRAME_STYLE);
+
+ g_assert (info->style);
+
+ if (ELEMENT_IS ("piece"))
+ {
+ const char *position = NULL;
+ const char *draw_ops = NULL;
+
+ if (!locate_attributes (context, element_name, attribute_names, attribute_values,
+ error,
+ "position", &position,
+ "draw_ops", &draw_ops,
+ NULL))
+ return;
+
+ if (position == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"position\" attribute on <%s> element"),
+ element_name);
+ return;
+ }
+
+ info->piece = meta_frame_piece_from_string (position);
+ if (info->piece == META_FRAME_PIECE_LAST)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Unknown position \"%s\" for frame piece"),
+ position);
+ return;
+ }
+
+ if (info->style->pieces[info->piece] != NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Frame style already has a piece at position %s"),
+ position);
+ return;
+ }
+
+ g_assert (info->op_list == NULL);
+
+ if (draw_ops)
+ {
+ MetaDrawOpList *op_list;
+
+ op_list = meta_theme_lookup_draw_op_list (info->theme,
+ draw_ops);
+
+ if (op_list == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No <draw_ops> with the name \"%s\" has been defined"),
+ draw_ops);
+ return;
+ }
+
+ meta_draw_op_list_ref (op_list);
+ info->op_list = op_list;
+ }
+
+ push_state (info, STATE_PIECE);
+ }
+ else if (ELEMENT_IS ("button"))
+ {
+ const char *function = NULL;
+ const char *state = NULL;
+ const char *draw_ops = NULL;
+
+ if (!locate_attributes (context, element_name, attribute_names, attribute_values,
+ error,
+ "function", &function,
+ "state", &state,
+ "draw_ops", &draw_ops,
+ NULL))
+ return;
+
+ if (function == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"function\" attribute on <%s> element"),
+ element_name);
+ return;
+ }
+
+ if (state == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"state\" attribute on <%s> element"),
+ element_name);
+ return;
+ }
+
+ info->button_type = meta_button_type_from_string (function);
+ if (info->button_type == META_BUTTON_TYPE_LAST)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Unknown function \"%s\" for button"),
+ function);
+ return;
+ }
+
+ info->button_state = meta_button_state_from_string (state);
+ if (info->button_state == META_BUTTON_STATE_LAST)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Unknown state \"%s\" for button"),
+ state);
+ return;
+ }
+
+ if (info->style->buttons[info->button_type][info->button_state] != NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Frame style already has a button for function %s state %s"),
+ function, state);
+ return;
+ }
+
+ g_assert (info->op_list == NULL);
+
+ if (draw_ops)
+ {
+ MetaDrawOpList *op_list;
+
+ op_list = meta_theme_lookup_draw_op_list (info->theme,
+ draw_ops);
+
+ if (op_list == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No <draw_ops> with the name \"%s\" has been defined"),
+ draw_ops);
+ return;
+ }
+
+ meta_draw_op_list_ref (op_list);
+ info->op_list = op_list;
+ }
+
+ push_state (info, STATE_BUTTON);
+ }
+ else
+ {
+ set_error (error, context,
+ G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Element <%s> is not allowed below <frame_style>"),
+ element_name);
+ }
+}
+
+static void
+parse_style_set_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ ParseInfo *info,
+ GError **error)
+{
+ g_return_if_fail (peek_state (info) == STATE_FRAME_STYLE_SET);
+
+ if (ELEMENT_IS ("frame"))
+ {
+ const char *focus = NULL;
+ const char *state = NULL;
+ const char *resize = NULL;
+ const char *style = NULL;
+ MetaFrameFocus frame_focus;
+ MetaFrameState frame_state;
+ MetaFrameResize frame_resize;
+ MetaFrameStyle *frame_style;
+
+ if (!locate_attributes (context, element_name, attribute_names, attribute_values,
+ error,
+ "focus", &focus,
+ "state", &state,
+ "resize", &resize,
+ "style", &style,
+ NULL))
+ return;
+
+ if (focus == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"focus\" attribute on <%s> element"),
+ element_name);
+ return;
+ }
+
+ if (state == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"state\" attribute on <%s> element"),
+ element_name);
+ return;
+ }
+
+ if (style == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"style\" attribute on <%s> element"),
+ element_name);
+ return;
+ }
+
+ frame_focus = meta_frame_focus_from_string (focus);
+ if (frame_focus == META_FRAME_FOCUS_LAST)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("\"%s\" is not a valid value for focus attribute"),
+ focus);
+ return;
+ }
+
+ frame_state = meta_frame_state_from_string (state);
+ if (frame_state == META_FRAME_STATE_LAST)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("\"%s\" is not a valid value for state attribute"),
+ focus);
+ return;
+ }
+
+ frame_style = meta_theme_lookup_style (info->theme, style);
+
+ if (frame_style == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("A style called \"%s\" has not been defined"),
+ style);
+ return;
+ }
+
+ if (frame_state == META_FRAME_STATE_NORMAL)
+ {
+ if (resize == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"resize\" attribute on <%s> element"),
+ element_name);
+ return;
+ }
+
+
+ frame_resize = meta_frame_resize_from_string (resize);
+ if (frame_resize == META_FRAME_RESIZE_LAST)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("\"%s\" is not a valid value for resize attribute"),
+ focus);
+ return;
+ }
+ }
+ else
+ {
+ if (resize != NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Should not have \"resize\" attribute on <%s> element for maximized/shaded states"),
+ element_name);
+ return;
+ }
+
+ frame_resize = META_FRAME_RESIZE_LAST;
+ }
+
+ switch (frame_state)
+ {
+ case META_FRAME_STATE_NORMAL:
+ if (info->style_set->normal_styles[frame_resize][frame_focus])
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Style has already been specified for state %s resize %s focus %s"),
+ state, resize, focus);
+ return;
+ }
+ meta_frame_style_ref (frame_style);
+ info->style_set->normal_styles[frame_resize][frame_focus] = frame_style;
+ break;
+ case META_FRAME_STATE_MAXIMIZED:
+ if (info->style_set->maximized_styles[frame_focus])
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Style has already been specified for state %s focus %s"),
+ state, focus);
+ return;
+ }
+ meta_frame_style_ref (frame_style);
+ info->style_set->maximized_styles[frame_focus] = frame_style;
+ break;
+ case META_FRAME_STATE_SHADED:
+ if (info->style_set->shaded_styles[frame_focus])
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Style has already been specified for state %s focus %s"),
+ state, focus);
+ return;
+ }
+ meta_frame_style_ref (frame_style);
+ info->style_set->shaded_styles[frame_focus] = frame_style;
+ break;
+ case META_FRAME_STATE_MAXIMIZED_AND_SHADED:
+ if (info->style_set->maximized_and_shaded_styles[frame_focus])
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Style has already been specified for state %s focus %s"),
+ state, focus);
+ return;
+ }
+ meta_frame_style_ref (frame_style);
+ info->style_set->maximized_and_shaded_styles[frame_focus] = frame_style;
+ break;
+ case META_FRAME_STATE_LAST:
+ g_assert_not_reached ();
+ break;
+ }
+
+ push_state (info, STATE_FRAME);
+ }
+ else
+ {
+ set_error (error, context,
+ G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Element <%s> is not allowed below <frame_style_set>"),
+ element_name);
+ }
+}
+
+static void
+parse_piece_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ ParseInfo *info,
+ GError **error)
+{
+ g_return_if_fail (peek_state (info) == STATE_PIECE);
+
+ if (ELEMENT_IS ("draw_ops"))
+ {
+ if (info->op_list)
+ {
+ set_error (error, context,
+ G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Can't have a two draw_ops for a <piece> element (theme specified a draw_ops attribute and also a <draw_ops> element, or specified two elements)"));
+ return;
+ }
+
+ if (!check_no_attributes (context, element_name, attribute_names, attribute_values,
+ error))
+ return;
+
+ g_assert (info->op_list == NULL);
+ info->op_list = meta_draw_op_list_new (2);
+
+ push_state (info, STATE_DRAW_OPS);
+ }
+ else
+ {
+ set_error (error, context,
+ G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Element <%s> is not allowed below <piece>"),
+ element_name);
+ }
+}
+
+static void
+parse_button_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ ParseInfo *info,
+ GError **error)
+{
+ g_return_if_fail (peek_state (info) == STATE_BUTTON);
+
+ if (ELEMENT_IS ("draw_ops"))
+ {
+ if (info->op_list)
+ {
+ set_error (error, context,
+ G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Can't have a two draw_ops for a <button> element (theme specified a draw_ops attribute and also a <draw_ops> element, or specified two elements)"));
+ return;
+ }
+
+ if (!check_no_attributes (context, element_name, attribute_names, attribute_values,
+ error))
+ return;
+
+ g_assert (info->op_list == NULL);
+ info->op_list = meta_draw_op_list_new (2);
+
+ push_state (info, STATE_DRAW_OPS);
+ }
+ else
+ {
+ set_error (error, context,
+ G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Element <%s> is not allowed below <piece>"),
+ element_name);
+ }
+}
+
+static void
+parse_menu_icon_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ ParseInfo *info,
+ GError **error)
+{
+ g_return_if_fail (peek_state (info) == STATE_MENU_ICON);
+
+ if (ELEMENT_IS ("draw_ops"))
+ {
+ if (info->op_list)
+ {
+ set_error (error, context,
+ G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Can't have a two draw_ops for a <menu_icon> element (theme specified a draw_ops attribute and also a <draw_ops> element, or specified two elements)"));
+ return;
+ }
+
+ if (!check_no_attributes (context, element_name, attribute_names, attribute_values,
+ error))
+ return;
+
+ g_assert (info->op_list == NULL);
+ info->op_list = meta_draw_op_list_new (2);
+
+ push_state (info, STATE_DRAW_OPS);
+ }
+ else
+ {
+ set_error (error, context,
+ G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Element <%s> is not allowed below <piece>"),
+ element_name);
+ }
+}
+
+
+static void
+start_element_handler (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ gpointer user_data,
+ GError **error)
+{
+ ParseInfo *info = user_data;
+
+ switch (peek_state (info))
+ {
+ case STATE_START:
+ if (strcmp (element_name, "metacity_theme") == 0)
+ {
+ info->theme = meta_theme_new ();
+ info->theme->name = g_strdup (info->theme_name);
+ info->theme->filename = g_strdup (info->theme_file);
+ info->theme->dirname = g_strdup (info->theme_dir);
+
+ push_state (info, STATE_THEME);
+ }
+ else
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Outermost element in theme must be <metacity_theme> not <%s>"),
+ element_name);
+ break;
+
+ case STATE_THEME:
+ parse_toplevel_element (context, element_name,
+ attribute_names, attribute_values,
+ info, error);
+ break;
+ case STATE_INFO:
+ parse_info_element (context, element_name,
+ attribute_names, attribute_values,
+ info, error);
+ break;
+ case STATE_NAME:
+ case STATE_AUTHOR:
+ case STATE_COPYRIGHT:
+ case STATE_DATE:
+ case STATE_DESCRIPTION:
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Element <%s> is not allowed inside a name/author/date/description element"),
+ element_name);
+ break;
+ case STATE_CONSTANT:
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Element <%s> is not allowed inside a <constant> element"),
+ element_name);
+ break;
+ case STATE_FRAME_GEOMETRY:
+ parse_geometry_element (context, element_name,
+ attribute_names, attribute_values,
+ info, error);
+ break;
+ case STATE_DISTANCE:
+ case STATE_BORDER:
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Element <%s> is not allowed inside a distance/border element"),
+ element_name);
+ break;
+ case STATE_DRAW_OPS:
+ parse_draw_op_element (context, element_name,
+ attribute_names, attribute_values,
+ info, error);
+ break;
+ case STATE_LINE:
+ case STATE_RECTANGLE:
+ case STATE_ARC:
+ case STATE_CLIP:
+ case STATE_TINT:
+ case STATE_IMAGE:
+ case STATE_GTK_ARROW:
+ case STATE_GTK_BOX:
+ case STATE_GTK_VLINE:
+ case STATE_ICON:
+ case STATE_TITLE:
+ case STATE_INCLUDE:
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Element <%s> is not allowed inside a draw operation element"),
+ element_name);
+ break;
+ case STATE_GRADIENT:
+ parse_gradient_element (context, element_name,
+ attribute_names, attribute_values,
+ info, error);
+ break;
+ case STATE_COLOR:
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Element <%s> is not allowed inside a <color> element"),
+ element_name);
+ break;
+ case STATE_FRAME_STYLE:
+ parse_style_element (context, element_name,
+ attribute_names, attribute_values,
+ info, error);
+ break;
+ case STATE_PIECE:
+ parse_piece_element (context, element_name,
+ attribute_names, attribute_values,
+ info, error);
+ break;
+ case STATE_BUTTON:
+ parse_button_element (context, element_name,
+ attribute_names, attribute_values,
+ info, error);
+ break;
+ case STATE_MENU_ICON:
+ parse_menu_icon_element (context, element_name,
+ attribute_names, attribute_values,
+ info, error);
+ break;
+ case STATE_FRAME_STYLE_SET:
+ parse_style_set_element (context, element_name,
+ attribute_names, attribute_values,
+ info, error);
+ break;
+ case STATE_FRAME:
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Element <%s> is not allowed inside a <frame> element"),
+ element_name);
+ break;
+ case STATE_WINDOW:
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Element <%s> is not allowed inside a <window> element"),
+ element_name);
+ break;
+ }
+}
+
+static void
+end_element_handler (GMarkupParseContext *context,
+ const gchar *element_name,
+ gpointer user_data,
+ GError **error)
+{
+ ParseInfo *info = user_data;
+
+ switch (peek_state (info))
+ {
+ case STATE_START:
+ break;
+ case STATE_THEME:
+ g_assert (info->theme);
+
+ if (!meta_theme_validate (info->theme, error))
+ {
+ add_context_to_error (error, context);
+ meta_theme_free (info->theme);
+ info->theme = NULL;
+ }
+
+ pop_state (info);
+ g_assert (peek_state (info) == STATE_START);
+ break;
+ case STATE_INFO:
+ pop_state (info);
+ g_assert (peek_state (info) == STATE_THEME);
+ break;
+ case STATE_NAME:
+ pop_state (info);
+ g_assert (peek_state (info) == STATE_INFO);
+ break;
+ case STATE_AUTHOR:
+ pop_state (info);
+ g_assert (peek_state (info) == STATE_INFO);
+ break;
+ case STATE_COPYRIGHT:
+ pop_state (info);
+ g_assert (peek_state (info) == STATE_INFO);
+ break;
+ case STATE_DATE:
+ pop_state (info);
+ g_assert (peek_state (info) == STATE_INFO);
+ break;
+ case STATE_DESCRIPTION:
+ pop_state (info);
+ g_assert (peek_state (info) == STATE_INFO);
+ break;
+ case STATE_CONSTANT:
+ pop_state (info);
+ g_assert (peek_state (info) == STATE_THEME);
+ break;
+ case STATE_FRAME_GEOMETRY:
+ g_assert (info->layout);
+
+ if (!meta_frame_layout_validate (info->layout,
+ error))
+ {
+ add_context_to_error (error, context);
+ }
+
+ /* layout will already be stored in the theme under
+ * its name
+ */
+ meta_frame_layout_unref (info->layout);
+ info->layout = NULL;
+ pop_state (info);
+ g_assert (peek_state (info) == STATE_THEME);
+ break;
+ case STATE_DISTANCE:
+ pop_state (info);
+ g_assert (peek_state (info) == STATE_FRAME_GEOMETRY);
+ break;
+ case STATE_BORDER:
+ pop_state (info);
+ g_assert (peek_state (info) == STATE_FRAME_GEOMETRY);
+ break;
+ case STATE_DRAW_OPS:
+ {
+ g_assert (info->op_list);
+
+ if (!meta_draw_op_list_validate (info->op_list,
+ error))
+ {
+ add_context_to_error (error, context);
+ meta_draw_op_list_unref (info->op_list);
+ info->op_list = NULL;
+ }
+
+ pop_state (info);
+
+ switch (peek_state (info))
+ {
+ case STATE_BUTTON:
+ case STATE_PIECE:
+ case STATE_MENU_ICON:
+ /* Leave info->op_list to be picked up
+ * when these elements are closed
+ */
+ g_assert (info->op_list);
+ break;
+ case STATE_THEME:
+ g_assert (info->op_list);
+ meta_draw_op_list_unref (info->op_list);
+ info->op_list = NULL;
+ break;
+ default:
+ /* Op list can't occur in other contexts */
+ g_assert_not_reached ();
+ break;
+ }
+ }
+ break;
+ case STATE_LINE:
+ pop_state (info);
+ g_assert (peek_state (info) == STATE_DRAW_OPS);
+ break;
+ case STATE_RECTANGLE:
+ pop_state (info);
+ g_assert (peek_state (info) == STATE_DRAW_OPS);
+ break;
+ case STATE_ARC:
+ pop_state (info);
+ g_assert (peek_state (info) == STATE_DRAW_OPS);
+ break;
+ case STATE_CLIP:
+ pop_state (info);
+ g_assert (peek_state (info) == STATE_DRAW_OPS);
+ break;
+ case STATE_TINT:
+ pop_state (info);
+ g_assert (peek_state (info) == STATE_DRAW_OPS);
+ break;
+ case STATE_GRADIENT:
+ g_assert (info->op);
+ g_assert (info->op->type == META_DRAW_GRADIENT);
+ if (!meta_gradient_spec_validate (info->op->data.gradient.gradient_spec,
+ error))
+ {
+ add_context_to_error (error, context);
+ meta_draw_op_free (info->op);
+ info->op = NULL;
+ }
+ else
+ {
+ g_assert (info->op_list);
+ meta_draw_op_list_append (info->op_list, info->op);
+ info->op = NULL;
+ }
+ pop_state (info);
+ g_assert (peek_state (info) == STATE_DRAW_OPS);
+ break;
+ case STATE_IMAGE:
+ pop_state (info);
+ g_assert (peek_state (info) == STATE_DRAW_OPS);
+ break;
+ case STATE_GTK_ARROW:
+ pop_state (info);
+ g_assert (peek_state (info) == STATE_DRAW_OPS);
+ break;
+ case STATE_GTK_BOX:
+ pop_state (info);
+ g_assert (peek_state (info) == STATE_DRAW_OPS);
+ break;
+ case STATE_GTK_VLINE:
+ pop_state (info);
+ g_assert (peek_state (info) == STATE_DRAW_OPS);
+ break;
+ case STATE_ICON:
+ pop_state (info);
+ g_assert (peek_state (info) == STATE_DRAW_OPS);
+ break;
+ case STATE_TITLE:
+ pop_state (info);
+ g_assert (peek_state (info) == STATE_DRAW_OPS);
+ break;
+ case STATE_INCLUDE:
+ pop_state (info);
+ g_assert (peek_state (info) == STATE_DRAW_OPS);
+ break;
+ case STATE_COLOR:
+ pop_state (info);
+ g_assert (peek_state (info) == STATE_GRADIENT);
+ break;
+ case STATE_FRAME_STYLE:
+ g_assert (info->style);
+
+ if (!meta_frame_style_validate (info->style,
+ error))
+ {
+ add_context_to_error (error, context);
+ }
+
+ /* Frame style is in the theme hash table and a ref
+ * is held there
+ */
+ meta_frame_style_unref (info->style);
+ info->style = NULL;
+ pop_state (info);
+ g_assert (peek_state (info) == STATE_THEME);
+ break;
+ case STATE_PIECE:
+ g_assert (info->style);
+ if (info->op_list == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No draw_ops provided for frame piece"));
+ }
+ else
+ {
+ info->style->pieces[info->piece] = info->op_list;
+ info->op_list = NULL;
+ }
+ pop_state (info);
+ g_assert (peek_state (info) == STATE_FRAME_STYLE);
+ break;
+ case STATE_BUTTON:
+ g_assert (info->style);
+ if (info->op_list == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No draw_ops provided for button"));
+ }
+ else
+ {
+ info->style->buttons[info->button_type][info->button_state] =
+ info->op_list;
+ info->op_list = NULL;
+ }
+ pop_state (info);
+ break;
+ case STATE_MENU_ICON:
+ g_assert (info->theme);
+ if (info->op_list == NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No draw_ops provided for menu icon"));
+ }
+ else
+ {
+ g_assert (info->theme->menu_icons[info->menu_icon_type][info->menu_icon_state] == NULL);
+ info->theme->menu_icons[info->menu_icon_type][info->menu_icon_state] =
+ info->op_list;
+ info->op_list = NULL;
+ }
+ pop_state (info);
+ g_assert (peek_state (info) == STATE_THEME);
+ break;
+ case STATE_FRAME_STYLE_SET:
+ g_assert (info->style_set);
+
+ if (!meta_frame_style_set_validate (info->style_set,
+ error))
+ {
+ add_context_to_error (error, context);
+ }
+
+ /* Style set is in the theme hash table and a reference
+ * is held there.
+ */
+ meta_frame_style_set_unref (info->style_set);
+ info->style_set = NULL;
+ pop_state (info);
+ g_assert (peek_state (info) == STATE_THEME);
+ break;
+ case STATE_FRAME:
+ pop_state (info);
+ g_assert (peek_state (info) == STATE_FRAME_STYLE_SET);
+ break;
+ case STATE_WINDOW:
+ pop_state (info);
+ g_assert (peek_state (info) == STATE_THEME);
+ break;
+ }
+}
+
+#define NO_TEXT(element_name) set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, _("No text is allowed inside element <%s>"), element_name)
+
+static gboolean
+all_whitespace (const char *text,
+ int text_len)
+{
+ const char *p;
+ const char *end;
+
+ p = text;
+ end = text + text_len;
+
+ while (p != end)
+ {
+ if (!g_ascii_isspace (*p))
+ return FALSE;
+
+ p = g_utf8_next_char (p);
+ }
+
+ return TRUE;
+}
+
+static void
+text_handler (GMarkupParseContext *context,
+ const gchar *text,
+ gsize text_len,
+ gpointer user_data,
+ GError **error)
+{
+ ParseInfo *info = user_data;
+
+ if (all_whitespace (text, text_len))
+ return;
+
+ /* FIXME http://bugzilla.gnome.org/show_bug.cgi?id=70448 would
+ * allow a nice cleanup here.
+ */
+
+ switch (peek_state (info))
+ {
+ case STATE_START:
+ g_assert_not_reached (); /* gmarkup shouldn't do this */
+ break;
+ case STATE_THEME:
+ NO_TEXT ("metacity_theme");
+ break;
+ case STATE_INFO:
+ NO_TEXT ("info");
+ break;
+ case STATE_NAME:
+ if (info->theme->readable_name != NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_PARSE,
+ _("<name> specified twice for this theme"));
+ return;
+ }
+
+ info->theme->readable_name = g_strndup (text, text_len);
+ break;
+ case STATE_AUTHOR:
+ if (info->theme->author != NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_PARSE,
+ _("<author> specified twice for this theme"));
+ return;
+ }
+
+ info->theme->author = g_strndup (text, text_len);
+ break;
+ case STATE_COPYRIGHT:
+ if (info->theme->copyright != NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_PARSE,
+ _("<copyright> specified twice for this theme"));
+ return;
+ }
+
+ info->theme->copyright = g_strndup (text, text_len);
+ break;
+ case STATE_DATE:
+ if (info->theme->date != NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_PARSE,
+ _("<date> specified twice for this theme"));
+ return;
+ }
+
+ info->theme->date = g_strndup (text, text_len);
+ break;
+ case STATE_DESCRIPTION:
+ if (info->theme->description != NULL)
+ {
+ set_error (error, context, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_PARSE,
+ _("<description> specified twice for this theme"));
+ return;
+ }
+
+ info->theme->description = g_strndup (text, text_len);
+ break;
+ case STATE_CONSTANT:
+ NO_TEXT ("constant");
+ break;
+ case STATE_FRAME_GEOMETRY:
+ NO_TEXT ("frame_geometry");
+ break;
+ case STATE_DISTANCE:
+ NO_TEXT ("distance");
+ break;
+ case STATE_BORDER:
+ NO_TEXT ("border");
+ break;
+ case STATE_DRAW_OPS:
+ NO_TEXT ("draw_ops");
+ break;
+ case STATE_LINE:
+ NO_TEXT ("line");
+ break;
+ case STATE_RECTANGLE:
+ NO_TEXT ("rectangle");
+ break;
+ case STATE_ARC:
+ NO_TEXT ("arc");
+ break;
+ case STATE_CLIP:
+ NO_TEXT ("clip");
+ break;
+ case STATE_TINT:
+ NO_TEXT ("tint");
+ break;
+ case STATE_GRADIENT:
+ NO_TEXT ("gradient");
+ break;
+ case STATE_IMAGE:
+ NO_TEXT ("image");
+ break;
+ case STATE_GTK_ARROW:
+ NO_TEXT ("gtk_arrow");
+ break;
+ case STATE_GTK_BOX:
+ NO_TEXT ("gtk_box");
+ break;
+ case STATE_GTK_VLINE:
+ NO_TEXT ("gtk_vline");
+ break;
+ case STATE_ICON:
+ NO_TEXT ("icon");
+ break;
+ case STATE_TITLE:
+ NO_TEXT ("title");
+ break;
+ case STATE_INCLUDE:
+ NO_TEXT ("include");
+ break;
+ case STATE_COLOR:
+ NO_TEXT ("color");
+ break;
+ case STATE_FRAME_STYLE:
+ NO_TEXT ("frame_style");
+ break;
+ case STATE_PIECE:
+ NO_TEXT ("piece");
+ break;
+ case STATE_BUTTON:
+ NO_TEXT ("button");
+ break;
+ case STATE_MENU_ICON:
+ NO_TEXT ("menu_icon");
+ break;
+ case STATE_FRAME_STYLE_SET:
+ NO_TEXT ("frame_style_set");
+ break;
+ case STATE_FRAME:
+ NO_TEXT ("frame");
+ break;
+ case STATE_WINDOW:
+ NO_TEXT ("window");
+ break;
+ }
+}
+
+/* We change the filename when we break the format,
+ * so themes can work with various metacity versions
+ */
+#define THEME_FILENAME "metacity-theme-1.xml"
+
+MetaTheme*
+meta_theme_load (const char *theme_name,
+ GError **err)
+{
+ GMarkupParseContext *context;
+ GError *error;
+ ParseInfo info;
+ char *text;
+ int length;
+ char *theme_file;
+ char *theme_dir;
+ MetaTheme *retval;
+
+ text = NULL;
+ length = 0;
+ retval = NULL;
+
+ theme_dir = NULL;
+ theme_file = NULL;
+
+ if (meta_is_debugging ())
+ {
+ theme_dir = g_build_filename ("./themes", theme_name, NULL);
+
+ theme_file = g_build_filename (theme_dir,
+ THEME_FILENAME,
+ NULL);
+
+ error = NULL;
+ if (!g_file_get_contents (theme_file,
+ &text,
+ &length,
+ &error))
+ {
+ meta_verbose ("Failed to read theme from file %s: %s\n",
+ theme_file, error->message);
+ g_error_free (error);
+ g_free (theme_dir);
+ g_free (theme_file);
+ theme_file = NULL;
+ }
+ }
+
+ /* We try in current dir, then home dir, then system dir for themes */
+ if (text == NULL)
+ {
+ theme_dir = g_build_filename ("./", theme_name, NULL);
+
+ theme_file = g_build_filename (theme_dir,
+ THEME_FILENAME,
+ NULL);
+
+ error = NULL;
+ if (!g_file_get_contents (theme_file,
+ &text,
+ &length,
+ &error))
+ {
+ meta_verbose ("Failed to read theme from file %s: %s\n",
+ theme_file, error->message);
+ g_error_free (error);
+ g_free (theme_dir);
+ g_free (theme_file);
+ theme_file = NULL;
+ }
+ }
+
+ if (text == NULL)
+ {
+ theme_dir = g_build_filename (g_get_home_dir (),
+ ".metacity/themes/", theme_name, NULL);
+
+ theme_file = g_build_filename (theme_dir,
+ THEME_FILENAME,
+ NULL);
+
+ error = NULL;
+ if (!g_file_get_contents (theme_file,
+ &text,
+ &length,
+ &error))
+ {
+ meta_verbose ("Failed to read theme from file %s: %s\n",
+ theme_file, error->message);
+ g_error_free (error);
+ g_free (theme_dir);
+ g_free (theme_file);
+ theme_file = NULL;
+ }
+ }
+
+ if (text == NULL)
+ {
+ theme_dir = g_build_filename (METACITY_PKGDATADIR,
+ "themes",
+ theme_name, NULL);
+
+ theme_file = g_build_filename (theme_dir,
+ THEME_FILENAME,
+ NULL);
+
+ error = NULL;
+ if (!g_file_get_contents (theme_file,
+ &text,
+ &length,
+ &error))
+ {
+ meta_warning (_("Failed to read theme from file %s: %s\n"),
+ theme_file, error->message);
+ g_propagate_error (err, error);
+ g_free (theme_file);
+ g_free (theme_dir);
+ return FALSE; /* all fallbacks failed */
+ }
+ }
+
+ g_assert (text);
+
+ meta_verbose ("Parsing theme file %s\n", theme_file);
+
+ parse_info_init (&info);
+ info.theme_name = theme_name;
+
+ /* pass ownership to info so we free it with the info */
+ info.theme_file = theme_file;
+ info.theme_dir = theme_dir;
+
+ context = g_markup_parse_context_new (&metacity_theme_parser,
+ 0, &info, NULL);
+
+ error = NULL;
+ if (!g_markup_parse_context_parse (context,
+ text,
+ length,
+ &error))
+ goto out;
+
+ error = NULL;
+ if (!g_markup_parse_context_end_parse (context, &error))
+ goto out;
+
+ g_markup_parse_context_free (context);
+
+ goto out;
+
+ out:
+
+ g_free (text);
+
+ if (error)
+ {
+ g_propagate_error (err, error);
+ }
+ else if (info.theme)
+ {
+ /* Steal theme from info */
+ retval = info.theme;
+ info.theme = NULL;
+ }
+ else
+ {
+ g_set_error (err, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Theme file %s did not contain a root <metacity_theme> element"),
+ info.theme_file);
+ }
+
+ parse_info_free (&info);
+
+ return retval;
+}
diff --git a/src/theme-parser.h b/src/theme-parser.h
new file mode 100644
index 00000000..2ba198e4
--- /dev/null
+++ b/src/theme-parser.h
@@ -0,0 +1,30 @@
+/* Metacity theme parsing */
+
+/*
+ * Copyright (C) 2001 Havoc Pennington
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "theme.h"
+
+#ifndef META_THEME_PARSER_H
+#define META_THEME_PARSER_H
+
+MetaTheme* meta_theme_load (const char *theme_name,
+ GError **err);
+
+#endif
diff --git a/src/theme-viewer.c b/src/theme-viewer.c
index db06bf68..dd2ecdf9 100644
--- a/src/theme-viewer.c
+++ b/src/theme-viewer.c
@@ -1,8 +1,8 @@
/* Metacity theme viewer and test app main() */
-/*
+/*
* Copyright (C) 2002 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
@@ -12,7 +12,7 @@
* 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
@@ -22,29 +22,121 @@
#include <config.h>
#include "util.h"
#include "theme.h"
+#include "theme-parser.h"
+#include "inlinepixbufs.h"
#include <gtk/gtk.h>
#include <time.h>
-
-static void run_position_expression_tests (void);
-static void run_position_expression_timings (void);
+#include <stdlib.h>
static int client_width = 200;
static int client_height = 200;
+static MetaTheme *global_theme = NULL;
+
+static void run_position_expression_tests (void);
+static void run_position_expression_timings (void);
+
+static MetaFrameFlags
+get_flags (GtkWidget *widget)
+{
+ return META_FRAME_ALLOWS_DELETE |
+ META_FRAME_ALLOWS_MENU |
+ META_FRAME_ALLOWS_MINIMIZE |
+ META_FRAME_ALLOWS_MAXIMIZE |
+ META_FRAME_ALLOWS_VERTICAL_RESIZE |
+ META_FRAME_ALLOWS_HORIZONTAL_RESIZE |
+ META_FRAME_HAS_FOCUS |
+ META_FRAME_ALLOWS_SHADE |
+ META_FRAME_ALLOWS_MOVE;
+}
+
+static int
+get_text_height (GtkWidget *widget)
+{
+ return meta_gtk_widget_get_text_height (widget);
+}
+
+static PangoLayout*
+create_title_layout (GtkWidget *widget)
+{
+ PangoLayout *layout;
+
+ layout = gtk_widget_create_pango_layout (widget, "Window Title Goes Here");
+
+ return layout;
+}
+
+
+#ifdef HAVE_GDK_PIXBUF_NEW_FROM_STREAM
+#define gdk_pixbuf_new_from_inline gdk_pixbuf_new_from_stream
+#endif
+
+static GdkPixbuf*
+get_icon (void)
+{
+ static GdkPixbuf *default_icon = NULL;
+
+ if (default_icon == NULL)
+ {
+ GdkPixbuf *base;
+
+ base = gdk_pixbuf_new_from_inline (-1, default_icon_data,
+ FALSE,
+ NULL);
+
+ g_assert (base);
+
+ default_icon = gdk_pixbuf_scale_simple (base,
+ META_ICON_WIDTH,
+ META_ICON_HEIGHT,
+ GDK_INTERP_BILINEAR);
+
+ g_object_unref (G_OBJECT (base));
+ }
+
+ return default_icon;
+}
+
+static GdkPixbuf*
+get_mini_icon (void)
+{
+ static GdkPixbuf *default_icon = NULL;
+
+ if (default_icon == NULL)
+ {
+ GdkPixbuf *base;
+
+ base = gdk_pixbuf_new_from_inline (-1, default_icon_data,
+ FALSE,
+ NULL);
+
+ g_assert (base);
+
+ default_icon = gdk_pixbuf_scale_simple (base,
+ META_MINI_ICON_WIDTH,
+ META_MINI_ICON_HEIGHT,
+ GDK_INTERP_BILINEAR);
+
+ g_object_unref (G_OBJECT (base));
+ }
+
+ return default_icon;
+}
+
+
static void
-set_widget_to_frame_size (MetaFrameStyle *style,
- GtkWidget *widget)
+set_widget_to_frame_size (GtkWidget *widget)
{
int top_height, bottom_height, left_width, right_width;
- meta_frame_layout_get_borders (style->layout,
- widget,
- 15, /* FIXME */
- 0, /* FIXME */
- &top_height,
- &bottom_height,
- &left_width,
- &right_width);
+ meta_theme_get_frame_borders (global_theme,
+ META_FRAME_TYPE_NORMAL,
+ get_text_height (widget),
+ get_flags (widget),
+ &top_height,
+ &bottom_height,
+ &left_width,
+ &right_width);
gtk_widget_set_size_request (widget,
client_width + left_width + right_width,
@@ -56,7 +148,6 @@ expose_handler (GtkWidget *widget,
GdkEventExpose *event,
gpointer data)
{
- MetaFrameStyle *style;
MetaButtonState button_states[META_BUTTON_TYPE_LAST] =
{
META_BUTTON_STATE_NORMAL,
@@ -65,36 +156,42 @@ expose_handler (GtkWidget *widget,
META_BUTTON_STATE_NORMAL
};
int top_height, bottom_height, left_width, right_width;
+ PangoLayout *layout;
- style = meta_frame_style_get_test ();
+ layout = create_title_layout (widget);
- meta_frame_layout_get_borders (style->layout,
- widget,
- 15, /* FIXME */
- 0, /* FIXME */
- &top_height,
- &bottom_height,
- &left_width,
- &right_width);
-
- meta_frame_style_draw (style,
+ meta_theme_get_frame_borders (global_theme,
+ META_FRAME_TYPE_NORMAL,
+ get_text_height (widget),
+ get_flags (widget),
+ &top_height,
+ &bottom_height,
+ &left_width,
+ &right_width);
+
+ meta_theme_draw_frame (global_theme,
widget,
widget->window,
- 0, 0,
&event->area,
- 0, /* flags */
+ 0, 0,
+ META_FRAME_TYPE_NORMAL,
+ get_flags (widget),
client_width, client_height,
- NULL, /* FIXME */
- 15, /* FIXME */
- button_states);
+ layout,
+ get_text_height (widget),
+ button_states,
+ get_mini_icon (),
+ get_icon ());
/* Draw the "client" */
gdk_draw_rectangle (widget->window,
- widget->style->white_gc,
+ widget->style->white_gc,
TRUE,
left_width, top_height,
client_width, client_height);
+
+ g_object_unref (G_OBJECT (layout));
return TRUE;
}
@@ -107,16 +204,43 @@ main (int argc, char **argv)
GtkWidget *sw;
GtkWidget *da;
GdkColor desktop_color;
-
+ GError *err;
+ clock_t start, end;
+
bindtextdomain (GETTEXT_PACKAGE, METACITY_LOCALEDIR);
-
+
run_position_expression_tests ();
#if 0
run_position_expression_timings ();
#endif
-
+
gtk_init (&argc, &argv);
+ start = clock ();
+ err = NULL;
+ if (argc == 1)
+ global_theme = meta_theme_load ("Default", &err);
+ else if (argc == 2)
+ global_theme = meta_theme_load (argv[1], &err);
+ else
+ {
+ g_printerr ("Usage: metacity-theme-viewer [THEMENAME]\n");
+ exit (1);
+ }
+ end = clock ();
+
+ if (global_theme == NULL)
+ {
+ g_printerr ("Error loading theme: %s\n",
+ err->message);
+ g_error_free (err);
+ exit (1);
+ }
+
+ g_print ("Loaded theme \"%s\" in %g seconds\n",
+ global_theme->name,
+ (end - start) / (double) CLOCKS_PER_SEC);
+
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_default_size (GTK_WINDOW (window), 270, 270);
@@ -129,8 +253,8 @@ main (int argc, char **argv)
layout = gtk_layout_new (NULL, NULL);
- gtk_layout_set_size (GTK_LAYOUT (layout), 500, 500);
-
+ gtk_layout_set_size (GTK_LAYOUT (layout), 250, 250);
+
gtk_container_add (GTK_CONTAINER (sw), layout);
g_signal_connect (G_OBJECT (window), "destroy",
@@ -139,12 +263,10 @@ main (int argc, char **argv)
desktop_color.red = 0x5144;
desktop_color.green = 0x75D6;
desktop_color.blue = 0xA699;
-
+
gtk_widget_modify_bg (layout, GTK_STATE_NORMAL, &desktop_color);
da = gtk_drawing_area_new ();
- set_widget_to_frame_size (meta_frame_style_get_test (),
- da);
g_signal_connect (G_OBJECT (da), "expose_event",
G_CALLBACK (expose_handler), NULL);
@@ -152,11 +274,14 @@ main (int argc, char **argv)
gtk_layout_put (GTK_LAYOUT (layout),
da,
5, 5);
+
+ gtk_widget_realize (da);
+ set_widget_to_frame_size (da);
gtk_widget_show_all (window);
-
+
gtk_main ();
-
+
return 0;
}
@@ -166,7 +291,7 @@ typedef struct
const char *expr;
int expected_x;
int expected_y;
- MetaPositionExprError expected_error;
+ MetaThemeError expected_error;
} PositionExpressionTest;
#define NO_ERROR -1
@@ -290,29 +415,29 @@ static const PositionExpressionTest position_expression_tests[] = {
"(8 + 8) / 2", 18, 28, NO_ERROR },
/* Errors */
{ { 10, 20, 40, 50 },
- "2 * foo", 0, 0, META_POSITION_EXPR_ERROR_UNKNOWN_VARIABLE },
+ "2 * foo", 0, 0, META_THEME_ERROR_UNKNOWN_VARIABLE },
{ { 10, 20, 40, 50 },
- "2 *", 0, 0, META_POSITION_EXPR_ERROR_FAILED },
+ "2 *", 0, 0, META_THEME_ERROR_FAILED },
{ { 10, 20, 40, 50 },
- "- width", 0, 0, META_POSITION_EXPR_ERROR_FAILED },
+ "- width", 0, 0, META_THEME_ERROR_FAILED },
{ { 10, 20, 40, 50 },
- "5 % 1.0", 0, 0, META_POSITION_EXPR_ERROR_MOD_ON_FLOAT },
+ "5 % 1.0", 0, 0, META_THEME_ERROR_MOD_ON_FLOAT },
{ { 10, 20, 40, 50 },
- "1.0 % 5", 0, 0, META_POSITION_EXPR_ERROR_MOD_ON_FLOAT },
+ "1.0 % 5", 0, 0, META_THEME_ERROR_MOD_ON_FLOAT },
{ { 10, 20, 40, 50 },
- "! * 2", 0, 0, META_POSITION_EXPR_ERROR_BAD_CHARACTER },
+ "! * 2", 0, 0, META_THEME_ERROR_BAD_CHARACTER },
{ { 10, 20, 40, 50 },
- " ", 0, 0, META_POSITION_EXPR_ERROR_FAILED },
+ " ", 0, 0, META_THEME_ERROR_FAILED },
{ { 10, 20, 40, 50 },
- "() () (( ) ()) ((()))", 0, 0, META_POSITION_EXPR_ERROR_FAILED },
+ "() () (( ) ()) ((()))", 0, 0, META_THEME_ERROR_FAILED },
{ { 10, 20, 40, 50 },
- "(*) () ((/) ()) ((()))", 0, 0, META_POSITION_EXPR_ERROR_FAILED },
+ "(*) () ((/) ()) ((()))", 0, 0, META_THEME_ERROR_FAILED },
{ { 10, 20, 40, 50 },
- "2 * 5 /", 0, 0, META_POSITION_EXPR_ERROR_FAILED },
+ "2 * 5 /", 0, 0, META_THEME_ERROR_FAILED },
{ { 10, 20, 40, 50 },
- "+ 2 * 5", 0, 0, META_POSITION_EXPR_ERROR_FAILED },
+ "+ 2 * 5", 0, 0, META_THEME_ERROR_FAILED },
{ { 10, 20, 40, 50 },
- "+ 2 * 5", 0, 0, META_POSITION_EXPR_ERROR_FAILED }
+ "+ 2 * 5", 0, 0, META_THEME_ERROR_FAILED }
};
static void
@@ -320,22 +445,22 @@ run_position_expression_tests (void)
{
int i;
MetaPositionExprEnv env;
-
+
i = 0;
- while (i < G_N_ELEMENTS (position_expression_tests))
+ while (i < (int) G_N_ELEMENTS (position_expression_tests))
{
GError *err;
gboolean retval;
const PositionExpressionTest *test;
int x, y;
-
+
test = &position_expression_tests[i];
if (g_getenv ("META_PRINT_TESTS") != NULL)
g_print ("Test expression: \"%s\" expecting x = %d y = %d",
test->expr, test->expected_x, test->expected_y);
-
- err = NULL;
+
+ err = NULL;
env.x = test->rect.x;
env.y = test->rect.y;
@@ -343,7 +468,18 @@ run_position_expression_tests (void)
env.height = test->rect.height;
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 = 5;
+ env.title_height = 5;
+ env.icon_width = 32;
+ env.icon_height = 32;
+ env.mini_icon_width = 16;
+ env.mini_icon_height = 16;
+ env.theme = NULL;
+
retval = meta_parse_position_expression (test->expr,
&env,
&x, &y,
@@ -353,11 +489,11 @@ run_position_expression_tests (void)
g_error ("position expression test returned TRUE but set error");
if (!retval && err == NULL)
g_error ("position expression test returned FALSE but didn't set error");
- if (test->expected_error != NO_ERROR)
+ if (((int) test->expected_error) != NO_ERROR)
{
if (err == NULL)
g_error ("Error was expected but none given");
- if (err->code != test->expected_error)
+ if (err->code != (int) test->expected_error)
g_error ("Error %d was expected but %d given",
test->expected_error, err->code);
}
@@ -389,18 +525,18 @@ run_position_expression_timings (void)
clock_t start;
clock_t end;
MetaPositionExprEnv env;
-
+
#define ITERATIONS 100000
start = clock ();
-
+
iters = 0;
i = 0;
while (iters < ITERATIONS)
{
const PositionExpressionTest *test;
int x, y;
-
+
test = &position_expression_tests[i];
env.x = test->rect.x;
@@ -409,7 +545,18 @@ run_position_expression_timings (void)
env.height = test->rect.height;
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 = 5;
+ env.title_height = 5;
+ env.icon_width = 32;
+ env.icon_height = 32;
+ env.mini_icon_width = 16;
+ env.mini_icon_height = 16;
+ env.theme = NULL;
+
meta_parse_position_expression (test->expr,
&env,
&x, &y, NULL);
@@ -426,5 +573,5 @@ run_position_expression_timings (void)
ITERATIONS,
((double)end - (double)start) / CLOCKS_PER_SEC,
((double)end - (double)start) / CLOCKS_PER_SEC / (double) ITERATIONS);
-
+
}
diff --git a/src/theme.c b/src/theme.c
index e7add4e4..bab18c70 100644
--- a/src/theme.c
+++ b/src/theme.c
@@ -1,8 +1,8 @@
/* Metacity Theme Rendering */
-/*
+/*
* 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
@@ -12,7 +12,7 @@
* 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
@@ -21,6 +21,7 @@
#include <config.h>
#include "theme.h"
+#include "theme-parser.h"
#include "util.h"
#include "gradient.h"
#include <gtk/gtkwidget.h>
@@ -40,6 +41,8 @@
#define ALPHA_TO_UCHAR(d) ((unsigned char) ((d) * 255))
+#define DEBUG_FILL_STRUCT(s) memset ((s), 0xef, sizeof (*(s)))
+
static void
color_composite (const GdkColor *bg,
const GdkColor *fg,
@@ -47,7 +50,7 @@ color_composite (const GdkColor *bg,
GdkColor *color)
{
guint16 alpha;
-
+
*color = *bg;
alpha = alpha_d * 0xffff;
color->red = color->red + (((fg->red - color->red) * alpha + 0x8000) >> 16);
@@ -55,6 +58,15 @@ color_composite (const GdkColor *bg,
color->blue = color->blue + (((fg->blue - color->blue) * alpha + 0x8000) >> 16);
}
+static void
+init_border (GtkBorder *border)
+{
+ border->top = -1;
+ border->bottom = -1;
+ border->left = -1;
+ border->right = -1;
+}
+
MetaFrameLayout*
meta_frame_layout_new (void)
{
@@ -62,20 +74,150 @@ meta_frame_layout_new (void)
layout = g_new0 (MetaFrameLayout, 1);
+ layout->refcount = 1;
+
+ /* Fill with -1 values to detect invalid themes */
+ layout->left_width = -1;
+ layout->right_width = -1;
+ layout->bottom_height = -1;
+
+ init_border (&layout->title_border);
+
+ layout->title_vertical_pad = -1;
+
+ layout->right_titlebar_edge = -1;
+ layout->left_titlebar_edge = -1;
+
+ layout->button_width = -1;
+ layout->button_height = -1;
+
+ init_border (&layout->button_border);
+
+ return layout;
+}
+
+static gboolean
+validate_border (const GtkBorder *border,
+ const char **bad)
+{
+ *bad = NULL;
+
+ if (border->top < 0)
+ *bad = _("top");
+ else if (border->bottom < 0)
+ *bad = _("bottom");
+ else if (border->left < 0)
+ *bad = _("left");
+ else if (border->right < 0)
+ *bad = _("right");
+
+ return *bad == NULL;
+}
+
+static gboolean
+validate_geometry_value (int val,
+ const char *name,
+ GError **error)
+{
+ if (val < 0)
+ {
+ g_set_error (error, META_THEME_ERROR,
+ META_THEME_ERROR_FRAME_GEOMETRY,
+ _("frame geometry does not specify \"%s\" dimension"),
+ name);
+ return FALSE;
+ }
+ else
+ return TRUE;
+}
+
+static gboolean
+validate_geometry_border (const GtkBorder *border,
+ const char *name,
+ GError **error)
+{
+ const char *bad;
+
+ if (!validate_border (border, &bad))
+ {
+ g_set_error (error, META_THEME_ERROR,
+ META_THEME_ERROR_FRAME_GEOMETRY,
+ _("frame geometry does not specify dimension \"%s\" for border \"%s\""),
+ bad, name);
+ return FALSE;
+ }
+ else
+ return TRUE;
+}
+
+gboolean
+meta_frame_layout_validate (const MetaFrameLayout *layout,
+ GError **error)
+{
+ g_return_val_if_fail (layout != NULL, FALSE);
+
+#define CHECK_GEOMETRY_VALUE(vname) if (!validate_geometry_value (layout->vname, #vname, error)) return FALSE
+
+#define CHECK_GEOMETRY_BORDER(bname) if (!validate_geometry_border (&layout->bname, #bname, error)) return FALSE
+
+ CHECK_GEOMETRY_VALUE (left_width);
+ CHECK_GEOMETRY_VALUE (right_width);
+ CHECK_GEOMETRY_VALUE (bottom_height);
+
+ CHECK_GEOMETRY_BORDER (title_border);
+
+ CHECK_GEOMETRY_VALUE (title_vertical_pad);
+
+ CHECK_GEOMETRY_VALUE (right_titlebar_edge);
+ CHECK_GEOMETRY_VALUE (left_titlebar_edge);
+
+ CHECK_GEOMETRY_VALUE (button_width);
+ CHECK_GEOMETRY_VALUE (button_height);
+
+ CHECK_GEOMETRY_BORDER (button_border);
+
+ return TRUE;
+}
+
+MetaFrameLayout*
+meta_frame_layout_copy (const MetaFrameLayout *src)
+{
+ MetaFrameLayout *layout;
+
+ layout = g_new0 (MetaFrameLayout, 1);
+
+ *layout = *src;
+
+ layout->refcount = 1;
+
return layout;
}
void
-meta_frame_layout_free (MetaFrameLayout *layout)
+meta_frame_layout_ref (MetaFrameLayout *layout)
{
g_return_if_fail (layout != NULL);
-
- g_free (layout);
+
+ layout->refcount += 1;
+}
+
+void
+meta_frame_layout_unref (MetaFrameLayout *layout)
+{
+ g_return_if_fail (layout != NULL);
+ g_return_if_fail (layout->refcount > 0);
+
+ layout->refcount -= 1;
+
+ if (layout->refcount == 0)
+ {
+ DEBUG_FILL_STRUCT (layout);
+ g_free (layout);
+ }
}
void
meta_frame_layout_get_borders (const MetaFrameLayout *layout,
- GtkWidget *widget,
int text_height,
MetaFrameFlags flags,
int *top_height,
@@ -83,24 +225,22 @@ meta_frame_layout_get_borders (const MetaFrameLayout *layout,
int *left_width,
int *right_width)
{
- int buttons_height, title_height, spacer_height;
+ int buttons_height, title_height;
g_return_if_fail (top_height != NULL);
g_return_if_fail (bottom_height != NULL);
g_return_if_fail (left_width != NULL);
g_return_if_fail (right_width != NULL);
-
+
buttons_height = layout->button_height +
layout->button_border.top + layout->button_border.bottom;
title_height = text_height +
- layout->text_border.top + layout->text_border.bottom +
+ layout->title_vertical_pad +
layout->title_border.top + layout->title_border.bottom;
- spacer_height = layout->spacer_height;
if (top_height)
{
*top_height = MAX (buttons_height, title_height);
- *top_height = MAX (*top_height, spacer_height);
}
if (left_width)
@@ -119,7 +259,6 @@ meta_frame_layout_get_borders (const MetaFrameLayout *layout,
void
meta_frame_layout_calc_geometry (const MetaFrameLayout *layout,
- GtkWidget *widget,
int text_height,
MetaFrameFlags flags,
int client_width,
@@ -131,25 +270,25 @@ meta_frame_layout_calc_geometry (const MetaFrameLayout *layout,
int title_right_edge;
int width, height;
- meta_frame_layout_get_borders (layout, widget, text_height,
+ meta_frame_layout_get_borders (layout, text_height,
flags,
&fgeom->top_height,
&fgeom->bottom_height,
&fgeom->left_width,
&fgeom->right_width);
-
+
width = client_width + fgeom->left_width + fgeom->right_width;
height = client_height + fgeom->top_height + fgeom->bottom_height;
-
+
fgeom->width = width;
fgeom->height = height;
fgeom->top_titlebar_edge = layout->title_border.top;
fgeom->bottom_titlebar_edge = layout->title_border.bottom;
- fgeom->left_titlebar_edge = layout->left_inset;
- fgeom->right_titlebar_edge = layout->right_inset;
-
- x = width - layout->right_inset;
+ fgeom->left_titlebar_edge = layout->left_titlebar_edge;
+ fgeom->right_titlebar_edge = layout->right_titlebar_edge;
+
+ x = width - layout->right_titlebar_edge;
/* center buttons */
button_y = (fgeom->top_height -
@@ -190,7 +329,7 @@ meta_frame_layout_calc_geometry (const MetaFrameLayout *layout,
fgeom->max_rect.width = 0;
fgeom->max_rect.height = 0;
}
-
+
if ((flags & META_FRAME_ALLOWS_MINIMIZE) &&
x >= 0)
{
@@ -209,31 +348,11 @@ meta_frame_layout_calc_geometry (const MetaFrameLayout *layout,
fgeom->min_rect.height = 0;
}
- if ((fgeom->close_rect.width > 0 ||
- fgeom->max_rect.width > 0 ||
- fgeom->min_rect.width > 0) &&
- x >= 0)
- {
- fgeom->spacer_rect.x = x - layout->spacer_padding - layout->spacer_width;
- fgeom->spacer_rect.y = (fgeom->top_height - layout->spacer_height) / 2;
- fgeom->spacer_rect.width = layout->spacer_width;
- fgeom->spacer_rect.height = layout->spacer_height;
-
- x = fgeom->spacer_rect.x - layout->spacer_padding;
- }
- else
- {
- fgeom->spacer_rect.x = 0;
- fgeom->spacer_rect.y = 0;
- fgeom->spacer_rect.width = 0;
- fgeom->spacer_rect.height = 0;
- }
-
title_right_edge = x - layout->title_border.right;
-
+
/* Now x changes to be position from the left */
- x = layout->left_inset;
-
+ x = layout->left_titlebar_edge;
+
if (flags & META_FRAME_ALLOWS_MENU)
{
fgeom->menu_rect.x = x + layout->button_border.left;
@@ -268,7 +387,7 @@ meta_frame_layout_calc_geometry (const MetaFrameLayout *layout,
fgeom->max_rect.width = 0;
fgeom->max_rect.height = 0;
}
-
+
/* Check for minimize overlap */
if (fgeom->min_rect.width > 0 &&
fgeom->min_rect.x < (fgeom->menu_rect.x + fgeom->menu_rect.height))
@@ -277,16 +396,8 @@ meta_frame_layout_calc_geometry (const MetaFrameLayout *layout,
fgeom->min_rect.height = 0;
}
- /* Check for spacer overlap */
- if (fgeom->spacer_rect.width > 0 &&
- fgeom->spacer_rect.x < (fgeom->menu_rect.x + fgeom->menu_rect.height))
- {
- fgeom->spacer_rect.width = 0;
- fgeom->spacer_rect.height = 0;
- }
-
/* We always fill as much vertical space as possible with title rect,
- * rather than centering it like the buttons and spacer
+ * rather than centering it like the buttons
*/
fgeom->title_rect.x = x + layout->title_border.left;
fgeom->title_rect.y = layout->title_border.top;
@@ -312,17 +423,25 @@ meta_gradient_spec_new (MetaGradientType type)
spec->type = type;
spec->color_specs = NULL;
-
+
return spec;
}
+static void
+free_color_spec (gpointer spec, gpointer user_data)
+{
+ meta_color_spec_free (spec);
+}
+
void
meta_gradient_spec_free (MetaGradientSpec *spec)
{
g_return_if_fail (spec != NULL);
-
- g_slist_foreach (spec->color_specs, (GFunc) meta_color_spec_free, NULL);
+
+ g_slist_foreach (spec->color_specs, free_color_spec, NULL);
g_slist_free (spec->color_specs);
+
+ DEBUG_FILL_STRUCT (spec);
g_free (spec);
}
@@ -337,12 +456,12 @@ meta_gradient_spec_render (const MetaGradientSpec *spec,
GSList *tmp;
int i;
GdkPixbuf *pixbuf;
-
+
n_colors = g_slist_length (spec->color_specs);
if (n_colors == 0)
return NULL;
-
+
colors = g_new (GdkColor, n_colors);
i = 0;
@@ -350,11 +469,11 @@ meta_gradient_spec_render (const MetaGradientSpec *spec,
while (tmp != NULL)
{
meta_color_spec_render (tmp->data, widget, &colors[i]);
-
+
tmp = tmp->next;
++i;
}
-
+
pixbuf = meta_gradient_create_multi (width, height,
colors, n_colors,
spec->type);
@@ -364,6 +483,23 @@ meta_gradient_spec_render (const MetaGradientSpec *spec,
return pixbuf;
}
+gboolean
+meta_gradient_spec_validate (MetaGradientSpec *spec,
+ GError **error)
+{
+ g_return_val_if_fail (spec != NULL, FALSE);
+
+ if (g_slist_length (spec->color_specs) < 2)
+ {
+ g_set_error (error, META_THEME_ERROR,
+ META_THEME_ERROR_FAILED,
+ _("Gradients should have at least two colors"));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
MetaColorSpec*
meta_color_spec_new (MetaColorSpecType type)
{
@@ -372,7 +508,7 @@ meta_color_spec_new (MetaColorSpecType type)
int size;
size = G_STRUCT_OFFSET (MetaColorSpec, data);
-
+
switch (type)
{
case META_COLOR_SPEC_BASIC:
@@ -387,11 +523,11 @@ meta_color_spec_new (MetaColorSpecType type)
size += sizeof (dummy.data.blend);
break;
}
-
+
spec = g_malloc0 (size);
- spec->type = type;
-
+ spec->type = type;
+
return spec;
}
@@ -399,23 +535,26 @@ void
meta_color_spec_free (MetaColorSpec *spec)
{
g_return_if_fail (spec != NULL);
-
+
switch (spec->type)
{
- case META_COLOR_SPEC_BASIC:
+ case META_COLOR_SPEC_BASIC:
+ DEBUG_FILL_STRUCT (&spec->data.basic);
break;
case META_COLOR_SPEC_GTK:
+ DEBUG_FILL_STRUCT (&spec->data.gtk);
break;
case META_COLOR_SPEC_BLEND:
if (spec->data.blend.foreground)
- meta_color_spec_free (spec->data.blend.foreground);
+ meta_color_spec_free (spec->data.blend.foreground);
if (spec->data.blend.background)
meta_color_spec_free (spec->data.blend.background);
+ DEBUG_FILL_STRUCT (&spec->data.blend);
break;
}
-
+
g_free (spec);
}
@@ -423,13 +562,164 @@ MetaColorSpec*
meta_color_spec_new_from_string (const char *str,
GError **err)
{
- /* FIXME handle GTK colors, etc. */
MetaColorSpec *spec;
- spec = meta_color_spec_new (META_COLOR_SPEC_BASIC);
+ spec = NULL;
- gdk_color_parse (str, &spec->data.basic.color);
+ if (str[0] == 'g' && str[1] == 't' && str[2] == 'k' && str[3] == ':')
+ {
+ /* GTK color */
+ const char *bracket;
+ const char *end_bracket;
+ char *tmp;
+ GtkStateType state;
+ MetaGtkColorComponent component;
+
+ bracket = str;
+ while (*bracket && *bracket != '[')
+ ++bracket;
+
+ if (*bracket == '\0')
+ {
+ g_set_error (err, META_THEME_ERROR,
+ META_THEME_ERROR_FAILED,
+ _("GTK color specification must have the state in brackets, e.g. gtk:fg[NORMAL] where NORMAL is the state; could not parse \"%s\""),
+ str);
+ return NULL;
+ }
+
+ end_bracket = bracket;
+ ++end_bracket;
+ while (*end_bracket && *end_bracket != ']')
+ ++end_bracket;
+
+ if (*end_bracket == '\0')
+ {
+ g_set_error (err, META_THEME_ERROR,
+ META_THEME_ERROR_FAILED,
+ _("GTK color specification must have a close bracket after the state, e.g. gtk:fg[NORMAL] where NORMAL is the state; could not parse \"%s\""),
+ str);
+ return NULL;
+ }
+
+ tmp = g_strndup (bracket + 1, end_bracket - bracket - 1);
+ state = meta_gtk_state_from_string (tmp);
+ if (((int) state) == -1)
+ {
+ g_set_error (err, META_THEME_ERROR,
+ META_THEME_ERROR_FAILED,
+ _("Did not understand state \"%s\" in color specification"),
+ tmp);
+ g_free (tmp);
+ return NULL;
+ }
+ g_free (tmp);
+
+ tmp = g_strndup (str + 4, bracket - str - 4);
+ component = meta_color_component_from_string (tmp);
+ if (component == META_GTK_COLOR_LAST)
+ {
+ g_set_error (err, META_THEME_ERROR,
+ META_THEME_ERROR_FAILED,
+ _("Did not understand color component \"%s\" in color specification"),
+ tmp);
+ g_free (tmp);
+ return NULL;
+ }
+ g_free (tmp);
+
+ spec = meta_color_spec_new (META_COLOR_SPEC_GTK);
+ spec->data.gtk.state = state;
+ spec->data.gtk.component = component;
+ g_assert (spec->data.gtk.state < N_GTK_STATES);
+ g_assert (spec->data.gtk.component < META_GTK_COLOR_LAST);
+ }
+ else if (str[0] == 'b' && str[1] == 'l' && str[2] == 'e' && str[3] == 'n' &&
+ str[4] == 'd' && str[5] == '/')
+ {
+ /* blend */
+ char **split;
+ double alpha;
+ char *end;
+ MetaColorSpec *fg;
+ MetaColorSpec *bg;
+
+ split = g_strsplit (str, "/", 4);
+
+ if (split[0] == NULL || split[1] == NULL ||
+ split[2] == NULL || split[3] == NULL)
+ {
+ g_set_error (err, META_THEME_ERROR,
+ META_THEME_ERROR_FAILED,
+ _("Blend format is \"blend/bg_color/fg_color/alpha\", \"%s\" does not fit the format"),
+ str);
+ g_strfreev (split);
+ return NULL;
+ }
+
+ alpha = g_ascii_strtod (split[3], &end);
+ if (end == split[3])
+ {
+ g_set_error (err, META_THEME_ERROR,
+ META_THEME_ERROR_FAILED,
+ _("Could not parse alpha value \"%s\" in blended color"),
+ split[3]);
+ g_strfreev (split);
+ return NULL;
+ }
+
+ if (alpha < (0.0 - 1e6) || alpha > (1.0 + 1e6))
+ {
+ g_set_error (err, META_THEME_ERROR,
+ META_THEME_ERROR_FAILED,
+ _("Alpha value \"%s\" in blended color is not between 0.0 and 1.0"),
+ split[3]);
+ g_strfreev (split);
+ return NULL;
+ }
+
+ fg = NULL;
+ bg = NULL;
+
+ bg = meta_color_spec_new_from_string (split[1], err);
+ if (bg == NULL)
+ {
+ g_strfreev (split);
+ return NULL;
+ }
+
+ fg = meta_color_spec_new_from_string (split[2], err);
+ if (fg == NULL)
+ {
+ meta_color_spec_free (bg);
+ g_strfreev (split);
+ return NULL;
+ }
+
+ g_strfreev (split);
+
+ spec = meta_color_spec_new (META_COLOR_SPEC_BLEND);
+ spec->data.blend.alpha = alpha;
+ spec->data.blend.background = bg;
+ spec->data.blend.foreground = fg;
+ }
+ else
+ {
+ spec = meta_color_spec_new (META_COLOR_SPEC_BASIC);
+
+ if (!gdk_color_parse (str, &spec->data.basic.color))
+ {
+ g_set_error (err, META_THEME_ERROR,
+ META_THEME_ERROR_FAILED,
+ _("Could not parse color \"%s\""),
+ str);
+ meta_color_spec_free (spec);
+ return NULL;
+ }
+ }
+ g_assert (spec);
+
return spec;
}
@@ -437,11 +727,10 @@ MetaColorSpec*
meta_color_spec_new_gtk (MetaGtkColorComponent component,
GtkStateType state)
{
- /* FIXME handle GTK colors, etc. */
MetaColorSpec *spec;
spec = meta_color_spec_new (META_COLOR_SPEC_GTK);
-
+
spec->data.gtk.component = component;
spec->data.gtk.state = state;
@@ -456,7 +745,7 @@ meta_color_spec_render (MetaColorSpec *spec,
g_return_if_fail (spec != NULL);
g_return_if_fail (GTK_IS_WIDGET (widget));
g_return_if_fail (widget->style != NULL);
-
+
switch (spec->type)
{
case META_COLOR_SPEC_BASIC:
@@ -490,6 +779,9 @@ meta_color_spec_render (MetaColorSpec *spec,
case META_GTK_COLOR_TEXT_AA:
*color = widget->style->text_aa[spec->data.gtk.state];
break;
+ case META_GTK_COLOR_LAST:
+ g_assert_not_reached ();
+ break;
}
break;
@@ -516,6 +808,92 @@ typedef enum
POS_TOKEN_CLOSE_PAREN
} PosTokenType;
+typedef enum
+{
+ POS_OP_NONE,
+ POS_OP_ADD,
+ POS_OP_SUBTRACT,
+ POS_OP_MULTIPLY,
+ POS_OP_DIVIDE,
+ POS_OP_MOD,
+ POS_OP_MAX,
+ POS_OP_MIN
+} PosOperatorType;
+
+static const char*
+op_name (PosOperatorType type)
+{
+ switch (type)
+ {
+ case POS_OP_ADD:
+ return "+";
+ case POS_OP_SUBTRACT:
+ return "-";
+ case POS_OP_MULTIPLY:
+ return "*";
+ case POS_OP_DIVIDE:
+ return "/";
+ case POS_OP_MOD:
+ return "%";
+ case POS_OP_MAX:
+ return "`max`";
+ case POS_OP_MIN:
+ return "`min`";
+ case POS_OP_NONE:
+ break;
+ }
+
+ return "<unknown>";
+}
+
+static PosOperatorType
+op_from_string (const char *p,
+ int *len)
+{
+ *len = 0;
+
+ switch (*p)
+ {
+ case '+':
+ *len = 1;
+ return POS_OP_ADD;
+ case '-':
+ *len = 1;
+ return POS_OP_SUBTRACT;
+ case '*':
+ *len = 1;
+ return POS_OP_MULTIPLY;
+ case '/':
+ *len = 1;
+ return POS_OP_DIVIDE;
+ case '%':
+ *len = 1;
+ return POS_OP_MOD;
+
+ case '`':
+ if (p[0] == '`' &&
+ p[1] == 'm' &&
+ p[2] == 'a' &&
+ p[3] == 'x' &&
+ p[4] == '`')
+ {
+ *len = 5;
+ return POS_OP_MAX;
+ }
+ else if (p[0] == '`' &&
+ p[1] == 'm' &&
+ p[2] == 'i' &&
+ p[3] == 'n' &&
+ p[4] == '`')
+ {
+ *len = 5;
+ return POS_OP_MIN;
+ }
+ }
+
+ return POS_OP_NONE;
+}
+
typedef struct
{
PosTokenType type;
@@ -531,13 +909,13 @@ typedef struct
} d;
struct {
- char op;
+ PosOperatorType op;
} o;
struct {
char *name;
} v;
-
+
} d;
} PosToken;
@@ -552,7 +930,7 @@ free_tokens (PosToken *tokens,
/* n_tokens can be 0 since tokens may have been allocated more than
* it was initialized
*/
-
+
i = 0;
while (i < n_tokens)
{
@@ -573,21 +951,23 @@ parse_number (const char *p,
char *end;
gboolean is_float;
char *num_str;
-
+
while (*p && (*p == '.' || g_ascii_isdigit (*p)))
++p;
-
+
if (p == start)
{
- g_set_error (err, META_POSITION_EXPR_ERROR,
- META_POSITION_EXPR_ERROR_BAD_CHARACTER,
- _("Coordinate expression contains character '%c' which is not allowed"),
- *p);
+ char buf[7] = { '\0' };
+ buf[g_unichar_to_utf8 (g_utf8_get_char (p), buf)] = '\0';
+ g_set_error (err, META_THEME_ERROR,
+ META_THEME_ERROR_BAD_CHARACTER,
+ _("Coordinate expression contains character '%s' which is not allowed"),
+ buf);
return FALSE;
}
*end_return = p;
-
+
/* we need this to exclude floats like "1e6" */
num_str = g_strndup (start, p - start);
start = num_str;
@@ -598,7 +978,7 @@ parse_number (const char *p,
is_float = TRUE;
++start;
}
-
+
if (is_float)
{
next->type = POS_TOKEN_DOUBLE;
@@ -606,8 +986,8 @@ parse_number (const char *p,
if (end == num_str)
{
- g_set_error (err, META_POSITION_EXPR_ERROR,
- META_POSITION_EXPR_ERROR_FAILED,
+ g_set_error (err, META_THEME_ERROR,
+ META_THEME_ERROR_FAILED,
_("Coordinate expression contains floating point number '%s' which could not be parsed"),
num_str);
g_free (num_str);
@@ -620,8 +1000,8 @@ parse_number (const char *p,
next->d.i.val = strtol (num_str, &end, 10);
if (end == num_str)
{
- g_set_error (err, META_POSITION_EXPR_ERROR,
- META_POSITION_EXPR_ERROR_FAILED,
+ g_set_error (err, META_THEME_ERROR,
+ META_THEME_ERROR_FAILED,
_("Coordinate expression contains integer '%s' which could not be parsed"),
num_str);
g_free (num_str);
@@ -632,10 +1012,12 @@ parse_number (const char *p,
g_free (num_str);
g_assert (next->type == POS_TOKEN_INT || next->type == POS_TOKEN_DOUBLE);
-
+
return TRUE;
}
+#define IS_VARIABLE_CHAR(c) (g_ascii_isalpha ((c)) || (c) == '_')
+
static gboolean
pos_tokenize (const char *expr,
PosToken **tokens_p,
@@ -658,6 +1040,7 @@ pos_tokenize (const char *expr,
while (*p)
{
PosToken *next;
+ int len;
if (n_tokens == allocated)
{
@@ -666,7 +1049,7 @@ pos_tokenize (const char *expr,
}
next = &tokens[n_tokens];
-
+
switch (*p)
{
case '*':
@@ -674,11 +1057,25 @@ pos_tokenize (const char *expr,
case '+':
case '-': /* negative numbers aren't allowed so this is easy */
case '%':
+ case '`':
next->type = POS_TOKEN_OPERATOR;
- next->d.o.op = *p;
- ++n_tokens;
+ next->d.o.op = op_from_string (p, &len);
+ if (next->d.o.op != POS_OP_NONE)
+ {
+ ++n_tokens;
+ p = p + (len - 1); /* -1 since we ++p later */
+ }
+ else
+ {
+ g_set_error (err, META_THEME_ERROR,
+ META_THEME_ERROR_FAILED,
+ _("Coordinate expression contained unknown operator at the start of this text: \"%s\""),
+ p);
+
+ goto error;
+ }
break;
-
+
case '(':
next->type = POS_TOKEN_OPEN_PAREN;
++n_tokens;
@@ -694,11 +1091,11 @@ pos_tokenize (const char *expr,
break;
default:
- if (g_ascii_isalpha (*p))
+ if (IS_VARIABLE_CHAR (*p))
{
/* Assume variable */
const char *start = p;
- while (*p && g_ascii_isalpha (*p))
+ while (*p && IS_VARIABLE_CHAR (*p))
++p;
g_assert (p != start);
next->type = POS_TOKEN_VARIABLE;
@@ -713,11 +1110,11 @@ pos_tokenize (const char *expr,
if (!parse_number (p, &end, next, err))
goto error;
-
+
++n_tokens;
p = end - 1; /* -1 since we ++p again at the end of while loop */
}
-
+
break;
}
@@ -726,16 +1123,16 @@ pos_tokenize (const char *expr,
if (n_tokens == 0)
{
- g_set_error (err, META_POSITION_EXPR_ERROR,
- META_POSITION_EXPR_ERROR_FAILED,
+ g_set_error (err, META_THEME_ERROR,
+ META_THEME_ERROR_FAILED,
_("Coordinate expression was empty or not understood"));
-
+
goto error;
}
*tokens_p = tokens;
*n_tokens_p = n_tokens;
-
+
return TRUE;
error:
@@ -757,7 +1154,7 @@ debug_print_tokens (PosToken *tokens,
PosToken *t = &tokens[i];
g_print (" ");
-
+
switch (t->type)
{
case POS_TOKEN_INT:
@@ -776,7 +1173,7 @@ debug_print_tokens (PosToken *tokens,
g_print ("\"%s\"", t->d.v.name);
break;
case POS_TOKEN_OPERATOR:
- g_print ("\"%c\"", t->d.o.op);
+ g_print ("\"%s\"", op_name (t->d.o.op));
break;
}
@@ -787,7 +1184,7 @@ debug_print_tokens (PosToken *tokens,
}
typedef enum
-{
+{
POS_EXPR_INT,
POS_EXPR_DOUBLE,
POS_EXPR_OPERATOR
@@ -822,10 +1219,10 @@ debug_print_exprs (PosExpr *exprs,
g_print (" %g", exprs[i].d.double_val);
break;
case POS_EXPR_OPERATOR:
- g_print (" %c", exprs[i].d.operator);
+ g_print (" %s", op_name (exprs[i].d.operator));
break;
}
-
+
++i;
}
g_print ("\n");
@@ -834,7 +1231,7 @@ debug_print_exprs (PosExpr *exprs,
static gboolean
do_operation (PosExpr *a,
PosExpr *b,
- char op,
+ PosOperatorType op,
GError **err)
{
/* Promote types to double if required */
@@ -854,71 +1251,89 @@ do_operation (PosExpr *a,
}
g_assert (a->type == b->type);
-
+
if (a->type == POS_EXPR_INT)
{
switch (op)
{
- case '*':
+ case POS_OP_MULTIPLY:
a->d.int_val = a->d.int_val * b->d.int_val;
break;
- case '/':
+ case POS_OP_DIVIDE:
if (b->d.int_val == 0)
{
- g_set_error (err, META_POSITION_EXPR_ERROR,
- META_POSITION_EXPR_ERROR_DIVIDE_BY_ZERO,
+ g_set_error (err, META_THEME_ERROR,
+ META_THEME_ERROR_DIVIDE_BY_ZERO,
_("Coordinate expression results in division by zero"));
return FALSE;
}
a->d.int_val = a->d.int_val / b->d.int_val;
break;
- case '%':
+ case POS_OP_MOD:
if (b->d.int_val == 0)
{
- g_set_error (err, META_POSITION_EXPR_ERROR,
- META_POSITION_EXPR_ERROR_DIVIDE_BY_ZERO,
+ g_set_error (err, META_THEME_ERROR,
+ META_THEME_ERROR_DIVIDE_BY_ZERO,
_("Coordinate expression results in division by zero"));
return FALSE;
}
a->d.int_val = a->d.int_val % b->d.int_val;
break;
- case '+':
+ case POS_OP_ADD:
a->d.int_val = a->d.int_val + b->d.int_val;
break;
- case '-':
+ case POS_OP_SUBTRACT:
a->d.int_val = a->d.int_val - b->d.int_val;
break;
+ case POS_OP_MAX:
+ a->d.int_val = MAX (a->d.int_val, b->d.int_val);
+ break;
+ case POS_OP_MIN:
+ a->d.int_val = MIN (a->d.int_val, b->d.int_val);
+ break;
+ case POS_OP_NONE:
+ g_assert_not_reached ();
+ break;
}
}
else if (a->type == POS_EXPR_DOUBLE)
{
switch (op)
{
- case '*':
+ case POS_OP_MULTIPLY:
a->d.double_val = a->d.double_val * b->d.double_val;
break;
- case '/':
+ case POS_OP_DIVIDE:
if (b->d.double_val == 0.0)
{
- g_set_error (err, META_POSITION_EXPR_ERROR,
- META_POSITION_EXPR_ERROR_DIVIDE_BY_ZERO,
+ g_set_error (err, META_THEME_ERROR,
+ META_THEME_ERROR_DIVIDE_BY_ZERO,
_("Coordinate expression results in division by zero"));
return FALSE;
}
a->d.double_val = a->d.double_val / b->d.double_val;
break;
- case '%':
- g_set_error (err, META_POSITION_EXPR_ERROR,
- META_POSITION_EXPR_ERROR_MOD_ON_FLOAT,
+ case POS_OP_MOD:
+ g_set_error (err, META_THEME_ERROR,
+ META_THEME_ERROR_MOD_ON_FLOAT,
_("Coordinate expression tries to use mod operator on a floating-point number"));
return FALSE;
break;
- case '+':
+ case POS_OP_ADD:
a->d.double_val = a->d.double_val + b->d.double_val;
break;
- case '-':
+ case POS_OP_SUBTRACT:
a->d.double_val = a->d.double_val - b->d.double_val;
break;
+ case POS_OP_MAX:
+ a->d.double_val = MAX (a->d.double_val, b->d.double_val);
+ break;
+ case POS_OP_MIN:
+ a->d.double_val = MIN (a->d.double_val, b->d.double_val);
+ break;
+ case POS_OP_NONE:
+ g_assert_not_reached ();
+ break;
}
}
else
@@ -939,13 +1354,13 @@ do_operations (PosExpr *exprs,
g_print ("Doing prec %d ops on %d exprs\n", precedence, *n_exprs);
debug_print_exprs (exprs, *n_exprs);
#endif
-
+
i = 1;
while (i < *n_exprs)
{
gboolean compress;
- /* exprs[i-1] first operand
+ /* exprs[i-1] first operand
* exprs[i] operator
* exprs[i+1] second operand
*
@@ -956,35 +1371,35 @@ do_operations (PosExpr *exprs,
if (exprs[i-1].type == POS_EXPR_OPERATOR)
{
- g_set_error (err, META_POSITION_EXPR_ERROR,
- META_POSITION_EXPR_ERROR_FAILED,
- _("Coordinate expression has an operator \"%c\" where an operand was expected"),
- exprs[i-1].d.operator);
+ g_set_error (err, META_THEME_ERROR,
+ META_THEME_ERROR_FAILED,
+ _("Coordinate expression has an operator \"%s\" where an operand was expected"),
+ op_name (exprs[i-1].d.operator));
return FALSE;
}
-
+
if (exprs[i].type != POS_EXPR_OPERATOR)
{
- g_set_error (err, META_POSITION_EXPR_ERROR,
- META_POSITION_EXPR_ERROR_FAILED,
+ g_set_error (err, META_THEME_ERROR,
+ META_THEME_ERROR_FAILED,
_("Coordinate expression had an operand where an operator was expected"));
return FALSE;
}
if (i == (*n_exprs - 1))
{
- g_set_error (err, META_POSITION_EXPR_ERROR,
- META_POSITION_EXPR_ERROR_FAILED,
+ g_set_error (err, META_THEME_ERROR,
+ META_THEME_ERROR_FAILED,
_("Coordinate expression ended with an operator instead of an operand"));
return FALSE;
}
g_assert ((i+1) < *n_exprs);
-
+
if (exprs[i+1].type == POS_EXPR_OPERATOR)
{
- g_set_error (err, META_POSITION_EXPR_ERROR,
- META_POSITION_EXPR_ERROR_FAILED,
+ g_set_error (err, META_THEME_ERROR,
+ META_THEME_ERROR_FAILED,
_("Coordinate expression has operator \"%c\" following operator \"%c\" with no operand in between"),
exprs[i+1].d.operator,
exprs[i].d.operator);
@@ -992,14 +1407,15 @@ do_operations (PosExpr *exprs,
}
compress = FALSE;
-
- if (precedence == 1)
+
+ switch (precedence)
{
+ case 2:
switch (exprs[i].d.operator)
{
- case '*':
- case '/':
- case '%':
+ case POS_OP_DIVIDE:
+ case POS_OP_MOD:
+ case POS_OP_MULTIPLY:
compress = TRUE;
if (!do_operation (&exprs[i-1], &exprs[i+1],
exprs[i].d.operator,
@@ -1007,13 +1423,26 @@ do_operations (PosExpr *exprs,
return FALSE;
break;
}
- }
- else if (precedence == 0)
- {
+ break;
+ case 1:
+ switch (exprs[i].d.operator)
+ {
+ case POS_OP_ADD:
+ case POS_OP_SUBTRACT:
+ compress = TRUE;
+ if (!do_operation (&exprs[i-1], &exprs[i+1],
+ exprs[i].d.operator,
+ err))
+ return FALSE;
+ break;
+ }
+ break;
+ /* I have no rationale at all for making these low-precedence */
+ case 0:
switch (exprs[i].d.operator)
{
- case '-':
- case '+':
+ case POS_OP_MAX:
+ case POS_OP_MIN:
compress = TRUE;
if (!do_operation (&exprs[i-1], &exprs[i+1],
exprs[i].d.operator,
@@ -1021,6 +1450,7 @@ do_operations (PosExpr *exprs,
return FALSE;
break;
}
+ break;
}
if (compress)
@@ -1037,7 +1467,7 @@ do_operations (PosExpr *exprs,
g_memmove (&exprs[i], &exprs[i+2],
sizeof (PosExpr) * (*n_exprs - i - 2));
}
-
+
*n_exprs -= 2;
}
else
@@ -1064,16 +1494,19 @@ pos_eval_helper (PosToken *tokens,
int i;
PosExpr exprs[MAX_EXPRS];
int n_exprs;
-
+ int ival;
+ double dval;
+ int precedence;
+
#if 0
g_print ("Pos eval helper on %d tokens:\n", n_tokens);
debug_print_tokens (tokens, n_tokens);
#endif
-
+
/* Our first goal is to get a list of PosExpr, essentially
* substituting variables and handling parentheses.
*/
-
+
first_paren = 0;
paren_level = 0;
n_exprs = 0;
@@ -1084,8 +1517,8 @@ pos_eval_helper (PosToken *tokens,
if (n_exprs >= MAX_EXPRS)
{
- g_set_error (err, META_POSITION_EXPR_ERROR,
- META_POSITION_EXPR_ERROR_FAILED,
+ g_set_error (err, META_THEME_ERROR,
+ META_THEME_ERROR_FAILED,
_("Coordinate expression parser overflowed its buffer, this is really a Metacity bug, but are you sure you need a huge expression like that?"));
return FALSE;
}
@@ -1113,15 +1546,19 @@ pos_eval_helper (PosToken *tokens,
break;
case POS_TOKEN_CLOSE_PAREN:
- g_set_error (err, META_POSITION_EXPR_ERROR,
- META_POSITION_EXPR_ERROR_BAD_PARENS,
+ g_set_error (err, META_THEME_ERROR,
+ META_THEME_ERROR_BAD_PARENS,
_("Coordinate expression had a close parenthesis with no open parenthesis"));
return FALSE;
break;
-
+
case POS_TOKEN_VARIABLE:
exprs[n_exprs].type = POS_EXPR_INT;
-
+
+ /* FIXME we should just dump all this crap
+ * in a hash, maybe keep width/height out
+ * for optimization purposes
+ */
if (strcmp (t->d.v.name, "width") == 0)
exprs[n_exprs].d.int_val = env->width;
else if (strcmp (t->d.v.name, "height") == 0)
@@ -1132,11 +1569,49 @@ pos_eval_helper (PosToken *tokens,
else if (env->object_height >= 0 &&
strcmp (t->d.v.name, "object_height") == 0)
exprs[n_exprs].d.int_val = env->object_height;
+ else if (strcmp (t->d.v.name, "left_width") == 0)
+ exprs[n_exprs].d.int_val = env->left_width;
+ else if (strcmp (t->d.v.name, "right_width") == 0)
+ exprs[n_exprs].d.int_val = env->right_width;
+ else if (strcmp (t->d.v.name, "top_height") == 0)
+ exprs[n_exprs].d.int_val = env->top_height;
+ else if (strcmp (t->d.v.name, "bottom_height") == 0)
+ exprs[n_exprs].d.int_val = env->bottom_height;
+ else if (strcmp (t->d.v.name, "mini_icon_width") == 0)
+ exprs[n_exprs].d.int_val = env->mini_icon_width;
+ else if (strcmp (t->d.v.name, "mini_icon_height") == 0)
+ exprs[n_exprs].d.int_val = env->mini_icon_height;
+ else if (strcmp (t->d.v.name, "icon_width") == 0)
+ exprs[n_exprs].d.int_val = env->icon_width;
+ else if (strcmp (t->d.v.name, "icon_height") == 0)
+ exprs[n_exprs].d.int_val = env->icon_height;
+ else if (strcmp (t->d.v.name, "title_width") == 0)
+ exprs[n_exprs].d.int_val = env->title_width;
+ else if (strcmp (t->d.v.name, "title_height") == 0)
+ exprs[n_exprs].d.int_val = env->title_height;
+ /* In practice we only hit this code on initial theme
+ * parse; after that we always optimize constants away
+ */
+ else if (env->theme &&
+ meta_theme_lookup_int_constant (env->theme,
+ t->d.v.name,
+ &ival))
+ {
+ exprs[n_exprs].d.int_val = ival;
+ }
+ else if (env->theme &&
+ meta_theme_lookup_float_constant (env->theme,
+ t->d.v.name,
+ &dval))
+ {
+ exprs[n_exprs].type = POS_EXPR_DOUBLE;
+ exprs[n_exprs].d.double_val = dval;
+ }
else
{
- g_set_error (err, META_POSITION_EXPR_ERROR,
- META_POSITION_EXPR_ERROR_UNKNOWN_VARIABLE,
- _("Coordinate expression had unknown variable \"%s\""),
+ g_set_error (err, META_THEME_ERROR,
+ META_THEME_ERROR_UNKNOWN_VARIABLE,
+ _("Coordinate expression had unknown variable or constant \"%s\""),
t->d.v.name);
return FALSE;
}
@@ -1147,13 +1622,13 @@ pos_eval_helper (PosToken *tokens,
exprs[n_exprs].type = POS_EXPR_OPERATOR;
exprs[n_exprs].d.operator = t->d.o.op;
++n_exprs;
- break;
+ break;
}
}
else
{
g_assert (paren_level > 0);
-
+
switch (t->type)
{
case POS_TOKEN_INT:
@@ -1161,7 +1636,7 @@ pos_eval_helper (PosToken *tokens,
case POS_TOKEN_VARIABLE:
case POS_TOKEN_OPERATOR:
break;
-
+
case POS_TOKEN_OPEN_PAREN:
++paren_level;
break;
@@ -1176,13 +1651,13 @@ pos_eval_helper (PosToken *tokens,
&exprs[n_exprs],
err))
return FALSE;
-
+
++n_exprs;
}
-
+
--paren_level;
break;
-
+
}
}
@@ -1191,10 +1666,10 @@ pos_eval_helper (PosToken *tokens,
if (paren_level > 0)
{
- g_set_error (err, META_POSITION_EXPR_ERROR,
- META_POSITION_EXPR_ERROR_BAD_PARENS,
+ g_set_error (err, META_THEME_ERROR,
+ META_THEME_ERROR_BAD_PARENS,
_("Coordinate expression had an open parenthesis with no close parenthesis"));
- return FALSE;
+ return FALSE;
}
/* Now we have no parens and no vars; so we just do all the multiplies
@@ -1202,19 +1677,20 @@ pos_eval_helper (PosToken *tokens,
*/
if (n_exprs == 0)
{
- g_set_error (err, META_POSITION_EXPR_ERROR,
- META_POSITION_EXPR_ERROR_FAILED,
+ g_set_error (err, META_THEME_ERROR,
+ META_THEME_ERROR_FAILED,
_("Coordinate expression doesn't seem to have any operators or operands"));
return FALSE;
}
/* precedence 1 ops */
- if (!do_operations (exprs, &n_exprs, 1, err))
- return FALSE;
-
- /* precedence 0 ops */
- if (!do_operations (exprs, &n_exprs, 0, err))
- return FALSE;
+ precedence = 2;
+ while (precedence >= 0)
+ {
+ if (!do_operations (exprs, &n_exprs, precedence, err))
+ return FALSE;
+ --precedence;
+ }
g_assert (n_exprs == 1);
@@ -1237,7 +1713,7 @@ pos_eval (PosToken *tokens,
GError **err)
{
PosExpr expr;
-
+
*val_p = 0;
if (pos_eval_helper (tokens, n_tokens, env, &expr, err))
@@ -1265,7 +1741,7 @@ pos_eval (PosToken *tokens,
/* We always return both X and Y, but only one will be meaningful in
* most contexts.
*/
-
+
gboolean
meta_parse_position_expression (const char *expr,
const MetaPositionExprEnv *env,
@@ -1282,7 +1758,7 @@ meta_parse_position_expression (const char *expr,
PosToken *tokens;
int n_tokens;
int val;
-
+
if (!pos_tokenize (expr, &tokens, &n_tokens, err))
{
g_assert (err == NULL || *err != NULL);
@@ -1296,9 +1772,9 @@ meta_parse_position_expression (const char *expr,
if (pos_eval (tokens, n_tokens, env, &val, err))
{
- if (x_return)
+ if (x_return)
*x_return = env->x + val;
- if (y_return)
+ if (y_return)
*y_return = env->y + val;
free_tokens (tokens, n_tokens);
return TRUE;
@@ -1321,7 +1797,7 @@ meta_parse_size_expression (const char *expr,
PosToken *tokens;
int n_tokens;
int val;
-
+
if (!pos_tokenize (expr, &tokens, &n_tokens, err))
{
g_assert (err == NULL || *err != NULL);
@@ -1335,8 +1811,8 @@ meta_parse_size_expression (const char *expr,
if (pos_eval (tokens, n_tokens, env, &val, err))
{
- if (val_return)
- *val_return = val;
+ if (val_return)
+ *val_return = MAX (val, 1); /* require that sizes be at least 1x1 */
free_tokens (tokens, n_tokens);
return TRUE;
}
@@ -1348,13 +1824,99 @@ meta_parse_size_expression (const char *expr,
}
}
+/* To do this we tokenize, replace variable tokens
+ * that are constants, then reassemble. The purpose
+ * here is to optimize expressions so we don't do hash
+ * lookups to eval them. Obviously it's a tradeoff that
+ * slows down theme load times.
+ */
+char*
+meta_theme_replace_constants (MetaTheme *theme,
+ const char *expr,
+ GError **err)
+{
+ PosToken *tokens;
+ int n_tokens;
+ int i;
+ GString *str;
+ char buf[G_ASCII_DTOSTR_BUF_SIZE];
+ double dval;
+ int ival;
+
+ if (!pos_tokenize (expr, &tokens, &n_tokens, err))
+ {
+ g_assert (err == NULL || *err != NULL);
+ return NULL;
+ }
+
+#if 0
+ g_print ("Tokenized \"%s\" to --->\n", expr);
+ debug_print_tokens (tokens, n_tokens);
+#endif
+
+ str = g_string_new (NULL);
+
+ i = 0;
+ while (i < n_tokens)
+ {
+ PosToken *t = &tokens[i];
+
+ /* spaces so we don't accidentally merge variables
+ * or anything like that
+ */
+ if (i > 0)
+ g_string_append_c (str, ' ');
+
+ switch (t->type)
+ {
+ case POS_TOKEN_INT:
+ g_string_append_printf (str, "%d", t->d.i.val);
+ break;
+ case POS_TOKEN_DOUBLE:
+ g_ascii_formatd (buf, G_ASCII_DTOSTR_BUF_SIZE,
+ "%g", t->d.d.val);
+ g_string_append (str, buf);
+ break;
+ case POS_TOKEN_OPEN_PAREN:
+ g_string_append_c (str, '(');
+ break;
+ case POS_TOKEN_CLOSE_PAREN:
+ g_string_append_c (str, ')');
+ break;
+ case POS_TOKEN_VARIABLE:
+ if (meta_theme_lookup_int_constant (theme, t->d.v.name, &ival))
+ g_string_append_printf (str, "%d", ival);
+ else if (meta_theme_lookup_float_constant (theme, t->d.v.name, &dval))
+ {
+ g_ascii_formatd (buf, G_ASCII_DTOSTR_BUF_SIZE,
+ "%g", dval);
+ g_string_append (str, buf);
+ }
+ else
+ {
+ g_string_append (str, t->d.v.name);
+ }
+ break;
+ case POS_TOKEN_OPERATOR:
+ g_string_append (str, op_name (t->d.o.op));
+ break;
+ }
+
+ ++i;
+ }
+
+ free_tokens (tokens, n_tokens);
+
+ return g_string_free (str, FALSE);
+}
+
static int
parse_x_position_unchecked (const char *expr,
const MetaPositionExprEnv *env)
{
int retval;
- GError *error;
-
+ GError *error;
+
retval = 0;
error = NULL;
if (!meta_parse_position_expression (expr, env,
@@ -1366,7 +1928,7 @@ parse_x_position_unchecked (const char *expr,
g_error_free (error);
}
-
+
return retval;
}
@@ -1375,8 +1937,8 @@ parse_y_position_unchecked (const char *expr,
const MetaPositionExprEnv *env)
{
int retval;
- GError *error;
-
+ GError *error;
+
retval = 0;
error = NULL;
if (!meta_parse_position_expression (expr, env,
@@ -1388,7 +1950,7 @@ parse_y_position_unchecked (const char *expr,
g_error_free (error);
}
-
+
return retval;
}
@@ -1397,8 +1959,8 @@ parse_size_unchecked (const char *expr,
MetaPositionExprEnv *env)
{
int retval;
- GError *error;
-
+ GError *error;
+
retval = 0;
error = NULL;
if (!meta_parse_size_expression (expr, env,
@@ -1409,7 +1971,7 @@ parse_size_unchecked (const char *expr,
g_error_free (error);
}
-
+
return retval;
}
@@ -1422,7 +1984,7 @@ meta_draw_op_new (MetaDrawType type)
int size;
size = G_STRUCT_OFFSET (MetaDrawOp, data);
-
+
switch (type)
{
case META_DRAW_LINE:
@@ -1437,6 +1999,10 @@ meta_draw_op_new (MetaDrawType type)
size += sizeof (dummy.data.arc);
break;
+ case META_DRAW_CLIP:
+ size += sizeof (dummy.data.clip);
+ break;
+
case META_DRAW_TINT:
size += sizeof (dummy.data.tint);
break;
@@ -1448,7 +2014,7 @@ meta_draw_op_new (MetaDrawType type)
case META_DRAW_IMAGE:
size += sizeof (dummy.data.image);
break;
-
+
case META_DRAW_GTK_ARROW:
size += sizeof (dummy.data.gtk_arrow);
break;
@@ -1460,12 +2026,23 @@ meta_draw_op_new (MetaDrawType type)
case META_DRAW_GTK_VLINE:
size += sizeof (dummy.data.gtk_vline);
break;
+
+ case META_DRAW_ICON:
+ size += sizeof (dummy.data.icon);
+ break;
+
+ case META_DRAW_TITLE:
+ size += sizeof (dummy.data.title);
+ break;
+ case META_DRAW_OP_LIST:
+ size += sizeof (dummy.data.op_list);
+ break;
}
-
+
op = g_malloc0 (size);
- op->type = type;
-
+ op->type = type;
+
return op;
}
@@ -1473,7 +2050,7 @@ void
meta_draw_op_free (MetaDrawOp *op)
{
g_return_if_fail (op != NULL);
-
+
switch (op->type)
{
case META_DRAW_LINE:
@@ -1501,6 +2078,13 @@ meta_draw_op_free (MetaDrawOp *op)
g_free (op->data.arc.height);
break;
+ case META_DRAW_CLIP:
+ g_free (op->data.clip.x);
+ g_free (op->data.clip.y);
+ g_free (op->data.clip.width);
+ g_free (op->data.clip.height);
+ break;
+
case META_DRAW_TINT:
if (op->data.tint.color_spec)
meta_color_spec_free (op->data.tint.color_spec);
@@ -1528,7 +2112,7 @@ meta_draw_op_free (MetaDrawOp *op)
g_free (op->data.image.height);
break;
-
+
case META_DRAW_GTK_ARROW:
g_free (op->data.gtk_arrow.x);
g_free (op->data.gtk_arrow.y);
@@ -1548,6 +2132,28 @@ meta_draw_op_free (MetaDrawOp *op)
g_free (op->data.gtk_vline.y1);
g_free (op->data.gtk_vline.y2);
break;
+
+ case META_DRAW_ICON:
+ g_free (op->data.icon.x);
+ g_free (op->data.icon.y);
+ g_free (op->data.icon.width);
+ g_free (op->data.icon.height);
+ break;
+
+ case META_DRAW_TITLE:
+ if (op->data.title.color_spec)
+ meta_color_spec_free (op->data.title.color_spec);
+ g_free (op->data.title.x);
+ g_free (op->data.title.y);
+ break;
+ case META_DRAW_OP_LIST:
+ if (op->data.op_list.op_list)
+ meta_draw_op_list_unref (op->data.op_list.op_list);
+ g_free (op->data.op_list.x);
+ g_free (op->data.op_list.y);
+ g_free (op->data.op_list.width);
+ g_free (op->data.op_list.height);
+ break;
}
g_free (op);
@@ -1561,15 +2167,15 @@ get_gc_for_primitive (GtkWidget *widget,
int line_width)
{
GdkGC *gc;
- GdkGCValues values;
+ GdkGCValues values;
GdkColor color;
-
+
meta_color_spec_render (color_spec, widget, &color);
-
+
values.foreground = color;
gdk_rgb_find_color (widget->style->colormap, &values.foreground);
values.line_width = line_width;
-
+
gc = gdk_gc_new_with_values (drawable, &values,
GDK_GC_FOREGROUND | GDK_GC_LINE_WIDTH);
@@ -1591,7 +2197,7 @@ multiply_alpha (GdkPixbuf *pixbuf,
int row;
g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
-
+
if (alpha == 255)
return pixbuf;
@@ -1601,7 +2207,7 @@ multiply_alpha (GdkPixbuf *pixbuf,
g_object_unref (G_OBJECT (pixbuf));
pixbuf = new_pixbuf;
}
-
+
pixels = gdk_pixbuf_get_pixels (pixbuf);
rowstride = gdk_pixbuf_get_rowstride (pixbuf);
height = gdk_pixbuf_get_height (pixbuf);
@@ -1625,10 +2231,10 @@ multiply_alpha (GdkPixbuf *pixbuf,
* pixbuf contains 0, it should remain 0.
*/
*p = (*p * alpha) / 65025; /* (*p / 255) * (alpha / 255); */
-
+
++p; /* skip A */
}
-
+
++row;
}
@@ -1646,11 +2252,14 @@ render_pixbuf (GdkDrawable *drawable,
* mask, so we have to go through some BS
*/
/* FIXME once GTK 1.3.13 has been out a while we can use
- * render_to_drawable() which now does alpha with clip
+ * render_to_drawable() which now does alpha with clip.
+ *
+ * Though the gdk_rectangle_intersect() check may be a useful
+ * optimization anyway.
*/
GdkRectangle pixbuf_rect;
GdkRectangle draw_rect;
-
+
pixbuf_rect.x = x;
pixbuf_rect.y = y;
pixbuf_rect.width = gdk_pixbuf_get_width (pixbuf);
@@ -1666,7 +2275,7 @@ render_pixbuf (GdkDrawable *drawable,
{
draw_rect = pixbuf_rect;
}
-
+
gdk_pixbuf_render_to_drawable_alpha (pixbuf,
drawable,
draw_rect.x - pixbuf_rect.x,
@@ -1682,8 +2291,40 @@ render_pixbuf (GdkDrawable *drawable,
}
static GdkPixbuf*
+scale_and_alpha_pixbuf (GdkPixbuf *src,
+ double alpha,
+ int width,
+ int height)
+{
+ GdkPixbuf *pixbuf;
+
+ pixbuf = NULL;
+
+
+ pixbuf = src;
+ if (gdk_pixbuf_get_width (pixbuf) == width &&
+ gdk_pixbuf_get_height (pixbuf) == height)
+ {
+ g_object_ref (G_OBJECT (pixbuf));
+ }
+ else
+ {
+ pixbuf = gdk_pixbuf_scale_simple (pixbuf,
+ width, height,
+ GDK_INTERP_BILINEAR);
+ }
+
+ if (pixbuf)
+ pixbuf = multiply_alpha (pixbuf,
+ ALPHA_TO_UCHAR (alpha));
+
+ return pixbuf;
+}
+
+static GdkPixbuf*
draw_op_as_pixbuf (const MetaDrawOp *op,
GtkWidget *widget,
+ const MetaDrawInfo *info,
int width,
int height)
{
@@ -1694,7 +2335,7 @@ draw_op_as_pixbuf (const MetaDrawOp *op,
GdkPixbuf *pixbuf;
pixbuf = NULL;
-
+
switch (op->type)
{
case META_DRAW_LINE:
@@ -1708,11 +2349,11 @@ draw_op_as_pixbuf (const MetaDrawOp *op,
meta_color_spec_render (op->data.rectangle.color_spec,
widget,
&color);
-
+
pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
FALSE,
8, width, height);
-
+
gdk_pixbuf_fill (pixbuf, GDK_COLOR_RGBA (color));
}
break;
@@ -1720,15 +2361,18 @@ draw_op_as_pixbuf (const MetaDrawOp *op,
case META_DRAW_ARC:
break;
+ case META_DRAW_CLIP:
+ break;
+
case META_DRAW_TINT:
{
GdkColor color;
guint32 rgba;
-
+
meta_color_spec_render (op->data.rectangle.color_spec,
widget,
&color);
-
+
pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
ALPHA_TO_UCHAR (op->data.tint.alpha) < 255,
8, width, height);
@@ -1736,7 +2380,7 @@ draw_op_as_pixbuf (const MetaDrawOp *op,
rgba = GDK_COLOR_RGBA (color);
rgba &= ~0xff;
rgba |= ALPHA_TO_UCHAR (op->data.tint.alpha);
-
+
gdk_pixbuf_fill (pixbuf, rgba);
}
break;
@@ -1752,103 +2396,108 @@ draw_op_as_pixbuf (const MetaDrawOp *op,
break;
case META_DRAW_IMAGE:
- {
- switch (op->data.image.scale_mode)
- {
- case META_SCALE_NONE:
- pixbuf = op->data.image.pixbuf;
- g_object_ref (G_OBJECT (pixbuf));
- break;
- case META_SCALE_VERTICALLY:
- pixbuf = op->data.image.pixbuf;
- if (gdk_pixbuf_get_height (pixbuf) == height)
- {
- g_object_ref (G_OBJECT (pixbuf));
- }
- else
- {
- pixbuf = gdk_pixbuf_scale_simple (pixbuf,
- gdk_pixbuf_get_width (pixbuf),
- height,
- GDK_INTERP_BILINEAR);
- }
- break;
- case META_SCALE_HORIZONTALLY:
- pixbuf = op->data.image.pixbuf;
- if (gdk_pixbuf_get_width (pixbuf) == width)
- {
- g_object_ref (G_OBJECT (pixbuf));
- }
- else
- {
- pixbuf = gdk_pixbuf_scale_simple (pixbuf,
- width,
- gdk_pixbuf_get_height (pixbuf),
- GDK_INTERP_BILINEAR);
- }
- break;
- case META_SCALE_BOTH:
- pixbuf = op->data.image.pixbuf;
- if (gdk_pixbuf_get_width (pixbuf) == width &&
- gdk_pixbuf_get_height (pixbuf) == height)
- {
- g_object_ref (G_OBJECT (pixbuf));
- }
- else
- {
- pixbuf = gdk_pixbuf_scale_simple (pixbuf,
- width, height,
- GDK_INTERP_BILINEAR);
- }
- break;
- }
-
- pixbuf = multiply_alpha (pixbuf,
- ALPHA_TO_UCHAR (op->data.image.alpha));
+ {
+ pixbuf = scale_and_alpha_pixbuf (op->data.image.pixbuf,
+ op->data.image.alpha,
+ width, height);
}
break;
-
+
case META_DRAW_GTK_ARROW:
case META_DRAW_GTK_BOX:
case META_DRAW_GTK_VLINE:
- break;
+ break;
+
+ case META_DRAW_ICON:
+ if (info->mini_icon &&
+ width <= gdk_pixbuf_get_width (info->mini_icon) &&
+ height <= gdk_pixbuf_get_height (info->mini_icon))
+ pixbuf = scale_and_alpha_pixbuf (info->mini_icon,
+ op->data.icon.alpha,
+ width, height);
+ else if (info->icon)
+ pixbuf = scale_and_alpha_pixbuf (info->icon,
+ op->data.icon.alpha,
+ width, height);
+ break;
+
+ case META_DRAW_TITLE:
+ break;
+
+ case META_DRAW_OP_LIST:
+ break;
}
return pixbuf;
}
-void
-meta_draw_op_draw (const MetaDrawOp *op,
- GtkWidget *widget,
- GdkDrawable *drawable,
- const GdkRectangle *clip,
- int x,
- int y,
- int width,
- int height)
+static void
+fill_env (MetaPositionExprEnv *env,
+ const MetaDrawInfo *info,
+ int x,
+ int y,
+ int width,
+ int height)
{
- GdkGC *gc;
- MetaPositionExprEnv env;
+ /* FIXME this stuff could be raised into draw_op_list_draw() probably
+ */
+ env->x = x;
+ env->y = y;
+ env->width = width;
+ env->height = height;
+ env->object_width = -1;
+ env->object_height = -1;
+ if (info->fgeom)
+ {
+ env->left_width = info->fgeom->left_width;
+ env->right_width = info->fgeom->right_width;
+ env->top_height = info->fgeom->top_height;
+ env->bottom_height = info->fgeom->bottom_height;
+ }
+ else
+ {
+ env->left_width = 0;
+ env->right_width = 0;
+ env->top_height = 0;
+ env->bottom_height = 0;
+ }
+
+ env->mini_icon_width = info->mini_icon ? gdk_pixbuf_get_width (info->mini_icon) : 0;
+ env->mini_icon_height = info->mini_icon ? gdk_pixbuf_get_height (info->mini_icon) : 0;
+ env->icon_width = info->icon ? gdk_pixbuf_get_width (info->icon) : 0;
+ env->icon_height = info->icon ? gdk_pixbuf_get_height (info->icon) : 0;
+
+ env->title_width = info->title_layout_width;
+ env->title_height = info->title_layout_height;
+ env->theme = NULL; /* not required, constants have been optimized out */
+}
- env.x = x;
- env.y = y;
- env.width = width;
- env.height = height;
- env.object_width = -1;
- env.object_height = -1;
+static void
+meta_draw_op_draw_with_env (const MetaDrawOp *op,
+ GtkWidget *widget,
+ GdkDrawable *drawable,
+ const GdkRectangle *clip,
+ const MetaDrawInfo *info,
+ int x,
+ int y,
+ int width,
+ int height,
+ MetaPositionExprEnv *env)
+{
+ GdkGC *gc;
switch (op->type)
{
case META_DRAW_LINE:
{
int x1, x2, y1, y2;
-
+
gc = get_gc_for_primitive (widget, drawable,
op->data.line.color_spec,
clip,
op->data.line.width);
-
+
if (op->data.line.dash_on_length > 0 &&
op->data.line.dash_off_length > 0)
{
@@ -1858,13 +2507,13 @@ meta_draw_op_draw (const MetaDrawOp *op,
gdk_gc_set_dashes (gc, 0, dash_list, 2);
}
- x1 = parse_x_position_unchecked (op->data.line.x1, &env);
- y1 = parse_y_position_unchecked (op->data.line.y1, &env);
- x2 = parse_x_position_unchecked (op->data.line.x2, &env);
- y2 = parse_y_position_unchecked (op->data.line.y2, &env);
+ x1 = parse_x_position_unchecked (op->data.line.x1, env);
+ y1 = parse_y_position_unchecked (op->data.line.y1, env);
+ x2 = parse_x_position_unchecked (op->data.line.x2, env);
+ y2 = parse_y_position_unchecked (op->data.line.y2, env);
gdk_draw_line (drawable, gc, x1, y1, x2, y2);
-
+
g_object_unref (G_OBJECT (gc));
}
break;
@@ -1872,20 +2521,20 @@ meta_draw_op_draw (const MetaDrawOp *op,
case META_DRAW_RECTANGLE:
{
int rx, ry, rwidth, rheight;
-
- gc = get_gc_for_primitive (widget, drawable,
+
+ gc = get_gc_for_primitive (widget, drawable,
op->data.rectangle.color_spec,
clip, 0);
-
- rx = parse_x_position_unchecked (op->data.rectangle.x, &env);
- ry = parse_y_position_unchecked (op->data.rectangle.y, &env);
- rwidth = parse_size_unchecked (op->data.rectangle.width, &env);
- rheight = parse_size_unchecked (op->data.rectangle.height, &env);
+
+ rx = parse_x_position_unchecked (op->data.rectangle.x, env);
+ ry = parse_y_position_unchecked (op->data.rectangle.y, env);
+ rwidth = parse_size_unchecked (op->data.rectangle.width, env);
+ rheight = parse_size_unchecked (op->data.rectangle.height, env);
gdk_draw_rectangle (drawable, gc,
op->data.rectangle.filled,
rx, ry, rwidth, rheight);
-
+
g_object_unref (G_OBJECT (gc));
}
break;
@@ -1893,15 +2542,15 @@ meta_draw_op_draw (const MetaDrawOp *op,
case META_DRAW_ARC:
{
int rx, ry, rwidth, rheight;
-
+
gc = get_gc_for_primitive (widget, drawable,
op->data.arc.color_spec,
clip, 0);
-
- rx = parse_x_position_unchecked (op->data.arc.x, &env);
- ry = parse_y_position_unchecked (op->data.arc.y, &env);
- rwidth = parse_size_unchecked (op->data.arc.width, &env);
- rheight = parse_size_unchecked (op->data.arc.height, &env);
+
+ rx = parse_x_position_unchecked (op->data.arc.x, env);
+ ry = parse_y_position_unchecked (op->data.arc.y, env);
+ rwidth = parse_size_unchecked (op->data.arc.width, env);
+ rheight = parse_size_unchecked (op->data.arc.height, env);
gdk_draw_arc (drawable,
gc,
@@ -1910,19 +2559,22 @@ meta_draw_op_draw (const MetaDrawOp *op,
op->data.arc.start_angle * (360.0 * 64.0) -
(90.0 * 64.0), /* start at 12 instead of 3 oclock */
op->data.arc.extent_angle * (360.0 * 64.0));
-
+
g_object_unref (G_OBJECT (gc));
}
break;
+ case META_DRAW_CLIP:
+ break;
+
case META_DRAW_TINT:
{
int rx, ry, rwidth, rheight;
- rx = parse_x_position_unchecked (op->data.tint.x, &env);
- ry = parse_y_position_unchecked (op->data.tint.y, &env);
- rwidth = parse_size_unchecked (op->data.tint.width, &env);
- rheight = parse_size_unchecked (op->data.tint.height, &env);
+ rx = parse_x_position_unchecked (op->data.tint.x, env);
+ ry = parse_y_position_unchecked (op->data.tint.y, env);
+ rwidth = parse_size_unchecked (op->data.tint.width, env);
+ rheight = parse_size_unchecked (op->data.tint.height, env);
if (ALPHA_TO_UCHAR (op->data.tint.alpha) == 255)
{
@@ -1933,18 +2585,22 @@ meta_draw_op_draw (const MetaDrawOp *op,
gdk_draw_rectangle (drawable, gc,
TRUE,
rx, ry, rwidth, rheight);
-
+
g_object_unref (G_OBJECT (gc));
}
else
{
GdkPixbuf *pixbuf;
- pixbuf = draw_op_as_pixbuf (op, widget, rwidth, rheight);
+ pixbuf = draw_op_as_pixbuf (op, widget, info,
+ rwidth, rheight);
- render_pixbuf (drawable, clip, pixbuf, rx, ry);
+ if (pixbuf)
+ {
+ render_pixbuf (drawable, clip, pixbuf, rx, ry);
- g_object_unref (G_OBJECT (pixbuf));
+ g_object_unref (G_OBJECT (pixbuf));
+ }
}
}
break;
@@ -1953,49 +2609,61 @@ meta_draw_op_draw (const MetaDrawOp *op,
{
int rx, ry, rwidth, rheight;
GdkPixbuf *pixbuf;
-
- rx = parse_x_position_unchecked (op->data.gradient.x, &env);
- ry = parse_y_position_unchecked (op->data.gradient.y, &env);
- rwidth = parse_size_unchecked (op->data.gradient.width, &env);
- rheight = parse_size_unchecked (op->data.gradient.height, &env);
- pixbuf = draw_op_as_pixbuf (op, widget, rwidth, rheight);
-
- render_pixbuf (drawable, clip, pixbuf, rx, ry);
-
- g_object_unref (G_OBJECT (pixbuf));
+ rx = parse_x_position_unchecked (op->data.gradient.x, env);
+ ry = parse_y_position_unchecked (op->data.gradient.y, env);
+ rwidth = parse_size_unchecked (op->data.gradient.width, env);
+ rheight = parse_size_unchecked (op->data.gradient.height, env);
+
+ pixbuf = draw_op_as_pixbuf (op, widget, info,
+ rwidth, rheight);
+
+ if (pixbuf)
+ {
+ render_pixbuf (drawable, clip, pixbuf, rx, ry);
+
+ g_object_unref (G_OBJECT (pixbuf));
+ }
}
break;
-
+
case META_DRAW_IMAGE:
{
int rx, ry, rwidth, rheight;
GdkPixbuf *pixbuf;
-
- rx = parse_x_position_unchecked (op->data.image.x, &env);
- ry = parse_y_position_unchecked (op->data.image.y, &env);
- rwidth = parse_size_unchecked (op->data.image.width, &env);
- rheight = parse_size_unchecked (op->data.image.height, &env);
- pixbuf = draw_op_as_pixbuf (op, widget, rwidth, rheight);
+ if (op->data.image.pixbuf)
+ {
+ env->object_width = gdk_pixbuf_get_width (op->data.image.pixbuf);
+ env->object_height = gdk_pixbuf_get_height (op->data.image.pixbuf);
+ }
- env.object_width = gdk_pixbuf_get_width (pixbuf);
- env.object_height = gdk_pixbuf_get_height (pixbuf);
+ rwidth = parse_size_unchecked (op->data.image.width, env);
+ rheight = parse_size_unchecked (op->data.image.height, env);
- render_pixbuf (drawable, clip, pixbuf, rx, ry);
-
- g_object_unref (G_OBJECT (pixbuf));
+ pixbuf = draw_op_as_pixbuf (op, widget, info,
+ rwidth, rheight);
+
+ if (pixbuf)
+ {
+ rx = parse_x_position_unchecked (op->data.image.x, env);
+ ry = parse_y_position_unchecked (op->data.image.y, env);
+
+ render_pixbuf (drawable, clip, pixbuf, rx, ry);
+
+ g_object_unref (G_OBJECT (pixbuf));
+ }
}
break;
-
+
case META_DRAW_GTK_ARROW:
{
int rx, ry, rwidth, rheight;
- rx = parse_x_position_unchecked (op->data.gtk_arrow.x, &env);
- ry = parse_y_position_unchecked (op->data.gtk_arrow.y, &env);
- rwidth = parse_size_unchecked (op->data.gtk_arrow.width, &env);
- rheight = parse_size_unchecked (op->data.gtk_arrow.height, &env);
+ rx = parse_x_position_unchecked (op->data.gtk_arrow.x, env);
+ ry = parse_y_position_unchecked (op->data.gtk_arrow.y, env);
+ rwidth = parse_size_unchecked (op->data.gtk_arrow.width, env);
+ rheight = parse_size_unchecked (op->data.gtk_arrow.height, env);
gtk_paint_arrow (widget->style,
drawable,
@@ -2013,11 +2681,11 @@ meta_draw_op_draw (const MetaDrawOp *op,
case META_DRAW_GTK_BOX:
{
int rx, ry, rwidth, rheight;
-
- rx = parse_x_position_unchecked (op->data.gtk_box.x, &env);
- ry = parse_y_position_unchecked (op->data.gtk_box.y, &env);
- rwidth = parse_size_unchecked (op->data.gtk_box.width, &env);
- rheight = parse_size_unchecked (op->data.gtk_box.height, &env);
+
+ rx = parse_x_position_unchecked (op->data.gtk_box.x, env);
+ ry = parse_y_position_unchecked (op->data.gtk_box.y, env);
+ rwidth = parse_size_unchecked (op->data.gtk_box.width, env);
+ rheight = parse_size_unchecked (op->data.gtk_box.height, env);
gtk_paint_box (widget->style,
drawable,
@@ -2033,37 +2701,114 @@ meta_draw_op_draw (const MetaDrawOp *op,
case META_DRAW_GTK_VLINE:
{
int rx, ry1, ry2;
-
- rx = parse_x_position_unchecked (op->data.gtk_vline.x, &env);
- ry1 = parse_y_position_unchecked (op->data.gtk_vline.y1, &env);
- ry2 = parse_y_position_unchecked (op->data.gtk_vline.y2, &env);
+ rx = parse_x_position_unchecked (op->data.gtk_vline.x, env);
+ ry1 = parse_y_position_unchecked (op->data.gtk_vline.y1, env);
+ ry2 = parse_y_position_unchecked (op->data.gtk_vline.y2, env);
+
gtk_paint_vline (widget->style,
drawable,
op->data.gtk_vline.state,
(GdkRectangle*) clip,
widget,
"metacity",
- rx, ry1, ry2);
+ ry1, ry2, rx);
+ }
+ break;
+
+ case META_DRAW_ICON:
+ {
+ int rx, ry, rwidth, rheight;
+ GdkPixbuf *pixbuf;
+
+ rwidth = parse_size_unchecked (op->data.icon.width, env);
+ rheight = parse_size_unchecked (op->data.icon.height, env);
+
+ pixbuf = draw_op_as_pixbuf (op, widget, info,
+ rwidth, rheight);
+
+ if (pixbuf)
+ {
+ rx = parse_x_position_unchecked (op->data.icon.x, env);
+ ry = parse_y_position_unchecked (op->data.icon.y, env);
+
+ render_pixbuf (drawable, clip, pixbuf, rx, ry);
+
+ g_object_unref (G_OBJECT (pixbuf));
+ }
+ }
+ break;
+
+ case META_DRAW_TITLE:
+ if (info->title_layout)
+ {
+ int rx, ry;
+
+ gc = get_gc_for_primitive (widget, drawable,
+ op->data.title.color_spec,
+ clip, 0);
+
+ rx = parse_x_position_unchecked (op->data.title.x, env);
+ ry = parse_y_position_unchecked (op->data.title.y, env);
+
+ gdk_draw_layout (drawable, gc,
+ rx, ry,
+ info->title_layout);
+
+ g_object_unref (G_OBJECT (gc));
+ }
+ break;
+ case META_DRAW_OP_LIST:
+ {
+ int rx, ry, rwidth, rheight;
+
+ rx = parse_x_position_unchecked (op->data.op_list.x, env);
+ ry = parse_y_position_unchecked (op->data.op_list.y, env);
+ rwidth = parse_size_unchecked (op->data.op_list.width, env);
+ rheight = parse_size_unchecked (op->data.op_list.height, env);
+
+ meta_draw_op_list_draw (op->data.op_list.op_list,
+ widget, drawable, clip, info,
+ rx, ry, rwidth, rheight);
}
break;
}
}
+void
+meta_draw_op_draw (const MetaDrawOp *op,
+ GtkWidget *widget,
+ GdkDrawable *drawable,
+ const GdkRectangle *clip,
+ const MetaDrawInfo *info,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ MetaPositionExprEnv env;
+
+ fill_env (&env, info, x, y, width, height);
+
+ meta_draw_op_draw_with_env (op, widget, drawable, clip,
+ info, x, y, width, height,
+ &env);
+}
+
MetaDrawOpList*
meta_draw_op_list_new (int n_preallocs)
{
MetaDrawOpList *op_list;
g_return_val_if_fail (n_preallocs >= 0, NULL);
-
+
op_list = g_new (MetaDrawOpList, 1);
op_list->refcount = 1;
op_list->n_allocated = n_preallocs;
op_list->ops = g_new (MetaDrawOp*, op_list->n_allocated);
op_list->n_ops = 0;
-
+
return op_list;
}
@@ -2095,7 +2840,8 @@ meta_draw_op_list_unref (MetaDrawOpList *op_list)
}
g_free (op_list->ops);
-
+
+ DEBUG_FILL_STRUCT (op_list);
g_free (op_list);
}
}
@@ -2105,26 +2851,68 @@ meta_draw_op_list_draw (const MetaDrawOpList *op_list,
GtkWidget *widget,
GdkDrawable *drawable,
const GdkRectangle *clip,
+ const MetaDrawInfo *info,
int x,
int y,
int width,
int height)
{
int i;
+ GdkRectangle active_clip;
+ GdkRectangle orig_clip;
+ MetaPositionExprEnv env;
+ if (op_list->n_ops == 0)
+ return;
+
+ fill_env (&env, info, x, y, width, height);
+
/* FIXME this can be optimized, potentially a lot, by
* compressing multiple ops when possible. For example,
* anything convertible to a pixbuf can be composited
* client-side, and putting a color tint over a pixbuf
* can be done without creating the solid-color pixbuf.
+ *
+ * To implement this my plan is to have the idea of a
+ * compiled draw op (with the string expressions already
+ * evaluated), we make an array of those, and then fold
+ * adjacent items when possible.
*/
+ if (clip)
+ orig_clip = *clip;
+ else
+ {
+ orig_clip.x = x;
+ orig_clip.y = y;
+ orig_clip.width = width;
+ orig_clip.height = height;
+ }
+
+ active_clip = orig_clip;
i = 0;
while (i < op_list->n_ops)
{
- meta_draw_op_draw (op_list->ops[i],
- widget, drawable, clip,
- x, y, width, height);
+ MetaDrawOp *op = op_list->ops[i];
+
+ if (op->type == META_DRAW_CLIP)
+ {
+ active_clip.x = parse_x_position_unchecked (op->data.clip.x, &env);
+ active_clip.y = parse_y_position_unchecked (op->data.clip.y, &env);
+ active_clip.width = parse_size_unchecked (op->data.clip.width, &env);
+ active_clip.height = parse_size_unchecked (op->data.clip.height, &env);
+
+ gdk_rectangle_intersect (&orig_clip, &active_clip, &active_clip);
+ }
+ else if (active_clip.width > 0 &&
+ active_clip.height > 0)
+ {
+ meta_draw_op_draw_with_env (op,
+ widget, drawable, &active_clip, info,
+ x, y, width, height,
+ &env);
+ }
+
++i;
}
}
@@ -2143,6 +2931,48 @@ meta_draw_op_list_append (MetaDrawOpList *op_list,
op_list->n_ops += 1;
}
+gboolean
+meta_draw_op_list_validate (MetaDrawOpList *op_list,
+ GError **error)
+{
+ g_return_val_if_fail (op_list != NULL, FALSE);
+
+ /* empty lists are OK, nothing else to check really */
+
+ return TRUE;
+}
+
+/* This is not done in validate, since we wouldn't know the name
+ * of the list to report the error. It might be nice to
+ * store names inside the list sometime.
+ */
+gboolean
+meta_draw_op_list_contains (MetaDrawOpList *op_list,
+ MetaDrawOpList *child)
+{
+ int i;
+
+ /* mmm, huge tree recursion */
+
+ i = 0;
+ while (i < op_list->n_ops)
+ {
+ if (op_list->ops[i]->type == META_DRAW_OP_LIST)
+ {
+ if (op_list->ops[i]->data.op_list.op_list == child)
+ return TRUE;
+
+ if (meta_draw_op_list_contains (op_list->ops[i]->data.op_list.op_list,
+ child))
+ return TRUE;
+ }
+
+ ++i;
+ }
+
+ return FALSE;
+}
+
MetaFrameStyle*
meta_frame_style_new (MetaFrameStyle *parent)
{
@@ -2155,7 +2985,7 @@ meta_frame_style_new (MetaFrameStyle *parent)
style->parent = parent;
if (parent)
meta_frame_style_ref (parent);
-
+
return style;
}
@@ -2163,7 +2993,7 @@ void
meta_frame_style_ref (MetaFrameStyle *style)
{
g_return_if_fail (style != NULL);
-
+
style->refcount += 1;
}
@@ -2171,7 +3001,7 @@ static void
free_button_ops (MetaDrawOpList *op_lists[META_BUTTON_TYPE_LAST][META_BUTTON_STATE_LAST])
{
int i, j;
-
+
i = 0;
while (i < META_BUTTON_TYPE_LAST)
{
@@ -2180,10 +3010,10 @@ free_button_ops (MetaDrawOpList *op_lists[META_BUTTON_TYPE_LAST][META_BUTTON_STA
{
if (op_lists[i][j])
meta_draw_op_list_unref (op_lists[i][j]);
-
+
++j;
}
-
+
++i;
}
}
@@ -2193,39 +3023,97 @@ meta_frame_style_unref (MetaFrameStyle *style)
{
g_return_if_fail (style != NULL);
g_return_if_fail (style->refcount > 0);
-
+
style->refcount -= 1;
if (style->refcount == 0)
{
int i;
-
- free_button_ops (style->button_icons);
- free_button_ops (style->button_backgrounds);
+ free_button_ops (style->buttons);
+
i = 0;
while (i < META_FRAME_PIECE_LAST)
{
if (style->pieces[i])
meta_draw_op_list_unref (style->pieces[i]);
-
+
++i;
}
if (style->layout)
- meta_frame_layout_free (style->layout);
+ meta_frame_layout_unref (style->layout);
/* we hold a reference to any parent style */
if (style->parent)
meta_frame_style_unref (style->parent);
-
+
+ DEBUG_FILL_STRUCT (style);
g_free (style);
}
}
+static MetaDrawOpList*
+get_button (MetaFrameStyle *style,
+ MetaButtonType type,
+ MetaButtonState state)
+{
+ MetaDrawOpList *op_list;
+ MetaFrameStyle *parent;
+
+ parent = style;
+ op_list = NULL;
+ while (parent && op_list == NULL)
+ {
+ op_list = parent->buttons[type][state];
+ parent = parent->parent;
+ }
+
+ /* We fall back to normal if no prelight */
+ if (op_list == NULL &&
+ state == META_BUTTON_STATE_PRELIGHT)
+ return get_button (style, type, META_BUTTON_STATE_NORMAL);
+
+ return op_list;
+}
+
+gboolean
+meta_frame_style_validate (MetaFrameStyle *style,
+ GError **error)
+{
+ int i, j;
+
+ g_return_val_if_fail (style != NULL, FALSE);
+ g_return_val_if_fail (style->layout != NULL, FALSE);
+
+ i = 0;
+ while (i < META_BUTTON_TYPE_LAST)
+ {
+ j = 0;
+ while (j < META_BUTTON_STATE_LAST)
+ {
+ if (get_button (style, i, j) == NULL)
+ {
+ g_set_error (error, META_THEME_ERROR,
+ META_THEME_ERROR_FAILED,
+ _("<button function=\"%s\" state=\"%s\" draw_ops=\"whatever\"/> must be specified for this frame style"),
+ meta_button_type_to_string (i),
+ meta_button_state_to_string (j));
+ return FALSE;
+ }
+
+ ++j;
+ }
+
+ ++i;
+ }
+
+ return TRUE;
+}
+
static void
button_rect (MetaButtonType type,
- MetaFrameGeometry *fgeom,
+ const MetaFrameGeometry *fgeom,
GdkRectangle *rect)
{
switch (type)
@@ -2233,15 +3121,15 @@ button_rect (MetaButtonType type,
case META_BUTTON_TYPE_CLOSE:
*rect = fgeom->close_rect;
break;
-
+
case META_BUTTON_TYPE_MAXIMIZE:
*rect = fgeom->max_rect;
break;
-
+
case META_BUTTON_TYPE_MINIMIZE:
*rect = fgeom->min_rect;
break;
-
+
case META_BUTTON_TYPE_MENU:
*rect = fgeom->menu_rect;
break;
@@ -2253,74 +3141,81 @@ button_rect (MetaButtonType type,
}
void
-meta_frame_style_draw (MetaFrameStyle *style,
- GtkWidget *widget,
- GdkDrawable *drawable,
- int x_offset,
- int y_offset,
- const GdkRectangle *clip,
- MetaFrameFlags flags,
- int client_width,
- int client_height,
- PangoLayout *title_layout,
- int text_height,
- MetaButtonState button_states[META_BUTTON_TYPE_LAST])
+meta_frame_style_draw (MetaFrameStyle *style,
+ GtkWidget *widget,
+ GdkDrawable *drawable,
+ int x_offset,
+ int y_offset,
+ const GdkRectangle *clip,
+ const MetaFrameGeometry *fgeom,
+ int client_width,
+ int client_height,
+ PangoLayout *title_layout,
+ int text_height,
+ MetaButtonState button_states[META_BUTTON_TYPE_LAST],
+ GdkPixbuf *mini_icon,
+ GdkPixbuf *icon)
{
int i;
- MetaFrameGeometry fgeom;
GdkRectangle titlebar_rect;
GdkRectangle left_titlebar_edge;
GdkRectangle right_titlebar_edge;
GdkRectangle bottom_titlebar_edge;
GdkRectangle top_titlebar_edge;
GdkRectangle left_edge, right_edge, bottom_edge;
+ PangoRectangle extents;
+ MetaDrawInfo draw_info;
- meta_frame_layout_calc_geometry (style->layout,
- widget,
- text_height,
- flags,
- client_width, client_height,
- &fgeom);
-
titlebar_rect.x = 0;
titlebar_rect.y = 0;
- titlebar_rect.width = fgeom.width;
- titlebar_rect.height = fgeom.top_height;
+ titlebar_rect.width = fgeom->width;
+ titlebar_rect.height = fgeom->top_height;
left_titlebar_edge.x = titlebar_rect.x;
- left_titlebar_edge.y = titlebar_rect.y + fgeom.top_titlebar_edge;
- left_titlebar_edge.width = fgeom.left_titlebar_edge;
- left_titlebar_edge.height = titlebar_rect.height - fgeom.top_titlebar_edge - fgeom.bottom_titlebar_edge;
+ left_titlebar_edge.y = titlebar_rect.y + fgeom->top_titlebar_edge;
+ left_titlebar_edge.width = fgeom->left_titlebar_edge;
+ left_titlebar_edge.height = titlebar_rect.height - fgeom->top_titlebar_edge - fgeom->bottom_titlebar_edge;
right_titlebar_edge.y = left_titlebar_edge.y;
right_titlebar_edge.height = left_titlebar_edge.height;
- right_titlebar_edge.width = fgeom.right_titlebar_edge;
+ right_titlebar_edge.width = fgeom->right_titlebar_edge;
right_titlebar_edge.x = titlebar_rect.x + titlebar_rect.width - right_titlebar_edge.width;
-
+
top_titlebar_edge.x = titlebar_rect.x;
top_titlebar_edge.y = titlebar_rect.y;
top_titlebar_edge.width = titlebar_rect.width;
- top_titlebar_edge.height = fgeom.top_titlebar_edge;
+ top_titlebar_edge.height = fgeom->top_titlebar_edge;
bottom_titlebar_edge.x = titlebar_rect.x;
bottom_titlebar_edge.width = titlebar_rect.width;
- bottom_titlebar_edge.height = fgeom.bottom_titlebar_edge;
- bottom_titlebar_edge.y = titlebar_rect.y + titlebar_rect.height - bottom_titlebar_edge.height;
+ bottom_titlebar_edge.height = fgeom->bottom_titlebar_edge;
+ bottom_titlebar_edge.y = titlebar_rect.y + titlebar_rect.height - bottom_titlebar_edge.height;
left_edge.x = 0;
- left_edge.y = fgeom.top_height;
- left_edge.width = fgeom.left_width;
- left_edge.height = fgeom.height - fgeom.top_height - fgeom.bottom_height;
+ left_edge.y = fgeom->top_height;
+ left_edge.width = fgeom->left_width;
+ left_edge.height = fgeom->height - fgeom->top_height - fgeom->bottom_height;
- right_edge.x = fgeom.width - fgeom.right_width;
- right_edge.y = fgeom.top_height;
- right_edge.width = fgeom.right_width;
- right_edge.height = fgeom.height - fgeom.top_height - fgeom.bottom_height;
+ right_edge.x = fgeom->width - fgeom->right_width;
+ right_edge.y = fgeom->top_height;
+ right_edge.width = fgeom->right_width;
+ right_edge.height = fgeom->height - fgeom->top_height - fgeom->bottom_height;
bottom_edge.x = 0;
- bottom_edge.y = fgeom.height - fgeom.bottom_height;
- bottom_edge.width = fgeom.width;
- bottom_edge.height = fgeom.bottom_height;
+ bottom_edge.y = fgeom->height - fgeom->bottom_height;
+ bottom_edge.width = fgeom->width;
+ bottom_edge.height = fgeom->bottom_height;
+
+ if (title_layout)
+ pango_layout_get_pixel_extents (title_layout,
+ NULL, &extents);
+
+ draw_info.mini_icon = mini_icon;
+ draw_info.icon = icon;
+ draw_info.title_layout = title_layout;
+ draw_info.title_layout_width = title_layout ? extents.width : 0;
+ draw_info.title_layout_height = title_layout ? extents.height : 0;
+ draw_info.fgeom = fgeom;
/* The enum is in the order the pieces should be rendered. */
i = 0;
@@ -2328,18 +3223,17 @@ meta_frame_style_draw (MetaFrameStyle *style,
{
GdkRectangle rect;
GdkRectangle combined_clip;
- gboolean draw_title_text = FALSE;
-
+
switch ((MetaFramePiece) i)
{
case META_FRAME_PIECE_ENTIRE_BACKGROUND:
rect.x = 0;
rect.y = 0;
- rect.width = fgeom.width;
- rect.height = fgeom.height;
+ rect.width = fgeom->width;
+ rect.height = fgeom->height;
break;
- case META_FRAME_PIECE_TITLEBAR_BACKGROUND:
+ case META_FRAME_PIECE_TITLEBAR:
rect = titlebar_rect;
break;
@@ -2366,14 +3260,11 @@ meta_frame_style_draw (MetaFrameStyle *style,
right_titlebar_edge.width;
rect.height = titlebar_rect.height - top_titlebar_edge.height - bottom_titlebar_edge.height;
break;
-
- case META_FRAME_PIECE_TITLE_BACKGROUND:
- rect = fgeom.title_rect;
- /* Trigger drawing the title itself */
- draw_title_text = TRUE;
+ case META_FRAME_PIECE_TITLE:
+ rect = fgeom->title_rect;
break;
-
+
case META_FRAME_PIECE_LEFT_EDGE:
rect = left_edge;
break;
@@ -2389,8 +3280,8 @@ meta_frame_style_draw (MetaFrameStyle *style,
case META_FRAME_PIECE_OVERLAY:
rect.x = 0;
rect.y = 0;
- rect.width = fgeom.width;
- rect.height = fgeom.height;
+ rect.width = fgeom->width;
+ rect.height = fgeom->height;
break;
case META_FRAME_PIECE_LAST:
@@ -2400,7 +3291,7 @@ meta_frame_style_draw (MetaFrameStyle *style,
rect.x += x_offset;
rect.y += y_offset;
-
+
if (clip == NULL)
combined_clip = rect;
else
@@ -2417,8 +3308,8 @@ meta_frame_style_draw (MetaFrameStyle *style,
op_list = NULL;
while (parent && op_list == NULL)
{
- op_list = style->pieces[i];
- parent = style->parent;
+ op_list = parent->pieces[i];
+ parent = parent->parent;
}
if (op_list)
@@ -2426,80 +3317,25 @@ meta_frame_style_draw (MetaFrameStyle *style,
widget,
drawable,
&combined_clip,
+ &draw_info,
rect.x, rect.y, rect.width, rect.height);
}
- if (draw_title_text)
- {
- /* FIXME */
-
- /* Deal with whole icon-in-the-titlebar issue; there should
- * probably be "this window's icon" and "this window's mini-icon"
- * textures, and a way to stick textures on either end of
- * the title text as well as behind the text.
- * Most likely the text_rect should extend a configurable
- * distance from either end of the text.
- */
- }
-
++i;
}
- /* Now we draw button backgrounds */
+ /* Now we draw buttons */
i = 0;
while (i < META_BUTTON_TYPE_LAST)
{
GdkRectangle rect;
GdkRectangle combined_clip;
- button_rect (i, &fgeom, &rect);
+ button_rect (i, fgeom, &rect);
rect.x += x_offset;
rect.y += y_offset;
-
- if (clip == NULL)
- combined_clip = rect;
- else
- gdk_rectangle_intersect ((GdkRectangle*) clip, /* const cast */
- &rect,
- &combined_clip);
- if (combined_clip.width > 0 && combined_clip.height > 0)
- {
- MetaDrawOpList *op_list;
- MetaFrameStyle *parent;
-
- parent = style;
- op_list = NULL;
- while (parent && op_list == NULL)
- {
- op_list = style->button_backgrounds[i][button_states[i]];
- parent = style->parent;
- }
-
- if (op_list)
- meta_draw_op_list_draw (op_list,
- widget,
- drawable,
- &combined_clip,
- rect.x, rect.y, rect.width, rect.height);
- }
-
- ++i;
- }
-
- /* And button icons */
- i = 0;
- while (i < META_BUTTON_TYPE_LAST)
- {
- GdkRectangle rect;
- GdkRectangle combined_clip;
-
- button_rect (i, &fgeom, &rect);
-
- rect.x += x_offset;
- rect.y += y_offset;
-
if (clip == NULL)
combined_clip = rect;
else
@@ -2510,24 +3346,18 @@ meta_frame_style_draw (MetaFrameStyle *style,
if (combined_clip.width > 0 && combined_clip.height > 0)
{
MetaDrawOpList *op_list;
- MetaFrameStyle *parent;
-
- parent = style;
- op_list = NULL;
- while (parent && op_list == NULL)
- {
- op_list = style->button_icons[i][button_states[i]];
- parent = style->parent;
- }
+
+ op_list = get_button (style, i, button_states[i]);
if (op_list)
meta_draw_op_list_draw (op_list,
widget,
drawable,
&combined_clip,
+ &draw_info,
rect.x, rect.y, rect.width, rect.height);
}
-
+
++i;
}
}
@@ -2542,6 +3372,8 @@ meta_frame_style_set_new (MetaFrameStyleSet *parent)
style_set->parent = parent;
if (parent)
meta_frame_style_set_ref (parent);
+
+ style_set->refcount = 1;
return style_set;
}
@@ -2565,7 +3397,7 @@ void
meta_frame_style_set_ref (MetaFrameStyleSet *style_set)
{
g_return_if_fail (style_set != NULL);
-
+
style_set->refcount += 1;
}
@@ -2585,21 +3417,192 @@ meta_frame_style_set_unref (MetaFrameStyleSet *style_set)
while (i < META_FRAME_RESIZE_LAST)
{
free_focus_styles (style_set->normal_styles[i]);
-
+
++i;
}
-
+
free_focus_styles (style_set->maximized_styles);
free_focus_styles (style_set->shaded_styles);
free_focus_styles (style_set->maximized_and_shaded_styles);
if (style_set->parent)
meta_frame_style_set_unref (style_set->parent);
-
+
+ DEBUG_FILL_STRUCT (style_set);
g_free (style_set);
}
}
+
+static MetaFrameStyle*
+get_style (MetaFrameStyleSet *style_set,
+ MetaFrameState state,
+ MetaFrameResize resize,
+ MetaFrameFocus focus)
+{
+ MetaFrameStyle *style;
+
+ style = NULL;
+
+ if (state == META_FRAME_STATE_NORMAL)
+ {
+ style = style_set->normal_styles[resize][focus];
+
+ /* Try parent if we failed here */
+ if (style == NULL && style_set->parent)
+ style = get_style (style_set->parent, state, resize, focus);
+
+ /* Allow people to omit the vert/horz/none resize modes */
+ if (style == NULL &&
+ resize != META_FRAME_RESIZE_BOTH)
+ style = get_style (style_set, state, META_FRAME_RESIZE_BOTH, focus);
+ }
+ else
+ {
+ MetaFrameStyle **styles;
+
+ styles = NULL;
+
+ switch (state)
+ {
+ case META_FRAME_STATE_SHADED:
+ styles = style_set->shaded_styles;
+ break;
+ case META_FRAME_STATE_MAXIMIZED:
+ styles = style_set->maximized_styles;
+ break;
+ case META_FRAME_STATE_MAXIMIZED_AND_SHADED:
+ styles = style_set->maximized_and_shaded_styles;
+ break;
+ case META_FRAME_STATE_NORMAL:
+ case META_FRAME_STATE_LAST:
+ g_assert_not_reached ();
+ break;
+ }
+
+ style = styles[focus];
+
+ /* Try parent if we failed here */
+ if (style == NULL && style_set->parent)
+ style = get_style (style_set->parent, state, resize, focus);
+ }
+
+ return style;
+}
+
+static gboolean
+check_state (MetaFrameStyleSet *style_set,
+ MetaFrameState state,
+ GError **error)
+{
+ int i;
+
+ i = 0;
+ while (i < META_FRAME_FOCUS_LAST)
+ {
+ if (get_style (style_set, state,
+ META_FRAME_RESIZE_NONE, i) == NULL)
+ {
+ g_set_error (error, META_THEME_ERROR,
+ META_THEME_ERROR_FAILED,
+ _("Missing <frame state=\"%s\" resize=\"%s\" focus=\"%s\" style=\"whatever\"/>"),
+ meta_frame_state_to_string (state),
+ meta_frame_resize_to_string (META_FRAME_RESIZE_NONE),
+ meta_frame_focus_to_string (i));
+ return FALSE;
+ }
+
+ ++i;
+ }
+
+ return TRUE;
+}
+
+gboolean
+meta_frame_style_set_validate (MetaFrameStyleSet *style_set,
+ GError **error)
+{
+ int i, j;
+
+ g_return_val_if_fail (style_set != NULL, FALSE);
+
+ i = 0;
+ while (i < META_FRAME_RESIZE_LAST)
+ {
+ j = 0;
+ while (j < META_FRAME_FOCUS_LAST)
+ {
+ if (get_style (style_set, META_FRAME_STATE_NORMAL,
+ i, j) == NULL)
+ {
+ g_set_error (error, META_THEME_ERROR,
+ META_THEME_ERROR_FAILED,
+ _("Missing <frame state=\"%s\" resize=\"%s\" focus=\"%s\" style=\"whatever\"/>"),
+ meta_frame_state_to_string (META_FRAME_STATE_NORMAL),
+ meta_frame_resize_to_string (i),
+ meta_frame_focus_to_string (j));
+ return FALSE;
+ }
+
+ ++j;
+ }
+ ++i;
+ }
+
+ if (!check_state (style_set, META_FRAME_STATE_SHADED, error))
+ return FALSE;
+
+ if (!check_state (style_set, META_FRAME_STATE_MAXIMIZED, error))
+ return FALSE;
+
+ if (!check_state (style_set, META_FRAME_STATE_MAXIMIZED_AND_SHADED, error))
+ return FALSE;
+
+ return TRUE;
+}
+
+static MetaTheme *meta_current_theme = NULL;
+
+MetaTheme*
+meta_theme_get_current (void)
+{
+ return meta_current_theme;
+}
+
+void
+meta_theme_set_current (const char *name,
+ gboolean force_reload)
+{
+ MetaTheme *new_theme;
+ GError *err;
+
+ meta_verbose ("Setting current theme to \"%s\"\n", name);
+
+ if (!force_reload &&
+ meta_current_theme &&
+ strcmp (name, meta_current_theme->name) == 0)
+ return;
+
+ err = NULL;
+ new_theme = meta_theme_load (name, &err);
+
+ if (new_theme == NULL)
+ {
+ meta_warning (_("Failed to load theme \"%s\": %s"),
+ name, err->message);
+ g_error_free (err);
+ }
+ else
+ {
+ if (meta_current_theme)
+ meta_theme_free (meta_current_theme);
+
+ meta_current_theme = new_theme;
+
+ meta_verbose ("New theme is \"%s\"\n", meta_current_theme->name);
+ }
+}
+
MetaTheme*
meta_theme_new (void)
{
@@ -2607,30 +3610,80 @@ meta_theme_new (void)
theme = g_new0 (MetaTheme, 1);
- theme->styles_by_name = g_hash_table_new_full (g_str_hash,
- g_str_equal,
- g_free,
- (GDestroyNotify) meta_frame_style_unref);
-
- theme->style_sets_by_name = g_hash_table_new_full (g_str_hash,
- g_str_equal,
- g_free,
- (GDestroyNotify) meta_frame_style_set_unref);
-
+ theme->images_by_filename =
+ g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ (GDestroyNotify) g_object_unref);
+
+ theme->layouts_by_name =
+ g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ (GDestroyNotify) meta_frame_layout_unref);
+
+ theme->draw_op_lists_by_name =
+ g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ (GDestroyNotify) meta_draw_op_list_unref);
+
+ theme->styles_by_name =
+ g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ (GDestroyNotify) meta_frame_style_unref);
+
+ theme->style_sets_by_name =
+ g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ (GDestroyNotify) meta_frame_style_set_unref);
return theme;
}
+
+static void
+free_menu_ops (MetaDrawOpList *op_lists[META_BUTTON_TYPE_LAST][N_GTK_STATES])
+{
+ int i, j;
+
+ i = 0;
+ while (i < META_BUTTON_TYPE_LAST)
+ {
+ j = 0;
+ while (j < N_GTK_STATES)
+ {
+ if (op_lists[i][j])
+ meta_draw_op_list_unref (op_lists[i][j]);
+
+ ++j;
+ }
+
+ ++i;
+ }
+}
+
void
meta_theme_free (MetaTheme *theme)
{
int i;
-
+
g_return_if_fail (theme != NULL);
g_free (theme->name);
+ g_free (theme->dirname);
g_free (theme->filename);
-
+ g_free (theme->readable_name);
+ g_free (theme->date);
+ g_free (theme->description);
+ g_free (theme->author);
+ g_free (theme->copyright);
+
+ g_hash_table_destroy (theme->images_by_filename);
+ g_hash_table_destroy (theme->layouts_by_name);
+ g_hash_table_destroy (theme->draw_op_lists_by_name);
g_hash_table_destroy (theme->styles_by_name);
g_hash_table_destroy (theme->style_sets_by_name);
@@ -2641,159 +3694,1087 @@ meta_theme_free (MetaTheme *theme)
meta_frame_style_set_unref (theme->style_sets_by_type[i]);
++i;
}
+
+ free_menu_ops (theme->menu_icons);
+ DEBUG_FILL_STRUCT (theme);
g_free (theme);
}
-static MetaDrawOp*
-line_op (MetaGtkColorComponent component,
- const char *x1,
- const char *y1,
- const char *x2,
- const char *y2)
+static MetaDrawOpList*
+get_menu_icon (MetaTheme *theme,
+ MetaMenuIconType type,
+ GtkStateType state)
{
- MetaDrawOp *op;
+ MetaDrawOpList *op_list;
+
+ op_list = theme->menu_icons[type][state];
+
+ /* We fall back to normal if other states aren't found */
+ if (op_list == NULL &&
+ state != GTK_STATE_NORMAL)
+ return get_menu_icon (theme, type, GTK_STATE_NORMAL);
- op = meta_draw_op_new (META_DRAW_LINE);
- op->data.line.color_spec =
- meta_color_spec_new_gtk (component, GTK_STATE_NORMAL);
- op->data.line.x1 = g_strdup (x1);
- op->data.line.x2 = g_strdup (x2);
- op->data.line.y1 = g_strdup (y1);
- op->data.line.y2 = g_strdup (y2);
- op->data.line.dash_on_length = 0;
- op->data.line.dash_off_length = 0;
- op->data.line.width = 0;
+ return op_list;
+}
- return op;
+gboolean
+meta_theme_validate (MetaTheme *theme,
+ GError **error)
+{
+ int i, j;
+
+ g_return_val_if_fail (theme != NULL, FALSE);
+
+ /* FIXME what else should be checked? */
+
+ g_assert (theme->name);
+
+ if (theme->readable_name == NULL)
+ {
+ g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
+ _("No <name> set for theme \"%s\""), theme->name);
+ return FALSE;
+ }
+
+ if (theme->author == NULL)
+ {
+ g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
+ _("No <author> set for theme \"%s\""), theme->name);
+ return FALSE;
+ }
+
+ if (theme->date == NULL)
+ {
+ g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
+ _("No <date> set for theme \"%s\""), theme->name);
+ return FALSE;
+ }
+
+ if (theme->description == NULL)
+ {
+ g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
+ _("No <description> set for theme \"%s\""), theme->name);
+ return FALSE;
+ }
+
+ if (theme->copyright == NULL)
+ {
+ g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
+ _("No <copyright> set for theme \"%s\""), theme->name);
+ return FALSE;
+ }
+
+ i = 0;
+ while (i < (int) META_FRAME_TYPE_LAST)
+ {
+ if (theme->style_sets_by_type[i] == NULL)
+ {
+ g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
+ _("No frame style set for window type \"%s\" in theme \"%s\", add a <window type=\"%s\" style_set=\"whatever\"/> element"),
+ meta_frame_type_to_string (i),
+ theme->name,
+ meta_frame_type_to_string (i));
+
+ return FALSE;
+ }
+
+ ++i;
+ }
+
+ i = 0;
+ while (i < META_MENU_ICON_TYPE_LAST)
+ {
+ j = 0;
+ while (j < N_GTK_STATES)
+ {
+
+ if (get_menu_icon (theme, i, j) == NULL)
+ {
+ g_set_error (error, META_THEME_ERROR,
+ META_THEME_ERROR_FAILED,
+ _("<menu_icon function=\"%s\" state=\"%s\" draw_ops=\"whatever\"/> must be specified for this theme"),
+ meta_menu_icon_type_to_string (i),
+ meta_gtk_state_to_string (j));
+ return FALSE;
+ }
+
+ ++j;
+ }
+
+ ++i;
+ }
+
+ return TRUE;
}
-#define DEFAULT_INNER_BUTTON_BORDER 3
-MetaFrameStyle*
-meta_frame_style_get_test (void)
-{
- static MetaFrameStyle *style = NULL;
- 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
- };
+GdkPixbuf*
+meta_theme_load_image (MetaTheme *theme,
+ const char *filename,
+ GError **error)
+{
+ GdkPixbuf *pixbuf;
+
+ pixbuf = g_hash_table_lookup (theme->images_by_filename,
+ filename);
+
+ if (pixbuf == NULL)
+ {
+ char *full_path;
+
+ full_path = g_build_filename (theme->dirname, filename, NULL);
+
+ pixbuf = gdk_pixbuf_new_from_file (full_path, error);
+ if (pixbuf == NULL)
+ {
+ g_free (full_path);
+ return NULL;
+ }
+
+ g_free (full_path);
+
+ g_hash_table_replace (theme->images_by_filename,
+ g_strdup (filename),
+ pixbuf);
+ }
+
+ g_assert (pixbuf);
+
+ g_object_ref (G_OBJECT (pixbuf));
+
+ return pixbuf;
+}
+
+static MetaFrameStyle*
+theme_get_style (MetaTheme *theme,
+ MetaFrameType type,
+ MetaFrameFlags flags)
+{
+ MetaFrameState state;
+ MetaFrameResize resize;
+ MetaFrameFocus focus;
+ MetaFrameStyle *style;
+ MetaFrameStyleSet *style_set;
+
+ style_set = theme->style_sets_by_type[type];
+
+ /* Right now the parser forces a style set for all types,
+ * but this fallback code is here in case I take that out.
+ */
+ if (style_set == NULL)
+ style_set = theme->style_sets_by_type[META_FRAME_TYPE_NORMAL];
+ if (style_set == NULL)
+ return NULL;
+
+ switch (flags & (META_FRAME_MAXIMIZED | META_FRAME_SHADED))
+ {
+ case 0:
+ state = META_FRAME_STATE_NORMAL;
+ break;
+ case META_FRAME_MAXIMIZED:
+ state = META_FRAME_STATE_MAXIMIZED;
+ break;
+ case META_FRAME_SHADED:
+ state = META_FRAME_STATE_SHADED;
+ break;
+ case (META_FRAME_MAXIMIZED | META_FRAME_SHADED):
+ state = META_FRAME_STATE_MAXIMIZED_AND_SHADED;
+ break;
+ default:
+ g_assert_not_reached ();
+ state = META_FRAME_STATE_LAST; /* compiler */
+ break;
+ }
+
+ switch (flags & (META_FRAME_ALLOWS_VERTICAL_RESIZE | META_FRAME_ALLOWS_HORIZONTAL_RESIZE))
+ {
+ case 0:
+ resize = META_FRAME_RESIZE_NONE;
+ break;
+ case META_FRAME_ALLOWS_VERTICAL_RESIZE:
+ resize = META_FRAME_RESIZE_VERTICAL;
+ break;
+ case META_FRAME_ALLOWS_HORIZONTAL_RESIZE:
+ resize = META_FRAME_RESIZE_HORIZONTAL;
+ break;
+ case (META_FRAME_ALLOWS_VERTICAL_RESIZE | META_FRAME_ALLOWS_HORIZONTAL_RESIZE):
+ resize = META_FRAME_RESIZE_BOTH;
+ break;
+ default:
+ g_assert_not_reached ();
+ resize = META_FRAME_RESIZE_LAST; /* compiler */
+ break;
+ }
+
+ if (flags & META_FRAME_HAS_FOCUS)
+ focus = META_FRAME_FOCUS_YES;
+ else
+ focus = META_FRAME_FOCUS_NO;
+
+ style = get_style (style_set, state, resize, focus);
+
+ return style;
+}
+
+void
+meta_theme_draw_frame (MetaTheme *theme,
+ GtkWidget *widget,
+ GdkDrawable *drawable,
+ const GdkRectangle *clip,
+ int x_offset,
+ int y_offset,
+ MetaFrameType type,
+ MetaFrameFlags flags,
+ int client_width,
+ int client_height,
+ PangoLayout *title_layout,
+ int text_height,
+ MetaButtonState button_states[META_BUTTON_TYPE_LAST],
+ GdkPixbuf *mini_icon,
+ GdkPixbuf *icon)
+{
+ MetaFrameGeometry fgeom;
+ MetaFrameStyle *style;
+
+ g_return_if_fail (type < META_FRAME_TYPE_LAST);
+
+ style = theme_get_style (theme, type, flags);
+
+ /* Parser is not supposed to allow this currently */
+ if (style == NULL)
+ return;
+
+ meta_frame_layout_calc_geometry (style->layout,
+ text_height,
+ flags,
+ client_width, client_height,
+ &fgeom);
+
+ meta_frame_style_draw (style,
+ widget,
+ drawable,
+ x_offset, y_offset,
+ clip,
+ &fgeom,
+ client_width, client_height,
+ title_layout,
+ text_height,
+ button_states,
+ mini_icon, icon);
+}
+
+void
+meta_theme_draw_menu_icon (MetaTheme *theme,
+ GtkWidget *widget,
+ GdkDrawable *drawable,
+ const GdkRectangle *clip,
+ int x_offset,
+ int y_offset,
+ int width,
+ int height,
+ MetaMenuIconType type)
+{
+ MetaDrawInfo info;
MetaDrawOpList *op_list;
- MetaDrawOp *op;
- MetaGradientSpec *gradient;
- if (style)
- return style;
+ g_return_if_fail (type < META_BUTTON_TYPE_LAST);
+
+ op_list = get_menu_icon (theme, type,
+ GTK_WIDGET_STATE (widget));
- style = meta_frame_style_new (NULL);
+ info.mini_icon = NULL;
+ info.icon = NULL;
+ info.title_layout = NULL;
+ info.title_layout_width = 0;
+ info.title_layout_height = 0;
+ info.fgeom = NULL;
- style->layout = meta_frame_layout_new ();
+ meta_draw_op_list_draw (op_list,
+ widget,
+ drawable,
+ clip,
+ &info,
+ x_offset, y_offset, width, height);
+}
+
+void
+meta_theme_get_frame_borders (MetaTheme *theme,
+ MetaFrameType type,
+ int text_height,
+ MetaFrameFlags flags,
+ int *top_height,
+ int *bottom_height,
+ int *left_width,
+ int *right_width)
+{
+ MetaFrameStyle *style;
- style->layout->title_border = default_title_border;
- style->layout->text_border = default_text_border;
- style->layout->button_border = default_button_border;
- style->layout->inner_button_border = default_inner_button_border;
+ g_return_if_fail (type < META_FRAME_TYPE_LAST);
- style->layout->left_width = 6;
- style->layout->right_width = 6;
- style->layout->bottom_height = 7;
- style->layout->spacer_padding = 3;
- style->layout->spacer_width = 2;
- style->layout->spacer_height = 11;
- style->layout->right_inset = 6;
- style->layout->left_inset = 6;
- style->layout->button_width = 17;
- style->layout->button_height = 17;
-
- op_list = meta_draw_op_list_new (3);
+ if (top_height)
+ *top_height = 0;
+ if (bottom_height)
+ *bottom_height = 0;
+ if (left_width)
+ *left_width = 0;
+ if (right_width)
+ *right_width = 0;
- /* Gradient background */
- op = meta_draw_op_new (META_DRAW_GRADIENT);
- gradient = meta_gradient_spec_new (META_GRADIENT_VERTICAL);
- gradient->color_specs =
- g_slist_prepend (gradient->color_specs,
- meta_color_spec_new_gtk (META_GTK_COLOR_BG,
- GTK_STATE_NORMAL));
- gradient->color_specs =
- g_slist_prepend (gradient->color_specs,
- meta_color_spec_new_gtk (META_GTK_COLOR_BG,
- GTK_STATE_SELECTED));
-
- op->data.gradient.gradient_spec = gradient;
- op->data.gradient.alpha = 1.0;
+ style = theme_get_style (theme, type, flags);
- op->data.gradient.x = g_strdup ("0");
- op->data.gradient.y = g_strdup ("0");
- op->data.gradient.width = g_strdup ("width");
- op->data.gradient.height = g_strdup ("height");
-
- meta_draw_op_list_append (op_list, op);
-
- /* Put orange tint on parts of window */
- op = meta_draw_op_new (META_DRAW_TINT);
- op->data.tint.color_spec = meta_color_spec_new_from_string ("orange", NULL);
- op->data.tint.alpha = 0.3;
- op->data.gradient.x = g_strdup ("0");
- op->data.gradient.y = g_strdup ("0");
- op->data.gradient.width = g_strdup ("width / 2");
- op->data.gradient.height = g_strdup ("height / 2");
-
- meta_draw_op_list_append (op_list, op);
-
- op = meta_draw_op_new (META_DRAW_TINT);
- op->data.tint.color_spec = meta_color_spec_new_from_string ("orange", NULL);
- op->data.tint.alpha = 0.3;
- op->data.gradient.x = g_strdup ("width / 2 + width % 2");
- op->data.gradient.y = g_strdup ("height / 2 + height % 2");
- op->data.gradient.width = g_strdup ("width / 2");
- op->data.gradient.height = g_strdup ("height / 2");
-
- meta_draw_op_list_append (op_list, op);
+ /* Parser is not supposed to allow this currently */
+ if (style == NULL)
+ return;
+
+ meta_frame_layout_get_borders (style->layout,
+ text_height,
+ flags,
+ top_height, bottom_height,
+ left_width, right_width);
+}
+
+void
+meta_theme_calc_geometry (MetaTheme *theme,
+ MetaFrameType type,
+ int text_height,
+ MetaFrameFlags flags,
+ int client_width,
+ int client_height,
+ MetaFrameGeometry *fgeom)
+{
+ MetaFrameStyle *style;
+
+ g_return_if_fail (type < META_FRAME_TYPE_LAST);
- style->pieces[META_FRAME_PIECE_ENTIRE_BACKGROUND] = op_list;
+ style = theme_get_style (theme, type, flags);
- op_list = meta_draw_op_list_new (5);
- style->pieces[META_FRAME_PIECE_OVERLAY] = op_list;
+ /* Parser is not supposed to allow this currently */
+ if (style == NULL)
+ return;
- op = meta_draw_op_new (META_DRAW_RECTANGLE);
- op->data.rectangle.color_spec =
- meta_color_spec_new_from_string ("#000000", NULL);
- op->data.rectangle.filled = FALSE;
- op->data.rectangle.x = g_strdup ("0");
- op->data.rectangle.y = g_strdup ("0");
- op->data.rectangle.width = g_strdup ("width - 1");
- op->data.rectangle.height = g_strdup ("height - 1");
+ meta_frame_layout_calc_geometry (style->layout,
+ text_height,
+ flags,
+ client_width, client_height,
+ fgeom);
+}
- meta_draw_op_list_append (op_list, op);
+MetaFrameLayout*
+meta_theme_lookup_layout (MetaTheme *theme,
+ const char *name)
+{
+ return g_hash_table_lookup (theme->layouts_by_name, name);
+}
- meta_draw_op_list_append (op_list,
- line_op (META_GTK_COLOR_LIGHT,
- "1", "1", "1", "height - 2"));
+void
+meta_theme_insert_layout (MetaTheme *theme,
+ const char *name,
+ MetaFrameLayout *layout)
+{
+ meta_frame_layout_ref (layout);
+ g_hash_table_replace (theme->layouts_by_name, g_strdup (name), layout);
+}
- meta_draw_op_list_append (op_list,
- line_op (META_GTK_COLOR_LIGHT,
- "1", "1", "width - 2", "1"));
+MetaDrawOpList*
+meta_theme_lookup_draw_op_list (MetaTheme *theme,
+ const char *name)
+{
+ return g_hash_table_lookup (theme->draw_op_lists_by_name, name);
+}
- meta_draw_op_list_append (op_list,
- line_op (META_GTK_COLOR_DARK,
- "width - 2", "1", "width - 2", "height - 2"));
+void
+meta_theme_insert_draw_op_list (MetaTheme *theme,
+ const char *name,
+ MetaDrawOpList *op_list)
+{
+ meta_draw_op_list_ref (op_list);
+ g_hash_table_replace (theme->draw_op_lists_by_name, g_strdup (name), op_list);
+}
- meta_draw_op_list_append (op_list,
- line_op (META_GTK_COLOR_DARK,
- "1", "height - 2", "width - 2", "height - 2"));
+MetaFrameStyle*
+meta_theme_lookup_style (MetaTheme *theme,
+ const char *name)
+{
+ return g_hash_table_lookup (theme->styles_by_name, name);
+}
+
+void
+meta_theme_insert_style (MetaTheme *theme,
+ const char *name,
+ MetaFrameStyle *style)
+{
+ meta_frame_style_ref (style);
+ g_hash_table_replace (theme->styles_by_name, g_strdup (name), style);
+}
+
+MetaFrameStyleSet*
+meta_theme_lookup_style_set (MetaTheme *theme,
+ const char *name)
+{
+ return g_hash_table_lookup (theme->style_sets_by_name, name);
+}
+void
+meta_theme_insert_style_set (MetaTheme *theme,
+ const char *name,
+ MetaFrameStyleSet *style_set)
+{
+ meta_frame_style_set_ref (style_set);
+ g_hash_table_replace (theme->style_sets_by_name, g_strdup (name), style_set);
+}
+
+static gboolean
+first_uppercase (const char *str)
+{
+ return g_ascii_isupper (*str);
+}
+
+gboolean
+meta_theme_define_int_constant (MetaTheme *theme,
+ const char *name,
+ int value,
+ GError **error)
+{
+ if (theme->integer_constants == NULL)
+ theme->integer_constants = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ NULL);
+
+ if (!first_uppercase (name))
+ {
+ g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
+ _("User-defined constants must begin with a capital letter; \"%s\" does not"),
+ name);
+ return FALSE;
+ }
+ if (g_hash_table_lookup_extended (theme->integer_constants, name, NULL, NULL))
+ {
+ g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
+ _("Constant \"%s\" has already been defined"),
+ name);
+
+ return FALSE;
+ }
+
+ g_hash_table_insert (theme->integer_constants,
+ g_strdup (name),
+ GINT_TO_POINTER (value));
+}
+
+gboolean
+meta_theme_lookup_int_constant (MetaTheme *theme,
+ const char *name,
+ int *value)
+{
+ gpointer old_value;
+
+ *value = 0;
- return style;
+ if (theme->integer_constants == NULL)
+ return FALSE;
+
+ if (g_hash_table_lookup_extended (theme->integer_constants,
+ name, NULL, &old_value))
+ {
+ *value = GPOINTER_TO_INT (old_value);
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
}
+gboolean
+meta_theme_define_float_constant (MetaTheme *theme,
+ const char *name,
+ double value,
+ GError **error)
+{
+ double *d;
+
+ if (theme->float_constants == NULL)
+ theme->float_constants = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ g_free);
+ if (!first_uppercase (name))
+ {
+ g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
+ _("User-defined constants must begin with a capital letter; \"%s\" does not"),
+ name);
+ return FALSE;
+ }
+
+ if (g_hash_table_lookup_extended (theme->float_constants, name, NULL, NULL))
+ {
+ g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
+ _("Constant \"%s\" has already been defined"),
+ name);
+
+ return FALSE;
+ }
+
+ d = g_new (double, 1);
+ *d = value;
+
+ g_hash_table_insert (theme->float_constants,
+ g_strdup (name), d);
+}
+
+gboolean
+meta_theme_lookup_float_constant (MetaTheme *theme,
+ const char *name,
+ double *value)
+{
+ double *d;
+
+ *value = 0.0;
+
+ if (theme->float_constants == NULL)
+ return FALSE;
+
+ d = g_hash_table_lookup (theme->float_constants, name);
+
+ if (d)
+ {
+ *value = *d;
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+int
+meta_gtk_widget_get_text_height (GtkWidget *widget)
+{
+ PangoFontMetrics *metrics;
+ PangoFont *font;
+ PangoLanguage *lang;
+ int retval;
+ g_return_val_if_fail (GTK_WIDGET_REALIZED (widget), 0);
+
+ 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));
+
+ retval = PANGO_PIXELS (pango_font_metrics_get_ascent (metrics) +
+ pango_font_metrics_get_descent (metrics));
+
+ pango_font_metrics_unref (metrics);
+ return retval;
+}
+
+MetaGtkColorComponent
+meta_color_component_from_string (const char *str)
+{
+ if (strcmp ("fg", str) == 0)
+ return META_GTK_COLOR_FG;
+ else if (strcmp ("bg", str) == 0)
+ return META_GTK_COLOR_BG;
+ else if (strcmp ("light", str) == 0)
+ return META_GTK_COLOR_LIGHT;
+ else if (strcmp ("dark", str) == 0)
+ return META_GTK_COLOR_DARK;
+ else if (strcmp ("mid", str) == 0)
+ return META_GTK_COLOR_MID;
+ else if (strcmp ("text", str) == 0)
+ return META_GTK_COLOR_TEXT;
+ else if (strcmp ("base", str) == 0)
+ return META_GTK_COLOR_BASE;
+ else if (strcmp ("text_aa", str) == 0)
+ return META_GTK_COLOR_TEXT_AA;
+ else
+ return META_GTK_COLOR_LAST;
+}
+
+const char*
+meta_color_component_to_string (MetaGtkColorComponent component)
+{
+ switch (component)
+ {
+ case META_GTK_COLOR_FG:
+ return "fg";
+ case META_GTK_COLOR_BG:
+ return "bg";
+ case META_GTK_COLOR_LIGHT:
+ return "light";
+ case META_GTK_COLOR_DARK:
+ return "dark";
+ case META_GTK_COLOR_MID:
+ return "mid";
+ case META_GTK_COLOR_TEXT:
+ return "text";
+ case META_GTK_COLOR_BASE:
+ return "base";
+ case META_GTK_COLOR_TEXT_AA:
+ return "text_aa";
+ case META_GTK_COLOR_LAST:
+ break;
+ }
+
+ return "<unknown>";
+}
+
+MetaButtonState
+meta_button_state_from_string (const char *str)
+{
+ if (strcmp ("normal", str) == 0)
+ return META_BUTTON_STATE_NORMAL;
+ else if (strcmp ("pressed", str) == 0)
+ return META_BUTTON_STATE_PRESSED;
+ else if (strcmp ("prelight", str) == 0)
+ return META_BUTTON_STATE_PRELIGHT;
+ else
+ return META_BUTTON_STATE_LAST;
+}
+
+const char*
+meta_button_state_to_string (MetaButtonState state)
+{
+ switch (state)
+ {
+ case META_BUTTON_STATE_NORMAL:
+ return "normal";
+ case META_BUTTON_STATE_PRESSED:
+ return "pressed";
+ case META_BUTTON_STATE_PRELIGHT:
+ return "prelight";
+ case META_BUTTON_STATE_LAST:
+ break;
+ }
+
+ return "<unknown>";
+}
+
+MetaButtonType
+meta_button_type_from_string (const char *str)
+{
+ if (strcmp ("close", str) == 0)
+ return META_BUTTON_TYPE_CLOSE;
+ else if (strcmp ("maximize", str) == 0)
+ return META_BUTTON_TYPE_MAXIMIZE;
+ else if (strcmp ("minimize", str) == 0)
+ return META_BUTTON_TYPE_MINIMIZE;
+ else if (strcmp ("menu", str) == 0)
+ return META_BUTTON_TYPE_MENU;
+ else
+ return META_BUTTON_TYPE_LAST;
+}
+
+const char*
+meta_button_type_to_string (MetaButtonType type)
+{
+ switch (type)
+ {
+ case META_BUTTON_TYPE_CLOSE:
+ return "close";
+ case META_BUTTON_TYPE_MAXIMIZE:
+ return "maximize";
+ case META_BUTTON_TYPE_MINIMIZE:
+ return "minimize";
+ case META_BUTTON_TYPE_MENU:
+ return "menu";
+ case META_BUTTON_TYPE_LAST:
+ break;
+ }
+
+ return "<unknown>";
+}
+
+MetaMenuIconType
+meta_menu_icon_type_from_string (const char *str)
+{
+ if (strcmp ("close", str) == 0)
+ return META_MENU_ICON_TYPE_CLOSE;
+ else if (strcmp ("maximize", str) == 0)
+ return META_MENU_ICON_TYPE_MAXIMIZE;
+ else if (strcmp ("minimize", str) == 0)
+ return META_MENU_ICON_TYPE_MINIMIZE;
+ else if (strcmp ("unmaximize", str) == 0)
+ return META_MENU_ICON_TYPE_UNMAXIMIZE;
+ else
+ return META_MENU_ICON_TYPE_LAST;
+}
+
+const char*
+meta_menu_icon_type_to_string (MetaMenuIconType type)
+{
+ switch (type)
+ {
+ case META_MENU_ICON_TYPE_CLOSE:
+ return "close";
+ case META_MENU_ICON_TYPE_MAXIMIZE:
+ return "maximize";
+ case META_MENU_ICON_TYPE_MINIMIZE:
+ return "minimize";
+ case META_MENU_ICON_TYPE_UNMAXIMIZE:
+ return "unmaximize";
+ case META_MENU_ICON_TYPE_LAST:
+ break;
+ }
+
+ return "<unknown>";
+}
+
+MetaFramePiece
+meta_frame_piece_from_string (const char *str)
+{
+ if (strcmp ("entire_background", str) == 0)
+ return META_FRAME_PIECE_ENTIRE_BACKGROUND;
+ else if (strcmp ("titlebar", str) == 0)
+ return META_FRAME_PIECE_TITLEBAR;
+ else if (strcmp ("titlebar_middle", str) == 0)
+ return META_FRAME_PIECE_TITLEBAR_MIDDLE;
+ else if (strcmp ("left_titlebar_edge", str) == 0)
+ return META_FRAME_PIECE_LEFT_TITLEBAR_EDGE;
+ else if (strcmp ("right_titlebar_edge", str) == 0)
+ return META_FRAME_PIECE_RIGHT_TITLEBAR_EDGE;
+ else if (strcmp ("top_titlebar_edge", str) == 0)
+ return META_FRAME_PIECE_TOP_TITLEBAR_EDGE;
+ else if (strcmp ("bottom_titlebar_edge", str) == 0)
+ return META_FRAME_PIECE_BOTTOM_TITLEBAR_EDGE;
+ else if (strcmp ("title", str) == 0)
+ return META_FRAME_PIECE_TITLE;
+ else if (strcmp ("left_edge", str) == 0)
+ return META_FRAME_PIECE_LEFT_EDGE;
+ else if (strcmp ("right_edge", str) == 0)
+ return META_FRAME_PIECE_RIGHT_EDGE;
+ else if (strcmp ("bottom_edge", str) == 0)
+ return META_FRAME_PIECE_BOTTOM_EDGE;
+ else if (strcmp ("overlay", str) == 0)
+ return META_FRAME_PIECE_OVERLAY;
+ else
+ return META_FRAME_PIECE_LAST;
+}
+
+const char*
+meta_frame_piece_to_string (MetaFramePiece piece)
+{
+ switch (piece)
+ {
+ case META_FRAME_PIECE_ENTIRE_BACKGROUND:
+ return "entire_background";
+ case META_FRAME_PIECE_TITLEBAR:
+ return "titlebar";
+ case META_FRAME_PIECE_TITLEBAR_MIDDLE:
+ return "titlebar_middle";
+ case META_FRAME_PIECE_LEFT_TITLEBAR_EDGE:
+ return "left_titlebar_edge";
+ case META_FRAME_PIECE_RIGHT_TITLEBAR_EDGE:
+ return "right_titlebar_edge";
+ case META_FRAME_PIECE_TOP_TITLEBAR_EDGE:
+ return "top_titlebar_edge";
+ case META_FRAME_PIECE_BOTTOM_TITLEBAR_EDGE:
+ return "bottom_titlebar_edge";
+ case META_FRAME_PIECE_TITLE:
+ return "title";
+ case META_FRAME_PIECE_LEFT_EDGE:
+ return "left_edge";
+ case META_FRAME_PIECE_RIGHT_EDGE:
+ return "right_edge";
+ case META_FRAME_PIECE_BOTTOM_EDGE:
+ return "bottom_edge";
+ case META_FRAME_PIECE_OVERLAY:
+ return "overlay";
+ case META_FRAME_PIECE_LAST:
+ break;
+ }
+
+ return "<unknown>";
+}
+
+MetaFrameState
+meta_frame_state_from_string (const char *str)
+{
+ if (strcmp ("normal", str) == 0)
+ return META_FRAME_STATE_NORMAL;
+ else if (strcmp ("maximized", str) == 0)
+ return META_FRAME_STATE_MAXIMIZED;
+ else if (strcmp ("shaded", str) == 0)
+ return META_FRAME_STATE_SHADED;
+ else if (strcmp ("maximized_and_shaded", str) == 0)
+ return META_FRAME_STATE_MAXIMIZED_AND_SHADED;
+ else
+ return META_FRAME_STATE_LAST;
+}
+
+const char*
+meta_frame_state_to_string (MetaFrameState state)
+{
+ switch (state)
+ {
+ case META_FRAME_STATE_NORMAL:
+ return "normal";
+ case META_FRAME_STATE_MAXIMIZED:
+ return "maximized";
+ case META_FRAME_STATE_SHADED:
+ return "shaded";
+ case META_FRAME_STATE_MAXIMIZED_AND_SHADED:
+ return "maximized_and_shaded";
+ case META_FRAME_STATE_LAST:
+ break;
+ }
+
+ return "<unknown>";
+}
+
+MetaFrameResize
+meta_frame_resize_from_string (const char *str)
+{
+ if (strcmp ("none", str) == 0)
+ return META_FRAME_RESIZE_NONE;
+ else if (strcmp ("vertical", str) == 0)
+ return META_FRAME_RESIZE_VERTICAL;
+ else if (strcmp ("horizontal", str) == 0)
+ return META_FRAME_RESIZE_HORIZONTAL;
+ else if (strcmp ("both", str) == 0)
+ return META_FRAME_RESIZE_BOTH;
+ else
+ return META_FRAME_RESIZE_LAST;
+}
+
+const char*
+meta_frame_resize_to_string (MetaFrameResize resize)
+{
+ switch (resize)
+ {
+ case META_FRAME_RESIZE_NONE:
+ return "none";
+ case META_FRAME_RESIZE_VERTICAL:
+ return "vertical";
+ case META_FRAME_RESIZE_HORIZONTAL:
+ return "horizontal";
+ case META_FRAME_RESIZE_BOTH:
+ return "both";
+ case META_FRAME_RESIZE_LAST:
+ break;
+ }
+
+ return "<unknown>";
+}
+
+MetaFrameFocus
+meta_frame_focus_from_string (const char *str)
+{
+ if (strcmp ("no", str) == 0)
+ return META_FRAME_FOCUS_NO;
+ else if (strcmp ("yes", str) == 0)
+ return META_FRAME_FOCUS_YES;
+ else
+ return META_FRAME_FOCUS_LAST;
+}
+
+const char*
+meta_frame_focus_to_string (MetaFrameFocus focus)
+{
+ switch (focus)
+ {
+ case META_FRAME_FOCUS_NO:
+ return "no";
+ case META_FRAME_FOCUS_YES:
+ return "yes";
+ case META_FRAME_FOCUS_LAST:
+ break;
+ }
+
+ return "<unknown>";
+}
+
+MetaFrameType
+meta_frame_type_from_string (const char *str)
+{
+ if (strcmp ("normal", str) == 0)
+ return META_FRAME_TYPE_NORMAL;
+ else if (strcmp ("dialog", str) == 0)
+ return META_FRAME_TYPE_DIALOG;
+ else if (strcmp ("modal_dialog", str) == 0)
+ return META_FRAME_TYPE_MODAL_DIALOG;
+ else if (strcmp ("utility", str) == 0)
+ return META_FRAME_TYPE_UTILITY;
+ else if (strcmp ("menu", str) == 0)
+ return META_FRAME_TYPE_MENU;
+#if 0
+ else if (strcmp ("toolbar", str) == 0)
+ return META_FRAME_TYPE_TOOLBAR;
+#endif
+ else
+ return META_FRAME_TYPE_LAST;
+}
+
+const char*
+meta_frame_type_to_string (MetaFrameType type)
+{
+ switch (type)
+ {
+ case META_FRAME_TYPE_NORMAL:
+ return "normal";
+ case META_FRAME_TYPE_DIALOG:
+ return "dialog";
+ case META_FRAME_TYPE_MODAL_DIALOG:
+ return "modal_dialog";
+ case META_FRAME_TYPE_UTILITY:
+ return "utility";
+ case META_FRAME_TYPE_MENU:
+ return "menu";
+#if 0
+ case META_FRAME_TYPE_TOOLBAR:
+ return "toolbar";
+#endif
+ case META_FRAME_TYPE_LAST:
+ break;
+ }
+
+ return "<unknown>";
+}
+
+MetaGradientType
+meta_gradient_type_from_string (const char *str)
+{
+ if (strcmp ("vertical", str) == 0)
+ return META_GRADIENT_VERTICAL;
+ else if (strcmp ("horizontal", str) == 0)
+ return META_GRADIENT_HORIZONTAL;
+ else if (strcmp ("diagonal", str) == 0)
+ return META_GRADIENT_DIAGONAL;
+ else
+ return META_GRADIENT_LAST;
+}
+
+const char*
+meta_gradient_type_to_string (MetaGradientType type)
+{
+ switch (type)
+ {
+ case META_GRADIENT_VERTICAL:
+ return "vertical";
+ case META_GRADIENT_HORIZONTAL:
+ return "horizontal";
+ case META_GRADIENT_DIAGONAL:
+ return "diagonal";
+ case META_GRADIENT_LAST:
+ break;
+ }
+
+ return "<unknown>";
+}
+
+GtkStateType
+meta_gtk_state_from_string (const char *str)
+{
+ if (strcmp ("normal", str) == 0 || strcmp ("NORMAL", str) == 0)
+ return GTK_STATE_NORMAL;
+ else if (strcmp ("prelight", str) == 0 || strcmp ("PRELIGHT", str) == 0)
+ return GTK_STATE_PRELIGHT;
+ else if (strcmp ("active", str) == 0 || strcmp ("ACTIVE", str) == 0)
+ return GTK_STATE_ACTIVE;
+ else if (strcmp ("selected", str) == 0 || strcmp ("SELECTED", str) == 0)
+ return GTK_STATE_SELECTED;
+ else if (strcmp ("insensitive", str) == 0 || strcmp ("INSENSITIVE", str) == 0)
+ return GTK_STATE_INSENSITIVE;
+ else
+ return -1; /* hack */
+}
+
+const char*
+meta_gtk_state_to_string (GtkStateType state)
+{
+ switch (state)
+ {
+ case GTK_STATE_NORMAL:
+ return "NORMAL";
+ case GTK_STATE_PRELIGHT:
+ return "PRELIGHT";
+ case GTK_STATE_ACTIVE:
+ return "ACTIVE";
+ case GTK_STATE_SELECTED:
+ return "SELECTED";
+ case GTK_STATE_INSENSITIVE:
+ return "INSENSITIVE";
+ }
+
+ return "<unknown>";
+}
+
+GtkShadowType
+meta_gtk_shadow_from_string (const char *str)
+{
+ if (strcmp ("none", str) == 0)
+ return GTK_SHADOW_NONE;
+ else if (strcmp ("in", str) == 0)
+ return GTK_SHADOW_IN;
+ else if (strcmp ("out", str) == 0)
+ return GTK_SHADOW_OUT;
+ else if (strcmp ("etched_in", str) == 0)
+ return GTK_SHADOW_ETCHED_IN;
+ else if (strcmp ("etched_out", str) == 0)
+ return GTK_SHADOW_ETCHED_OUT;
+ else
+ return -1;
+}
+
+const char*
+meta_gtk_shadow_to_string (GtkShadowType shadow)
+{
+ switch (shadow)
+ {
+ case GTK_SHADOW_NONE:
+ return "none";
+ case GTK_SHADOW_IN:
+ return "in";
+ case GTK_SHADOW_OUT:
+ return "out";
+ case GTK_SHADOW_ETCHED_IN:
+ return "etched_in";
+ case GTK_SHADOW_ETCHED_OUT:
+ return "etched_out";
+ }
+
+ return "<unknown>";
+}
+
+GtkArrowType
+meta_gtk_arrow_from_string (const char *str)
+{
+ if (strcmp ("up", str) == 0)
+ return GTK_ARROW_UP;
+ else if (strcmp ("down", str) == 0)
+ return GTK_ARROW_DOWN;
+ else if (strcmp ("left", str) == 0)
+ return GTK_ARROW_LEFT;
+ else if (strcmp ("right", str) == 0)
+ return GTK_ARROW_RIGHT;
+ else
+ return -1;
+}
+
+const char*
+meta_gtk_arrow_to_string (GtkArrowType arrow)
+{
+ switch (arrow)
+ {
+ case GTK_ARROW_UP:
+ return "up";
+ case GTK_ARROW_DOWN:
+ return "down";
+ case GTK_ARROW_LEFT:
+ return "left";
+ case GTK_ARROW_RIGHT:
+ return "right";
+ }
+
+ return "<unknown>";
+}
#if 0
@@ -2817,15 +4798,15 @@ draw_bg_solid_composite (const MetaTextureSpec *bg,
int height)
{
GdkColor bg_color;
-
+
g_assert (bg->type == META_TEXTURE_SOLID);
g_assert (fg->type != META_TEXTURE_COMPOSITE);
g_assert (fg->type != META_TEXTURE_SHAPE_LIST);
-
+
meta_color_spec_render (bg->data.solid.color_spec,
widget,
- &bg_color);
-
+ &bg_color);
+
switch (fg->type)
{
case META_TEXTURE_SOLID:
@@ -2853,13 +4834,13 @@ draw_bg_solid_composite (const MetaTextureSpec *bg,
{
GdkPixbuf *pixbuf;
GdkPixbuf *composited;
-
+
pixbuf = meta_texture_spec_render (fg, widget, mode, 255,
width, height);
if (pixbuf == NULL)
return;
-
+
composited = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
gdk_pixbuf_get_has_alpha (pixbuf), 8,
gdk_pixbuf_get_width (pixbuf),
@@ -2870,7 +4851,7 @@ draw_bg_solid_composite (const MetaTextureSpec *bg,
g_object_unref (G_OBJECT (pixbuf));
return;
}
-
+
gdk_pixbuf_composite_color (pixbuf,
composited,
0, 0,
@@ -2890,17 +4871,17 @@ draw_bg_solid_composite (const MetaTextureSpec *bg,
*/
draw_color_rectangle (widget, drawable, &bg_color, clip,
x, y, width, height);
-
+
render_pixbuf_aligned (drawable, clip, composited,
xalign, yalign,
x, y, width, height);
-
+
g_object_unref (G_OBJECT (pixbuf));
g_object_unref (G_OBJECT (composited));
}
break;
- case META_TEXTURE_BLANK:
+ case META_TEXTURE_BLANK:
case META_TEXTURE_COMPOSITE:
case META_TEXTURE_SHAPE_LIST:
g_assert_not_reached ();
@@ -2926,7 +4907,7 @@ draw_bg_gradient_composite (const MetaTextureSpec *bg,
g_assert (bg->type == META_TEXTURE_GRADIENT);
g_assert (fg->type != META_TEXTURE_COMPOSITE);
g_assert (fg->type != META_TEXTURE_SHAPE_LIST);
-
+
switch (fg->type)
{
case META_TEXTURE_SOLID:
@@ -2937,7 +4918,7 @@ draw_bg_gradient_composite (const MetaTextureSpec *bg,
GdkPixbuf *fg_pixbuf;
GdkPixbuf *composited;
int fg_width, fg_height;
-
+
bg_pixbuf = meta_texture_spec_render (bg, widget, mode, 255,
width, height);
@@ -2949,14 +4930,14 @@ draw_bg_gradient_composite (const MetaTextureSpec *bg,
if (fg_pixbuf == NULL)
{
- g_object_unref (G_OBJECT (bg_pixbuf));
+ g_object_unref (G_OBJECT (bg_pixbuf));
return;
}
/* gradients always fill the entire target area */
g_assert (gdk_pixbuf_get_width (bg_pixbuf) == width);
g_assert (gdk_pixbuf_get_height (bg_pixbuf) == height);
-
+
composited = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
gdk_pixbuf_get_has_alpha (bg_pixbuf), 8,
gdk_pixbuf_get_width (bg_pixbuf),
@@ -2976,7 +4957,7 @@ draw_bg_gradient_composite (const MetaTextureSpec *bg,
* offsets and try to composite only in the clip rectangle,
* but I just don't care enough to figure it out.
*/
-
+
gdk_pixbuf_composite (fg_pixbuf,
composited,
x + (width - fg_width) * xalign,
@@ -2987,9 +4968,9 @@ draw_bg_gradient_composite (const MetaTextureSpec *bg,
1.0, 1.0, /* scale */
GDK_INTERP_BILINEAR,
255 * alpha);
-
+
render_pixbuf (drawable, clip, composited, x, y);
-
+
g_object_unref (G_OBJECT (bg_pixbuf));
g_object_unref (G_OBJECT (fg_pixbuf));
g_object_unref (G_OBJECT (composited));
diff --git a/src/theme.h b/src/theme.h
index 249cb558..ebb48a8b 100644
--- a/src/theme.h
+++ b/src/theme.h
@@ -36,18 +36,27 @@ typedef struct _MetaFrameLayout MetaFrameLayout;
typedef struct _MetaFrameGeometry MetaFrameGeometry;
typedef struct _MetaTheme MetaTheme;
typedef struct _MetaPositionExprEnv MetaPositionExprEnv;
+typedef struct _MetaDrawInfo MetaDrawInfo;
+
+#define META_THEME_ERROR (g_quark_from_static_string ("meta-theme-error"))
typedef enum
{
- META_SCALE_NONE,
- META_SCALE_VERTICALLY,
- META_SCALE_HORIZONTALLY,
- META_SCALE_BOTH
-} MetaScaleMode;
+ META_THEME_ERROR_FRAME_GEOMETRY,
+ META_THEME_ERROR_BAD_CHARACTER,
+ META_THEME_ERROR_BAD_PARENS,
+ META_THEME_ERROR_UNKNOWN_VARIABLE,
+ META_THEME_ERROR_DIVIDE_BY_ZERO,
+ META_THEME_ERROR_MOD_ON_FLOAT,
+ META_THEME_ERROR_FAILED
+} MetaThemeError;
+
/* Parameters used to calculate the geometry of the frame */
struct _MetaFrameLayout
{
+ int refcount;
+
/* Size of left/right/bottom sides */
int left_width;
int right_width;
@@ -56,19 +65,12 @@ struct _MetaFrameLayout
/* Border of blue title region */
GtkBorder title_border;
- /* Border inside title region, around title */
- GtkBorder text_border;
-
- /* padding on either side of spacer */
- int spacer_padding;
-
- /* Size of spacer */
- int spacer_width;
- int spacer_height;
-
+ /* Extra height for inside of title region, above the font height */
+ int title_vertical_pad;
+
/* indent of buttons from edges of frame */
- int right_inset;
- int left_inset;
+ int right_titlebar_edge;
+ int left_titlebar_edge;
/* Size of buttons */
int button_width;
@@ -76,11 +78,6 @@ struct _MetaFrameLayout
/* Space around buttons */
GtkBorder button_border;
-
- /* Space inside button which is clickable but doesn't draw the
- * button icon
- */
- GtkBorder inner_button_border;
};
@@ -98,7 +95,6 @@ struct _MetaFrameGeometry
GdkRectangle close_rect;
GdkRectangle max_rect;
GdkRectangle min_rect;
- GdkRectangle spacer_rect;
GdkRectangle menu_rect;
GdkRectangle title_rect;
@@ -125,7 +121,8 @@ typedef enum
META_GTK_COLOR_MID,
META_GTK_COLOR_TEXT,
META_GTK_COLOR_BASE,
- META_GTK_COLOR_TEXT_AA
+ META_GTK_COLOR_TEXT_AA,
+ META_GTK_COLOR_LAST
} MetaGtkColorComponent;
struct _MetaColorSpec
@@ -154,6 +151,16 @@ struct _MetaGradientSpec
GSList *color_specs;
};
+struct _MetaDrawInfo
+{
+ GdkPixbuf *mini_icon;
+ GdkPixbuf *icon;
+ PangoLayout *title_layout;
+ int title_layout_width;
+ int title_layout_height;
+ const MetaFrameGeometry *fgeom;
+};
+
typedef enum
{
/* Basic drawing */
@@ -161,6 +168,9 @@ typedef enum
META_DRAW_RECTANGLE,
META_DRAW_ARC,
+ /* Clip to a rectangle */
+ META_DRAW_CLIP,
+
/* Texture thingies */
META_DRAW_TINT, /* just a filled rectangle with alpha */
META_DRAW_GRADIENT,
@@ -169,7 +179,14 @@ typedef enum
/* GTK theme engine stuff */
META_DRAW_GTK_ARROW,
META_DRAW_GTK_BOX,
- META_DRAW_GTK_VLINE
+ META_DRAW_GTK_VLINE,
+
+ /* App's window icon */
+ META_DRAW_ICON,
+ /* App's window title */
+ META_DRAW_TITLE,
+ /* a draw op list */
+ META_DRAW_OP_LIST
} MetaDrawType;
struct _MetaDrawOp
@@ -209,6 +226,13 @@ struct _MetaDrawOp
double start_angle;
double extent_angle;
} arc;
+
+ struct {
+ char *x;
+ char *y;
+ char *width;
+ char *height;
+ } clip;
struct {
MetaColorSpec *color_spec;
@@ -231,7 +255,6 @@ struct _MetaDrawOp
struct {
GdkPixbuf *pixbuf;
double alpha;
- MetaScaleMode scale_mode;
char *x;
char *y;
char *width;
@@ -264,6 +287,28 @@ struct _MetaDrawOp
char *y1;
char *y2;
} gtk_vline;
+
+ struct {
+ double alpha;
+ char *x;
+ char *y;
+ char *width;
+ char *height;
+ } icon;
+
+ struct {
+ MetaColorSpec *color_spec;
+ char *x;
+ char *y;
+ } title;
+
+ struct {
+ MetaDrawOpList *op_list;
+ char *x;
+ char *y;
+ char *width;
+ char *height;
+ } op_list;
} data;
};
@@ -295,6 +340,15 @@ typedef enum
typedef enum
{
+ META_MENU_ICON_TYPE_CLOSE,
+ META_MENU_ICON_TYPE_MAXIMIZE,
+ META_MENU_ICON_TYPE_UNMAXIMIZE,
+ META_MENU_ICON_TYPE_MINIMIZE,
+ META_MENU_ICON_TYPE_LAST
+} MetaMenuIconType;
+
+typedef enum
+{
/* Listed in the order in which the textures are drawn.
* (though this only matters for overlaps of course.)
* Buttons are drawn after the frame textures.
@@ -311,7 +365,7 @@ typedef enum
/* entire frame */
META_FRAME_PIECE_ENTIRE_BACKGROUND,
/* entire titlebar background */
- META_FRAME_PIECE_TITLEBAR_BACKGROUND,
+ META_FRAME_PIECE_TITLEBAR,
/* portion of the titlebar background inside the titlebar
* background edges
*/
@@ -325,7 +379,7 @@ typedef enum
/* bottom edge of titlebar */
META_FRAME_PIECE_BOTTOM_TITLEBAR_EDGE,
/* render over title background (text area) */
- META_FRAME_PIECE_TITLE_BACKGROUND,
+ META_FRAME_PIECE_TITLE,
/* left edge of the frame */
META_FRAME_PIECE_LEFT_EDGE,
/* right edge of the frame */
@@ -338,12 +392,12 @@ typedef enum
META_FRAME_PIECE_LAST
} MetaFramePiece;
+#define N_GTK_STATES 5
struct _MetaFrameStyle
{
int refcount;
MetaFrameStyle *parent;
- MetaDrawOpList *button_icons[META_BUTTON_TYPE_LAST][META_BUTTON_STATE_LAST];
- MetaDrawOpList *button_backgrounds[META_BUTTON_TYPE_LAST][META_BUTTON_STATE_LAST];
+ MetaDrawOpList *buttons[META_BUTTON_TYPE_LAST][META_BUTTON_STATE_LAST];
MetaDrawOpList *pieces[META_FRAME_PIECE_LAST];
MetaFrameLayout *layout;
};
@@ -402,39 +456,54 @@ struct _MetaFrameStyleSet
struct _MetaTheme
{
char *name;
+ char *dirname;
char *filename;
-
+ char *readable_name;
+ char *author;
+ char *copyright;
+ char *date;
+ char *description;
+
+ GHashTable *integer_constants;
+ GHashTable *float_constants;
+ GHashTable *images_by_filename;
+ GHashTable *layouts_by_name;
+ GHashTable *draw_op_lists_by_name;
GHashTable *styles_by_name;
GHashTable *style_sets_by_name;
MetaFrameStyleSet *style_sets_by_type[META_FRAME_TYPE_LAST];
+ MetaDrawOpList *menu_icons[META_MENU_ICON_TYPE_LAST][N_GTK_STATES];
};
-#define META_POSITION_EXPR_ERROR (g_quark_from_static_string ("meta-position-expr-error"))
-typedef enum
-{
- META_POSITION_EXPR_ERROR_BAD_CHARACTER,
- META_POSITION_EXPR_ERROR_BAD_PARENS,
- META_POSITION_EXPR_ERROR_UNKNOWN_VARIABLE,
- META_POSITION_EXPR_ERROR_DIVIDE_BY_ZERO,
- META_POSITION_EXPR_ERROR_MOD_ON_FLOAT,
- META_POSITION_EXPR_ERROR_FAILED
-} MetaPositionExprError;
-
struct _MetaPositionExprEnv
{
int x;
int y;
int width;
int height;
- /* size of an image or whatever */
+ /* size of an object being drawn, if it has a natural size */
int object_width;
int object_height;
+ /* global object sizes, always available */
+ int left_width;
+ int right_width;
+ int top_height;
+ int bottom_height;
+ int title_width;
+ int title_height;
+ int mini_icon_width;
+ int mini_icon_height;
+ int icon_width;
+ int icon_height;
+ /* Theme so we can look up constants */
+ MetaTheme *theme;
};
MetaFrameLayout* meta_frame_layout_new (void);
-void meta_frame_layout_free (MetaFrameLayout *layout);
+MetaFrameLayout* meta_frame_layout_copy (const MetaFrameLayout *src);
+void meta_frame_layout_ref (MetaFrameLayout *layout);
+void meta_frame_layout_unref (MetaFrameLayout *layout);
void meta_frame_layout_get_borders (const MetaFrameLayout *layout,
- GtkWidget *widget,
int text_height,
MetaFrameFlags flags,
int *top_height,
@@ -442,13 +511,15 @@ void meta_frame_layout_get_borders (const MetaFrameLayout *layout,
int *left_width,
int *right_width);
void meta_frame_layout_calc_geometry (const MetaFrameLayout *layout,
- GtkWidget *widget,
int text_height,
MetaFrameFlags flags,
int client_width,
int client_height,
MetaFrameGeometry *fgeom);
+gboolean meta_frame_layout_validate (const MetaFrameLayout *layout,
+ GError **error);
+
gboolean meta_parse_position_expression (const char *expr,
const MetaPositionExprEnv *env,
int *x_return,
@@ -476,6 +547,7 @@ void meta_draw_op_draw (const MetaDrawOp *op,
GtkWidget *widget,
GdkDrawable *drawable,
const GdkRectangle *clip,
+ const MetaDrawInfo *info,
/* logical region being drawn */
int x,
int y,
@@ -490,12 +562,17 @@ void meta_draw_op_list_draw (const MetaDrawOpList *op_list,
GtkWidget *widget,
GdkDrawable *drawable,
const GdkRectangle *clip,
+ const MetaDrawInfo *info,
int x,
int y,
int width,
int height);
void meta_draw_op_list_append (MetaDrawOpList *op_list,
MetaDrawOp *op);
+gboolean meta_draw_op_list_validate (MetaDrawOpList *op_list,
+ GError **error);
+gboolean meta_draw_op_list_contains (MetaDrawOpList *op_list,
+ MetaDrawOpList *child);
MetaGradientSpec* meta_gradient_spec_new (MetaGradientType type);
void meta_gradient_spec_free (MetaGradientSpec *desc);
@@ -503,32 +580,164 @@ GdkPixbuf* meta_gradient_spec_render (const MetaGradientSpec *desc,
GtkWidget *widget,
int width,
int height);
+gboolean meta_gradient_spec_validate (MetaGradientSpec *spec,
+ GError **error);
MetaFrameStyle* meta_frame_style_new (MetaFrameStyle *parent);
void meta_frame_style_ref (MetaFrameStyle *style);
void meta_frame_style_unref (MetaFrameStyle *style);
-void meta_frame_style_draw (MetaFrameStyle *style,
+void meta_frame_style_draw (MetaFrameStyle *style,
+ GtkWidget *widget,
+ GdkDrawable *drawable,
+ int x_offset,
+ int y_offset,
+ const GdkRectangle *clip,
+ const MetaFrameGeometry *fgeom,
+ int client_width,
+ int client_height,
+ PangoLayout *title_layout,
+ int text_height,
+ MetaButtonState button_states[META_BUTTON_TYPE_LAST],
+ GdkPixbuf *mini_icon,
+ GdkPixbuf *icon);
+
+
+gboolean meta_frame_style_validate (MetaFrameStyle *style,
+ GError **error);
+
+MetaFrameStyleSet* meta_frame_style_set_new (MetaFrameStyleSet *parent);
+void meta_frame_style_set_ref (MetaFrameStyleSet *style_set);
+void meta_frame_style_set_unref (MetaFrameStyleSet *style_set);
+
+gboolean meta_frame_style_set_validate (MetaFrameStyleSet *style_set,
+ GError **error);
+
+MetaTheme* meta_theme_get_current (void);
+void meta_theme_set_current (const char *name,
+ gboolean force_reload);
+
+MetaTheme* meta_theme_new (void);
+void meta_theme_free (MetaTheme *theme);
+gboolean meta_theme_validate (MetaTheme *theme,
+ GError **error);
+GdkPixbuf* meta_theme_load_image (MetaTheme *theme,
+ const char *filename,
+ GError **error);
+
+void meta_theme_draw_frame (MetaTheme *theme,
GtkWidget *widget,
GdkDrawable *drawable,
+ const GdkRectangle *clip,
int x_offset,
int y_offset,
- const GdkRectangle *clip,
+ MetaFrameType type,
MetaFrameFlags flags,
int client_width,
int client_height,
PangoLayout *title_layout,
int text_height,
- MetaButtonState button_states[META_BUTTON_TYPE_LAST]);
-
-MetaFrameStyleSet* meta_frame_style_set_new (MetaFrameStyleSet *parent);
-void meta_frame_style_set_ref (MetaFrameStyleSet *style_set);
-void meta_frame_style_set_unref (MetaFrameStyleSet *style_set);
-
-MetaTheme* meta_theme_new (void);
-void meta_theme_free (MetaTheme *theme);
-
-MetaFrameStyle* meta_frame_style_get_test (void);
+ MetaButtonState button_states[META_BUTTON_TYPE_LAST],
+ GdkPixbuf *mini_icon,
+ GdkPixbuf *icon);
+
+void meta_theme_draw_menu_icon (MetaTheme *theme,
+ GtkWidget *widget,
+ GdkDrawable *drawable,
+ const GdkRectangle *clip,
+ int x_offset,
+ int y_offset,
+ int width,
+ int height,
+ MetaMenuIconType type);
+
+void meta_theme_get_frame_borders (MetaTheme *theme,
+ MetaFrameType type,
+ int text_height,
+ MetaFrameFlags flags,
+ int *top_height,
+ int *bottom_height,
+ int *left_width,
+ int *right_width);
+void meta_theme_calc_geometry (MetaTheme *theme,
+ MetaFrameType type,
+ int text_height,
+ MetaFrameFlags flags,
+ int client_width,
+ int client_height,
+ MetaFrameGeometry *fgeom);
+
+
+MetaFrameLayout* meta_theme_lookup_layout (MetaTheme *theme,
+ const char *name);
+void meta_theme_insert_layout (MetaTheme *theme,
+ const char *name,
+ MetaFrameLayout *layout);
+MetaDrawOpList* meta_theme_lookup_draw_op_list (MetaTheme *theme,
+ const char *name);
+void meta_theme_insert_draw_op_list (MetaTheme *theme,
+ const char *name,
+ MetaDrawOpList *op_list);
+MetaFrameStyle* meta_theme_lookup_style (MetaTheme *theme,
+ const char *name);
+void meta_theme_insert_style (MetaTheme *theme,
+ const char *name,
+ MetaFrameStyle *style);
+MetaFrameStyleSet* meta_theme_lookup_style_set (MetaTheme *theme,
+ const char *name);
+void meta_theme_insert_style_set (MetaTheme *theme,
+ const char *name,
+ MetaFrameStyleSet *style_set);
+gboolean meta_theme_define_int_constant (MetaTheme *theme,
+ const char *name,
+ int value,
+ GError **error);
+gboolean meta_theme_lookup_int_constant (MetaTheme *theme,
+ const char *name,
+ int *value);
+gboolean meta_theme_define_float_constant (MetaTheme *theme,
+ const char *name,
+ double value,
+ GError **error);
+gboolean meta_theme_lookup_float_constant (MetaTheme *theme,
+ const char *name,
+ double *value);
+
+char* meta_theme_replace_constants (MetaTheme *theme,
+ const char *expr,
+ GError **err);
+
+/* random stuff */
+
+int meta_gtk_widget_get_text_height (GtkWidget *widget);
+
+/* Enum converters */
+MetaGtkColorComponent meta_color_component_from_string (const char *str);
+const char* meta_color_component_to_string (MetaGtkColorComponent component);
+MetaButtonState meta_button_state_from_string (const char *str);
+const char* meta_button_state_to_string (MetaButtonState state);
+MetaButtonType meta_button_type_from_string (const char *str);
+const char* meta_button_type_to_string (MetaButtonType type);
+MetaMenuIconType meta_menu_icon_type_from_string (const char *str);
+const char* meta_menu_icon_type_to_string (MetaMenuIconType type);
+MetaFramePiece meta_frame_piece_from_string (const char *str);
+const char* meta_frame_piece_to_string (MetaFramePiece piece);
+MetaFrameState meta_frame_state_from_string (const char *str);
+const char* meta_frame_state_to_string (MetaFrameState state);
+MetaFrameResize meta_frame_resize_from_string (const char *str);
+const char* meta_frame_resize_to_string (MetaFrameResize resize);
+MetaFrameFocus meta_frame_focus_from_string (const char *str);
+const char* meta_frame_focus_to_string (MetaFrameFocus focus);
+MetaFrameType meta_frame_type_from_string (const char *str);
+const char* meta_frame_type_to_string (MetaFrameType type);
+MetaGradientType meta_gradient_type_from_string (const char *str);
+const char* meta_gradient_type_to_string (MetaGradientType type);
+GtkStateType meta_gtk_state_from_string (const char *str);
+const char* meta_gtk_state_to_string (GtkStateType state);
+GtkShadowType meta_gtk_shadow_from_string (const char *str);
+const char* meta_gtk_shadow_to_string (GtkShadowType shadow);
+GtkArrowType meta_gtk_arrow_from_string (const char *str);
+const char* meta_gtk_arrow_to_string (GtkArrowType arrow);
#endif
diff --git a/src/themes/Atlanta/metacity-theme-1.xml b/src/themes/Atlanta/metacity-theme-1.xml
new file mode 100644
index 00000000..dd767325
--- /dev/null
+++ b/src/themes/Atlanta/metacity-theme-1.xml
@@ -0,0 +1,239 @@
+<?xml version="1.0"?>
+<metacity_theme>
+<info>
+ <name>Default</name>
+ <author>Havoc Pennington &lt;hp@redhat.com&gt;</author>
+ <copyright>&#194; Havoc Pennington, 2002</copyright>
+ <date>February 3, 2002</date>
+ <description>Simple low-overhead default theme that comes with Metacity.</description>
+</info>
+
+<frame_geometry name="normal">
+ <distance name="left_width" value="6"/>
+ <distance name="right_width" value="6"/>
+ <distance name="bottom_height" value="7"/>
+ <distance name="left_titlebar_edge" value="6"/>
+ <distance name="right_titlebar_edge" value="6"/>
+ <distance name="button_width" value="17"/>
+ <distance name="button_height" value="17"/>
+ <distance name="title_vertical_pad" value="3"/>
+ <border name="title_border" left="3" right="4" top="4" bottom="3"/>
+ <border name="button_border" left="0" right="0" top="1" bottom="1"/>
+</frame_geometry>
+
+<!-- strip borders off the normal geometry -->
+<frame_geometry name="normal_borderless" parent="normal">
+ <distance name="left_width" value="0"/>
+ <distance name="right_width" value="0"/>
+ <distance name="bottom_height" value="0"/>
+ <distance name="left_titlebar_edge" value="0"/>
+ <distance name="right_titlebar_edge" value="0"/>
+</frame_geometry>
+
+<!-- define constants -->
+<constant name="ArrowWidth" value="7"/>
+<constant name="ArrowHeight" value="5"/>
+<constant name="ButtonIPad" value="3"/>
+<constant name="ThickLineWidth" value="3"/>
+<constant name="IconTitleSpacing" value="2"/>
+<constant name="SpacerWidth" value="8"/>
+<constant name="SpacerHeight" value="11"/>
+
+<!-- Buttons -->
+
+<draw_ops name="button_pressed_bg">
+ <gtk_box state="active" shadow="in" x="0" y="0" width="width" height="height"/>
+</draw_ops>
+
+<draw_ops name="menu_button">
+ <gtk_arrow state="normal" shadow="out" arrow="down"
+ x="(width - ArrowWidth) / 2"
+ y="(height - ArrowHeight) / 2"
+ width="ArrowWidth"
+ height="ArrowHeight"/>
+</draw_ops>
+
+<draw_ops name="menu_button_pressed">
+ <include name="button_pressed_bg"/>
+ <include name="menu_button"/>
+</draw_ops>
+
+<draw_ops name="minimize_button">
+ <line color="gtk:fg[NORMAL]"
+ x1="ButtonIPad"
+ y1="height - ButtonIPad - ThickLineWidth + 1"
+ x2="width - ButtonIPad"
+ y2="height - ButtonIPad - ThickLineWidth + 1"
+ width="3"/> <!-- FIXME allow a constant here -->
+</draw_ops>
+
+<draw_ops name="minimize_button_pressed">
+ <include name="button_pressed_bg"/>
+ <include name="minimize_button"/>
+</draw_ops>
+
+<draw_ops name="maximize_button">
+ <rectangle color="gtk:fg[NORMAL]" filled="false"
+ x="ButtonIPad" y="ButtonIPad" width="width-ButtonIPad*2-1" height="height-ButtonIPad*2-1"/>
+ <line color="gtk:fg[NORMAL]" width="3"
+ x1="ButtonIPad" y1="ButtonIPad+1" x2="width-ButtonIPad" y2="ButtonIPad+1"/>
+</draw_ops>
+
+<draw_ops name="maximize_button_pressed">
+ <include name="button_pressed_bg"/>
+ <include name="maximize_button"/>
+</draw_ops>
+
+<draw_ops name="mini_window_icon">
+ <rectangle color="gtk:bg[NORMAL]" filled="true"
+ x="0" y="0" width="width-1" height="height-1"/>
+ <rectangle color="gtk:fg[NORMAL]" filled="false"
+ x="0" y="0" width="width-1" height="height-1"/>
+ <line color="gtk:fg[NORMAL]" width="2"
+ x1="0" y1="1" x2="width" y2="1"/>
+</draw_ops>
+
+<draw_ops name="restore_button">
+ <include name="mini_window_icon"
+ x="ButtonIPad" y="ButtonIPad"
+ width="width - 5 - ButtonIPad"
+ height="height - 5 - ButtonIPad"/>
+ <include name="mini_window_icon"
+ x="3 + ButtonIPad" y="3 + ButtonIPad"
+ width="width - 5 - ButtonIPad"
+ height="height - 5 - ButtonIPad"/>
+</draw_ops>
+
+<draw_ops name="restore_button_pressed">
+ <include name="button_pressed_bg"/>
+ <include name="restore_button"/>
+</draw_ops>
+
+<draw_ops name="close_button">
+ <line color="gtk:fg[NORMAL]"
+ x1="ButtonIPad" y1="ButtonIPad"
+ x2="width - ButtonIPad - 1" y2="height - ButtonIPad - 1"/>
+ <line color="gtk:fg[NORMAL]"
+ x1="ButtonIPad" y1="height - ButtonIPad - 1"
+ x2="width - ButtonIPad - 1" y2="ButtonIPad"/>
+</draw_ops>
+
+<draw_ops name="close_button_pressed">
+ <include name="button_pressed_bg"/>
+ <include name="close_button"/>
+</draw_ops>
+
+<draw_ops name="outer_bevel">
+ <rectangle color="#000000"
+ x="0" y="0" width="width-1" height="height-1"/>
+ <line color="gtk:light[NORMAL]"
+ x1="1" y1="1" x2="1" y2="height-2"/>
+ <line color="gtk:light[NORMAL]"
+ x1="1" y1="1" x2="width-2" y2="1"/>
+ <line color="gtk:dark[NORMAL]"
+ x1="width-2" y1="1" x2="width-2" y2="height-2"/>
+ <line color="gtk:dark[NORMAL]"
+ x1="1" y1="height-2" x2="width-2" y2="height-2"/>
+</draw_ops>
+
+<draw_ops name="focus_background">
+ <include name="outer_bevel"/>
+ <rectangle color="#000000"
+ x="left_width-1" y="top_height-1"
+ width="width-left_width-right_width+1"
+ height="height-top_height-bottom_height+1"/>
+</draw_ops>
+
+<draw_ops name="title_gradient">
+ <gradient type="diagonal" x="0" y="0" width="width-SpacerWidth" height="height">
+ <color value="blend/gtk:bg[NORMAL]/gtk:bg[SELECTED]/0.6"/>
+ <color value="gtk:bg[SELECTED]"/>
+ </gradient>
+ <gtk_vline state="normal" x="width+1-SpacerWidth/2"
+ y1="(height-SpacerHeight)/2"
+ y2="height - (height-SpacerHeight)/2"/>
+</draw_ops>
+
+<draw_ops name="title_text_focused">
+ <clip x="0" y="0" width="width-SpacerWidth" height="height"/>
+ <title color="gtk:fg[SELECTED]"
+ x="(0 `max` (width-title_width-mini_icon_width-IconTitleSpacing)) / 2 + mini_icon_width + IconTitleSpacing"
+ y="((height - title_height) / 2) `max` 0"/>
+ <icon x="(0 `max` (width-title_width-mini_icon_width-IconTitleSpacing)) / 2"
+ y="(height-mini_icon_height) / 2"
+ width="mini_icon_width" height="mini_icon_height"/>
+</draw_ops>
+
+<draw_ops name="title_text">
+ <clip x="0" y="0" width="width-SpacerWidth" height="height"/>
+ <title color="gtk:fg[NORMAL]"
+ x="(0 `max` (width-title_width-mini_icon_width-IconTitleSpacing)) / 2 + mini_icon_width + IconTitleSpacing"
+ y="((height - title_height) / 2) `max` 0"/>
+ <icon x="(0 `max` (width-title_width-mini_icon_width-IconTitleSpacing)) / 2"
+ y="(height-mini_icon_height) / 2"
+ width="mini_icon_width" height="mini_icon_height"/>
+</draw_ops>
+
+<draw_ops name="title_normal">
+ <include name="title_text"/>
+</draw_ops>
+
+<draw_ops name="title_focused">
+ <include name="title_gradient"/>
+ <include name="title_text_focused"/>
+</draw_ops>
+
+<frame_style name="normal_unfocused" geometry="normal">
+ <piece position="entire_background" draw_ops="outer_bevel"/>
+ <piece position="title" draw_ops="title_normal"/>
+
+ <!-- we don't specify for prelight, so normal is used -->
+ <button function="close" state="normal" draw_ops="close_button"/>
+ <button function="close" state="pressed" draw_ops="close_button_pressed"/>
+ <button function="minimize" state="normal" draw_ops="minimize_button"/>
+ <button function="minimize" state="pressed" draw_ops="minimize_button_pressed"/>
+ <button function="maximize" state="normal" draw_ops="maximize_button"/>
+ <button function="maximize" state="pressed" draw_ops="maximize_button_pressed"/>
+ <button function="menu" state="normal" draw_ops="menu_button"/>
+ <button function="menu" state="pressed" draw_ops="menu_button_pressed"/>
+</frame_style>
+
+<frame_style name="normal_focused" geometry="normal" parent="normal_unfocused">
+ <piece position="entire_background" draw_ops="focus_background"/>
+ <piece position="title" draw_ops="title_focused"/>
+</frame_style>
+
+<frame_style name="maximized_unfocused" parent="normal_unfocused">
+ <button function="maximize" state="normal" draw_ops="restore_button"/>
+ <button function="maximize" state="pressed" draw_ops="restore_button_pressed"/>
+</frame_style>
+
+<frame_style name="maximized_focused" parent="normal_focused">
+ <button function="maximize" state="normal" draw_ops="restore_button"/>
+ <button function="maximize" state="pressed" draw_ops="restore_button_pressed"/>
+</frame_style>
+
+
+<frame_style_set name="normal">
+<frame focus="yes" state="normal" resize="both" style="normal_focused"/>
+<frame focus="no" state="normal" resize="both" style="normal_unfocused"/>
+<frame focus="yes" state="maximized" style="maximized_focused"/>
+<frame focus="no" state="maximized" style="maximized_unfocused"/>
+<frame focus="yes" state="shaded" style="normal_focused"/>
+<frame focus="no" state="shaded" style="normal_unfocused"/>
+<frame focus="yes" state="maximized_and_shaded" style="maximized_focused"/>
+<frame focus="no" state="maximized_and_shaded" style="maximized_unfocused"/>
+</frame_style_set>
+
+<window type="normal" style_set="normal"/>
+<window type="dialog" style_set="normal"/>
+<window type="modal_dialog" style_set="normal"/>
+<window type="menu" style_set="normal"/>
+<window type="utility" style_set="normal"/>
+
+<menu_icon function="close" state="normal" draw_ops="close_button"/>
+<menu_icon function="maximize" state="normal" draw_ops="maximize_button"/>
+<menu_icon function="unmaximize" state="normal" draw_ops="restore_button"/>
+<menu_icon function="minimize" state="normal" draw_ops="minimize_button"/>
+
+</metacity_theme>
diff --git a/src/themes/Makefile.am b/src/themes/Makefile.am
new file mode 100644
index 00000000..9edfd4b5
--- /dev/null
+++ b/src/themes/Makefile.am
@@ -0,0 +1,29 @@
+THEMES= \
+ Atlanta \
+ Crux
+
+THEME_DIR=$(pkgdatadir)/themes
+
+install-data-local:
+ $(mkinstalldirs) $(DESTDIR)$(THEME_DIR); \
+ for THEME in $(THEMES); do \
+ echo '-- Installing theme '$$THEME; \
+ $(mkinstalldirs) $(DESTDIR)$(THEME_DIR)/$$THEME; \
+ (installfiles=`find $(srcdir)/$$THEME -name "*.png" -o -name "*.xml"`; \
+ for i in $$installfiles; do \
+ echo '-- Installing '$$i ; \
+ $(INSTALL_DATA) $$i $(DESTDIR)$(THEME_DIR)/$$THEME; \
+ done) \
+ done
+
+dist-hook:
+ mkdir $(distdir)/themes; \
+ for THEME in $(THEMES); do \
+ echo '-- Disting theme '$$THEME; \
+ mkdir $(distdir)/$$THEME; \
+ (installfiles=`find $(srcdir)/$$THEME -name "*.png" -o -name "*.xml"`; \
+ for i in $$installfiles; do \
+ echo '-- Disting '$$i ; \
+ cp $$i $(distdir)/$$THEME; \
+ done) \
+ done
diff --git a/src/themewidget.c b/src/themewidget.c
new file mode 100644
index 00000000..b3092a56
--- /dev/null
+++ b/src/themewidget.c
@@ -0,0 +1,181 @@
+/* Metacity theme widget (displays themed draw operations) */
+
+/*
+ * Copyright (C) 2002 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 "themewidget.h"
+#include <math.h>
+
+static void meta_area_class_init (MetaAreaClass *klass);
+static void meta_area_init (MetaArea *area);
+static void meta_area_size_request (GtkWidget *widget,
+ GtkRequisition *req);
+static gint meta_area_expose (GtkWidget *widget,
+ GdkEventExpose *event);
+static void meta_area_finalize (GObject *object);
+
+
+static GtkMiscClass *parent_class;
+
+GtkType
+meta_area_get_type (void)
+{
+ static GtkType area_type = 0;
+
+ if (!area_type)
+ {
+ static const GtkTypeInfo area_info =
+ {
+ "MetaArea",
+ sizeof (MetaArea),
+ sizeof (MetaAreaClass),
+ (GtkClassInitFunc) meta_area_class_init,
+ (GtkObjectInitFunc) meta_area_init,
+ /* reserved_1 */ NULL,
+ /* reserved_2 */ NULL,
+ (GtkClassInitFunc) NULL,
+ };
+
+ area_type = gtk_type_unique (GTK_TYPE_MISC, &area_info);
+ }
+
+ return area_type;
+}
+
+static void
+meta_area_class_init (MetaAreaClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+ GtkObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+
+ object_class = (GtkObjectClass*) class;
+ widget_class = (GtkWidgetClass*) class;
+ parent_class = gtk_type_class (gtk_misc_get_type ());
+
+ gobject_class->finalize = meta_area_finalize;
+
+ widget_class->expose_event = meta_area_expose;
+ widget_class->size_request = meta_area_size_request;
+}
+
+static void
+meta_area_init (MetaArea *area)
+{
+ GTK_WIDGET_SET_FLAGS (area, GTK_NO_WINDOW);
+}
+
+GtkWidget*
+meta_area_new (void)
+{
+ MetaArea *area;
+
+ area = gtk_type_new (META_TYPE_AREA);
+
+ return GTK_WIDGET (area);
+}
+
+static void
+meta_area_finalize (GObject *object)
+{
+ MetaArea *area;
+
+ area = META_AREA (object);
+
+ if (area->dnotify)
+ (* area->dnotify) (area->user_data);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static gint
+meta_area_expose (GtkWidget *widget,
+ GdkEventExpose *event)
+{
+ MetaArea *area;
+ GtkMisc *misc;
+ gint x, y;
+ gfloat xalign;
+
+ g_return_val_if_fail (META_IS_AREA (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ if (GTK_WIDGET_DRAWABLE (widget))
+ {
+ area = META_AREA (widget);
+ misc = GTK_MISC (widget);
+
+ if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
+ xalign = misc->xalign;
+ else
+ xalign = 1.0 - misc->xalign;
+
+ x = floor (widget->allocation.x + misc->xpad
+ + ((widget->allocation.width - widget->requisition.width) * xalign)
+ + 0.5);
+ y = floor (widget->allocation.y + misc->ypad
+ + ((widget->allocation.height - widget->requisition.height) * misc->yalign)
+ + 0.5);
+
+ if (area->expose_func)
+ {
+ (* area->expose_func) (area, event, x, y,
+ area->user_data);
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+meta_area_size_request (GtkWidget *widget,
+ GtkRequisition *req)
+{
+ MetaArea *area;
+
+ area = META_AREA (widget);
+
+ req->width = 0;
+ req->height = 0;
+
+ if (area->size_func)
+ {
+ (* area->size_func) (area, &req->width, &req->height,
+ area->user_data);
+ }
+}
+
+void
+meta_area_setup (MetaArea *area,
+ MetaAreaSizeFunc size_func,
+ MetaAreaExposeFunc expose_func,
+ void *user_data,
+ GDestroyNotify dnotify)
+{
+ if (area->dnotify)
+ (* area->dnotify) (area->user_data);
+
+ area->size_func = size_func;
+ area->expose_func = expose_func;
+ area->user_data = user_data;
+ area->dnotify = dnotify;
+
+ gtk_widget_queue_resize (GTK_WIDGET (area));
+}
+
diff --git a/src/themewidget.h b/src/themewidget.h
new file mode 100644
index 00000000..55161fbc
--- /dev/null
+++ b/src/themewidget.h
@@ -0,0 +1,76 @@
+/* Metacity theme widget (displays themed draw operations) */
+
+/*
+ * Copyright (C) 2002 Havoc Pennington
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "theme.h"
+#include <gtk/gtkmisc.h>
+
+#ifndef META_THEME_WIDGET_H
+#define META_THEME_WIDGET_H
+
+#define META_TYPE_AREA (meta_area_get_type ())
+#define META_AREA(obj) (GTK_CHECK_CAST ((obj), META_TYPE_AREA, MetaArea))
+#define META_AREA_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), META_TYPE_AREA, MetaAreaClass))
+#define META_IS_AREA(obj) (GTK_CHECK_TYPE ((obj), META_TYPE_AREA))
+#define META_IS_AREA_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), META_TYPE_AREA))
+#define META_AREA_GET_CLASS(obj) (GTK_CHECK_GET_CLASS ((obj), META_TYPE_AREA, MetaAreaClass))
+
+typedef struct _MetaArea MetaArea;
+typedef struct _MetaAreaClass MetaAreaClass;
+
+
+typedef void (* MetaAreaSizeFunc) (MetaArea *area,
+ int *width,
+ int *height,
+ void *user_data);
+
+typedef void (* MetaAreaExposeFunc) (MetaArea *area,
+ GdkEventExpose *event,
+ int x_offset,
+ int y_offset,
+ void *user_data);
+
+struct _MetaArea
+{
+ GtkMisc misc;
+
+ MetaAreaSizeFunc size_func;
+ MetaAreaExposeFunc expose_func;
+ void *user_data;
+ GDestroyNotify dnotify;
+};
+
+struct _MetaAreaClass
+{
+ GtkMiscClass parent_class;
+};
+
+
+GtkType meta_area_get_type (void) G_GNUC_CONST;
+GtkWidget* meta_area_new (void);
+
+void meta_area_setup (MetaArea *area,
+ MetaAreaSizeFunc size_func,
+ MetaAreaExposeFunc expose_func,
+ void *user_data,
+ GDestroyNotify dnotify);
+
+
+#endif
diff --git a/src/tools/Makefile.am b/src/tools/Makefile.am
index 0db4a691..4b971512 100644
--- a/src/tools/Makefile.am
+++ b/src/tools/Makefile.am
@@ -1,9 +1,13 @@
-INCLUDES=@METACITY_RESTART_CFLAGS@
+INCLUDES=@METACITY_RESTART_CFLAGS@ @METACITY_RELOAD_THEME_CFLAGS@
metacity_restart_SOURCES= \
metacity-restart.c
-bin_PROGRAMS=metacity-restart
+metacity_reload_theme_SOURCES= \
+ metacity-reload-theme.c
+
+bin_PROGRAMS=metacity-restart metacity-reload-theme
metacity_restart_LDADD= @METACITY_RESTART_LIBS@
+metacity_reload_theme_LDADD= @METACITY_RELOAD_THEME_LIBS@
diff --git a/src/tools/metacity-reload-theme.c b/src/tools/metacity-reload-theme.c
new file mode 100644
index 00000000..0ef19d55
--- /dev/null
+++ b/src/tools/metacity-reload-theme.c
@@ -0,0 +1,56 @@
+/* Metacity theme-reloader app */
+
+/*
+ * Copyright (C) 2002 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 <gtk/gtk.h>
+#include <gdk/gdkx.h>
+
+int
+main (int argc, char **argv)
+{
+ XEvent xev;
+
+ gtk_init (&argc, &argv);
+
+ xev.xclient.type = ClientMessage;
+ xev.xclient.serial = 0;
+ xev.xclient.send_event = True;
+ xev.xclient.display = gdk_display;
+ xev.xclient.window = gdk_x11_get_default_root_xwindow ();
+ xev.xclient.message_type = XInternAtom (gdk_display,
+ "_METACITY_RELOAD_THEME_MESSAGE",
+ False);
+ xev.xclient.format = 32;
+ xev.xclient.data.l[0] = 0;
+ xev.xclient.data.l[1] = 0;
+ xev.xclient.data.l[2] = 0;
+
+ XSendEvent (gdk_display,
+ gdk_x11_get_default_root_xwindow (),
+ False,
+ SubstructureRedirectMask | SubstructureNotifyMask,
+ &xev);
+
+ XFlush (gdk_display);
+ XSync (gdk_display, False);
+
+ return 0;
+}
+
diff --git a/src/ui.c b/src/ui.c
index 4f2c726c..11f78667 100644
--- a/src/ui.c
+++ b/src/ui.c
@@ -25,6 +25,7 @@
#include "util.h"
#include "menu.h"
#include "core.h"
+#include "theme.h"
#include "inlinepixbufs.h"
@@ -544,3 +545,16 @@ meta_text_property_to_utf8 (Display *xdisplay,
return retval;
}
+
+void
+meta_ui_set_current_theme (const char *name,
+ gboolean force_reload)
+{
+ meta_theme_set_current (name, force_reload);
+}
+
+gboolean
+meta_ui_have_a_theme (void)
+{
+ return meta_theme_get_current () != NULL;
+}
diff --git a/src/ui.h b/src/ui.h
index 2ac4858c..d123a24e 100644
--- a/src/ui.h
+++ b/src/ui.h
@@ -141,6 +141,11 @@ gboolean meta_ui_window_should_not_cause_focus (Display *xdisplay,
char* meta_text_property_to_utf8 (Display *xdisplay,
const XTextProperty *prop);
+void meta_ui_set_current_theme (const char *name,
+ gboolean force_reload);
+gboolean meta_ui_have_a_theme (void);
+
+
#include "tabpopup.h"
#endif
diff --git a/src/window.c b/src/window.c
index 4d7699dc..3e663f84 100644
--- a/src/window.c
+++ b/src/window.c
@@ -459,7 +459,7 @@ meta_window_new (MetaDisplay *display, Window xwindow,
if (window->initial_workspace_set)
{
- if (window->initial_workspace == 0xFFFFFFFF)
+ if (window->initial_workspace == (int) 0xFFFFFFFF)
{
meta_workspace_add_window (window->screen->active_workspace, window);
window->on_all_workspaces = TRUE;
@@ -487,17 +487,17 @@ meta_window_new (MetaDisplay *display, Window xwindow,
if (parent)
{
- GList *tmp;
+ GList *tmp_list;
if (parent->on_all_workspaces)
window->on_all_workspaces = TRUE;
- tmp = parent->workspaces;
- while (tmp != NULL)
+ tmp_list = parent->workspaces;
+ while (tmp_list != NULL)
{
- meta_workspace_add_window (tmp->data, window);
+ meta_workspace_add_window (tmp_list->data, window);
- tmp = tmp->next;
+ tmp_list = tmp_list->next;
}
}
}
@@ -2716,7 +2716,7 @@ meta_window_client_message (MetaWindow *window,
meta_window_unstick (window);
meta_window_change_workspace (window, workspace);
}
- else if (space == 0xFFFFFFFF)
+ else if (space == (int) 0xFFFFFFFF)
{
meta_window_stick (window);
}
@@ -4893,7 +4893,7 @@ recalc_do_not_cover_struts (MetaWindow *window)
static void
recalc_window_type (MetaWindow *window)
{
- int old_type;
+ MetaWindowType old_type;
old_type = window->type;
diff --git a/theme-format.txt b/theme-format.txt
new file mode 100644
index 00000000..744f74a9
--- /dev/null
+++ b/theme-format.txt
@@ -0,0 +1,218 @@
+Docs on the theme format
+
+Themes are in a simple XML-subset format.
+
+<?xml version="1.0"?>
+<metacity_theme>
+<!-- Only one info section is allowed -->
+<info>
+ <name>Foo</name>
+ <author>Foo P. Bar</author>
+ <copyright>whoever, 2002</copyright>
+ <date>Jan 31 2005</date>
+ <description>A sentence about the theme.</description>
+</info>
+
+<!-- define a frame geometry to be referenced later -->
+<frame_geometry name="normal">
+ <distance name="left_width" value="6"/>
+ <distance name="right_width" value="6"/>
+ <distance name="bottom_height" value="7"/>
+ <distance name="left_titlebar_edge" value="6"/>
+ <distance name="right_titlebar_edge" value="6"/>
+ <distance name="button_width" value="17"/>
+ <distance name="button_height" value="17"/>
+ <distance name="title_vertical_pad" value="4"/>
+ <border name="title_border" left="3" right="12" top="4" bottom="3"/>
+ <border name="button_border" left="0" right="0" top="1" bottom="1"/>
+</frame_geometry>
+
+<!-- inheritance is allowed; simply overwrites values from parent -->
+<frame_geometry name="borderless" parent="normal">
+ <distance name="left_width" value="0"/>
+ <distance name="right_width" value="0"/>
+ <distance name="bottom_height" value="0"/>
+ <distance name="left_titlebar_edge" value="0"/>
+ <distance name="right_titlebar_edge" value="0"/>
+</frame_geometry>
+
+<!-- define a constant to use in positions/sizes of draw operations;
+ constant names must start with a capital letter.
+ -->
+<constant name="LineOffset" value="3"/>
+
+<!-- define drawing operations to be referenced later;
+ these draw-op lists can also be placed inline.
+
+ Positions/lengths are given as expressions.
+ Operators are: +,-,*,/,%,`max`,`min`
+ All operators are infix including `max` and `min`,
+ i.e. "2 `max` 5"
+
+ Some variables are predefined, and constants can also
+ be used. Variables are:
+
+ width - width of target area
+ height - height of target area
+ object_width - natural width of object being drawn
+ object_height - natural height of object being drawn
+ left_width - distance from left of frame to client window
+ right_width - distance from right of frame to client window
+ top_height - distance from top of frame to client window
+ bottom_height - distance from bottom of frame to client window
+ mini_icon_width - width of mini icon for window
+ mini_icon_height - height of mini icon
+ icon_width - width of large icon
+ icon_height - height of large icon
+ title_width - width of title text
+ title_height - height of title text
+
+ All these are always defined, except object_width/object_height
+ which only exists for <image> right now.
+
+ -->
+
+<draw_ops name="demo_all_ops">
+ <line color="#00FF00" x1="LineOffset" y1="0" x2="0" y2="height"/>
+ <line color="gtk:fg[NORMAL]"
+ x1="width - 1" y1="0" x2="width - 1" y2="height"
+ width="3" dash_on_length="2" dash_off_length="3"/>
+ <rectangle color="blend/gtk:fg[NORMAL]/gtk:bg[NORMAL]/0.7"
+ x="0" y="0" width="width - 1" height="height - 1" filled="true"/>
+ <arc x="0" y="0" width="width - 1" height="height - 1"
+ filled="false" start_angle="30" extent_angle="180"/>
+ <tint color="orange" alpha="0.5" x1="0" y1="0" x2="0" y2="height"/>
+ <!-- may be vertical, horizontal, diagonal -->
+ <gradient type="diagonal"
+ x="10" y="30" width="width / 3" height="height / 4">
+ <!-- any number of colors allowed here. A color can be
+ a color name like "blue" (look at gcolorsel), a hex color
+ as in HTML (#FFBB99), or a color from the gtk theme
+ given as "gtk:base[NORMAL]", "gtk:fg[ACTIVE]", etc.
+ -->
+ <color value="gtk:fg[SELECTED]"/>
+ <!-- color obtained by a 0.5 alpha composite of the second color onto the first -->
+ <color value="blend/gtk:bg[SELECTED]/gtk:fg[SELECTED]/0.5"/>
+ </gradient>
+ <image filename="foo.png" alpha="0.7"
+ x="10" y="30" width="width / 3" height="height / 4"/>
+ <gtk_arrow state="normal" shadow="in" arrow="up"
+ filled="true"
+ x="2" y="2" width="width - 4" height="height - 4"/>
+ <gtk_box state="normal" shadow="out"
+ x="2" y="2" width="width - 4" height="height - 4"/>
+ <gtk_vline state="normal" x="2" y1="0" y2="height"/>
+ <!-- window's icon -->
+ <icon alpha="0.7"
+ x="10" y="30" width="width / 3" height="height / 4"/>
+ <!-- window's title -->
+ <title color="gtk:text[NORMAL]" x="20" y="30"/>
+ <!-- include another draw ops list -->
+ <include name="some_other_draw_ops"/>
+</draw_ops>
+
+<frame_style name="normal" geometry="normal">
+ <!-- How to draw each piece of the frame.
+ For each piece, a draw_ops can be given inline or referenced
+ by name. If a piece is omitted, then nothing will be drawn
+ for that piece.
+
+ For each piece, the "width" and "height" variables in
+ coordinate expressions refers to the dimensions of the piece,
+ the origin is at the top left of the piece.
+
+ So <rectangle x="0" y="0" width="width-1" height="height-1"/>
+ will outline a piece.
+ -->
+
+ <piece position="entire_background" draw_ops="demo_all_ops"/>
+ <piece position="left_titlebar_edge">
+ <draw_ops>
+ <line color="#00FF00" x1="0" y1="0" x2="0" y2="height"/>
+ </draw_ops>
+ </piece>
+
+ <!-- The complete list of frame pieces:
+
+ entire_background: whole frame
+ titlebar: entire area above the app's window
+ titlebar_middle: area of titlebar_background not considered
+ part of an edge
+ left_titlebar_edge: left side of titlebar background
+ right_titlebar_edge: right side of titlebar background
+ top_titlebar_edge: top side of titlebar background
+ bottom_titlebar_edge: bottom side of titlebar background
+ title: the title area (doesn't include buttons)
+ left_edge: left edge of the frame
+ right_edge: right edge of the frame
+ bottom_edge: bottom edge of the frame
+ overlay: same area as entire_background, but drawn after
+ drawing all sub-pieces instead of before
+
+ -->
+
+ <!-- For buttons, drawing methods have to be provided for
+ each of three states:
+ normal, pressed, prelight
+ and the button name must be provided:
+ close, maximize, minimize, menu
+ So a working theme needs 3*4 = 12 button declarations
+ -->
+
+ <button function="close" state="normal" draw_ops="previously_named"/>
+ <button function="menu" state="normal">
+ <draw_ops>
+ <icon alpha="0.7"
+ x="0" y="0" width="object_width" height="object_height"/>
+ </draw_ops>
+ </button>
+
+</frame_style>
+
+<!-- styles can inherit from each other with the parent="" attribute.
+ In a subclass anything can be re-specified to override
+ the parent style. -->
+<frame_style name="focused" parent="normal">
+ <piece position="title">
+ <draw_ops>
+ <rectangle color="gtk:bg[SELECTED]"
+ x="0" y="0" width="width-1" height="height-1"/>
+ <title color="gtk:fg[SELECTED]" x="(width - title_width) / 2"
+ y="(height - title_height) / 2"/>
+ </draw_ops>
+ </piece>
+</frame_style>
+
+<!-- Maps styles to states of frame.
+
+ Focus: yes (focused), no (not focused)
+ Window states: normal, maximized, shaded, maximized_and_shaded
+ Window resizability: none, vertical, horizontal, both
+
+ Everything unspecified just does the same as
+ unfocused/normal/both.
+
+ only state="normal" needs a resize="" attribute.
+ -->
+<frame_style_set name="normal">
+<frame focus="yes" state="normal" resize="both" style="focused"/>
+<frame focus="no" state="normal" resize="both" style="normal"/>
+</frame_style_set>
+
+<!-- Each window type needs a style set
+ Types: normal, dialog, modal_dialog, toolbar, menu, utility
+ -->
+<window type="normal" style_set="normal"/>
+
+
+<!-- For menu icons, drawing methods are needed for the same
+ four types as the buttons, and GTK states
+ (insensitive,prelight,normal,etc.)
+ -->
+
+<menu_icon function="close" state="normal" draw_ops="previously_named"/>
+
+
+</metacity_theme>
+
+