diff options
author | Jasper St. Pierre <jstpierre@mecheye.net> | 2012-05-05 02:48:14 -0300 |
---|---|---|
committer | Jasper St. Pierre <jstpierre@mecheye.net> | 2012-05-07 20:37:09 -0300 |
commit | 7623b9739975e462e6bd954bc144ccf6396b66c1 (patch) | |
tree | 6b2b3fed5fa1e48e9aac479470e3b46ceebfea56 | |
parent | 8d8e0cb05582aefd2b63365e9938bb2ed938503a (diff) | |
download | mutter-wip/cb2eb3.tar.gz |
Remove legacy theming APIwip/cb2eb3
Since the move to GTK+ toplevels, it required removing the preview-widget
and theme-viewer parts of the code. (They didn't work with the new CSS
stuff anyway... shhh..)
-rw-r--r-- | src/Makefile.am | 15 | ||||
-rw-r--r-- | src/core/display.c | 3 | ||||
-rw-r--r-- | src/core/frame.c | 6 | ||||
-rw-r--r-- | src/core/main.c | 38 | ||||
-rw-r--r-- | src/core/prefs.c | 44 | ||||
-rw-r--r-- | src/core/util.c | 38 | ||||
-rw-r--r-- | src/core/window-props.c | 4 | ||||
-rw-r--r-- | src/meta/prefs.h | 2 | ||||
-rw-r--r-- | src/meta/preview-widget.h | 81 | ||||
-rw-r--r-- | src/meta/theme.h | 47 | ||||
-rw-r--r-- | src/ui/draw-workspace.c | 11 | ||||
-rw-r--r-- | src/ui/preview-widget.c | 422 | ||||
-rw-r--r-- | src/ui/theme-parser.c | 4242 | ||||
-rw-r--r-- | src/ui/theme-private.h | 1138 | ||||
-rw-r--r-- | src/ui/theme-viewer.c | 1330 | ||||
-rw-r--r-- | src/ui/theme.c | 6228 | ||||
-rw-r--r-- | src/ui/ui.c | 62 | ||||
-rw-r--r-- | src/ui/ui.h | 10 | ||||
-rw-r--r-- | src/ui/uiframe.c | 794 | ||||
-rw-r--r-- | src/ui/uiframe.h | 11 |
20 files changed, 240 insertions, 14286 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index c0faa3a98..8c87f2d64 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -140,13 +140,7 @@ libmutter_la_SOURCES = \ ui/tabpopup.h \ ui/tile-preview.c \ ui/tile-preview.h \ - ui/theme-parser.c \ - ui/theme.c \ - meta/theme.h \ - ui/theme-private.h \ ui/ui.c \ - meta/preview-widget.h \ - ui/preview-widget.c \ $(mutter_built_sources) libmutter_la_LDFLAGS = -no-undefined @@ -171,7 +165,6 @@ libmutterinclude_base_headers = \ meta/meta-window-actor.h \ meta/prefs.h \ meta/screen.h \ - meta/theme.h \ meta/types.h \ meta/util.h \ meta/window.h \ @@ -180,7 +173,6 @@ libmutterinclude_base_headers = \ # Excluded from scanning for introspection but installed # atomnames.h: macros cause problems for scanning process libmutterinclude_extra_headers = \ - meta/preview-widget.h \ meta/atomnames.h libmutterincludedir = $(includedir)/mutter/meta @@ -189,10 +181,7 @@ libmutterinclude_HEADERS = \ $(libmutterinclude_base_headers) \ $(libmutterinclude_extra_headers) -mutter_theme_viewer_SOURCES= \ - ui/theme-viewer.c - -bin_PROGRAMS=mutter mutter-theme-viewer +bin_PROGRAMS=mutter mutter_SOURCES = core/mutter.c mutter_LDADD = $(MUTTER_LIBS) libmutter.la @@ -230,8 +219,6 @@ Meta-$(api_version).gir: libmutter.la endif -mutter_theme_viewer_LDADD= $(MUTTER_LIBS) libmutter.la - testboxes_SOURCES = core/testboxes.c testasyncgetprop_SOURCES = core/testasyncgetprop.c diff --git a/src/core/display.c b/src/core/display.c index b73da9fdb..f76c62aca 100644 --- a/src/core/display.c +++ b/src/core/display.c @@ -2571,9 +2571,6 @@ event_callback (XEvent *event, else if (event->xclient.message_type == display->atom__MUTTER_RELOAD_THEME_MESSAGE) { - meta_verbose ("Received reload theme request\n"); - meta_ui_set_current_theme (meta_prefs_get_theme (), - TRUE); meta_display_retheme_all (); } else if (event->xclient.message_type == diff --git a/src/core/frame.c b/src/core/frame.c index 1b6fb0ffe..03855553e 100644 --- a/src/core/frame.c +++ b/src/core/frame.c @@ -118,11 +118,6 @@ meta_window_ensure_frame (MetaWindow *window) meta_display_register_x_window (window->display, &frame->xwindow, window); - /* Now that frame->xwindow is registered with window, we can set its - * style and background. - */ - meta_ui_update_frame_style (window->screen->ui, frame->xwindow); - meta_ui_realize_frame_window (window->screen->ui, frame->xwindow); if (window->title) @@ -162,7 +157,6 @@ meta_window_ensure_frame (MetaWindow *window) window->rect.y); /* FIXME handle this error */ meta_error_trap_pop (window->display); - /* Move keybindings to frame instead of window */ meta_window_grab_keys (window); diff --git a/src/core/main.c b/src/core/main.c index 8718319fe..422369782 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -477,42 +477,6 @@ meta_run (void) if (g_getenv ("MUTTER_G_FATAL_WARNINGS") != NULL) g_log_set_always_fatal (G_LOG_LEVEL_MASK); - meta_ui_set_current_theme (meta_prefs_get_theme (), FALSE); - - /* Try to find some theme that'll work if the theme preference - * doesn't exist. First try Simple (the default theme) then just - * try anything in the themes directory. - */ - if (!meta_ui_have_a_theme ()) - meta_ui_set_current_theme ("Simple", FALSE); - - if (!meta_ui_have_a_theme ()) - { - const char *dir_entry = NULL; - GError *err = NULL; - GDir *themes_dir = NULL; - - if (!(themes_dir = g_dir_open (MUTTER_DATADIR"/themes", 0, &err))) - { - meta_fatal (_("Failed to scan themes directory: %s\n"), err->message); - g_error_free (err); - } - else - { - while (((dir_entry = g_dir_read_name (themes_dir)) != NULL) && - (!meta_ui_have_a_theme ())) - { - meta_ui_set_current_theme (dir_entry, FALSE); - } - - g_dir_close (themes_dir); - } - } - - if (!meta_ui_have_a_theme ()) - meta_fatal (_("Could not find a theme! Be sure %s exists and contains the usual themes.\n"), - MUTTER_DATADIR"/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 * info @@ -588,9 +552,7 @@ prefs_changed_callback (MetaPreference pref, { switch (pref) { - case META_PREF_THEME: case META_PREF_DRAGGABLE_BORDER_WIDTH: - meta_ui_set_current_theme (meta_prefs_get_theme (), FALSE); meta_display_retheme_all (); break; diff --git a/src/core/prefs.c b/src/core/prefs.c index 3ae6d0022..dd66e22b3 100644 --- a/src/core/prefs.c +++ b/src/core/prefs.c @@ -73,7 +73,6 @@ static GDesktopFocusMode focus_mode = G_DESKTOP_FOCUS_MODE_CLICK; static GDesktopFocusNewWindows focus_new_windows = G_DESKTOP_FOCUS_NEW_WINDOWS_SMART; static gboolean raise_on_click = TRUE; static gboolean attach_modal_dialogs = FALSE; -static char* current_theme = NULL; static int num_workspaces = 4; static GDesktopTitlebarAction action_double_click_titlebar = G_DESKTOP_TITLEBAR_ACTION_TOGGLE_MAXIMIZE; static GDesktopTitlebarAction action_middle_click_titlebar = G_DESKTOP_TITLEBAR_ACTION_LOWER; @@ -124,7 +123,6 @@ static void queue_changed (MetaPreference pref); static void maybe_give_disable_workarounds_warning (void); -static gboolean theme_name_handler (GVariant*, gpointer*, gpointer); static gboolean mouse_button_mods_handler (GVariant*, gpointer*, gpointer); static gboolean button_layout_handler (GVariant*, gpointer*, gpointer); @@ -360,14 +358,6 @@ static MetaStringPreference preferences_string[] = NULL, }, { - { "theme", - SCHEMA_GENERAL, - META_PREF_THEME, - }, - theme_name_handler, - NULL, - }, - { { "button-layout", SCHEMA_GENERAL, META_PREF_BUTTON_LAYOUT, @@ -1081,12 +1071,6 @@ meta_prefs_get_raise_on_click (void) } const char* -meta_prefs_get_theme (void) -{ - return current_theme; -} - -const char* meta_prefs_get_cursor_theme (void) { return cursor_theme; @@ -1104,31 +1088,6 @@ meta_prefs_get_cursor_size (void) /****************************************************************************/ static gboolean -theme_name_handler (GVariant *value, - gpointer *result, - gpointer data) -{ - const gchar *string_value; - - *result = NULL; /* ignored */ - string_value = g_variant_get_string (value, NULL); - - if (!string_value || !*string_value) - return FALSE; - - if (g_strcmp0 (current_theme, string_value) != 0) - { - if (current_theme) - g_free (current_theme); - - current_theme = g_strdup (string_value); - queue_changed (META_PREF_THEME); - } - - return TRUE; -} - -static gboolean mouse_button_mods_handler (GVariant *value, gpointer *result, gpointer data) @@ -1467,9 +1426,6 @@ meta_preference_to_string (MetaPreference pref) case META_PREF_RAISE_ON_CLICK: return "RAISE_ON_CLICK"; - - case META_PREF_THEME: - return "THEME"; case META_PREF_NUM_WORKSPACES: return "NUM_WORKSPACES"; diff --git a/src/core/util.c b/src/core/util.c index 69de5f053..437f1c72a 100644 --- a/src/core/util.c +++ b/src/core/util.c @@ -926,5 +926,43 @@ meta_later_remove (guint later_id) } } +/** + * meta_frame_type_to_string: + * + * Converts a frame type enum value to the name string that would + * appear in the theme definition file. + * + * Return value: the string value + */ +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"; + case META_FRAME_TYPE_BORDER: + return "border"; + case META_FRAME_TYPE_ATTACHED: + return "attached"; +#if 0 + case META_FRAME_TYPE_TOOLBAR: + return "toolbar"; +#endif + case META_FRAME_TYPE_LAST: + break; + } + + return "<unknown>"; +} + /* eof util.c */ diff --git a/src/core/window-props.c b/src/core/window-props.c index 1f5f04525..e2baa6723 100644 --- a/src/core/window-props.c +++ b/src/core/window-props.c @@ -1588,7 +1588,7 @@ reload_gtk_theme_variant (MetaWindow *window, window->gtk_theme_variant = g_strdup (requested_variant); if (window->frame) - meta_ui_update_frame_style (window->screen->ui, window->frame->xwindow); + meta_ui_queue_frame_draw (window->screen->ui, window->frame->xwindow); } } @@ -1616,7 +1616,7 @@ reload_gtk_hide_titlebar_when_maximized (MetaWindow *window, meta_window_queue (window, META_QUEUE_MOVE_RESIZE); if (window->frame) - meta_ui_update_frame_style (window->screen->ui, window->frame->xwindow); + meta_ui_queue_frame_draw (window->screen->ui, window->frame->xwindow); } } diff --git a/src/meta/prefs.h b/src/meta/prefs.h index 2f76cd9d8..5662fa739 100644 --- a/src/meta/prefs.h +++ b/src/meta/prefs.h @@ -45,7 +45,6 @@ typedef enum META_PREF_ACTION_RIGHT_CLICK_TITLEBAR, META_PREF_AUTO_RAISE, META_PREF_AUTO_RAISE_DELAY, - META_PREF_THEME, META_PREF_NUM_WORKSPACES, META_PREF_DYNAMIC_WORKSPACES, META_PREF_APPLICATION_BASED, @@ -90,7 +89,6 @@ GDesktopFocusMode meta_prefs_get_focus_mode (void); GDesktopFocusNewWindows meta_prefs_get_focus_new_windows (void); gboolean meta_prefs_get_attach_modal_dialogs (void); gboolean meta_prefs_get_raise_on_click (void); -const char* meta_prefs_get_theme (void); /* returns NULL if GTK default should be used */ const PangoFontDescription* meta_prefs_get_titlebar_font (void); int meta_prefs_get_num_workspaces (void); diff --git a/src/meta/preview-widget.h b/src/meta/preview-widget.h deleted file mode 100644 index 189379ead..000000000 --- a/src/meta/preview-widget.h +++ /dev/null @@ -1,81 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/* Metacity theme preview widget */ - -/* - * 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 <config.h> - -#include <meta/common.h> -#include <meta/theme.h> -#include <gtk/gtk.h> - -#ifndef META_PREVIEW_WIDGET_H -#define META_PREVIEW_WIDGET_H - -#define META_TYPE_PREVIEW (meta_preview_get_type ()) -#define META_PREVIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_PREVIEW, MetaPreview)) -#define META_PREVIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_PREVIEW, MetaPreviewClass)) -#define META_IS_PREVIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_PREVIEW)) -#define META_IS_PREVIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_PREVIEW)) -#define META_PREVIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_PREVIEW, MetaPreviewClass)) - -typedef struct _MetaPreview MetaPreview; -typedef struct _MetaPreviewClass MetaPreviewClass; - -struct _MetaPreview -{ - GtkBin bin; - - MetaTheme *theme; - char *title; - MetaFrameType type; - MetaFrameFlags flags; - - MetaFrameBorders borders; - guint borders_cached : 1; - - MetaButtonLayout button_layout; -}; - -struct _MetaPreviewClass -{ - GtkBinClass parent_class; -}; - - -GType meta_preview_get_type (void) G_GNUC_CONST; -GtkWidget* meta_preview_new (void); - -void meta_preview_set_theme (MetaPreview *preview, - MetaTheme *theme); -void meta_preview_set_title (MetaPreview *preview, - const char *title); -void meta_preview_set_frame_type (MetaPreview *preview, - MetaFrameType type); -void meta_preview_set_frame_flags (MetaPreview *preview, - MetaFrameFlags flags); -void meta_preview_set_button_layout (MetaPreview *preview, - const MetaButtonLayout *button_layout); - -GdkPixbuf* meta_preview_get_icon (void); -GdkPixbuf* meta_preview_get_mini_icon (void); - -#endif diff --git a/src/meta/theme.h b/src/meta/theme.h deleted file mode 100644 index a6aa5d835..000000000 --- a/src/meta/theme.h +++ /dev/null @@ -1,47 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/* 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 - * 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. - */ - -#ifndef META_THEME_H -#define META_THEME_H - -#include <glib.h> - -/** - * MetaTheme: - * - */ -typedef struct _MetaTheme MetaTheme; - -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); - -MetaTheme* meta_theme_load (const char *theme_name, - GError **err); - -#endif diff --git a/src/ui/draw-workspace.c b/src/ui/draw-workspace.c index 58cff001a..9e6813d71 100644 --- a/src/ui/draw-workspace.c +++ b/src/ui/draw-workspace.c @@ -26,7 +26,6 @@ */ #include "draw-workspace.h" -#include "theme-private.h" static void @@ -75,11 +74,8 @@ draw_window (GtkWidget *widget, { GdkPixbuf *icon; int icon_x, icon_y, icon_w, icon_h; - gboolean is_active; GdkRGBA color; GtkStyleContext *style; - - is_active = win->is_active; cairo_save (cr); @@ -87,10 +83,7 @@ draw_window (GtkWidget *widget, cairo_clip (cr); style = gtk_widget_get_style_context (widget); - if (is_active) - meta_gtk_style_get_light_color (style, state, &color); - else - gtk_style_context_get_background_color (style, state, &color); + gtk_style_context_get_background_color (style, state, &color); gdk_cairo_set_source_rgba (cr, &color); cairo_rectangle (cr, @@ -197,7 +190,7 @@ wnck_draw_workspace (GtkWidget *widget, { GdkRGBA color; - meta_gtk_style_get_dark_color (style,state, &color); + gtk_style_context_get_background_color (style, state, &color); gdk_cairo_set_source_rgba (cr, &color); cairo_rectangle (cr, x, y, width, height); cairo_fill (cr); diff --git a/src/ui/preview-widget.c b/src/ui/preview-widget.c deleted file mode 100644 index 5eee668cb..000000000 --- a/src/ui/preview-widget.c +++ /dev/null @@ -1,422 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/* Metacity theme preview widget */ - -/* - * 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. - */ - -#define _GNU_SOURCE -#define _XOPEN_SOURCE 600 /* for the maths routines over floats */ - -#include <math.h> -#include <gtk/gtk.h> -#include <meta/preview-widget.h> -#include "theme-private.h" - -static void meta_preview_get_preferred_width (GtkWidget *widget, - gint *minimum, - gint *natural); -static void meta_preview_get_preferred_height (GtkWidget *widget, - gint *minimum, - gint *natural); -static void meta_preview_size_allocate (GtkWidget *widget, - GtkAllocation *allocation); -static gboolean meta_preview_draw (GtkWidget *widget, - cairo_t *cr); -static void meta_preview_finalize (GObject *object); - -G_DEFINE_TYPE (MetaPreview, meta_preview, GTK_TYPE_BIN); - -static void -meta_preview_class_init (MetaPreviewClass *class) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (class); - GtkWidgetClass *widget_class; - - widget_class = (GtkWidgetClass*) class; - - gobject_class->finalize = meta_preview_finalize; - - widget_class->draw = meta_preview_draw; - widget_class->get_preferred_width = meta_preview_get_preferred_width; - widget_class->get_preferred_height = meta_preview_get_preferred_height; - widget_class->size_allocate = meta_preview_size_allocate; - - gtk_container_class_handle_border_width (GTK_CONTAINER_CLASS (class)); -} - -static void -meta_preview_init (MetaPreview *preview) -{ - int i; - - gtk_widget_set_has_window (GTK_WIDGET (preview), FALSE); - - i = 0; - while (i < MAX_BUTTONS_PER_CORNER) - { - preview->button_layout.left_buttons[i] = META_BUTTON_FUNCTION_LAST; - preview->button_layout.right_buttons[i] = META_BUTTON_FUNCTION_LAST; - ++i; - } - - preview->button_layout.left_buttons[0] = META_BUTTON_FUNCTION_MENU; - - preview->button_layout.right_buttons[0] = META_BUTTON_FUNCTION_MINIMIZE; - preview->button_layout.right_buttons[1] = META_BUTTON_FUNCTION_MAXIMIZE; - preview->button_layout.right_buttons[2] = META_BUTTON_FUNCTION_CLOSE; - - preview->type = META_FRAME_TYPE_NORMAL; - preview->flags = - 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; - - preview->borders_cached = FALSE; -} - -GtkWidget* -meta_preview_new (void) -{ - MetaPreview *preview; - - preview = g_object_new (META_TYPE_PREVIEW, NULL); - - return GTK_WIDGET (preview); -} - -static void -meta_preview_finalize (GObject *object) -{ - MetaPreview *preview; - - preview = META_PREVIEW (object); - - g_free (preview->title); - preview->title = NULL; - - G_OBJECT_CLASS (meta_preview_parent_class)->finalize (object); -} - -static void -ensure_info (MetaPreview *preview) -{ - GtkWidget *widget; - - widget = GTK_WIDGET (preview); - - if (!preview->borders_cached) - { - if (preview->theme) - meta_theme_get_frame_borders (preview->theme, - meta_theme_get_variant (preview->theme, NULL)->style_context, - preview->type, - preview->flags, - &preview->borders); - else - meta_frame_borders_clear (&preview->borders); - preview->borders_cached = TRUE; - } -} - -static gboolean -meta_preview_draw (GtkWidget *widget, - cairo_t *cr) -{ - MetaPreview *preview = META_PREVIEW (widget); - GtkAllocation allocation; - - gtk_widget_get_allocation (widget, &allocation); - - if (preview->theme) - { - int client_width; - int client_height; - MetaButtonState button_states[META_BUTTON_TYPE_LAST] = - { - META_BUTTON_STATE_NORMAL, - META_BUTTON_STATE_NORMAL, - META_BUTTON_STATE_NORMAL, - META_BUTTON_STATE_NORMAL - }; - - ensure_info (preview); - cairo_save (cr); - - client_width = allocation.width - preview->borders.total.left - preview->borders.total.right; - client_height = allocation.height - preview->borders.total.top - preview->borders.total.bottom; - - if (client_width < 0) - client_width = 1; - if (client_height < 0) - client_height = 1; - - meta_theme_draw_frame_with_style (preview->theme, - gtk_widget_get_style_context (widget), - cr, - preview->type, - preview->flags, - client_width, - client_height, - &preview->button_layout, - button_states, - meta_preview_get_mini_icon (), - meta_preview_get_icon ()); - - cairo_restore (cr); - } - - /* draw child */ - return GTK_WIDGET_CLASS (meta_preview_parent_class)->draw (widget, cr); -} - -#define NO_CHILD_WIDTH 80 -#define NO_CHILD_HEIGHT 20 - -static void -meta_preview_get_preferred_width (GtkWidget *widget, - gint *minimum, - gint *natural) -{ - MetaPreview *preview; - GtkWidget *child; - - preview = META_PREVIEW (widget); - - ensure_info (preview); - - *minimum = *natural = preview->borders.total.left + preview->borders.total.right; - - child = gtk_bin_get_child (GTK_BIN (preview)); - if (child && gtk_widget_get_visible (child)) - { - gint child_min, child_nat; - - gtk_widget_get_preferred_width (child, &child_min, &child_nat); - - *minimum += child_min; - *natural += child_nat; - } - else - { - *minimum += NO_CHILD_WIDTH; - *natural += NO_CHILD_WIDTH; - } -} - -static void -meta_preview_get_preferred_height (GtkWidget *widget, - gint *minimum, - gint *natural) -{ - MetaPreview *preview; - GtkWidget *child; - - preview = META_PREVIEW (widget); - - ensure_info (preview); - - *minimum = *natural = preview->borders.total.top + preview->borders.total.bottom; - - child = gtk_bin_get_child (GTK_BIN (preview)); - if (child && gtk_widget_get_visible (child)) - { - gint child_min, child_nat; - - gtk_widget_get_preferred_height (child, &child_min, &child_nat); - - *minimum += child_min; - *natural += child_nat; - } - else - { - *minimum += NO_CHILD_HEIGHT; - *natural += NO_CHILD_HEIGHT; - } -} - -static void -meta_preview_size_allocate (GtkWidget *widget, - GtkAllocation *allocation) -{ - MetaPreview *preview; - GtkAllocation widget_allocation, child_allocation; - GtkWidget *child; - - preview = META_PREVIEW (widget); - - ensure_info (preview); - - gtk_widget_set_allocation (widget, allocation); - - child = gtk_bin_get_child (GTK_BIN (widget)); - if (child && gtk_widget_get_visible (child)) - { - gtk_widget_get_allocation (widget, &widget_allocation); - child_allocation.x = widget_allocation.x + preview->borders.total.left; - child_allocation.y = widget_allocation.y + preview->borders.total.top; - - child_allocation.width = MAX (1, widget_allocation.width - preview->borders.total.left - preview->borders.total.right); - child_allocation.height = MAX (1, widget_allocation.height - preview->borders.total.top - preview->borders.total.bottom); - - gtk_widget_size_allocate (child, &child_allocation); - } -} - -static void -clear_cache (MetaPreview *preview) -{ - preview->borders_cached = FALSE; -} - -void -meta_preview_set_theme (MetaPreview *preview, - MetaTheme *theme) -{ - g_return_if_fail (META_IS_PREVIEW (preview)); - - preview->theme = theme; - - clear_cache (preview); - - gtk_widget_queue_resize (GTK_WIDGET (preview)); -} - -void -meta_preview_set_title (MetaPreview *preview, - const char *title) -{ - g_return_if_fail (META_IS_PREVIEW (preview)); - - g_free (preview->title); - preview->title = g_strdup (title); - - clear_cache (preview); - - gtk_widget_queue_resize (GTK_WIDGET (preview)); -} - -void -meta_preview_set_frame_type (MetaPreview *preview, - MetaFrameType type) -{ - g_return_if_fail (META_IS_PREVIEW (preview)); - - preview->type = type; - - clear_cache (preview); - - gtk_widget_queue_resize (GTK_WIDGET (preview)); -} - -void -meta_preview_set_frame_flags (MetaPreview *preview, - MetaFrameFlags flags) -{ - g_return_if_fail (META_IS_PREVIEW (preview)); - - preview->flags = flags; - - clear_cache (preview); - - gtk_widget_queue_resize (GTK_WIDGET (preview)); -} - -void -meta_preview_set_button_layout (MetaPreview *preview, - const MetaButtonLayout *button_layout) -{ - g_return_if_fail (META_IS_PREVIEW (preview)); - - preview->button_layout = *button_layout; - - gtk_widget_queue_draw (GTK_WIDGET (preview)); -} - -GdkPixbuf* -meta_preview_get_icon (void) -{ - static GdkPixbuf *default_icon = NULL; - - if (default_icon == NULL) - { - GtkIconTheme *theme; - gboolean icon_exists; - - theme = gtk_icon_theme_get_default (); - - icon_exists = gtk_icon_theme_has_icon (theme, META_DEFAULT_ICON_NAME); - - if (icon_exists) - default_icon = gtk_icon_theme_load_icon (theme, - META_DEFAULT_ICON_NAME, - META_ICON_WIDTH, - 0, - NULL); - else - default_icon = gtk_icon_theme_load_icon (theme, - "gtk-missing-image", - META_ICON_WIDTH, - 0, - NULL); - - g_assert (default_icon); - } - - return default_icon; -} - -GdkPixbuf* -meta_preview_get_mini_icon (void) -{ - static GdkPixbuf *default_icon = NULL; - - if (default_icon == NULL) - { - GtkIconTheme *theme; - gboolean icon_exists; - - theme = gtk_icon_theme_get_default (); - - icon_exists = gtk_icon_theme_has_icon (theme, META_DEFAULT_ICON_NAME); - - if (icon_exists) - default_icon = gtk_icon_theme_load_icon (theme, - META_DEFAULT_ICON_NAME, - META_MINI_ICON_WIDTH, - 0, - NULL); - else - default_icon = gtk_icon_theme_load_icon (theme, - "gtk-missing-image", - META_MINI_ICON_WIDTH, - 0, - NULL); - - g_assert (default_icon); - } - - return default_icon; -} diff --git a/src/ui/theme-parser.c b/src/ui/theme-parser.c deleted file mode 100644 index 6769046d4..000000000 --- a/src/ui/theme-parser.c +++ /dev/null @@ -1,4242 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/* Metacity theme parsing */ - -/* - * Copyright (C) 2001 Havoc Pennington - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - */ - -#include <config.h> -#include "theme-private.h" -#include <meta/util.h> -#include <string.h> -#include <stdlib.h> - -/* We were intending to put the version number - * in the subdirectory name, but we ended up - * using the filename instead. The "-1" survives - * as a fossil. - */ -#define THEME_SUBDIR "metacity-1" - -/* Highest version of the theme format to - * look out for. - */ -#define THEME_MAJOR_VERSION 3 -#define THEME_MINOR_VERSION 4 -#define THEME_VERSION (1000 * THEME_MAJOR_VERSION + THEME_MINOR_VERSION) - -#define METACITY_THEME_FILENAME_FORMAT "metacity-theme-%d.xml" - -typedef enum -{ - STATE_START, - STATE_THEME, - /* info section */ - STATE_INFO, - STATE_NAME, - STATE_AUTHOR, - STATE_COPYRIGHT, - STATE_DATE, - STATE_DESCRIPTION, - /* constants */ - STATE_CONSTANT, - /* geometry */ - STATE_FRAME_GEOMETRY, - STATE_DISTANCE, - STATE_BORDER, - STATE_ASPECT_RATIO, - /* draw ops */ - STATE_DRAW_OPS, - STATE_LINE, - STATE_RECTANGLE, - STATE_ARC, - STATE_CLIP, - STATE_TINT, - STATE_GRADIENT, - STATE_IMAGE, - STATE_GTK_ARROW, - STATE_GTK_BOX, - STATE_GTK_VLINE, - STATE_ICON, - STATE_TITLE, - STATE_INCLUDE, /* include another draw op list */ - STATE_TILE, /* tile another draw op list */ - /* sub-parts of gradient */ - STATE_COLOR, - /* frame style */ - STATE_FRAME_STYLE, - STATE_PIECE, - STATE_BUTTON, - /* style set */ - STATE_FRAME_STYLE_SET, - STATE_FRAME, - /* assigning style sets to windows */ - STATE_WINDOW, - /* things we don't use any more but we can still parse: */ - STATE_MENU_ICON, - STATE_FALLBACK -} ParseState; - -typedef struct -{ - /* This two lists contain stacks of state and required version - * (cast to pointers.) There is one list item for each currently - * open element. */ - GSList *states; - GSList *required_versions; - - const char *theme_name; /* name of theme (directory it's in) */ - const char *theme_file; /* theme filename */ - const char *theme_dir; /* dir the theme is inside */ - MetaTheme *theme; /* theme being parsed */ - guint format_version; /* version of format of theme file */ - char *name; /* name of named thing being parsed */ - MetaFrameLayout *layout; /* layout being parsed if any */ - MetaDrawOpList *op_list; /* op list being parsed if any */ - MetaDrawOp *op; /* op being parsed if any */ - MetaFrameStyle *style; /* frame style being parsed if any */ - MetaFrameStyleSet *style_set; /* frame style set being parsed if any */ - MetaFramePiece piece; /* position of piece being parsed */ - MetaButtonType button_type; /* type of button/menuitem being parsed */ - MetaButtonState button_state; /* state of button being parsed */ - int skip_level; /* depth of elements that we're ignoring */ -} ParseInfo; - -typedef enum { - THEME_PARSE_ERROR_TOO_OLD, - THEME_PARSE_ERROR_TOO_FAILED -} ThemeParseError; - -static GQuark -theme_parse_error_quark (void) -{ - return g_quark_from_static_string ("theme-parse-error-quark"); -} - -#define THEME_PARSE_ERROR (theme_parse_error_quark ()) - -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); - -/* Translators: This means that an attribute which should have been found - * on an XML element was not in fact found. - */ -#define ATTRIBUTE_NOT_FOUND _("No \"%s\" attribute on element <%s>") - -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->required_versions = NULL; - 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; - info->skip_level = 0; -} - -static void -parse_info_free (ParseInfo *info) -{ - g_slist_free (info->states); - g_slist_free (info->required_versions); - - 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); -} - -static void -push_required_version (ParseInfo *info, - int version) -{ - info->required_versions = g_slist_prepend (info->required_versions, - GINT_TO_POINTER (version)); -} - -static void -pop_required_version (ParseInfo *info) -{ - g_return_if_fail (info->required_versions != NULL); - - info->required_versions = g_slist_delete_link (info->required_versions, info->required_versions); -} - -static int -peek_required_version (ParseInfo *info) -{ - if (info->required_versions) - return GPOINTER_TO_INT (info->required_versions->data); - else - return info->format_version; -} - -#define ELEMENT_IS(name) (strcmp (element_name, (name)) == 0) - -typedef struct -{ - const char *name; - const char **retloc; - gboolean required; -} LocateAttr; - -/* Attribute names can have a leading '!' to indicate that they are - * required. - */ -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; - - /* FIXME: duplicated code; refactor loop */ - n_attrs = 1; - attrs[0].name = first_attribute_name; - attrs[0].retloc = first_attribute_retloc; - attrs[0].required = attrs[0].name[0]=='!'; - if (attrs[0].required) - attrs[0].name++; /* skip past it */ - *first_attribute_retloc = NULL; - - va_start (args, first_attribute_retloc); - - name = va_arg (args, const char*); - retloc = va_arg (args, const char**); - - while (name != NULL) - { - g_return_val_if_fail (retloc != NULL, FALSE); - - g_assert (n_attrs < MAX_ATTRS); - - attrs[n_attrs].name = name; - attrs[n_attrs].retloc = retloc; - attrs[n_attrs].required = attrs[n_attrs].name[0]=='!'; - if (attrs[n_attrs].required) - attrs[n_attrs].name++; /* skip past it */ - - n_attrs += 1; - *retloc = NULL; - - name = va_arg (args, const char*); - retloc = va_arg (args, const char**); - } - - va_end (args); - - i = 0; - while (attribute_names[i]) - { - int j; - gboolean found; - - /* Can be present anywhere */ - if (strcmp (attribute_names[i], "version") == 0) - { - ++i; - continue; - } - - found = FALSE; - j = 0; - while (j < n_attrs) - { - if (strcmp (attrs[j].name, attribute_names[i]) == 0) - { - retloc = attrs[j].retloc; - - if (*retloc != NULL) - { - - set_error (error, context, - G_MARKUP_ERROR, - G_MARKUP_ERROR_PARSE, - _("Attribute \"%s\" repeated twice on the same <%s> element"), - attrs[j].name, element_name); - retval = FALSE; - goto out; - } - - *retloc = attribute_values[i]; - found = TRUE; - } - - ++j; - } - - if (!found) - { - j = 0; - while (j < n_attrs) - { - g_warning ("It could have been %s.\n", attrs[j++].name); - } - - set_error (error, context, - G_MARKUP_ERROR, - G_MARKUP_ERROR_PARSE, - _("Attribute \"%s\" is invalid on <%s> element in this context"), - attribute_names[i], element_name); - retval = FALSE; - goto out; - } - - ++i; - } - - /* Did we catch them all? */ - i = 0; - while (i < n_attrs) - { - if (attrs[i].required && *(attrs[i].retloc)==NULL) - { - set_error (error, context, - G_MARKUP_ERROR, - G_MARKUP_ERROR_PARSE, - ATTRIBUTE_NOT_FOUND, - attrs[i].name, element_name); - retval = FALSE; - goto out; - } - - ++i; - } - - out: - return retval; -} - -static gboolean -check_no_attributes (GMarkupParseContext *context, - const char *element_name, - const char **attribute_names, - const char **attribute_values, - GError **error) -{ - int i = 0; - - /* Can be present anywhere */ - if (attribute_names[0] && strcmp (attribute_names[i], "version") == 0) - i++; - - if (attribute_names[i] != NULL) - { - set_error (error, context, - G_MARKUP_ERROR, - G_MARKUP_ERROR_PARSE, - _("Attribute \"%s\" is invalid on <%s> element in this context"), - attribute_names[0], element_name); - return FALSE; - } - - return TRUE; -} - -#define MAX_REASONABLE 4096 -static gboolean -parse_positive_integer (const char *str, - int *val, - GMarkupParseContext *context, - MetaTheme *theme, - GError **error) -{ - char *end; - long l; - int j; - - *val = 0; - - end = NULL; - - /* Is str a constant? */ - - if (META_THEME_ALLOWS (theme, META_THEME_UBIQUITOUS_CONSTANTS) && - meta_theme_lookup_int_constant (theme, str, &j)) - { - /* Yes. */ - l = j; - } - else - { - /* No. Let's try parsing it instead. */ - - l = strtol (str, &end, 10); - - if (end == NULL || end == str) - { - set_error (error, context, G_MARKUP_ERROR, - G_MARKUP_ERROR_PARSE, - _("Could not parse \"%s\" as an integer"), - str); - return FALSE; - } - - if (*end != '\0') - { - set_error (error, context, G_MARKUP_ERROR, - G_MARKUP_ERROR_PARSE, - _("Did not understand trailing characters \"%s\" in string \"%s\""), - end, str); - return FALSE; - } - } - - if (l < 0) - { - set_error (error, context, G_MARKUP_ERROR, - G_MARKUP_ERROR_PARSE, - _("Integer %ld must be positive"), l); - return FALSE; - } - - if (l > MAX_REASONABLE) - { - set_error (error, context, G_MARKUP_ERROR, - G_MARKUP_ERROR_PARSE, - _("Integer %ld is too large, current max is %d"), - l, MAX_REASONABLE); - return FALSE; - } - - *val = (int) l; - - return TRUE; -} - -static gboolean -parse_double (const char *str, - double *val, - GMarkupParseContext *context, - GError **error) -{ - char *end; - - *val = 0; - - end = NULL; - - *val = g_ascii_strtod (str, &end); - - if (end == NULL || end == str) - { - set_error (error, context, G_MARKUP_ERROR, - G_MARKUP_ERROR_PARSE, - _("Could not parse \"%s\" as a floating point number"), - str); - return FALSE; - } - - if (*end != '\0') - { - set_error (error, context, G_MARKUP_ERROR, - G_MARKUP_ERROR_PARSE, - _("Did not understand trailing characters \"%s\" in string \"%s\""), - end, str); - return FALSE; - } - - return TRUE; -} - -static gboolean -parse_boolean (const char *str, - gboolean *val, - GMarkupParseContext *context, - GError **error) -{ - if (strcmp ("true", str) == 0) - *val = TRUE; - else if (strcmp ("false", str) == 0) - *val = FALSE; - else - { - set_error (error, context, G_MARKUP_ERROR, - G_MARKUP_ERROR_PARSE, - _("Boolean values must be \"true\" or \"false\" not \"%s\""), - str); - return FALSE; - } - - return TRUE; -} - -static gboolean -parse_angle (const char *str, - double *val, - GMarkupParseContext *context, - GError **error) -{ - if (!parse_double (str, val, context, error)) - return FALSE; - - if (*val < (0.0 - 1e6) || *val > (360.0 + 1e6)) - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Angle must be between 0.0 and 360.0, was %g\n"), - *val); - return FALSE; - } - - return TRUE; -} - -static gboolean -parse_alpha (const char *str, - MetaAlphaGradientSpec **spec_ret, - GMarkupParseContext *context, - GError **error) -{ - char **split; - int i; - int n_alphas; - MetaAlphaGradientSpec *spec; - - *spec_ret = NULL; - - split = g_strsplit (str, ":", -1); - - i = 0; - while (split[i]) - ++i; - - if (i == 0) - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Could not parse \"%s\" as a floating point number"), - str); - - g_strfreev (split); - - return FALSE; - } - - n_alphas = i; - - /* FIXME allow specifying horizontal/vertical/diagonal in theme format, - * once we implement vertical/diagonal in gradient.c - */ - spec = meta_alpha_gradient_spec_new (META_GRADIENT_HORIZONTAL, - n_alphas); - - i = 0; - while (i < n_alphas) - { - double v; - - if (!parse_double (split[i], &v, context, error)) - { - /* clear up, but don't set error: it was set by parse_double */ - g_strfreev (split); - meta_alpha_gradient_spec_free (spec); - - return FALSE; - } - - if (v < (0.0 - 1e-6) || v > (1.0 + 1e-6)) - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Alpha must be between 0.0 (invisible) and 1.0 (fully opaque), was %g\n"), - v); - - g_strfreev (split); - meta_alpha_gradient_spec_free (spec); - - return FALSE; - } - - spec->alphas[i] = (unsigned char) (v * 255); - - ++i; - } - - g_strfreev (split); - - *spec_ret = spec; - - return TRUE; -} - -static MetaColorSpec* -parse_color (MetaTheme *theme, - const char *str, - GError **err) -{ - char* referent; - - if (META_THEME_ALLOWS (theme, META_THEME_COLOR_CONSTANTS) && - meta_theme_lookup_color_constant (theme, str, &referent)) - { - if (referent) - return meta_color_spec_new_from_string (referent, err); - - /* no need to free referent: it's a pointer into the actual hash table */ - } - - return meta_color_spec_new_from_string (str, err); -} - -static gboolean -parse_title_scale (const char *str, - double *val, - GMarkupParseContext *context, - GError **error) -{ - double factor; - - if (strcmp (str, "xx-small") == 0) - factor = PANGO_SCALE_XX_SMALL; - else if (strcmp (str, "x-small") == 0) - factor = PANGO_SCALE_X_SMALL; - else if (strcmp (str, "small") == 0) - factor = PANGO_SCALE_SMALL; - else if (strcmp (str, "medium") == 0) - factor = PANGO_SCALE_MEDIUM; - else if (strcmp (str, "large") == 0) - factor = PANGO_SCALE_LARGE; - else if (strcmp (str, "x-large") == 0) - factor = PANGO_SCALE_X_LARGE; - else if (strcmp (str, "xx-large") == 0) - factor = PANGO_SCALE_XX_LARGE; - else - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Invalid title scale \"%s\" (must be one of xx-small,x-small,small,medium,large,x-large,xx-large)\n"), - str); - return FALSE; - } - - *val = factor; - - return TRUE; -} - -static void -parse_toplevel_element (GMarkupParseContext *context, - const gchar *element_name, - const gchar **attribute_names, - const gchar **attribute_values, - ParseInfo *info, - GError **error) -{ - g_return_if_fail (peek_state (info) == STATE_THEME); - - if (ELEMENT_IS ("info")) - { - if (!check_no_attributes (context, element_name, - attribute_names, attribute_values, - error)) - return; - - push_state (info, STATE_INFO); - } - else if (ELEMENT_IS ("constant")) - { - const char *name; - const char *value; - int ival = 0; - double dval = 0.0; - - if (!locate_attributes (context, element_name, attribute_names, attribute_values, - error, - "!name", &name, "!value", &value, - NULL)) - return; - - /* We don't know how a a constant is going to be used, so we have guess its - * type from its contents: - * - * - Starts like a number and contains a '.': float constant - * - Starts like a number and doesn't contain a '.': int constant - * - Starts with anything else: a color constant. - * (colors always start with # or a letter) - */ - if (value[0] == '.' || value[0] == '+' || value[0] == '-' || (value[0] >= '0' && value[0] <= '9')) - { - if (strchr (value, '.')) - { - 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 - { - if (!parse_positive_integer (value, &ival, context, info->theme, error)) - return; - - if (!meta_theme_define_int_constant (info->theme, - name, - ival, - error)) - { - add_context_to_error (error, context); - return; - } - } - } - else - { - if (!meta_theme_define_color_constant (info->theme, - name, - value, - error)) - { - add_context_to_error (error, context); - return; - } - } - - push_state (info, STATE_CONSTANT); - } - else if (ELEMENT_IS ("frame_geometry")) - { - const char *name = NULL; - const char *parent = NULL; - const char *has_title = NULL; - const char *title_scale = NULL; - const char *hide_buttons = NULL; - gboolean has_title_val; - gboolean hide_buttons_val; - double title_scale_val; - MetaFrameLayout *parent_layout; - - if (!locate_attributes (context, element_name, attribute_names, attribute_values, - error, - "!name", &name, "parent", &parent, - "has_title", &has_title, "title_scale", &title_scale, - "hide_buttons", &hide_buttons, - NULL)) - return; - - has_title_val = TRUE; - if (has_title && !parse_boolean (has_title, &has_title_val, context, error)) - return; - - hide_buttons_val = FALSE; - if (hide_buttons && !parse_boolean (hide_buttons, &hide_buttons_val, context, error)) - return; - - title_scale_val = 1.0; - if (title_scale && !parse_title_scale (title_scale, &title_scale_val, context, error)) - return; - - if (meta_theme_lookup_layout (info->theme, name)) - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("<%s> name \"%s\" used a second time"), - element_name, name); - return; - } - - parent_layout = NULL; - if (parent) - { - parent_layout = meta_theme_lookup_layout (info->theme, parent); - if (parent_layout == NULL) - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("<%s> parent \"%s\" has not been defined"), - element_name, parent); - return; - } - } - - g_assert (info->layout == NULL); - - if (parent_layout) - info->layout = meta_frame_layout_copy (parent_layout); - else - info->layout = meta_frame_layout_new (); - - if (has_title) /* only if explicit, otherwise inherit */ - info->layout->has_title = has_title_val; - - if (META_THEME_ALLOWS (info->theme, META_THEME_HIDDEN_BUTTONS) && hide_buttons_val) - info->layout->hide_buttons = hide_buttons_val; - - if (title_scale) - info->layout->title_scale = title_scale_val; - - 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 (meta_theme_lookup_draw_op_list (info->theme, name)) - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("<%s> name \"%s\" used a second time"), - element_name, name); - return; - } - - g_assert (info->op_list == NULL); - info->op_list = meta_draw_op_list_new (2); - - meta_theme_insert_draw_op_list (info->theme, name, info->op_list); - - push_state (info, STATE_DRAW_OPS); - } - else if (ELEMENT_IS ("frame_style")) - { - const char *name = NULL; - const char *parent = NULL; - const char *geometry = NULL; - const char *background = NULL; - const char *alpha = NULL; - MetaFrameStyle *parent_style; - MetaFrameLayout *layout; - - if (!locate_attributes (context, element_name, attribute_names, attribute_values, - error, - "!name", &name, "parent", &parent, - "geometry", &geometry, - "background", &background, - "alpha", &alpha, - NULL)) - return; - - if (meta_theme_lookup_style (info->theme, name)) - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("<%s> name \"%s\" used a second time"), - element_name, name); - return; - } - - parent_style = NULL; - if (parent) - { - parent_style = meta_theme_lookup_style (info->theme, parent); - if (parent_style == NULL) - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("<%s> parent \"%s\" has not been defined"), - element_name, parent); - return; - } - } - - layout = NULL; - if (geometry) - { - layout = meta_theme_lookup_layout (info->theme, geometry); - if (layout == NULL) - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("<%s> geometry \"%s\" has not been defined"), - element_name, geometry); - return; - } - } - else if (parent_style) - { - layout = parent_style->layout; - } - - if (layout == NULL) - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("<%s> must specify either a geometry or a parent that has a geometry"), - element_name); - return; - } - - g_assert (info->style == NULL); - - info->style = meta_frame_style_new (parent_style); - g_assert (info->style->layout == NULL); - meta_frame_layout_ref (layout); - info->style->layout = layout; - - if (background != NULL && META_THEME_ALLOWS (info->theme, META_THEME_FRAME_BACKGROUNDS)) - { - info->style->window_background_color = meta_color_spec_new_from_string (background, error); - if (!info->style->window_background_color) - return; - - if (alpha != NULL) - { - - gboolean success; - MetaAlphaGradientSpec *alpha_vector; - - g_clear_error (error); - /* fortunately, we already have a routine to parse alpha values, - * though it produces a vector of them, which is a superset of - * what we want. - */ - success = parse_alpha (alpha, &alpha_vector, context, error); - if (!success) - return; - - /* alpha_vector->alphas must contain at least one element */ - info->style->window_background_alpha = alpha_vector->alphas[0]; - - meta_alpha_gradient_spec_free (alpha_vector); - } - } - else if (alpha != NULL) - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("You must specify a background for an alpha value to be meaningful")); - return; - } - - meta_theme_insert_style (info->theme, name, info->style); - - push_state (info, STATE_FRAME_STYLE); - } - else if (ELEMENT_IS ("frame_style_set")) - { - const char *name = NULL; - const char *parent = NULL; - MetaFrameStyleSet *parent_set; - - if (!locate_attributes (context, element_name, attribute_names, attribute_values, - error, - "!name", &name, "parent", &parent, - NULL)) - return; - - if (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; - - type = meta_frame_type_from_string (type_name); - - if (type == META_FRAME_TYPE_LAST || - (type == META_FRAME_TYPE_ATTACHED && peek_required_version (info) < 3002)) - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Unknown type \"%s\" on <%s> element"), - type_name, element_name); - return; - } - - style_set = meta_theme_lookup_style_set (info->theme, - style_set_name); - - if (style_set == NULL) - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Unknown style_set \"%s\" on <%s> element"), - style_set_name, element_name); - return; - } - - if (info->theme->style_sets_by_type[type] != NULL) - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Window type \"%s\" has already been assigned a style set"), - type_name); - return; - } - - meta_frame_style_set_ref (style_set); - info->theme->style_sets_by_type[type] = style_set; - - push_state (info, STATE_WINDOW); - } - else if (ELEMENT_IS ("menu_icon")) - { - /* Not supported any more, but we have to parse it if they include it, - * for backwards compatibility. - */ - g_assert (info->op_list == NULL); - - push_state (info, STATE_MENU_ICON); - } - else if (ELEMENT_IS ("fallback")) - { - /* Not supported any more, but we have to parse it if they include it, - * for backwards compatibility. - */ - push_state (info, STATE_FALLBACK); - } - else - { - set_error (error, context, - G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Element <%s> is not allowed below <%s>"), - element_name, "metacity_theme"); - } -} - -static void -parse_info_element (GMarkupParseContext *context, - const gchar *element_name, - const gchar **attribute_names, - const gchar **attribute_values, - ParseInfo *info, - GError **error) -{ - g_return_if_fail (peek_state (info) == STATE_INFO); - - if (ELEMENT_IS ("name")) - { - if (!check_no_attributes (context, element_name, - attribute_names, attribute_values, - error)) - return; - - push_state (info, STATE_NAME); - } - else if (ELEMENT_IS ("author")) - { - if (!check_no_attributes (context, element_name, - attribute_names, attribute_values, - error)) - return; - - push_state (info, STATE_AUTHOR); - } - else if (ELEMENT_IS ("copyright")) - { - if (!check_no_attributes (context, element_name, - attribute_names, attribute_values, - error)) - return; - - push_state (info, STATE_COPYRIGHT); - } - else if (ELEMENT_IS ("description")) - { - if (!check_no_attributes (context, element_name, - attribute_names, attribute_values, - error)) - return; - - push_state (info, STATE_DESCRIPTION); - } - else if (ELEMENT_IS ("date")) - { - if (!check_no_attributes (context, element_name, - attribute_names, attribute_values, - error)) - return; - - push_state (info, STATE_DATE); - } - else - { - set_error (error, context, - G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Element <%s> is not allowed below <%s>"), - element_name, "info"); - } -} - -static void -parse_distance (GMarkupParseContext *context, - const gchar *element_name, - const gchar **attribute_names, - const gchar **attribute_values, - ParseInfo *info, - GError **error) -{ - const char *name; - const char *value; - int val; - - if (!locate_attributes (context, element_name, attribute_names, attribute_values, - error, - "!name", &name, "!value", &value, - NULL)) - return; - - val = 0; - if (!parse_positive_integer (value, &val, context, info->theme, error)) - return; - - g_assert (val >= 0); /* yeah, "non-negative" not "positive" get over it */ - g_assert (info->layout); - - if (strcmp (name, "right_titlebar_edge") == 0) - info->layout->right_titlebar_edge = val; - else if (strcmp (name, "left_titlebar_edge") == 0) - info->layout->left_titlebar_edge = val; - else if (strcmp (name, "button_width") == 0) - { - info->layout->button_width = val; - - if (!(info->layout->button_sizing == META_BUTTON_SIZING_LAST || - info->layout->button_sizing == META_BUTTON_SIZING_FIXED)) - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Cannot specify both \"button_width\"/\"button_height\" and \"aspect_ratio\" for buttons")); - return; - } - - info->layout->button_sizing = META_BUTTON_SIZING_FIXED; - } - else if (strcmp (name, "button_height") == 0) - { - info->layout->button_height = val; - - if (!(info->layout->button_sizing == META_BUTTON_SIZING_LAST || - info->layout->button_sizing == META_BUTTON_SIZING_FIXED)) - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Cannot specify both \"button_width\"/\"button_height\" and \"aspect_ratio\" for buttons")); - return; - } - - info->layout->button_sizing = META_BUTTON_SIZING_FIXED; - } - else - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Distance \"%s\" is unknown"), name); - return; - } -} - -static void -parse_aspect_ratio (GMarkupParseContext *context, - const gchar *element_name, - const gchar **attribute_names, - const gchar **attribute_values, - ParseInfo *info, - GError **error) -{ - const char *name; - const char *value; - double val; - - if (!locate_attributes (context, element_name, attribute_names, attribute_values, - error, - "!name", &name, "!value", &value, - NULL)) - return; - - val = 0; - if (!parse_double (value, &val, context, error)) - return; - - g_assert (info->layout); - - if (strcmp (name, "button") == 0) - { - info->layout->button_aspect = val; - - if (info->layout->button_sizing != META_BUTTON_SIZING_LAST) - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Cannot specify both \"button_width\"/\"button_height\" and \"aspect_ratio\" for buttons")); - return; - } - - info->layout->button_sizing = META_BUTTON_SIZING_ASPECT; - } - else - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Aspect ratio \"%s\" is unknown"), name); - return; - } -} - -static void -parse_border (GMarkupParseContext *context, - const gchar *element_name, - const gchar **attribute_names, - const gchar **attribute_values, - ParseInfo *info, - GError **error) -{ - const char *name; - const char *top; - const char *bottom; - const char *left; - const char *right; - int top_val; - int bottom_val; - int left_val; - int right_val; - GtkBorder *border; - - if (!locate_attributes (context, element_name, attribute_names, attribute_values, - error, - "!name", &name, - "!top", &top, - "!bottom", &bottom, - "!left", &left, - "!right", &right, - NULL)) - return; - - top_val = 0; - if (!parse_positive_integer (top, &top_val, context, info->theme, error)) - return; - - bottom_val = 0; - if (!parse_positive_integer (bottom, &bottom_val, context, info->theme, error)) - return; - - left_val = 0; - if (!parse_positive_integer (left, &left_val, context, info->theme, error)) - return; - - right_val = 0; - if (!parse_positive_integer (right, &right_val, context, info->theme, error)) - return; - - g_assert (info->layout); - - border = NULL; - - if (strcmp (name, "title_border") == 0) - border = &info->layout->title_border; - else if (strcmp (name, "button_border") == 0) - border = &info->layout->button_border; - - if (border == NULL) - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Border \"%s\" is unknown"), name); - return; - } - - border->top = top_val; - border->bottom = bottom_val; - border->left = left_val; - border->right = right_val; -} - -static void -parse_geometry_element (GMarkupParseContext *context, - const gchar *element_name, - const gchar **attribute_names, - const gchar **attribute_values, - ParseInfo *info, - GError **error) -{ - g_return_if_fail (peek_state (info) == STATE_FRAME_GEOMETRY); - - if (ELEMENT_IS ("distance")) - { - parse_distance (context, element_name, - attribute_names, attribute_values, - info, error); - push_state (info, STATE_DISTANCE); - } - else if (ELEMENT_IS ("border")) - { - parse_border (context, element_name, - attribute_names, attribute_values, - info, error); - push_state (info, STATE_BORDER); - } - else if (ELEMENT_IS ("aspect_ratio")) - { - parse_aspect_ratio (context, element_name, - attribute_names, attribute_values, - info, error); - - push_state (info, STATE_ASPECT_RATIO); - } - else - { - set_error (error, context, - G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Element <%s> is not allowed below <%s>"), - element_name, "frame_geometry"); - } -} - -#if 0 -static gboolean -check_expression (PosToken *tokens, - int n_tokens, - gboolean has_object, - MetaTheme *theme, - GMarkupParseContext *context, - GError **error) -{ - MetaPositionExprEnv env; - int x, y; - - /* We set it all to 0 to try and catch divide-by-zero screwups. - * it's possible we should instead guarantee that widths and heights - * are at least 1. - */ - - env.rect = meta_rect (0, 0, 0, 0); - if (has_object) - { - env.object_width = 0; - env.object_height = 0; - } - else - { - env.object_width = -1; - env.object_height = -1; - } - - env.left_width = 0; - env.right_width = 0; - env.top_height = 0; - env.bottom_height = 0; - env.title_width = 0; - env.title_height = 0; - - env.icon_width = 0; - env.icon_height = 0; - env.mini_icon_width = 0; - env.mini_icon_height = 0; - env.theme = theme; - - if (!meta_parse_position_expression (tokens, n_tokens, - &env, - &x, &y, - error)) - { - add_context_to_error (error, context); - return FALSE; - } - - return TRUE; -} -#endif - -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 0 - 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; -#endif - - dash_on_val = 0; - if (dash_on_length && - !parse_positive_integer (dash_on_length, &dash_on_val, context, info->theme, error)) - return; - - dash_off_val = 0; - if (dash_off_length && - !parse_positive_integer (dash_off_length, &dash_off_val, context, info->theme, error)) - return; - - width_val = 0; - if (width && - !parse_positive_integer (width, &width_val, context, info->theme, error)) - return; - - /* Check last so we don't have to free it when other - * stuff fails - */ - color_spec = parse_color (info->theme, color, error); - if (color_spec == NULL) - { - add_context_to_error (error, context); - return; - } - - op = meta_draw_op_new (META_DRAW_LINE); - - op->data.line.color_spec = color_spec; - - op->data.line.x1 = meta_draw_spec_new (info->theme, x1, NULL); - op->data.line.y1 = meta_draw_spec_new (info->theme, y1, NULL); - - if (strcmp(x1, x2)==0) - op->data.line.x2 = NULL; - else - op->data.line.x2 = meta_draw_spec_new (info->theme, x2, NULL); - - if (strcmp(y1, y2)==0) - op->data.line.y2 = NULL; - else - op->data.line.y2 = meta_draw_spec_new (info->theme, y2, NULL); - - op->data.line.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 0 - 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; -#endif - - filled_val = FALSE; - if (filled && !parse_boolean (filled, &filled_val, context, error)) - return; - - /* Check last so we don't have to free it when other - * stuff fails - */ - color_spec = parse_color (info->theme, color, error); - if (color_spec == NULL) - { - add_context_to_error (error, context); - return; - } - - op = meta_draw_op_new (META_DRAW_RECTANGLE); - - op->data.rectangle.color_spec = color_spec; - op->data.rectangle.x = meta_draw_spec_new (info->theme, x, NULL); - op->data.rectangle.y = meta_draw_spec_new (info->theme, y, NULL); - op->data.rectangle.width = meta_draw_spec_new (info->theme, width, NULL); - op->data.rectangle.height = meta_draw_spec_new (info->theme, - height, NULL); - - op->data.rectangle.filled = filled_val; - - g_assert (info->op_list); - - meta_draw_op_list_append (info->op_list, op); - - push_state (info, STATE_RECTANGLE); - } - else if (ELEMENT_IS ("arc")) - { - MetaDrawOp *op; - const char *color; - const char *x; - const char *y; - const char *width; - const char *height; - const char *filled; - const char *start_angle; - const char *extent_angle; - const char *from; - const char *to; - gboolean filled_val; - double start_angle_val; - double extent_angle_val; - MetaColorSpec *color_spec; - - if (!locate_attributes (context, element_name, attribute_names, attribute_values, - error, - "!color", &color, - "!x", &x, "!y", &y, - "!width", &width, "!height", &height, - "filled", &filled, - "start_angle", &start_angle, - "extent_angle", &extent_angle, - "from", &from, - "to", &to, - NULL)) - return; - - if (META_THEME_ALLOWS (info->theme, META_THEME_DEGREES_IN_ARCS) ) - { - if (start_angle == NULL && from == NULL) - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("No \"start_angle\" or \"from\" attribute on element <%s>"), element_name); - return; - } - - if (extent_angle == NULL && to == NULL) - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("No \"extent_angle\" or \"to\" attribute on element <%s>"), element_name); - return; - } - } - else - { - if (start_angle == NULL) - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - ATTRIBUTE_NOT_FOUND, "start_angle", element_name); - return; - } - - if (extent_angle == NULL) - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - ATTRIBUTE_NOT_FOUND, "extent_angle", element_name); - return; - } - } - -#if 0 - 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; -#endif - - if (start_angle == NULL) - { - if (!parse_angle (from, &start_angle_val, context, error)) - return; - - start_angle_val = (180-start_angle_val)/360.0; - } - else - { - if (!parse_angle (start_angle, &start_angle_val, context, error)) - return; - } - - if (extent_angle == NULL) - { - if (!parse_angle (to, &extent_angle_val, context, error)) - return; - - extent_angle_val = ((180-extent_angle_val)/360.0) - start_angle_val; - } - else - { - if (!parse_angle (extent_angle, &extent_angle_val, context, error)) - return; - } - - filled_val = FALSE; - if (filled && !parse_boolean (filled, &filled_val, context, error)) - return; - - /* Check last so we don't have to free it when other - * stuff fails - */ - color_spec = parse_color (info->theme, color, error); - if (color_spec == NULL) - { - add_context_to_error (error, context); - return; - } - - op = meta_draw_op_new (META_DRAW_ARC); - - op->data.arc.color_spec = color_spec; - - op->data.arc.x = meta_draw_spec_new (info->theme, x, NULL); - op->data.arc.y = meta_draw_spec_new (info->theme, y, NULL); - op->data.arc.width = meta_draw_spec_new (info->theme, width, NULL); - op->data.arc.height = meta_draw_spec_new (info->theme, height, NULL); - - op->data.arc.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 0 - 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; -#endif - op = meta_draw_op_new (META_DRAW_CLIP); - - op->data.clip.x = meta_draw_spec_new (info->theme, x, NULL); - op->data.clip.y = meta_draw_spec_new (info->theme, y, NULL); - op->data.clip.width = meta_draw_spec_new (info->theme, width, NULL); - op->data.clip.height = meta_draw_spec_new (info->theme, height, NULL); - - g_assert (info->op_list); - - meta_draw_op_list_append (info->op_list, op); - - push_state (info, STATE_CLIP); - } - else if (ELEMENT_IS ("tint")) - { - MetaDrawOp *op; - const char *color; - const char *x; - const char *y; - const char *width; - const char *height; - const char *alpha; - MetaAlphaGradientSpec *alpha_spec; - MetaColorSpec *color_spec; - - if (!locate_attributes (context, element_name, attribute_names, attribute_values, - error, - "!color", &color, - "!x", &x, "!y", &y, - "!width", &width, "!height", &height, - "!alpha", &alpha, - NULL)) - return; - -#if 0 - 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; -#endif - alpha_spec = NULL; - if (!parse_alpha (alpha, &alpha_spec, context, error)) - return; - - /* Check last so we don't have to free it when other - * stuff fails - */ - color_spec = parse_color (info->theme, color, error); - if (color_spec == NULL) - { - if (alpha_spec) - meta_alpha_gradient_spec_free (alpha_spec); - - add_context_to_error (error, context); - return; - } - - op = meta_draw_op_new (META_DRAW_TINT); - - op->data.tint.color_spec = color_spec; - op->data.tint.alpha_spec = alpha_spec; - - op->data.tint.x = meta_draw_spec_new (info->theme, x, NULL); - op->data.tint.y = meta_draw_spec_new (info->theme, y, NULL); - op->data.tint.width = meta_draw_spec_new (info->theme, width, NULL); - op->data.tint.height = meta_draw_spec_new (info->theme, height, NULL); - - g_assert (info->op_list); - - meta_draw_op_list_append (info->op_list, op); - - push_state (info, STATE_TINT); - } - else if (ELEMENT_IS ("gradient")) - { - const char *x; - const char *y; - const char *width; - const char *height; - const char *type; - const char *alpha; - MetaAlphaGradientSpec *alpha_spec; - MetaGradientType type_val; - - if (!locate_attributes (context, element_name, attribute_names, attribute_values, - error, - "!type", &type, - "!x", &x, "!y", &y, - "!width", &width, "!height", &height, - "alpha", &alpha, - NULL)) - return; - -#if 0 - 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; -#endif - - type_val = meta_gradient_type_from_string (type); - if (type_val == META_GRADIENT_LAST) - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Did not understand value \"%s\" for type of gradient"), - type); - return; - } - - alpha_spec = NULL; - if (alpha && !parse_alpha (alpha, &alpha_spec, context, error)) - return; - - g_assert (info->op == NULL); - info->op = meta_draw_op_new (META_DRAW_GRADIENT); - - info->op->data.gradient.x = meta_draw_spec_new (info->theme, x, NULL); - info->op->data.gradient.y = meta_draw_spec_new (info->theme, y, NULL); - info->op->data.gradient.width = meta_draw_spec_new (info->theme, - width, NULL); - info->op->data.gradient.height = meta_draw_spec_new (info->theme, - height, NULL); - - info->op->data.gradient.gradient_spec = meta_gradient_spec_new (type_val); - - info->op->data.gradient.alpha_spec = alpha_spec; - - push_state (info, STATE_GRADIENT); - - /* op gets appended on close tag */ - } - else if (ELEMENT_IS ("image")) - { - MetaDrawOp *op; - const char *filename; - const char *x; - const char *y; - const char *width; - const char *height; - const char *fill_type; - GdkPixbuf *pixbuf; - MetaImageFillType fill_type_val; - int h, w, c; - int pixbuf_width, pixbuf_height, pixbuf_n_channels, pixbuf_rowstride; - guchar *pixbuf_pixels; - - if (!locate_attributes (context, element_name, attribute_names, attribute_values, - error, - "!x", &x, "!y", &y, - "!width", &width, "!height", &height, - "!filename", &filename, - "fill_type", &fill_type, - NULL)) - return; - -#if 0 - 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; -#endif - fill_type_val = META_IMAGE_FILL_SCALE; - if (fill_type) - { - fill_type_val = meta_image_fill_type_from_string (fill_type); - - if (((int) fill_type_val) == -1) - { - set_error (error, context, G_MARKUP_ERROR, - G_MARKUP_ERROR_PARSE, - _("Did not understand fill type \"%s\" for <%s> element"), - fill_type, element_name); - } - } - - /* Check last so we don't have to free it when other - * stuff fails. - * - * If it's a theme image, ask for it at 64px, which is - * the largest possible. We scale it anyway. - */ - pixbuf = meta_theme_load_image (info->theme, filename, 64, error); - - if (pixbuf == NULL) - { - add_context_to_error (error, context); - return; - } - op = meta_draw_op_new (META_DRAW_IMAGE); - - op->data.image.pixbuf = pixbuf; - - op->data.image.x = meta_draw_spec_new (info->theme, x, NULL); - op->data.image.y = meta_draw_spec_new (info->theme, y, NULL); - op->data.image.width = meta_draw_spec_new (info->theme, width, NULL); - op->data.image.height = meta_draw_spec_new (info->theme, height, NULL); - - op->data.image.fill_type = fill_type_val; - - /* Check for vertical & horizontal stripes */ - pixbuf_n_channels = gdk_pixbuf_get_n_channels(pixbuf); - pixbuf_width = gdk_pixbuf_get_width(pixbuf); - pixbuf_height = gdk_pixbuf_get_height(pixbuf); - pixbuf_rowstride = gdk_pixbuf_get_rowstride(pixbuf); - pixbuf_pixels = gdk_pixbuf_get_pixels(pixbuf); - - /* Check for horizontal stripes */ - for (h = 0; h < pixbuf_height; h++) - { - for (w = 1; w < pixbuf_width; w++) - { - for (c = 0; c < pixbuf_n_channels; c++) - { - if (pixbuf_pixels[(h * pixbuf_rowstride) + c] != - pixbuf_pixels[(h * pixbuf_rowstride) + w + c]) - break; - } - if (c < pixbuf_n_channels) - break; - } - if (w < pixbuf_width) - break; - } - - if (h >= pixbuf_height) - { - op->data.image.horizontal_stripes = TRUE; - } - else - { - op->data.image.horizontal_stripes = FALSE; - } - - /* Check for vertical stripes */ - for (w = 0; w < pixbuf_width; w++) - { - for (h = 1; h < pixbuf_height; h++) - { - for (c = 0; c < pixbuf_n_channels; c++) - { - if (pixbuf_pixels[w + c] != - pixbuf_pixels[(h * pixbuf_rowstride) + w + c]) - break; - } - if (c < pixbuf_n_channels) - break; - } - if (h < pixbuf_height) - break; - } - - if (w >= pixbuf_width) - { - op->data.image.vertical_stripes = TRUE; - } - else - { - op->data.image.vertical_stripes = FALSE; - } - - g_assert (info->op_list); - - meta_draw_op_list_append (info->op_list, op); - - push_state (info, STATE_IMAGE); - } - else if (ELEMENT_IS ("gtk_arrow")) - { - MetaDrawOp *op; - const char *state; - const char *shadow; - const char *arrow; - const char *x; - const char *y; - const char *width; - const char *height; - const char *filled; - gboolean filled_val; - GtkStateFlags 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 0 - 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; -#endif - 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 = meta_draw_spec_new (info->theme, x, NULL); - op->data.gtk_arrow.y = meta_draw_spec_new (info->theme, y, NULL); - op->data.gtk_arrow.width = meta_draw_spec_new (info->theme, width, NULL); - op->data.gtk_arrow.height = meta_draw_spec_new (info->theme, - height, NULL); - - 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; - GtkStateFlags 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 0 - 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; -#endif - 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 = meta_draw_spec_new (info->theme, x, NULL); - op->data.gtk_box.y = meta_draw_spec_new (info->theme, y, NULL); - op->data.gtk_box.width = meta_draw_spec_new (info->theme, width, NULL); - op->data.gtk_box.height = meta_draw_spec_new (info->theme, height, NULL); - - op->data.gtk_box.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; - GtkStateFlags state_val; - - if (!locate_attributes (context, element_name, attribute_names, attribute_values, - error, - "!state", &state, - "!x", &x, "!y1", &y1, "!y2", &y2, - NULL)) - return; - -#if 0 - 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; -#endif - - 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 = meta_draw_spec_new (info->theme, x, NULL); - op->data.gtk_vline.y1 = meta_draw_spec_new (info->theme, y1, NULL); - op->data.gtk_vline.y2 = meta_draw_spec_new (info->theme, y2, NULL); - - op->data.gtk_vline.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 *fill_type; - MetaImageFillType fill_type_val; - - if (!locate_attributes (context, element_name, attribute_names, attribute_values, - error, - "!x", &x, "!y", &y, - "!width", &width, "!height", &height, - "fill_type", &fill_type, - NULL)) - return; - -#if 0 - 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; -#endif - fill_type_val = META_IMAGE_FILL_SCALE; - if (fill_type) - { - fill_type_val = meta_image_fill_type_from_string (fill_type); - - if (((int) fill_type_val) == -1) - { - set_error (error, context, G_MARKUP_ERROR, - G_MARKUP_ERROR_PARSE, - _("Did not understand fill type \"%s\" for <%s> element"), - fill_type, element_name); - } - } - - - op = meta_draw_op_new (META_DRAW_ICON); - - op->data.icon.x = meta_draw_spec_new (info->theme, x, NULL); - op->data.icon.y = meta_draw_spec_new (info->theme, y, NULL); - op->data.icon.width = meta_draw_spec_new (info->theme, width, NULL); - op->data.icon.height = meta_draw_spec_new (info->theme, height, NULL); - - op->data.icon.fill_type = fill_type_val; - - g_assert (info->op_list); - - meta_draw_op_list_append (info->op_list, op); - - push_state (info, STATE_ICON); - } - else if (ELEMENT_IS ("title")) - { - MetaDrawOp *op; - const char *color; - const char *x; - const char *y; - const char *ellipsize_width; - MetaColorSpec *color_spec; - - if (!locate_attributes (context, element_name, attribute_names, attribute_values, - error, - "!color", &color, - "!x", &x, "!y", &y, - "ellipsize_width", &ellipsize_width, - NULL)) - return; - -#if 0 - if (!check_expression (x, FALSE, info->theme, context, error)) - return; - - if (!check_expression (y, FALSE, info->theme, context, error)) - return; - - if (!check_expression (ellipsize_width, FALSE, info->theme, context, error)) - return; -#endif - - if (ellipsize_width && peek_required_version (info) < 3001) - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - ATTRIBUTE_NOT_FOUND, "ellipsize_width", element_name); - return; - } - - /* Check last so we don't have to free it when other - * stuff fails - */ - color_spec = parse_color (info->theme, color, error); - if (color_spec == NULL) - { - add_context_to_error (error, context); - return; - } - - op = meta_draw_op_new (META_DRAW_TITLE); - - op->data.title.color_spec = color_spec; - - op->data.title.x = meta_draw_spec_new (info->theme, x, NULL); - op->data.title.y = meta_draw_spec_new (info->theme, y, NULL); - if (ellipsize_width) - op->data.title.ellipsize_width = meta_draw_spec_new (info->theme, ellipsize_width, NULL); - - 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; - - /* x/y/width/height default to 0,0,width,height - should - * probably do this for all the draw ops - */ -#if 0 - 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; -#endif - - 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 = meta_draw_spec_new (info->theme, x ? x : "0", NULL); - op->data.op_list.y = meta_draw_spec_new (info->theme, y ? y : "0", NULL); - op->data.op_list.width = meta_draw_spec_new (info->theme, - width ? width : "width", - NULL); - op->data.op_list.height = meta_draw_spec_new (info->theme, - height ? height : "height", - NULL); - - meta_draw_op_list_append (info->op_list, op); - - push_state (info, STATE_INCLUDE); - } - else if (ELEMENT_IS ("tile")) - { - MetaDrawOp *op; - const char *name; - const char *x; - const char *y; - const char *width; - const char *height; - const char *tile_xoffset; - const char *tile_yoffset; - const char *tile_width; - const char *tile_height; - MetaDrawOpList *op_list; - - if (!locate_attributes (context, element_name, attribute_names, attribute_values, - error, - "x", &x, "y", &y, - "width", &width, "height", &height, - "!name", &name, - "tile_xoffset", &tile_xoffset, - "tile_yoffset", &tile_yoffset, - "!tile_width", &tile_width, - "!tile_height", &tile_height, - NULL)) - return; - - /* These default to 0 */ -#if 0 - if (tile_xoffset && !check_expression (tile_xoffset, FALSE, info->theme, context, error)) - return; - - if (tile_yoffset && !check_expression (tile_yoffset, FALSE, info->theme, context, error)) - return; - - /* x/y/width/height default to 0,0,width,height - should - * probably do this for all the draw ops - */ - if (x && !check_expression (x, FALSE, info->theme, context, error)) - return; - - if (y && !check_expression (y, FALSE, info->theme, context, error)) - return; - - if (width && !check_expression (width, FALSE, info->theme, context, error)) - return; - - if (height && !check_expression (height, FALSE, info->theme, context, error)) - return; - - if (!check_expression (tile_width, FALSE, info->theme, context, error)) - return; - - if (!check_expression (tile_height, FALSE, info->theme, context, error)) - return; -#endif - op_list = meta_theme_lookup_draw_op_list (info->theme, - name); - if (op_list == NULL) - { - set_error (error, context, G_MARKUP_ERROR, - G_MARKUP_ERROR_PARSE, - _("No <draw_ops> called \"%s\" has been defined"), - name); - return; - } - - g_assert (info->op_list); - - if (op_list == info->op_list || - meta_draw_op_list_contains (op_list, info->op_list)) - { - set_error (error, context, G_MARKUP_ERROR, - G_MARKUP_ERROR_PARSE, - _("Including draw_ops \"%s\" here would create a circular reference"), - name); - return; - } - - op = meta_draw_op_new (META_DRAW_TILE); - - meta_draw_op_list_ref (op_list); - - op->data.tile.x = meta_draw_spec_new (info->theme, x ? x : "0", NULL); - op->data.tile.y = meta_draw_spec_new (info->theme, y ? y : "0", NULL); - op->data.tile.width = meta_draw_spec_new (info->theme, - width ? width : "width", - NULL); - op->data.tile.height = meta_draw_spec_new (info->theme, - height ? height : "height", - NULL); - op->data.tile.tile_xoffset = meta_draw_spec_new (info->theme, - tile_xoffset ? tile_xoffset : "0", - NULL); - op->data.tile.tile_yoffset = meta_draw_spec_new (info->theme, - tile_yoffset ? tile_yoffset : "0", - NULL); - op->data.tile.tile_width = meta_draw_spec_new (info->theme, tile_width, NULL); - op->data.tile.tile_height = meta_draw_spec_new (info->theme, tile_height, NULL); - - op->data.tile.op_list = op_list; - - meta_draw_op_list_append (info->op_list, op); - - push_state (info, STATE_TILE); - } - else - { - set_error (error, context, - G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Element <%s> is not allowed below <%s>"), - element_name, "draw_ops"); - } -} - -static void -parse_gradient_element (GMarkupParseContext *context, - const gchar *element_name, - const gchar **attribute_names, - const gchar **attribute_values, - ParseInfo *info, - GError **error) -{ - g_return_if_fail (peek_state (info) == STATE_GRADIENT); - - if (ELEMENT_IS ("color")) - { - const char *value = NULL; - MetaColorSpec *color_spec; - - if (!locate_attributes (context, element_name, attribute_names, attribute_values, - error, - "!value", &value, - NULL)) - return; - - color_spec = parse_color (info->theme, value, error); - if (color_spec == NULL) - { - add_context_to_error (error, context); - return; - } - - g_assert (info->op); - g_assert (info->op->type == META_DRAW_GRADIENT); - g_assert (info->op->data.gradient.gradient_spec != NULL); - info->op->data.gradient.gradient_spec->color_specs = - g_slist_append (info->op->data.gradient.gradient_spec->color_specs, - color_spec); - - push_state (info, STATE_COLOR); - } - else - { - set_error (error, context, - G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Element <%s> is not allowed below <%s>"), - element_name, "gradient"); - } -} - -static void -parse_style_element (GMarkupParseContext *context, - const gchar *element_name, - const gchar **attribute_names, - const gchar **attribute_values, - ParseInfo *info, - GError **error) -{ - g_return_if_fail (peek_state (info) == STATE_FRAME_STYLE); - - g_assert (info->style); - - if (ELEMENT_IS ("piece")) - { - const char *position = NULL; - const char *draw_ops = NULL; - - if (!locate_attributes (context, element_name, attribute_names, attribute_values, - error, - "!position", &position, - "draw_ops", &draw_ops, - NULL)) - return; - - 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; - gint required_version; - - if (!locate_attributes (context, element_name, attribute_names, attribute_values, - error, - "!function", &function, - "!state", &state, - "draw_ops", &draw_ops, - NULL)) - return; - - info->button_type = meta_button_type_from_string (function, info->theme); - if (info->button_type == META_BUTTON_TYPE_LAST) - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Unknown function \"%s\" for button"), - function); - return; - } - - required_version = peek_required_version (info); - if (meta_theme_earliest_version_with_button (info->button_type) > - (guint)required_version) - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Button function \"%s\" does not exist in this version (%d, need %d)"), - function, - required_version, - meta_theme_earliest_version_with_button (info->button_type) - ); - return; - } - - info->button_state = meta_button_state_from_string (state); - if (info->button_state == META_BUTTON_STATE_LAST) - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Unknown state \"%s\" for button"), - state); - return; - } - - if (info->style->buttons[info->button_type][info->button_state] != NULL) - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Frame style already has a button for function %s state %s"), - function, state); - return; - } - - g_assert (info->op_list == NULL); - - if (draw_ops) - { - MetaDrawOpList *op_list; - - op_list = meta_theme_lookup_draw_op_list (info->theme, - draw_ops); - - if (op_list == NULL) - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("No <draw_ops> with the name \"%s\" has been defined"), - draw_ops); - return; - } - - meta_draw_op_list_ref (op_list); - info->op_list = op_list; - } - - push_state (info, STATE_BUTTON); - } - else - { - set_error (error, context, - G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Element <%s> is not allowed below <%s>"), - element_name, "frame_style"); - } -} - -static void -parse_style_set_element (GMarkupParseContext *context, - const gchar *element_name, - const gchar **attribute_names, - const gchar **attribute_values, - ParseInfo *info, - GError **error) -{ - g_return_if_fail (peek_state (info) == STATE_FRAME_STYLE_SET); - - if (ELEMENT_IS ("frame")) - { - const char *focus = NULL; - const char *state = NULL; - const char *resize = NULL; - const char *style = NULL; - MetaFrameFocus frame_focus; - MetaFrameState frame_state; - MetaFrameResize frame_resize; - MetaFrameStyle *frame_style; - - if (!locate_attributes (context, element_name, attribute_names, attribute_values, - error, - "!focus", &focus, - "!state", &state, - "resize", &resize, - "!style", &style, - NULL)) - return; - - frame_focus = meta_frame_focus_from_string (focus); - if (frame_focus == META_FRAME_FOCUS_LAST) - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("\"%s\" is not a valid value for focus attribute"), - focus); - return; - } - - frame_state = meta_frame_state_from_string (state); - if (frame_state == META_FRAME_STATE_LAST) - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("\"%s\" is not a valid value for state attribute"), - focus); - return; - } - - frame_style = meta_theme_lookup_style (info->theme, style); - - if (frame_style == NULL) - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("A style called \"%s\" has not been defined"), - style); - return; - } - - switch (frame_state) - { - case META_FRAME_STATE_NORMAL: - if (resize == NULL) - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - ATTRIBUTE_NOT_FOUND, - "resize", element_name); - return; - } - - - frame_resize = meta_frame_resize_from_string (resize); - if (frame_resize == META_FRAME_RESIZE_LAST) - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("\"%s\" is not a valid value for resize attribute"), - focus); - return; - } - - break; - - case META_FRAME_STATE_SHADED: - if (META_THEME_ALLOWS (info->theme, META_THEME_UNRESIZABLE_SHADED_STYLES)) - { - if (resize == NULL) - /* In state="normal" we would complain here. But instead we accept - * not having a resize attribute and default to resize="both", since - * that most closely mimics what we did in v1, and thus people can - * upgrade a theme to v2 without as much hassle. - */ - frame_resize = META_FRAME_RESIZE_BOTH; - else - { - frame_resize = meta_frame_resize_from_string (resize); - if (frame_resize == META_FRAME_RESIZE_LAST) - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("\"%s\" is not a valid value for resize attribute"), - focus); - return; - } - } - } - else /* v1 theme */ - { - if (resize != NULL) - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Should not have \"resize\" attribute on <%s> element for maximized/shaded states"), - element_name); - return; - } - - /* resize="both" is equivalent to the old behaviour */ - frame_resize = META_FRAME_RESIZE_BOTH; - } - break; - - default: - if (resize != NULL) - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Should not have \"resize\" attribute on <%s> element for maximized states"), - element_name); - return; - } - - frame_resize = META_FRAME_RESIZE_LAST; - } - - switch (frame_state) - { - case META_FRAME_STATE_NORMAL: - if (info->style_set->normal_styles[frame_resize][frame_focus]) - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Style has already been specified for state %s resize %s focus %s"), - state, resize, focus); - return; - } - meta_frame_style_ref (frame_style); - info->style_set->normal_styles[frame_resize][frame_focus] = frame_style; - break; - case META_FRAME_STATE_MAXIMIZED: - if (info->style_set->maximized_styles[frame_focus]) - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Style has already been specified for state %s focus %s"), - state, focus); - return; - } - meta_frame_style_ref (frame_style); - info->style_set->maximized_styles[frame_focus] = frame_style; - break; - case META_FRAME_STATE_TILED_LEFT: - if (info->style_set->tiled_left_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->tiled_left_styles[frame_focus] = frame_style; - break; - case META_FRAME_STATE_TILED_RIGHT: - if (info->style_set->tiled_right_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->tiled_right_styles[frame_focus] = frame_style; - break; - meta_frame_style_ref (frame_style); - info->style_set->tiled_right_styles[frame_focus] = frame_style; - break; - case META_FRAME_STATE_SHADED: - if (info->style_set->shaded_styles[frame_resize][frame_focus]) - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Style has already been specified for state %s resize %s focus %s"), - state, resize, focus); - return; - } - meta_frame_style_ref (frame_style); - info->style_set->shaded_styles[frame_resize][frame_focus] = frame_style; - break; - case META_FRAME_STATE_MAXIMIZED_AND_SHADED: - if (info->style_set->maximized_and_shaded_styles[frame_focus]) - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Style has already been specified for state %s focus %s"), - state, focus); - return; - } - meta_frame_style_ref (frame_style); - info->style_set->maximized_and_shaded_styles[frame_focus] = frame_style; - break; - case META_FRAME_STATE_TILED_LEFT_AND_SHADED: - if (info->style_set->tiled_left_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->tiled_left_and_shaded_styles[frame_focus] = frame_style; - break; - case META_FRAME_STATE_TILED_RIGHT_AND_SHADED: - if (info->style_set->tiled_right_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->tiled_right_and_shaded_styles[frame_focus] = frame_style; - break; - case META_FRAME_STATE_LAST: - g_assert_not_reached (); - break; - } - - push_state (info, STATE_FRAME); - } - else - { - set_error (error, context, - G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Element <%s> is not allowed below <%s>"), - element_name, "frame_style_set"); - } -} - -static void -parse_piece_element (GMarkupParseContext *context, - const gchar *element_name, - const gchar **attribute_names, - const gchar **attribute_values, - ParseInfo *info, - GError **error) -{ - g_return_if_fail (peek_state (info) == STATE_PIECE); - - if (ELEMENT_IS ("draw_ops")) - { - if (info->op_list) - { - set_error (error, context, - G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Can't have a two draw_ops for a <piece> element (theme specified a draw_ops attribute and also a <draw_ops> element, or specified two elements)")); - return; - } - - if (!check_no_attributes (context, element_name, attribute_names, attribute_values, - error)) - return; - - g_assert (info->op_list == NULL); - info->op_list = meta_draw_op_list_new (2); - - push_state (info, STATE_DRAW_OPS); - } - else - { - set_error (error, context, - G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Element <%s> is not allowed below <%s>"), - element_name, "piece"); - } -} - -static void -parse_button_element (GMarkupParseContext *context, - const gchar *element_name, - const gchar **attribute_names, - const gchar **attribute_values, - ParseInfo *info, - GError **error) -{ - g_return_if_fail (peek_state (info) == STATE_BUTTON); - - if (ELEMENT_IS ("draw_ops")) - { - if (info->op_list) - { - set_error (error, context, - G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Can't have a two draw_ops for a <button> element (theme specified a draw_ops attribute and also a <draw_ops> element, or specified two elements)")); - return; - } - - if (!check_no_attributes (context, element_name, attribute_names, attribute_values, - error)) - return; - - g_assert (info->op_list == NULL); - info->op_list = meta_draw_op_list_new (2); - - push_state (info, STATE_DRAW_OPS); - } - else - { - set_error (error, context, - G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Element <%s> is not allowed below <%s>"), - element_name, "button"); - } -} - -static void -parse_menu_icon_element (GMarkupParseContext *context, - const gchar *element_name, - const gchar **attribute_names, - const gchar **attribute_values, - ParseInfo *info, - GError **error) -{ - g_return_if_fail (peek_state (info) == STATE_MENU_ICON); - - if (ELEMENT_IS ("draw_ops")) - { - if (info->op_list) - { - set_error (error, context, - G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Can't have a two draw_ops for a <menu_icon> element (theme specified a draw_ops attribute and also a <draw_ops> element, or specified two elements)")); - return; - } - - if (!check_no_attributes (context, element_name, attribute_names, attribute_values, - error)) - return; - - g_assert (info->op_list == NULL); - info->op_list = meta_draw_op_list_new (2); - - push_state (info, STATE_DRAW_OPS); - } - else - { - set_error (error, context, - G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Element <%s> is not allowed below <%s>"), - element_name, "menu_icon"); - } -} - -static const char * -find_version (const char **attribute_names, - const char **attribute_values) -{ - int i; - - for (i = 0; attribute_names[i]; i++) - { - if (strcmp (attribute_names[i], "version") == 0) - return attribute_values[i]; - } - - return NULL; -} - -/* Returns whether the version element was successfully parsed. - * If successfully parsed, then two additional items are returned: - * - * satisfied: whether this version of Mutter meets the version check - * minimum_required: minimum version of theme format required by version check - */ -static gboolean -check_version (GMarkupParseContext *context, - const char *version_str, - gboolean *satisfied, - guint *minimum_required, - GError **error) -{ - static GRegex *version_regex; - GMatchInfo *info; - char *comparison_str, *major_str, *minor_str; - guint version; - - *minimum_required = 0; - - if (!version_regex) - version_regex = g_regex_new ("^\\s*([<>]=?)\\s*(\\d+)(\\.\\d+)?\\s*$", 0, 0, NULL); - - if (!g_regex_match (version_regex, version_str, 0, &info)) - { - g_match_info_free (info); - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Bad version specification '%s'"), version_str); - return FALSE; - } - - comparison_str = g_match_info_fetch (info, 1); - major_str = g_match_info_fetch (info, 2); - minor_str = g_match_info_fetch (info, 3); - - version = 1000 * atoi (major_str); - /* might get NULL, see: https://bugzilla.gnome.org/review?bug=588217 */ - if (minor_str && minor_str[0]) - version += atoi (minor_str + 1); - - if (comparison_str[0] == '<') - { - if (comparison_str[1] == '=') - *satisfied = THEME_VERSION <= version; - else - { - *satisfied = THEME_VERSION < version; - } - } - else - { - if (comparison_str[1] == '=') - { - *satisfied = THEME_VERSION >= version; - *minimum_required = version; - } - else - { - *satisfied = THEME_VERSION > version; - *minimum_required = version + 1; - } - } - - g_free (comparison_str); - g_free (major_str); - g_free (minor_str); - g_match_info_free (info); - - return TRUE; -} - -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; - const char *version; - guint required_version = 0; - - if (info->skip_level > 0) - { - info->skip_level++; - return; - } - - required_version = peek_required_version (info); - - version = find_version (attribute_names, attribute_values); - if (version != NULL) - { - gboolean satisfied; - guint element_required; - - if (required_version < 3000) - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("\"version\" attribute cannot be used in metacity-theme-1.xml or metacity-theme-2.xml")); - return; - } - - if (!check_version (context, version, &satisfied, &element_required, error)) - return; - - /* Two different ways of handling an unsatisfied version check: - * for the toplevel element of a file, we throw an error back so - * that the controlling code can go ahead and look for an - * alternate metacity-theme-1.xml or metacity-theme-2.xml; for - * other elements we just silently skip the element and children. - */ - if (peek_state (info) == STATE_START) - { - if (satisfied) - { - if (element_required > info->format_version) - info->format_version = element_required; - } - else - { - set_error (error, context, THEME_PARSE_ERROR, THEME_PARSE_ERROR_TOO_OLD, - _("Theme requires version %s but latest supported theme version is %d.%d"), - version, THEME_VERSION, THEME_MINOR_VERSION); - return; - } - } - else if (!satisfied) - { - info->skip_level = 1; - return; - } - - if (element_required > required_version) - required_version = element_required; - } - - push_required_version (info, required_version); - - switch (peek_state (info)) - { - case STATE_START: - if (strcmp (element_name, "metacity_theme") == 0) - { - info->theme = meta_theme_new (); - info->theme->name = g_strdup (info->theme_name); - info->theme->filename = g_strdup (info->theme_file); - info->theme->dirname = g_strdup (info->theme_dir); - info->theme->format_version = info->format_version; - - push_state (info, STATE_THEME); - } - else - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Outermost element in theme must be <metacity_theme> not <%s>"), - element_name); - break; - - case STATE_THEME: - parse_toplevel_element (context, element_name, - attribute_names, attribute_values, - info, error); - break; - case STATE_INFO: - parse_info_element (context, element_name, - attribute_names, attribute_values, - info, error); - break; - case STATE_NAME: - case STATE_AUTHOR: - case STATE_COPYRIGHT: - case STATE_DATE: - case STATE_DESCRIPTION: - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Element <%s> is not allowed inside a name/author/date/description element"), - element_name); - break; - case STATE_CONSTANT: - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Element <%s> is not allowed inside a <constant> element"), - element_name); - break; - case STATE_FRAME_GEOMETRY: - parse_geometry_element (context, element_name, - attribute_names, attribute_values, - info, error); - break; - case STATE_DISTANCE: - case STATE_BORDER: - case STATE_ASPECT_RATIO: - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Element <%s> is not allowed inside a distance/border/aspect_ratio element"), - element_name); - break; - case STATE_DRAW_OPS: - parse_draw_op_element (context, element_name, - attribute_names, attribute_values, - info, error); - break; - case STATE_LINE: - case STATE_RECTANGLE: - case STATE_ARC: - case STATE_CLIP: - case STATE_TINT: - case STATE_IMAGE: - case STATE_GTK_ARROW: - case STATE_GTK_BOX: - case STATE_GTK_VLINE: - case STATE_ICON: - case STATE_TITLE: - case STATE_INCLUDE: - case STATE_TILE: - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Element <%s> is not allowed inside a draw operation element"), - element_name); - break; - case STATE_GRADIENT: - parse_gradient_element (context, element_name, - attribute_names, attribute_values, - info, error); - break; - case STATE_COLOR: - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Element <%s> is not allowed inside a <%s> element"), - element_name, "color"); - break; - case STATE_FRAME_STYLE: - parse_style_element (context, element_name, - attribute_names, attribute_values, - info, error); - break; - case STATE_PIECE: - parse_piece_element (context, element_name, - attribute_names, attribute_values, - info, error); - break; - case STATE_BUTTON: - parse_button_element (context, element_name, - attribute_names, attribute_values, - info, error); - break; - case STATE_MENU_ICON: - parse_menu_icon_element (context, element_name, - attribute_names, attribute_values, - info, error); - break; - case STATE_FRAME_STYLE_SET: - parse_style_set_element (context, element_name, - attribute_names, attribute_values, - info, error); - break; - case STATE_FRAME: - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Element <%s> is not allowed inside a <%s> element"), - element_name, "frame"); - break; - case STATE_WINDOW: - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Element <%s> is not allowed inside a <%s> element"), - element_name, "window"); - break; - case STATE_FALLBACK: - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Element <%s> is not allowed inside a <%s> element"), - element_name, "fallback"); - break; - } -} - -static void -end_element_handler (GMarkupParseContext *context, - const gchar *element_name, - gpointer user_data, - GError **error) -{ - ParseInfo *info = user_data; - - if (info->skip_level > 0) - { - info->skip_level--; - return; - } - - switch (peek_state (info)) - { - case STATE_START: - break; - case STATE_THEME: - g_assert (info->theme); - - if (!meta_theme_validate (info->theme, error)) - { - add_context_to_error (error, context); - meta_theme_free (info->theme); - info->theme = NULL; - } - - pop_state (info); - g_assert (peek_state (info) == STATE_START); - break; - case STATE_INFO: - pop_state (info); - g_assert (peek_state (info) == STATE_THEME); - break; - case STATE_NAME: - pop_state (info); - g_assert (peek_state (info) == STATE_INFO); - break; - case STATE_AUTHOR: - pop_state (info); - g_assert (peek_state (info) == STATE_INFO); - break; - case STATE_COPYRIGHT: - pop_state (info); - g_assert (peek_state (info) == STATE_INFO); - break; - case STATE_DATE: - pop_state (info); - g_assert (peek_state (info) == STATE_INFO); - break; - case STATE_DESCRIPTION: - pop_state (info); - g_assert (peek_state (info) == STATE_INFO); - break; - case STATE_CONSTANT: - pop_state (info); - g_assert (peek_state (info) == STATE_THEME); - break; - case STATE_FRAME_GEOMETRY: - g_assert (info->layout); - - if (!meta_frame_layout_validate (info->layout, - error)) - { - add_context_to_error (error, context); - } - - /* layout will already be stored in the theme under - * its name - */ - meta_frame_layout_unref (info->layout); - info->layout = NULL; - pop_state (info); - g_assert (peek_state (info) == STATE_THEME); - break; - case STATE_DISTANCE: - pop_state (info); - g_assert (peek_state (info) == STATE_FRAME_GEOMETRY); - break; - case STATE_BORDER: - pop_state (info); - g_assert (peek_state (info) == STATE_FRAME_GEOMETRY); - break; - case STATE_ASPECT_RATIO: - pop_state (info); - g_assert (peek_state (info) == STATE_FRAME_GEOMETRY); - break; - case STATE_DRAW_OPS: - { - g_assert (info->op_list); - - if (!meta_draw_op_list_validate (info->op_list, - error)) - { - add_context_to_error (error, context); - meta_draw_op_list_unref (info->op_list); - info->op_list = NULL; - } - - pop_state (info); - - switch (peek_state (info)) - { - case STATE_BUTTON: - case STATE_PIECE: - case STATE_MENU_ICON: - /* Leave info->op_list to be picked up - * when these elements are closed - */ - g_assert (info->op_list); - break; - case STATE_THEME: - g_assert (info->op_list); - meta_draw_op_list_unref (info->op_list); - info->op_list = NULL; - break; - default: - /* Op list can't occur in other contexts */ - g_assert_not_reached (); - break; - } - } - break; - case STATE_LINE: - pop_state (info); - g_assert (peek_state (info) == STATE_DRAW_OPS); - break; - case STATE_RECTANGLE: - pop_state (info); - g_assert (peek_state (info) == STATE_DRAW_OPS); - break; - case STATE_ARC: - pop_state (info); - g_assert (peek_state (info) == STATE_DRAW_OPS); - break; - case STATE_CLIP: - pop_state (info); - g_assert (peek_state (info) == STATE_DRAW_OPS); - break; - case STATE_TINT: - pop_state (info); - g_assert (peek_state (info) == STATE_DRAW_OPS); - break; - case STATE_GRADIENT: - g_assert (info->op); - g_assert (info->op->type == META_DRAW_GRADIENT); - if (!meta_gradient_spec_validate (info->op->data.gradient.gradient_spec, - error)) - { - add_context_to_error (error, context); - meta_draw_op_free (info->op); - info->op = NULL; - } - else - { - g_assert (info->op_list); - meta_draw_op_list_append (info->op_list, info->op); - info->op = NULL; - } - pop_state (info); - g_assert (peek_state (info) == STATE_DRAW_OPS); - break; - case STATE_IMAGE: - pop_state (info); - g_assert (peek_state (info) == STATE_DRAW_OPS); - break; - case STATE_GTK_ARROW: - pop_state (info); - g_assert (peek_state (info) == STATE_DRAW_OPS); - break; - case STATE_GTK_BOX: - pop_state (info); - g_assert (peek_state (info) == STATE_DRAW_OPS); - break; - case STATE_GTK_VLINE: - pop_state (info); - g_assert (peek_state (info) == STATE_DRAW_OPS); - break; - case STATE_ICON: - pop_state (info); - g_assert (peek_state (info) == STATE_DRAW_OPS); - break; - case STATE_TITLE: - pop_state (info); - g_assert (peek_state (info) == STATE_DRAW_OPS); - break; - case STATE_INCLUDE: - pop_state (info); - g_assert (peek_state (info) == STATE_DRAW_OPS); - break; - case STATE_TILE: - pop_state (info); - g_assert (peek_state (info) == STATE_DRAW_OPS); - break; - case STATE_COLOR: - pop_state (info); - g_assert (peek_state (info) == STATE_GRADIENT); - break; - case STATE_FRAME_STYLE: - g_assert (info->style); - - if (!meta_frame_style_validate (info->style, - peek_required_version (info), - error)) - { - add_context_to_error (error, context); - } - - /* Frame style is in the theme hash table and a ref - * is held there - */ - meta_frame_style_unref (info->style); - info->style = NULL; - pop_state (info); - g_assert (peek_state (info) == STATE_THEME); - break; - case STATE_PIECE: - g_assert (info->style); - if (info->op_list == NULL) - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("No draw_ops provided for frame piece")); - } - else - { - info->style->pieces[info->piece] = info->op_list; - info->op_list = NULL; - } - pop_state (info); - g_assert (peek_state (info) == STATE_FRAME_STYLE); - break; - case STATE_BUTTON: - g_assert (info->style); - if (info->op_list == NULL) - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("No draw_ops provided for button")); - } - else - { - info->style->buttons[info->button_type][info->button_state] = - info->op_list; - info->op_list = NULL; - } - pop_state (info); - break; - case STATE_MENU_ICON: - g_assert (info->theme); - if (info->op_list != NULL) - { - meta_draw_op_list_unref (info->op_list); - info->op_list = NULL; - } - pop_state (info); - g_assert (peek_state (info) == STATE_THEME); - break; - case STATE_FRAME_STYLE_SET: - g_assert (info->style_set); - - if (!meta_frame_style_set_validate (info->style_set, - error)) - { - add_context_to_error (error, context); - } - - /* Style set is in the theme hash table and a reference - * is held there. - */ - meta_frame_style_set_unref (info->style_set); - info->style_set = NULL; - pop_state (info); - g_assert (peek_state (info) == STATE_THEME); - break; - case STATE_FRAME: - pop_state (info); - g_assert (peek_state (info) == STATE_FRAME_STYLE_SET); - break; - case STATE_WINDOW: - pop_state (info); - g_assert (peek_state (info) == STATE_THEME); - break; - case STATE_FALLBACK: - pop_state (info); - g_assert (peek_state (info) == STATE_THEME); - break; - } - - pop_required_version (info); -} - -#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 (info->skip_level > 0) - return; - - 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, - _("<%s> specified twice for this theme"), - "name"); - 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, - _("<%s> specified twice for this theme"), - "author"); - 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, - _("<%s> specified twice for this theme"), - "copyright"); - 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, - _("<%s> specified twice for this theme"), - "date"); - 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, - _("<%s> specified twice for this theme"), - "description"); - return; - } - - info->theme->description = g_strndup (text, text_len); - break; - case STATE_CONSTANT: - NO_TEXT ("constant"); - break; - case STATE_FRAME_GEOMETRY: - NO_TEXT ("frame_geometry"); - break; - case STATE_DISTANCE: - NO_TEXT ("distance"); - break; - case STATE_BORDER: - NO_TEXT ("border"); - break; - case STATE_ASPECT_RATIO: - NO_TEXT ("aspect_ratio"); - break; - case STATE_DRAW_OPS: - NO_TEXT ("draw_ops"); - break; - case STATE_LINE: - NO_TEXT ("line"); - break; - case STATE_RECTANGLE: - NO_TEXT ("rectangle"); - break; - case STATE_ARC: - NO_TEXT ("arc"); - break; - case STATE_CLIP: - NO_TEXT ("clip"); - break; - case STATE_TINT: - NO_TEXT ("tint"); - break; - case STATE_GRADIENT: - NO_TEXT ("gradient"); - break; - case STATE_IMAGE: - NO_TEXT ("image"); - break; - case STATE_GTK_ARROW: - NO_TEXT ("gtk_arrow"); - break; - case STATE_GTK_BOX: - NO_TEXT ("gtk_box"); - break; - case STATE_GTK_VLINE: - NO_TEXT ("gtk_vline"); - break; - case STATE_ICON: - NO_TEXT ("icon"); - break; - case STATE_TITLE: - NO_TEXT ("title"); - break; - case STATE_INCLUDE: - NO_TEXT ("include"); - break; - case STATE_TILE: - NO_TEXT ("tile"); - break; - case STATE_COLOR: - NO_TEXT ("color"); - break; - case STATE_FRAME_STYLE: - NO_TEXT ("frame_style"); - break; - case STATE_PIECE: - NO_TEXT ("piece"); - break; - case STATE_BUTTON: - NO_TEXT ("button"); - break; - case STATE_MENU_ICON: - NO_TEXT ("menu_icon"); - break; - case STATE_FRAME_STYLE_SET: - NO_TEXT ("frame_style_set"); - break; - case STATE_FRAME: - NO_TEXT ("frame"); - break; - case STATE_WINDOW: - NO_TEXT ("window"); - break; - case STATE_FALLBACK: - NO_TEXT ("fallback"); - break; - } -} - -/* If the theme is not-corrupt, keep looking for alternate versions - * in other locations we might be compatible with - */ -static gboolean -theme_error_is_fatal (GError *error) -{ - return !(error->domain == G_FILE_ERROR || - (error->domain == THEME_PARSE_ERROR && - error->code == THEME_PARSE_ERROR_TOO_OLD)); -} - -static MetaTheme * -load_theme (const char *theme_dir, - const char *theme_name, - guint major_version, - GError **error) -{ - GMarkupParseContext *context; - ParseInfo info; - char *text; - gsize length; - char *theme_filename; - char *theme_file; - MetaTheme *retval; - - g_return_val_if_fail (error && *error == NULL, NULL); - - text = NULL; - retval = NULL; - context = NULL; - - theme_filename = g_strdup_printf (METACITY_THEME_FILENAME_FORMAT, major_version); - theme_file = g_build_filename (theme_dir, theme_filename, NULL); - - if (!g_file_get_contents (theme_file, - &text, - &length, - error)) - goto out; - - meta_topic (META_DEBUG_THEMES, "Parsing theme file %s\n", theme_file); - - parse_info_init (&info); - - info.theme_name = theme_name; - info.theme_file = theme_file; - info.theme_dir = theme_dir; - - info.format_version = 1000 * major_version; - - context = g_markup_parse_context_new (&metacity_theme_parser, - 0, &info, NULL); - - if (!g_markup_parse_context_parse (context, - text, - length, - error)) - goto out; - - if (!g_markup_parse_context_end_parse (context, error)) - goto out; - - retval = info.theme; - info.theme = NULL; - - out: - if (*error && !theme_error_is_fatal (*error)) - { - meta_topic (META_DEBUG_THEMES, "Failed to read theme from file %s: %s\n", - theme_file, (*error)->message); - } - - g_free (theme_filename); - g_free (theme_file); - g_free (text); - - if (context) - { - g_markup_parse_context_free (context); - parse_info_free (&info); - } - - return retval; -} - -static gboolean -keep_trying (GError **error) -{ - if (*error && !theme_error_is_fatal (*error)) - { - g_clear_error (error); - return TRUE; - } - - return FALSE; -} - -/** - * meta_theme_load: (skip) - * - */ -MetaTheme* -meta_theme_load (const char *theme_name, - GError **err) -{ - GError *error = NULL; - char *theme_dir; - MetaTheme *retval; - const gchar* const* xdg_data_dirs; - int major_version; - int i; - - retval = NULL; - - if (meta_is_debugging ()) - { - /* Try in themes in our source tree */ - /* We try all supported major versions from current to oldest */ - for (major_version = THEME_MAJOR_VERSION; (major_version > 0); major_version--) - { - theme_dir = g_build_filename ("./themes", theme_name, NULL); - retval = load_theme (theme_dir, theme_name, major_version, &error); - g_free (theme_dir); - if (!keep_trying (&error)) - goto out; - } - } - - /* We try all supported major versions from current to oldest */ - for (major_version = THEME_MAJOR_VERSION; (major_version > 0); major_version--) - { - /* We try first in home dir, XDG_DATA_DIRS, then system dir for themes */ - - /* Try home dir for themes */ - theme_dir = g_build_filename (g_get_home_dir (), - ".themes", - theme_name, - THEME_SUBDIR, - NULL); - - retval = load_theme (theme_dir, theme_name, major_version, &error); - g_free (theme_dir); - if (!keep_trying (&error)) - goto out; - - /* Try each XDG_DATA_DIRS for theme */ - xdg_data_dirs = g_get_system_data_dirs(); - for(i = 0; xdg_data_dirs[i] != NULL; i++) - { - theme_dir = g_build_filename (xdg_data_dirs[i], - "themes", - theme_name, - THEME_SUBDIR, - NULL); - - retval = load_theme (theme_dir, theme_name, major_version, &error); - g_free (theme_dir); - if (!keep_trying (&error)) - goto out; - } - - /* Look for themes in MUTTER_DATADIR */ - theme_dir = g_build_filename (MUTTER_DATADIR, - "themes", - theme_name, - THEME_SUBDIR, - NULL); - - retval = load_theme (theme_dir, theme_name, major_version, &error); - g_free (theme_dir); - if (!keep_trying (&error)) - goto out; - } - - out: - if (!error && !retval) - g_set_error (&error, META_THEME_ERROR, META_THEME_ERROR_FAILED, - _("Failed to find a valid file for theme %s\n"), - theme_name); - - if (error) - { - g_propagate_error (err, error); - } - - return retval; -} diff --git a/src/ui/theme-private.h b/src/ui/theme-private.h deleted file mode 100644 index 48538b278..000000000 --- a/src/ui/theme-private.h +++ /dev/null @@ -1,1138 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/* 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 - * 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. - */ - -#ifndef META_THEME_PRIVATE_H -#define META_THEME_PRIVATE_H - -#include <meta/boxes.h> -#include <meta/theme.h> -#include <meta/common.h> -#include <gtk/gtk.h> - -typedef struct _MetaThemeVariant MetaThemeVariant; - -/** - * MetaFrameStyle: (skip) - * - */ -typedef struct _MetaFrameStyle MetaFrameStyle; -/** - * MetaFrameStyleSet: (skip) - * - */ -typedef struct _MetaFrameStyleSet MetaFrameStyleSet; -/** - * MetaDrawOp: (skip) - * - */ -typedef struct _MetaDrawOp MetaDrawOp; -/** - * MetaDrawOpList: (skip) - * - */ -typedef struct _MetaDrawOpList MetaDrawOpList; -/** - * MetaGradientSpec: (skip) - * - */ -typedef struct _MetaGradientSpec MetaGradientSpec; -/** - * MetaAlphaGradientSpec: (skip) - * - */ -typedef struct _MetaAlphaGradientSpec MetaAlphaGradientSpec; -/** - * MetaColorSpec: (skip) - * - */ -typedef struct _MetaColorSpec MetaColorSpec; -/** - * MetaFrameLayout: (skip) - * - */ -typedef struct _MetaFrameLayout MetaFrameLayout; -/** - * MetaButtonSpace: (skip) - * - */ -typedef struct _MetaButtonSpace MetaButtonSpace; -/** - * MetaFrameGeometry: (skip) - * - */ -typedef struct _MetaFrameGeometry MetaFrameGeometry; - -/** - * MetaPositionExprEnv: (skip) - * - */ -typedef struct _MetaPositionExprEnv MetaPositionExprEnv; -/** - * MetaDrawInfo: (skip) - * - */ -typedef struct _MetaDrawInfo MetaDrawInfo; - -#define META_THEME_ERROR (g_quark_from_static_string ("meta-theme-error")) - -typedef enum -{ - 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; - -/** - * Whether a button's size is calculated from the area around it (aspect - * sizing) or is given as a fixed height and width in pixels (fixed sizing). - * - * \bug This could be done away with; see the comment at the top of - * MetaFrameLayout. - */ -typedef enum -{ - META_BUTTON_SIZING_ASPECT, - META_BUTTON_SIZING_FIXED, - META_BUTTON_SIZING_LAST -} MetaButtonSizing; - -/** - * Various parameters used to calculate the geometry of a frame. - * They are used inside a MetaFrameStyle. - * This corresponds closely to the <frame_geometry> tag in a theme file. - * - * \bug button_sizing isn't really necessary, because we could easily say - * that if button_aspect is zero, the height and width are fixed values. - * This would also mean that MetaButtonSizing didn't need to exist, and - * save code. - **/ -struct _MetaFrameLayout -{ - /** Reference count. */ - int refcount; - - /** Border of blue title region - * \bug (blue?!) - **/ - GtkBorder title_border; - - /** Right indent of buttons from edges of frame */ - int right_titlebar_edge; - /** Left indent of buttons from edges of frame */ - int left_titlebar_edge; - - /** - * Sizing rule of buttons, either META_BUTTON_SIZING_ASPECT - * (in which case button_aspect will be honoured, and - * button_width and button_height set from it), or - * META_BUTTON_SIZING_FIXED (in which case we read the width - * and height directly). - */ - MetaButtonSizing button_sizing; - - /** - * Ratio of height/width. Honoured only if - * button_sizing==META_BUTTON_SIZING_ASPECT. - * Otherwise we figure out the height from the button_border. - */ - double button_aspect; - - /** Width of a button; set even when we are using aspect sizing */ - int button_width; - - /** Height of a button; set even when we are using aspect sizing */ - int button_height; - - /** Space around buttons */ - GtkBorder button_border; - - /** scale factor for title text */ - double title_scale; - - /** Whether title text will be displayed */ - guint has_title : 1; - - /** Whether we should hide the buttons */ - guint hide_buttons : 1; -}; - -/** - * The computed size of a button (really just a way of tying its - * visible and clickable areas together). - * The reason for two different rectangles here is Fitts' law & maximized - * windows; see bug #97703 for more details. - */ -struct _MetaButtonSpace -{ - /** The screen area where the button's image is drawn */ - GdkRectangle visible; - /** The screen area where the button can be activated by clicking */ - GdkRectangle clickable; -}; - -/** - * Calculated actual geometry of the frame - */ -struct _MetaFrameGeometry -{ - MetaFrameBorders borders; - - int width; - int height; - - GdkRectangle title_rect; - - int left_titlebar_edge; - int right_titlebar_edge; - int top_titlebar_edge; - int bottom_titlebar_edge; - - /* used for a memset hack */ -#define ADDRESS_OF_BUTTON_RECTS(fgeom) (((char*)(fgeom)) + G_STRUCT_OFFSET (MetaFrameGeometry, close_rect)) -#define LENGTH_OF_BUTTON_RECTS (G_STRUCT_OFFSET (MetaFrameGeometry, right_single_background) + sizeof (GdkRectangle) - G_STRUCT_OFFSET (MetaFrameGeometry, close_rect)) - - /* The button rects (if changed adjust memset hack) */ - MetaButtonSpace close_rect; - MetaButtonSpace max_rect; - MetaButtonSpace min_rect; - MetaButtonSpace menu_rect; - MetaButtonSpace shade_rect; - MetaButtonSpace above_rect; - MetaButtonSpace stick_rect; - MetaButtonSpace unshade_rect; - MetaButtonSpace unabove_rect; - MetaButtonSpace unstick_rect; - -#define MAX_MIDDLE_BACKGROUNDS (MAX_BUTTONS_PER_CORNER - 2) - GdkRectangle left_left_background; - GdkRectangle left_middle_backgrounds[MAX_MIDDLE_BACKGROUNDS]; - GdkRectangle left_right_background; - GdkRectangle left_single_background; - GdkRectangle right_left_background; - GdkRectangle right_middle_backgrounds[MAX_MIDDLE_BACKGROUNDS]; - GdkRectangle right_right_background; - GdkRectangle right_single_background; - /* End of button rects (if changed adjust memset hack) */ - - /* Saved button layout */ - MetaButtonLayout button_layout; - int n_left_buttons; - int n_right_buttons; -}; - -typedef enum -{ - META_IMAGE_FILL_SCALE, /* default, needs to be all-bits-zero for g_new0 */ - META_IMAGE_FILL_TILE -} MetaImageFillType; - -typedef enum -{ - META_GRADIENT_VERTICAL, - META_GRADIENT_HORIZONTAL, - META_GRADIENT_DIAGONAL, - META_GRADIENT_LAST -} MetaGradientType; - -typedef enum -{ - META_COLOR_SPEC_BASIC, - META_COLOR_SPEC_GTK, - META_COLOR_SPEC_GTK_CUSTOM, - META_COLOR_SPEC_BLEND, - META_COLOR_SPEC_SHADE -} MetaColorSpecType; - -typedef enum -{ - META_GTK_COLOR_FG, - META_GTK_COLOR_BG, - META_GTK_COLOR_LIGHT, - META_GTK_COLOR_DARK, - META_GTK_COLOR_MID, - META_GTK_COLOR_TEXT, - META_GTK_COLOR_BASE, - META_GTK_COLOR_TEXT_AA, - META_GTK_COLOR_LAST -} MetaGtkColorComponent; - -struct _MetaColorSpec -{ - MetaColorSpecType type; - union - { - struct { - GdkRGBA color; - } basic; - struct { - MetaGtkColorComponent component; - GtkStateFlags state; - } gtk; - struct { - char *color_name; - MetaColorSpec *fallback; - } gtkcustom; - struct { - MetaColorSpec *foreground; - MetaColorSpec *background; - double alpha; - - GdkRGBA color; - } blend; - struct { - MetaColorSpec *base; - double factor; - - GdkRGBA color; - } shade; - } data; -}; - -struct _MetaGradientSpec -{ - MetaGradientType type; - GSList *color_specs; -}; - -struct _MetaAlphaGradientSpec -{ - MetaGradientType type; - unsigned char *alphas; - int n_alphas; -}; - -struct _MetaDrawInfo -{ - GdkPixbuf *mini_icon; - GdkPixbuf *icon; - PangoLayout *title_layout; - int title_layout_width; - int title_layout_height; - const MetaFrameGeometry *fgeom; -}; - -/** - * A drawing operation in our simple vector drawing language. - */ -typedef enum -{ - /** Basic drawing-- line */ - META_DRAW_LINE, - /** Basic drawing-- rectangle */ - META_DRAW_RECTANGLE, - /** Basic drawing-- arc */ - META_DRAW_ARC, - - /** Clip to a rectangle */ - META_DRAW_CLIP, - - /* Texture thingies */ - - /** Just a filled rectangle with alpha */ - META_DRAW_TINT, - META_DRAW_GRADIENT, - META_DRAW_IMAGE, - - /** GTK theme engine stuff */ - META_DRAW_GTK_ARROW, - META_DRAW_GTK_BOX, - 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, - /** tiled draw op list */ - META_DRAW_TILE -} MetaDrawType; - -typedef enum -{ - POS_TOKEN_INT, - POS_TOKEN_DOUBLE, - POS_TOKEN_OPERATOR, - POS_TOKEN_VARIABLE, - POS_TOKEN_OPEN_PAREN, - POS_TOKEN_CLOSE_PAREN -} PosTokenType; - -typedef enum -{ - POS_OP_NONE, - POS_OP_ADD, - POS_OP_SUBTRACT, - POS_OP_MULTIPLY, - POS_OP_DIVIDE, - POS_OP_MOD, - POS_OP_MAX, - POS_OP_MIN -} PosOperatorType; - -/** - * A token, as output by the tokeniser. - * - * \ingroup tokenizer - */ -typedef struct -{ - PosTokenType type; - - union - { - struct { - int val; - } i; - - struct { - double val; - } d; - - struct { - PosOperatorType op; - } o; - - struct { - char *name; - GQuark name_quark; - } v; - - } d; -} PosToken; - -/** - * MetaDrawSpec: (skip) - * - * A computed expression in our simple vector drawing language. - * While it appears to take the form of a tree, this is actually - * merely a list; concerns such as precedence of operators are - * currently recomputed on every recalculation. - * - * Created by meta_draw_spec_new(), destroyed by meta_draw_spec_free(). - * pos_eval() fills this with ...FIXME. Are tokens a tree or a list? - * \ingroup parser - */ -typedef struct _MetaDrawSpec MetaDrawSpec; -struct _MetaDrawSpec -{ - /** - * If this spec is constant, this is the value of the constant; - * otherwise it is zero. - */ - int value; - - /** A list of tokens in the expression. */ - PosToken *tokens; - - /** How many tokens are in the tokens list. */ - int n_tokens; - - /** Does the expression contain any variables? */ - gboolean constant : 1; -}; - -/** - * A single drawing operation in our simple vector drawing language. - */ -struct _MetaDrawOp -{ - MetaDrawType type; - - /* Positions are strings because they can be expressions */ - union - { - struct { - MetaColorSpec *color_spec; - int dash_on_length; - int dash_off_length; - int width; - MetaDrawSpec *x1; - MetaDrawSpec *y1; - MetaDrawSpec *x2; - MetaDrawSpec *y2; - } line; - - struct { - MetaColorSpec *color_spec; - gboolean filled; - MetaDrawSpec *x; - MetaDrawSpec *y; - MetaDrawSpec *width; - MetaDrawSpec *height; - } rectangle; - - struct { - MetaColorSpec *color_spec; - gboolean filled; - MetaDrawSpec *x; - MetaDrawSpec *y; - MetaDrawSpec *width; - MetaDrawSpec *height; - double start_angle; - double extent_angle; - } arc; - - struct { - MetaDrawSpec *x; - MetaDrawSpec *y; - MetaDrawSpec *width; - MetaDrawSpec *height; - } clip; - - struct { - MetaColorSpec *color_spec; - MetaAlphaGradientSpec *alpha_spec; - MetaDrawSpec *x; - MetaDrawSpec *y; - MetaDrawSpec *width; - MetaDrawSpec *height; - } tint; - - struct { - MetaGradientSpec *gradient_spec; - MetaAlphaGradientSpec *alpha_spec; - MetaDrawSpec *x; - MetaDrawSpec *y; - MetaDrawSpec *width; - MetaDrawSpec *height; - } gradient; - - struct { - GdkPixbuf *pixbuf; - MetaDrawSpec *x; - MetaDrawSpec *y; - MetaDrawSpec *width; - MetaDrawSpec *height; - - MetaImageFillType fill_type; - unsigned int vertical_stripes : 1; - unsigned int horizontal_stripes : 1; - } image; - - struct { - GtkStateFlags state; - GtkShadowType shadow; - GtkArrowType arrow; - gboolean filled; - - MetaDrawSpec *x; - MetaDrawSpec *y; - MetaDrawSpec *width; - MetaDrawSpec *height; - } gtk_arrow; - - struct { - GtkStateFlags state; - GtkShadowType shadow; - MetaDrawSpec *x; - MetaDrawSpec *y; - MetaDrawSpec *width; - MetaDrawSpec *height; - } gtk_box; - - struct { - GtkStateFlags state; - MetaDrawSpec *x; - MetaDrawSpec *y1; - MetaDrawSpec *y2; - } gtk_vline; - - struct { - MetaDrawSpec *x; - MetaDrawSpec *y; - MetaDrawSpec *width; - MetaDrawSpec *height; - MetaImageFillType fill_type; - } icon; - - struct { - MetaColorSpec *color_spec; - MetaDrawSpec *x; - MetaDrawSpec *y; - MetaDrawSpec *ellipsize_width; - } title; - - struct { - MetaDrawOpList *op_list; - MetaDrawSpec *x; - MetaDrawSpec *y; - MetaDrawSpec *width; - MetaDrawSpec *height; - } op_list; - - struct { - MetaDrawOpList *op_list; - MetaDrawSpec *x; - MetaDrawSpec *y; - MetaDrawSpec *width; - MetaDrawSpec *height; - MetaDrawSpec *tile_xoffset; - MetaDrawSpec *tile_yoffset; - MetaDrawSpec *tile_width; - MetaDrawSpec *tile_height; - } tile; - - } data; -}; - -/** - * A list of MetaDrawOp objects. Maintains a reference count. - * Grows as necessary and allows the allocation of unused spaces - * to keep reallocations to a minimum. - * - * \bug Do we really win anything from not using the equivalent - * GLib structures? - */ -struct _MetaDrawOpList -{ - int refcount; - MetaDrawOp **ops; - int n_ops; - int n_allocated; -}; - -typedef enum -{ - META_BUTTON_STATE_NORMAL, - META_BUTTON_STATE_PRESSED, - META_BUTTON_STATE_PRELIGHT, - META_BUTTON_STATE_LAST -} MetaButtonState; - -typedef enum -{ - /* Ordered so that background is drawn first */ - META_BUTTON_TYPE_LEFT_LEFT_BACKGROUND, - META_BUTTON_TYPE_LEFT_MIDDLE_BACKGROUND, - META_BUTTON_TYPE_LEFT_RIGHT_BACKGROUND, - META_BUTTON_TYPE_LEFT_SINGLE_BACKGROUND, - META_BUTTON_TYPE_RIGHT_LEFT_BACKGROUND, - META_BUTTON_TYPE_RIGHT_MIDDLE_BACKGROUND, - META_BUTTON_TYPE_RIGHT_RIGHT_BACKGROUND, - META_BUTTON_TYPE_RIGHT_SINGLE_BACKGROUND, - META_BUTTON_TYPE_CLOSE, - META_BUTTON_TYPE_MAXIMIZE, - META_BUTTON_TYPE_MINIMIZE, - META_BUTTON_TYPE_MENU, - META_BUTTON_TYPE_SHADE, - META_BUTTON_TYPE_ABOVE, - META_BUTTON_TYPE_STICK, - META_BUTTON_TYPE_UNSHADE, - META_BUTTON_TYPE_UNABOVE, - META_BUTTON_TYPE_UNSTICK, - META_BUTTON_TYPE_LAST -} MetaButtonType; - -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. - * - * On the corners, horizontal pieces are arbitrarily given the - * corner area: - * - * ===== |==== - * | | - * | rather than | - * - */ - - /* entire frame */ - META_FRAME_PIECE_ENTIRE_BACKGROUND, - /* entire titlebar background */ - META_FRAME_PIECE_TITLEBAR, - /* portion of the titlebar background inside the titlebar - * background edges - */ - META_FRAME_PIECE_TITLEBAR_MIDDLE, - /* left end of titlebar */ - META_FRAME_PIECE_LEFT_TITLEBAR_EDGE, - /* right end of titlebar */ - META_FRAME_PIECE_RIGHT_TITLEBAR_EDGE, - /* top edge of titlebar */ - META_FRAME_PIECE_TOP_TITLEBAR_EDGE, - /* bottom edge of titlebar */ - META_FRAME_PIECE_BOTTOM_TITLEBAR_EDGE, - /* render over title background (text area) */ - META_FRAME_PIECE_TITLE, - /* left edge of the frame */ - META_FRAME_PIECE_LEFT_EDGE, - /* right edge of the frame */ - META_FRAME_PIECE_RIGHT_EDGE, - /* bottom edge of the frame */ - META_FRAME_PIECE_BOTTOM_EDGE, - /* place over entire frame, after drawing everything else */ - META_FRAME_PIECE_OVERLAY, - /* Used to get size of the enum */ - META_FRAME_PIECE_LAST -} MetaFramePiece; - -/** - * How to draw a frame in a particular state (say, a focussed, non-maximised, - * resizable frame). This corresponds closely to the <frame_style> tag - * in a theme file. - */ -struct _MetaFrameStyle -{ - /** Reference count. */ - int refcount; - /** - * Parent style. - * Settings which are unspecified here will be taken from there. - */ - MetaFrameStyle *parent; - /** Operations for drawing each kind of button in each state. */ - MetaDrawOpList *buttons[META_BUTTON_TYPE_LAST][META_BUTTON_STATE_LAST]; - /** Operations for drawing each piece of the frame. */ - MetaDrawOpList *pieces[META_FRAME_PIECE_LAST]; - /** - * Details such as the height and width of each edge, the corner rounding, - * and the aspect ratio of the buttons. - */ - MetaFrameLayout *layout; - /** - * Background colour of the window. Only present in theme formats - * 2 and above. Can be NULL to use the standard GTK theme engine. - */ - MetaColorSpec *window_background_color; - /** - * Transparency of the window background. 0=transparent; 255=opaque. - */ - guint8 window_background_alpha; -}; - -/* Kinds of frame... - * - * normal -> noresize / vert only / horz only / both - * focused / unfocused - * max -> focused / unfocused - * shaded -> focused / unfocused - * max/shaded -> focused / unfocused - * - * so 4 states with 8 sub-states in one, 2 sub-states in the other 3, - * meaning 14 total - * - * 14 window states times 7 or 8 window types. Except some - * window types never get a frame so that narrows it down a bit. - * - */ -typedef enum -{ - META_FRAME_STATE_NORMAL, - META_FRAME_STATE_MAXIMIZED, - META_FRAME_STATE_TILED_LEFT, - META_FRAME_STATE_TILED_RIGHT, - META_FRAME_STATE_SHADED, - META_FRAME_STATE_MAXIMIZED_AND_SHADED, - META_FRAME_STATE_TILED_LEFT_AND_SHADED, - META_FRAME_STATE_TILED_RIGHT_AND_SHADED, - META_FRAME_STATE_LAST -} MetaFrameState; - -typedef enum -{ - META_FRAME_RESIZE_NONE, - META_FRAME_RESIZE_VERTICAL, - META_FRAME_RESIZE_HORIZONTAL, - META_FRAME_RESIZE_BOTH, - META_FRAME_RESIZE_LAST -} MetaFrameResize; - -typedef enum -{ - META_FRAME_FOCUS_NO, - META_FRAME_FOCUS_YES, - META_FRAME_FOCUS_LAST -} MetaFrameFocus; - -/** - * How to draw frames at different times: when it's maximised or not, shaded - * or not, when it's focussed or not, and (for non-maximised windows), when - * it can be horizontally or vertically resized, both, or neither. - * Not all window types actually get a frame. - * - * A theme contains one of these objects for each type of window (each - * MetaFrameType), that is, normal, dialogue (modal and non-modal), etc. - * - * This corresponds closely to the <frame_style_set> tag in a theme file. - */ -struct _MetaFrameStyleSet -{ - int refcount; - MetaFrameStyleSet *parent; - MetaFrameStyle *normal_styles[META_FRAME_RESIZE_LAST][META_FRAME_FOCUS_LAST]; - MetaFrameStyle *maximized_styles[META_FRAME_FOCUS_LAST]; - MetaFrameStyle *tiled_left_styles[META_FRAME_FOCUS_LAST]; - MetaFrameStyle *tiled_right_styles[META_FRAME_FOCUS_LAST]; - MetaFrameStyle *shaded_styles[META_FRAME_RESIZE_LAST][META_FRAME_FOCUS_LAST]; - MetaFrameStyle *maximized_and_shaded_styles[META_FRAME_FOCUS_LAST]; - MetaFrameStyle *tiled_left_and_shaded_styles[META_FRAME_FOCUS_LAST]; - MetaFrameStyle *tiled_right_and_shaded_styles[META_FRAME_FOCUS_LAST]; -}; - -/** - * A theme. This is a singleton class which groups all settings from a theme - * on disk together. - * - * \bug It is rather useless to keep the metadata fields in core, I think. - */ -struct _MetaTheme -{ - /** Name of the theme (on disk), e.g. "Crux" */ - char *name; - /** Path to the files associated with the theme */ - char *dirname; - /** - * Filename of the XML theme file. - * \bug Kept lying around for no discernable reason. - */ - char *filename; - /** Metadata: Human-readable name of the theme. */ - char *readable_name; - /** Metadata: Author of the theme. */ - char *author; - /** Metadata: Copyright holder. */ - char *copyright; - /** Metadata: Date of the theme. */ - char *date; - /** Metadata: Description of the theme. */ - char *description; - /** Version of the theme format. Older versions cannot use the features - * of newer versions even if they think they can (this is to allow forward - * and backward compatibility. - */ - guint format_version; - - /** Symbol table of integer constants. */ - GHashTable *integer_constants; - /** Symbol table of float constants. */ - GHashTable *float_constants; - /** - * Symbol table of colour constants (hex triples, and triples - * plus alpha). - * */ - GHashTable *color_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]; - - GQuark quark_width; - GQuark quark_height; - GQuark quark_object_width; - GQuark quark_object_height; - GQuark quark_left_width; - GQuark quark_right_width; - GQuark quark_top_height; - GQuark quark_bottom_height; - GQuark quark_mini_icon_width; - GQuark quark_mini_icon_height; - GQuark quark_icon_width; - GQuark quark_icon_height; - GQuark quark_title_width; - GQuark quark_title_height; - GQuark quark_frame_x_center; - GQuark quark_frame_y_center; - - GHashTable *theme_variants; - MetaThemeVariant *normal_variant; -}; - -struct _MetaThemeVariant -{ - MetaTheme *theme; - GtkStyleContext *style_context; -}; - -struct _MetaPositionExprEnv -{ - MetaRectangle rect; - /* size of an object being drawn, if it has a natural size */ - int object_width; - int object_height; - /* global object sizes, always available */ - int left_width; - int right_width; - int top_height; - int bottom_height; - int title_width; - int title_height; - int frame_x_center; - int frame_y_center; - int mini_icon_width; - int mini_icon_height; - int icon_width; - int icon_height; - /* Theme so we can look up constants */ - MetaTheme *theme; -}; - -MetaFrameLayout* meta_frame_layout_new (void); -MetaFrameLayout* meta_frame_layout_copy (const MetaFrameLayout *src); -void meta_frame_layout_ref (MetaFrameLayout *layout); -void meta_frame_layout_unref (MetaFrameLayout *layout); -gboolean meta_frame_layout_validate (const MetaFrameLayout *layout, - GError **error); - -gboolean meta_parse_position_expression (MetaDrawSpec *spec, - const MetaPositionExprEnv *env, - int *x_return, - int *y_return, - GError **err); -gboolean meta_parse_size_expression (MetaDrawSpec *spec, - const MetaPositionExprEnv *env, - int *val_return, - GError **err); - -MetaDrawSpec* meta_draw_spec_new (MetaTheme *theme, - const char *expr, - GError **error); -void meta_draw_spec_free (MetaDrawSpec *spec); - -MetaColorSpec* meta_color_spec_new (MetaColorSpecType type); -MetaColorSpec* meta_color_spec_new_from_string (const char *str, - GError **err); -MetaColorSpec* meta_color_spec_new_gtk (MetaGtkColorComponent component, - GtkStateFlags state); -void meta_color_spec_free (MetaColorSpec *spec); -void meta_color_spec_render (MetaColorSpec *spec, - GtkStyleContext *style_gtk, - GdkRGBA *color); - - -MetaDrawOp* meta_draw_op_new (MetaDrawType type); -void meta_draw_op_free (MetaDrawOp *op); - -void meta_draw_op_draw_with_style (const MetaDrawOp *op, - GtkStyleContext *style_gtk, - cairo_t *cr, - const MetaDrawInfo *info, - /* logical region being drawn */ - MetaRectangle logical_region); - -MetaDrawOpList* meta_draw_op_list_new (int n_preallocs); -void meta_draw_op_list_ref (MetaDrawOpList *op_list); -void meta_draw_op_list_unref (MetaDrawOpList *op_list); -void meta_draw_op_list_draw_with_style (const MetaDrawOpList *op_list, - GtkStyleContext *style_gtk, - cairo_t *cr, - const MetaDrawInfo *info, - MetaRectangle rect); -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); -void meta_gradient_spec_render (const MetaGradientSpec *spec, - const MetaAlphaGradientSpec *alpha_spec, - cairo_t *cr, - GtkStyleContext *style, - int x, - int y, - int width, - int height); -gboolean meta_gradient_spec_validate (MetaGradientSpec *spec, - GError **error); - -MetaAlphaGradientSpec* meta_alpha_gradient_spec_new (MetaGradientType type, - int n_alphas); -void meta_alpha_gradient_spec_free (MetaAlphaGradientSpec *spec); - - -MetaFrameStyle* meta_frame_style_new (MetaFrameStyle *parent); -void meta_frame_style_ref (MetaFrameStyle *style); -void meta_frame_style_unref (MetaFrameStyle *style); - - -gboolean meta_frame_style_validate (MetaFrameStyle *style, - guint current_theme_version, - 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); - -GdkPixbuf* meta_theme_load_image (MetaTheme *theme, - const char *filename, - guint size_of_theme_icons, - GError **error); - -void meta_theme_render_background (GtkStyleContext *style, - cairo_t *cr, - MetaFrameFlags flags, - const MetaFrameGeometry *fgeom); - -void meta_theme_draw_frame_with_style (MetaTheme *theme, - GtkStyleContext *style_gtk, - cairo_t *cr, - MetaFrameType type, - MetaFrameFlags flags, - int client_width, - int client_height, - const MetaButtonLayout *button_layout, - MetaButtonState button_states[META_BUTTON_TYPE_LAST], - GdkPixbuf *mini_icon, - GdkPixbuf *icon); - -void meta_theme_get_frame_borders (MetaTheme *theme, - GtkStyleContext *style_context, - MetaFrameType type, - MetaFrameFlags flags, - MetaFrameBorders *borders); - -void meta_theme_calc_geometry (MetaTheme *theme, - GtkStyleContext *ctx, - MetaFrameType type, - MetaFrameFlags flags, - int client_width, - int client_height, - const MetaButtonLayout *button_layout, - 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); - -gboolean meta_theme_define_color_constant (MetaTheme *theme, - const char *name, - const char *value, - GError **error); -gboolean meta_theme_lookup_color_constant (MetaTheme *theme, - const char *name, - char **value); - -gboolean meta_theme_replace_constants (MetaTheme *theme, - PosToken *tokens, - int n_tokens, - GError **err); - -/* 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, - MetaTheme *theme); -const char* meta_button_type_to_string (MetaButtonType 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); -MetaGradientType meta_gradient_type_from_string (const char *str); -const char* meta_gradient_type_to_string (MetaGradientType type); -GtkStateFlags meta_gtk_state_from_string (const char *str); -const char* meta_gtk_state_to_string (GtkStateFlags 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); -MetaImageFillType meta_image_fill_type_from_string (const char *str); -const char* meta_image_fill_type_to_string (MetaImageFillType fill_type); - -void meta_gtk_style_get_light_color (GtkStyleContext *style, - GtkStateFlags state, - GdkRGBA *color); -void meta_gtk_style_get_dark_color (GtkStyleContext *style, - GtkStateFlags state, - GdkRGBA *color); - -guint meta_theme_earliest_version_with_button (MetaButtonType type); - - -MetaThemeVariant * meta_theme_get_variant (MetaTheme *theme, - gchar *variant); - -#define META_THEME_ALLOWS(theme, feature) (theme->format_version >= feature) - -/* What version of the theme file format were various features introduced in? */ -#define META_THEME_SHADE_STICK_ABOVE_BUTTONS 2 -#define META_THEME_UBIQUITOUS_CONSTANTS 2 -#define META_THEME_VARIED_ROUND_CORNERS 2 -#define META_THEME_IMAGES_FROM_ICON_THEMES 2 -#define META_THEME_UNRESIZABLE_SHADED_STYLES 2 -#define META_THEME_DEGREES_IN_ARCS 2 -#define META_THEME_HIDDEN_BUTTONS 2 -#define META_THEME_COLOR_CONSTANTS 2 -#define META_THEME_FRAME_BACKGROUNDS 2 - -#endif /* META_THEME_PRIVATE_H */ diff --git a/src/ui/theme-viewer.c b/src/ui/theme-viewer.c deleted file mode 100644 index 4ad446161..000000000 --- a/src/ui/theme-viewer.c +++ /dev/null @@ -1,1330 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/* 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 - * 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 <meta/util.h> -#include <meta/theme.h> -#include "theme-private.h" -#include <meta/preview-widget.h> -#include <gtk/gtk.h> -#include <time.h> -#include <stdlib.h> -#include <string.h> - -#include <libintl.h> -#define _(x) dgettext (GETTEXT_PACKAGE, x) -#define N_(x) x - -/* We need to compute all different button arrangements - * in terms of button location. We don't care about - * different arrangements in terms of button function. - * - * So if dups are allowed, from 0-4 buttons on the left, from 0-4 on - * the right, 5x5=25 combinations. - * - * If no dups, 0-4 on left determines the number on the right plus - * we have a special case for the "no buttons on either side" case. - */ -#ifndef ALLOW_DUPLICATE_BUTTONS -#define BUTTON_LAYOUT_COMBINATIONS (MAX_BUTTONS_PER_CORNER + 1 + 1) -#else -#define BUTTON_LAYOUT_COMBINATIONS ((MAX_BUTTONS_PER_CORNER+1)*(MAX_BUTTONS_PER_CORNER+1)) -#endif - -enum -{ - FONT_SIZE_SMALL, - FONT_SIZE_NORMAL, - FONT_SIZE_LARGE, - FONT_SIZE_LAST -}; - -static MetaTheme *global_theme = NULL; -static GtkWidget *previews[META_FRAME_TYPE_LAST*FONT_SIZE_LAST + BUTTON_LAYOUT_COMBINATIONS] = { NULL, }; -static double milliseconds_to_draw_frame = 0.0; - -static void run_position_expression_tests (void); -#if 0 -static void run_position_expression_timings (void); -#endif -static void run_theme_benchmark (void); - - -static const gchar *menu_item_string = - "<ui>\n" - "<menubar>\n" - "<menu name='Windows' action='Windows'>\n" - "<menuitem name='Dialog' action='Dialog'/>\n" - "<menuitem name='Modal dialog' action='Modal dialog'/>\n" - "<menuitem name='Utility' action='Utility'/>\n" - "<menuitem name='Splashscreen' action='Splashscreen'/>\n" - "<menuitem name='Top dock' action='Top dock'/>\n" - "<menuitem name='Bottom dock' action='Bottom dock'/>\n" - "<menuitem name='Left dock' action='Left dock'/>\n" - "<menuitem name='Right dock' action='Right dock'/>\n" - "<menuitem name='Desktop' action='Desktop'/>\n" - "</menu>\n" - "</menubar>\n" - "<toolbar>\n" - "<separator/>\n" - "<toolitem name='New' action='New'/>\n" - "<toolitem name='Open' action='Open'/>\n" - "<toolitem name='Quit' action='Quit'/>\n" - "<separator/>\n" - "</toolbar>\n" - "</ui>\n"; - -static GtkActionEntry menu_items[] = -{ - { "Windows", NULL, N_("_Windows"), NULL, NULL, NULL }, - { "Dialog", NULL, N_("_Dialog"), "<control>d", NULL, NULL }, - { "Modal dialog", NULL, N_("_Modal dialog"), NULL, NULL, NULL }, - { "Utility", NULL, N_("_Utility"), "<control>u", NULL, NULL }, - { "Splashscreen", NULL, N_("_Splashscreen"), "<control>s", NULL, NULL }, - { "Top dock", NULL, N_("_Top dock"), NULL, NULL, NULL }, - { "Bottom dock", NULL, N_("_Bottom dock"), NULL, NULL, NULL }, - { "Left dock", NULL, N_("_Left dock"), NULL, NULL, NULL }, - { "Right dock", NULL, N_("_Right dock"), NULL, NULL, NULL }, - { "All docks", NULL, N_("_All docks"), NULL, NULL, NULL }, - { "Desktop", NULL, N_("Des_ktop"), NULL, NULL, NULL } -}; - -static GtkActionEntry tool_items[] = -{ - { "New", GTK_STOCK_NEW, NULL, NULL, - N_("Open another one of these windows"), NULL }, - { "Open", GTK_STOCK_OPEN, NULL, NULL, - N_("This is a demo button with an 'open' icon"), NULL }, - { "Quit", GTK_STOCK_QUIT, NULL, NULL, - N_("This is a demo button with a 'quit' icon"), NULL } -}; - -static GtkWidget * -normal_contents (void) -{ - GtkWidget *grid; - GtkWidget *statusbar; - GtkWidget *contents; - GtkWidget *sw; - GtkActionGroup *action_group; - GtkUIManager *ui_manager; - - grid = gtk_grid_new (); - - /* Create the menubar - */ - - action_group = gtk_action_group_new ("mainmenu"); - gtk_action_group_add_actions (action_group, - menu_items, - G_N_ELEMENTS (menu_items), - NULL); - gtk_action_group_add_actions (action_group, - tool_items, - G_N_ELEMENTS (tool_items), - NULL); - - ui_manager = gtk_ui_manager_new (); - - gtk_ui_manager_insert_action_group (ui_manager, action_group, 0); - - /* create menu items */ - gtk_ui_manager_add_ui_from_string (ui_manager, menu_item_string, -1, NULL); - - gtk_grid_attach (GTK_GRID (grid), - gtk_ui_manager_get_widget (ui_manager, "/ui/menubar"), - 0, 0, 1, 1); - - gtk_widget_set_hexpand (gtk_ui_manager_get_widget (ui_manager, "/ui/menubar"), - TRUE); - - /* Create the toolbar - */ - gtk_grid_attach (GTK_GRID (grid), - gtk_ui_manager_get_widget (ui_manager, "/ui/toolbar"), - 0, 1, 1, 1); - - gtk_widget_set_hexpand (gtk_ui_manager_get_widget (ui_manager, "/ui/toolbar"), - TRUE); - - /* Create document - */ - - sw = gtk_scrolled_window_new (NULL, NULL); - - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), - GTK_POLICY_AUTOMATIC, - GTK_POLICY_AUTOMATIC); - - gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), - GTK_SHADOW_IN); - - gtk_grid_attach (GTK_GRID (grid), - sw, - 0, 2, 1, 1); - - gtk_widget_set_hexpand (sw, TRUE); - gtk_widget_set_vexpand (sw, TRUE); - - contents = gtk_text_view_new (); - gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (contents), - PANGO_WRAP_WORD); - - gtk_container_add (GTK_CONTAINER (sw), - contents); - - /* Create statusbar */ - - statusbar = gtk_statusbar_new (); - gtk_grid_attach (GTK_GRID (grid), - statusbar, - 0, 3, 1, 1); - - gtk_widget_set_hexpand (statusbar, TRUE); - - gtk_widget_show_all (grid); - - g_object_unref (ui_manager); - - return grid; -} - -static void -update_spacings (GtkWidget *vbox, - GtkWidget *action_area) -{ - gtk_container_set_border_width (GTK_CONTAINER (vbox), 2); - gtk_box_set_spacing (GTK_BOX (action_area), 10); - gtk_container_set_border_width (GTK_CONTAINER (action_area), 5); -} - -static GtkWidget* -dialog_contents (void) -{ - GtkWidget *vbox; - GtkWidget *hbox; - GtkWidget *action_area; - GtkWidget *label; - GtkWidget *image; - GtkWidget *button; - - vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); - - action_area = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL); - - gtk_button_box_set_layout (GTK_BUTTON_BOX (action_area), - GTK_BUTTONBOX_END); - - button = gtk_button_new_from_stock (GTK_STOCK_OK); - gtk_box_pack_end (GTK_BOX (action_area), - button, - FALSE, TRUE, 0); - - gtk_box_pack_end (GTK_BOX (vbox), action_area, - FALSE, TRUE, 0); - - update_spacings (vbox, action_area); - - label = gtk_label_new (_("This is a sample message in a sample dialog")); - image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_INFO, - GTK_ICON_SIZE_DIALOG); - gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0); - - gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); - gtk_label_set_selectable (GTK_LABEL (label), TRUE); - - hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); - - gtk_box_pack_start (GTK_BOX (hbox), image, - FALSE, FALSE, 0); - - gtk_box_pack_start (GTK_BOX (hbox), label, - TRUE, TRUE, 0); - - gtk_box_pack_start (GTK_BOX (vbox), - hbox, - FALSE, FALSE, 0); - - gtk_widget_show_all (vbox); - - return vbox; -} - -static GtkWidget* -utility_contents (void) -{ - GtkWidget *grid; - GtkWidget *button; - int i, j; - - grid = gtk_grid_new (); - - i = 0; - while (i < 3) - { - j = 0; - while (j < 4) - { - char *str; - - str = g_strdup_printf ("_%c", (char) ('A' + 4*i + j)); - - button = gtk_button_new_with_mnemonic (str); - - g_free (str); - - gtk_grid_attach (GTK_GRID (grid), - button, - i, j, 1, 1); - - ++j; - } - - ++i; - } - - gtk_widget_show_all (grid); - - return grid; -} - -static GtkWidget* -menu_contents (void) -{ - GtkWidget *vbox; - GtkWidget *mi; - int i; - GtkWidget *frame; - - frame = gtk_frame_new (NULL); - gtk_frame_set_shadow_type (GTK_FRAME (frame), - GTK_SHADOW_OUT); - - vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); - - i = 0; - while (i < 10) - { - char *str = g_strdup_printf (_("Fake menu item %d\n"), i + 1); - mi = gtk_label_new (str); - gtk_misc_set_alignment (GTK_MISC (mi), 0.0, 0.5); - g_free (str); - gtk_box_pack_start (GTK_BOX (vbox), mi, FALSE, FALSE, 0); - - ++i; - } - - gtk_container_add (GTK_CONTAINER (frame), vbox); - - gtk_widget_show_all (frame); - - return frame; -} - -static GtkWidget* -border_only_contents (void) -{ - GtkWidget *event_box; - GtkWidget *vbox; - GtkWidget *w; - GdkRGBA color; - - event_box = gtk_event_box_new (); - - color.red = 0.6; - color.green = 0; - color.blue = 0.6; - color.alpha = 1.0; - gtk_widget_override_background_color (event_box, 0, &color); - - vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); - gtk_container_set_border_width (GTK_CONTAINER (vbox), 3); - - w = gtk_label_new (_("Border-only window")); - gtk_box_pack_start (GTK_BOX (vbox), w, FALSE, FALSE, 0); - w = gtk_button_new_with_label (_("Bar")); - gtk_box_pack_start (GTK_BOX (vbox), w, FALSE, FALSE, 0); - - gtk_container_add (GTK_CONTAINER (event_box), vbox); - - gtk_widget_show_all (event_box); - - return event_box; -} - -static GtkWidget* -get_window_contents (MetaFrameType type, - const char **title) -{ - switch (type) - { - case META_FRAME_TYPE_NORMAL: - *title = _("Normal Application Window"); - return normal_contents (); - - case META_FRAME_TYPE_DIALOG: - *title = _("Dialog Box"); - return dialog_contents (); - - case META_FRAME_TYPE_MODAL_DIALOG: - *title = _("Modal Dialog Box"); - return dialog_contents (); - - case META_FRAME_TYPE_UTILITY: - *title = _("Utility Palette"); - return utility_contents (); - - case META_FRAME_TYPE_MENU: - *title = _("Torn-off Menu"); - return menu_contents (); - - case META_FRAME_TYPE_BORDER: - *title = _("Border"); - return border_only_contents (); - - case META_FRAME_TYPE_ATTACHED: - *title = _("Attached Modal Dialog"); - return dialog_contents (); - - case META_FRAME_TYPE_LAST: - g_assert_not_reached (); - break; - } - - return NULL; -} - -static MetaFrameFlags -get_window_flags (MetaFrameType type) -{ - MetaFrameFlags flags; - - flags = 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; - - switch (type) - { - case META_FRAME_TYPE_NORMAL: - break; - - case META_FRAME_TYPE_DIALOG: - case META_FRAME_TYPE_MODAL_DIALOG: - flags &= ~(META_FRAME_ALLOWS_MINIMIZE | - META_FRAME_ALLOWS_MAXIMIZE); - break; - - case META_FRAME_TYPE_UTILITY: - flags &= ~(META_FRAME_ALLOWS_MINIMIZE | - META_FRAME_ALLOWS_MAXIMIZE); - break; - - case META_FRAME_TYPE_MENU: - flags &= ~(META_FRAME_ALLOWS_MINIMIZE | - META_FRAME_ALLOWS_MAXIMIZE); - break; - - case META_FRAME_TYPE_BORDER: - break; - - case META_FRAME_TYPE_ATTACHED: - break; - - case META_FRAME_TYPE_LAST: - g_assert_not_reached (); - break; - } - - return flags; -} - -static GtkWidget* -preview_collection (int font_size, - const PangoFontDescription *base_desc) -{ - GtkWidget *box; - GtkWidget *sw; - GdkRGBA desktop_color; - int i; - GtkWidget *eventbox; - - sw = gtk_scrolled_window_new (NULL, NULL); - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), - GTK_POLICY_AUTOMATIC, - GTK_POLICY_AUTOMATIC); - - box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); - gtk_box_set_spacing (GTK_BOX (box), 20); - gtk_container_set_border_width (GTK_CONTAINER (box), 20); - - eventbox = gtk_event_box_new (); - gtk_container_add (GTK_CONTAINER (eventbox), box); - - gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (sw), eventbox); - - desktop_color.red = 0.32; - desktop_color.green = 0.46; - desktop_color.blue = 0.65; - desktop_color.alpha = 1.0; - - gtk_widget_override_background_color (eventbox, 0, &desktop_color); - - i = 0; - while (i < META_FRAME_TYPE_LAST) - { - const char *title = NULL; - GtkWidget *contents; - GtkWidget *align; - double xalign, yalign; - GtkWidget *eventbox2; - GtkWidget *preview; - PangoFontDescription *font_desc; - double scale; - - eventbox2 = gtk_event_box_new (); - - preview = meta_preview_new (); - - gtk_container_add (GTK_CONTAINER (eventbox2), preview); - - meta_preview_set_frame_type (META_PREVIEW (preview), i); - meta_preview_set_frame_flags (META_PREVIEW (preview), - get_window_flags (i)); - - meta_preview_set_theme (META_PREVIEW (preview), global_theme); - - contents = get_window_contents (i, &title); - - meta_preview_set_title (META_PREVIEW (preview), title); - - gtk_container_add (GTK_CONTAINER (preview), contents); - - if (i == META_FRAME_TYPE_MENU) - { - xalign = 0.0; - yalign = 0.0; - } - else - { - xalign = 0.5; - yalign = 0.5; - } - - align = gtk_alignment_new (0.0, 0.0, xalign, yalign); - gtk_container_add (GTK_CONTAINER (align), eventbox2); - - gtk_box_pack_start (GTK_BOX (box), align, TRUE, TRUE, 0); - - switch (font_size) - { - case FONT_SIZE_SMALL: - scale = PANGO_SCALE_XX_SMALL; - break; - case FONT_SIZE_LARGE: - scale = PANGO_SCALE_XX_LARGE; - break; - default: - scale = 1.0; - break; - } - - if (scale != 1.0) - { - font_desc = pango_font_description_new (); - - pango_font_description_set_size (font_desc, - MAX (pango_font_description_get_size (base_desc) * scale, 1)); - - gtk_widget_modify_font (preview, font_desc); - - pango_font_description_free (font_desc); - } - - previews[font_size*META_FRAME_TYPE_LAST + i] = preview; - - ++i; - } - - return sw; -} - -static MetaButtonLayout different_layouts[BUTTON_LAYOUT_COMBINATIONS]; - -static void -init_layouts (void) -{ - int i; - - /* Blank out all the layouts */ - i = 0; - while (i < (int) G_N_ELEMENTS (different_layouts)) - { - int j; - - j = 0; - while (j < MAX_BUTTONS_PER_CORNER) - { - different_layouts[i].left_buttons[j] = META_BUTTON_FUNCTION_LAST; - different_layouts[i].right_buttons[j] = META_BUTTON_FUNCTION_LAST; - ++j; - } - ++i; - } - -#ifndef ALLOW_DUPLICATE_BUTTONS - i = 0; - while (i <= MAX_BUTTONS_PER_CORNER) - { - int j; - - j = 0; - while (j < i) - { - different_layouts[i].right_buttons[j] = (MetaButtonFunction) j; - ++j; - } - while (j < MAX_BUTTONS_PER_CORNER) - { - different_layouts[i].left_buttons[j-i] = (MetaButtonFunction) j; - ++j; - } - - ++i; - } - - /* Special extra case for no buttons on either side */ - different_layouts[i].left_buttons[0] = META_BUTTON_FUNCTION_LAST; - different_layouts[i].right_buttons[0] = META_BUTTON_FUNCTION_LAST; - -#else - /* FIXME this code is if we allow duplicate buttons, - * which we currently do not - */ - int left; - int i; - - left = 0; - i = 0; - - while (left < MAX_BUTTONS_PER_CORNER) - { - int right; - - right = 0; - - while (right < MAX_BUTTONS_PER_CORNER) - { - int j; - - static MetaButtonFunction left_functions[MAX_BUTTONS_PER_CORNER] = { - META_BUTTON_FUNCTION_MENU, - META_BUTTON_FUNCTION_MINIMIZE, - META_BUTTON_FUNCTION_MAXIMIZE, - META_BUTTON_FUNCTION_CLOSE - }; - static MetaButtonFunction right_functions[MAX_BUTTONS_PER_CORNER] = { - META_BUTTON_FUNCTION_MINIMIZE, - META_BUTTON_FUNCTION_MAXIMIZE, - META_BUTTON_FUNCTION_CLOSE, - META_BUTTON_FUNCTION_MENU - }; - - g_assert (i < BUTTON_LAYOUT_COMBINATIONS); - - j = 0; - while (j <= left) - { - different_layouts[i].left_buttons[j] = left_functions[j]; - ++j; - } - - j = 0; - while (j <= right) - { - different_layouts[i].right_buttons[j] = right_functions[j]; - ++j; - } - - ++i; - - ++right; - } - - ++left; - } -#endif -} - - -static GtkWidget* -previews_of_button_layouts (void) -{ - static gboolean initted = FALSE; - GtkWidget *box; - GtkWidget *sw; - GdkRGBA desktop_color; - int i; - GtkWidget *eventbox; - - if (!initted) - { - init_layouts (); - initted = TRUE; - } - - sw = gtk_scrolled_window_new (NULL, NULL); - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), - GTK_POLICY_AUTOMATIC, - GTK_POLICY_AUTOMATIC); - - box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); - gtk_box_set_spacing (GTK_BOX (box), 20); - gtk_container_set_border_width (GTK_CONTAINER (box), 20); - - eventbox = gtk_event_box_new (); - gtk_container_add (GTK_CONTAINER (eventbox), box); - - gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (sw), eventbox); - - desktop_color.red = 0.32; - desktop_color.green = 0.46; - desktop_color.blue = 0.65; - desktop_color.alpha = 1.0; - - gtk_widget_override_background_color (eventbox, 0, &desktop_color); - - i = 0; - while (i < BUTTON_LAYOUT_COMBINATIONS) - { - GtkWidget *align; - double xalign, yalign; - GtkWidget *eventbox2; - GtkWidget *preview; - char *title; - - eventbox2 = gtk_event_box_new (); - - preview = meta_preview_new (); - - gtk_container_add (GTK_CONTAINER (eventbox2), preview); - - meta_preview_set_theme (META_PREVIEW (preview), global_theme); - - title = g_strdup_printf (_("Button layout test %d"), i+1); - meta_preview_set_title (META_PREVIEW (preview), title); - g_free (title); - - meta_preview_set_button_layout (META_PREVIEW (preview), - &different_layouts[i]); - - xalign = 0.5; - yalign = 0.5; - - align = gtk_alignment_new (0.0, 0.0, xalign, yalign); - gtk_container_add (GTK_CONTAINER (align), eventbox2); - - gtk_box_pack_start (GTK_BOX (box), align, TRUE, TRUE, 0); - - previews[META_FRAME_TYPE_LAST*FONT_SIZE_LAST + i] = preview; - - ++i; - } - - return sw; -} - -static GtkWidget* -benchmark_summary (void) -{ - char *msg; - GtkWidget *label; - - msg = g_strdup_printf (_("%g milliseconds to draw one window frame"), - milliseconds_to_draw_frame); - label = gtk_label_new (msg); - g_free (msg); - - return label; -} - -int -main (int argc, char **argv) -{ - GtkStyleContext *style; - const PangoFontDescription *font_desc; - GtkWidget *window; - GtkWidget *collection; - GError *err; - clock_t start, end; - GtkWidget *notebook; - int i; - - bindtextdomain (GETTEXT_PACKAGE, MUTTER_LOCALEDIR); - textdomain(GETTEXT_PACKAGE); - bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); - - run_position_expression_tests (); -#if 0 - run_position_expression_timings (); -#endif - - gtk_init (&argc, &argv); - - if (g_getenv ("MUTTER_DEBUG") != NULL) - { - meta_set_debugging (TRUE); - meta_set_verbose (TRUE); - } - - start = clock (); - err = NULL; - if (argc == 1) - global_theme = meta_theme_load ("Atlanta", &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); - - run_theme_benchmark (); - - window = gtk_window_new (GTK_WINDOW_TOPLEVEL); - gtk_window_set_default_size (GTK_WINDOW (window), 350, 350); - - if (strcmp (global_theme->name, global_theme->readable_name)==0) - gtk_window_set_title (GTK_WINDOW (window), - global_theme->readable_name); - else - { - /* The theme directory name is different from the name the theme - * gives itself within its file. Display both, directory name first. - */ - gchar *title = g_strconcat (global_theme->name, " - ", - global_theme->readable_name, - NULL); - - gtk_window_set_title (GTK_WINDOW (window), - title); - - g_free (title); - } - - g_signal_connect (G_OBJECT (window), "destroy", - G_CALLBACK (gtk_main_quit), NULL); - - gtk_widget_realize (window); - style = gtk_widget_get_style_context (window); - font_desc = gtk_style_context_get_font (style, 0); - - g_assert (style); - g_assert (font_desc); - - notebook = gtk_notebook_new (); - gtk_container_add (GTK_CONTAINER (window), notebook); - - collection = preview_collection (FONT_SIZE_NORMAL, - font_desc); - gtk_notebook_append_page (GTK_NOTEBOOK (notebook), - collection, - gtk_label_new (_("Normal Title Font"))); - - collection = preview_collection (FONT_SIZE_SMALL, - font_desc); - gtk_notebook_append_page (GTK_NOTEBOOK (notebook), - collection, - gtk_label_new (_("Small Title Font"))); - - collection = preview_collection (FONT_SIZE_LARGE, - font_desc); - gtk_notebook_append_page (GTK_NOTEBOOK (notebook), - collection, - gtk_label_new (_("Large Title Font"))); - - collection = previews_of_button_layouts (); - gtk_notebook_append_page (GTK_NOTEBOOK (notebook), - collection, - gtk_label_new (_("Button Layouts"))); - - collection = benchmark_summary (); - gtk_notebook_append_page (GTK_NOTEBOOK (notebook), - collection, - gtk_label_new (_("Benchmark"))); - - i = 0; - while (i < (int) G_N_ELEMENTS (previews)) - { - /* preview widget likes to be realized before its size request. - * it's lame that way. - */ - gtk_widget_realize (previews[i]); - - ++i; - } - - gtk_widget_show_all (window); - - gtk_main (); - - return 0; -} - - -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 void -run_theme_benchmark (void) -{ - GtkWidget* widget; - cairo_surface_t *pixmap; - MetaFrameBorders borders; - MetaButtonState button_states[META_BUTTON_TYPE_LAST] = - { - META_BUTTON_STATE_NORMAL, - META_BUTTON_STATE_NORMAL, - META_BUTTON_STATE_NORMAL, - META_BUTTON_STATE_NORMAL - }; - clock_t start; - clock_t end; - GTimer *timer; - int i; - MetaButtonLayout button_layout; -#define ITERATIONS 100 - int client_width; - int client_height; - cairo_t *cr; - int inc; - - widget = gtk_window_new (GTK_WINDOW_TOPLEVEL); - gtk_widget_realize (widget); - - meta_theme_get_frame_borders (global_theme, - meta_theme_get_variant (global_theme, NULL)->style_context, - META_FRAME_TYPE_NORMAL, - get_flags (widget), - &borders); - - i = 0; - while (i < MAX_BUTTONS_PER_CORNER) - { - button_layout.left_buttons[i] = META_BUTTON_FUNCTION_LAST; - button_layout.right_buttons[i] = META_BUTTON_FUNCTION_LAST; - ++i; - } - - button_layout.left_buttons[0] = META_BUTTON_FUNCTION_MENU; - - button_layout.right_buttons[0] = META_BUTTON_FUNCTION_MINIMIZE; - button_layout.right_buttons[1] = META_BUTTON_FUNCTION_MAXIMIZE; - button_layout.right_buttons[2] = META_BUTTON_FUNCTION_CLOSE; - - timer = g_timer_new (); - start = clock (); - - client_width = 50; - client_height = 50; - inc = 1000 / ITERATIONS; /* Increment to grow width/height, - * eliminates caching effects. - */ - - i = 0; - while (i < ITERATIONS) - { - /* Creating the pixmap in the loop is right, since - * GDK does the same with its double buffering. - */ - pixmap = gdk_window_create_similar_surface (gtk_widget_get_window (widget), - CAIRO_CONTENT_COLOR, - client_width + borders.total.left + borders.total.right, - client_height + borders.total.top + borders.total.bottom); - - cr = cairo_create (pixmap); - - meta_theme_draw_frame_with_style (global_theme, - gtk_widget_get_style_context (widget), - cr, - META_FRAME_TYPE_NORMAL, - get_flags (widget), - client_width, client_height, - &button_layout, - button_states, - meta_preview_get_mini_icon (), - meta_preview_get_icon ()); - - cairo_destroy (cr); - cairo_surface_destroy (pixmap); - - ++i; - client_width += inc; - client_height += inc; - } - - end = clock (); - g_timer_stop (timer); - - milliseconds_to_draw_frame = (g_timer_elapsed (timer, NULL) / (double) ITERATIONS) * 1000; - - g_print (_("Drew %d frames in %g client-side seconds (%g milliseconds per frame) and %g seconds wall clock time including X server resources (%g milliseconds per frame)\n"), - ITERATIONS, - ((double)end - (double)start) / CLOCKS_PER_SEC, - (((double)end - (double)start) / CLOCKS_PER_SEC / (double) ITERATIONS) * 1000, - g_timer_elapsed (timer, NULL), - milliseconds_to_draw_frame); - - g_timer_destroy (timer); - gtk_widget_destroy (widget); - -#undef ITERATIONS -} - -typedef struct -{ - GdkRectangle rect; - const char *expr; - int expected_x; - int expected_y; - MetaThemeError expected_error; -} PositionExpressionTest; - -#define NO_ERROR -1 - -static const PositionExpressionTest position_expression_tests[] = { - /* Just numbers */ - { { 10, 20, 40, 50 }, - "10", 20, 30, NO_ERROR }, - { { 10, 20, 40, 50 }, - "14.37", 24, 34, NO_ERROR }, - /* Binary expressions with 2 ints */ - { { 10, 20, 40, 50 }, - "14 * 10", 150, 160, NO_ERROR }, - { { 10, 20, 40, 50 }, - "14 + 10", 34, 44, NO_ERROR }, - { { 10, 20, 40, 50 }, - "14 - 10", 14, 24, NO_ERROR }, - { { 10, 20, 40, 50 }, - "8 / 2", 14, 24, NO_ERROR }, - { { 10, 20, 40, 50 }, - "8 % 3", 12, 22, NO_ERROR }, - /* Binary expressions with floats and mixed float/ints */ - { { 10, 20, 40, 50 }, - "7.0 / 3.5", 12, 22, NO_ERROR }, - { { 10, 20, 40, 50 }, - "12.1 / 3", 14, 24, NO_ERROR }, - { { 10, 20, 40, 50 }, - "12 / 2.95", 14, 24, NO_ERROR }, - /* Binary expressions without whitespace after first number */ - { { 10, 20, 40, 50 }, - "14* 10", 150, 160, NO_ERROR }, - { { 10, 20, 40, 50 }, - "14+ 10", 34, 44, NO_ERROR }, - { { 10, 20, 40, 50 }, - "14- 10", 14, 24, NO_ERROR }, - { { 10, 20, 40, 50 }, - "8/ 2", 14, 24, NO_ERROR }, - { { 10, 20, 40, 50 }, - "7.0/ 3.5", 12, 22, NO_ERROR }, - { { 10, 20, 40, 50 }, - "12.1/ 3", 14, 24, NO_ERROR }, - { { 10, 20, 40, 50 }, - "12/ 2.95", 14, 24, NO_ERROR }, - /* Binary expressions without whitespace before second number */ - { { 10, 20, 40, 50 }, - "14 *10", 150, 160, NO_ERROR }, - { { 10, 20, 40, 50 }, - "14 +10", 34, 44, NO_ERROR }, - { { 10, 20, 40, 50 }, - "14 -10", 14, 24, NO_ERROR }, - { { 10, 20, 40, 50 }, - "8 /2", 14, 24, NO_ERROR }, - { { 10, 20, 40, 50 }, - "7.0 /3.5", 12, 22, NO_ERROR }, - { { 10, 20, 40, 50 }, - "12.1 /3", 14, 24, NO_ERROR }, - { { 10, 20, 40, 50 }, - "12 /2.95", 14, 24, NO_ERROR }, - /* Binary expressions without any whitespace */ - { { 10, 20, 40, 50 }, - "14*10", 150, 160, NO_ERROR }, - { { 10, 20, 40, 50 }, - "14+10", 34, 44, NO_ERROR }, - { { 10, 20, 40, 50 }, - "14-10", 14, 24, NO_ERROR }, - { { 10, 20, 40, 50 }, - "8/2", 14, 24, NO_ERROR }, - { { 10, 20, 40, 50 }, - "7.0/3.5", 12, 22, NO_ERROR }, - { { 10, 20, 40, 50 }, - "12.1/3", 14, 24, NO_ERROR }, - { { 10, 20, 40, 50 }, - "12/2.95", 14, 24, NO_ERROR }, - /* Binary expressions with parentheses */ - { { 10, 20, 40, 50 }, - "(14) * (10)", 150, 160, NO_ERROR }, - { { 10, 20, 40, 50 }, - "(14) + (10)", 34, 44, NO_ERROR }, - { { 10, 20, 40, 50 }, - "(14) - (10)", 14, 24, NO_ERROR }, - { { 10, 20, 40, 50 }, - "(8) / (2)", 14, 24, NO_ERROR }, - { { 10, 20, 40, 50 }, - "(7.0) / (3.5)", 12, 22, NO_ERROR }, - { { 10, 20, 40, 50 }, - "(12.1) / (3)", 14, 24, NO_ERROR }, - { { 10, 20, 40, 50 }, - "(12) / (2.95)", 14, 24, NO_ERROR }, - /* Lots of extra parentheses */ - { { 10, 20, 40, 50 }, - "(((14)) * ((10)))", 150, 160, NO_ERROR }, - { { 10, 20, 40, 50 }, - "((((14)))) + ((((((((10))))))))", 34, 44, NO_ERROR }, - { { 10, 20, 40, 50 }, - "((((((((((14 - 10))))))))))", 14, 24, NO_ERROR }, - /* Binary expressions with variables */ - { { 10, 20, 40, 50 }, - "2 * width", 90, 100, NO_ERROR }, - { { 10, 20, 40, 50 }, - "2 * height", 110, 120, NO_ERROR }, - { { 10, 20, 40, 50 }, - "width - 10", 40, 50, NO_ERROR }, - { { 10, 20, 40, 50 }, - "height / 2", 35, 45, NO_ERROR }, - /* More than two operands */ - { { 10, 20, 40, 50 }, - "8 / 2 + 5", 19, 29, NO_ERROR }, - { { 10, 20, 40, 50 }, - "8 * 2 + 5", 31, 41, NO_ERROR }, - { { 10, 20, 40, 50 }, - "8 + 2 * 5", 28, 38, NO_ERROR }, - { { 10, 20, 40, 50 }, - "8 + 8 / 2", 22, 32, NO_ERROR }, - { { 10, 20, 40, 50 }, - "14 / (2 + 5)", 12, 22, NO_ERROR }, - { { 10, 20, 40, 50 }, - "8 * (2 + 5)", 66, 76, NO_ERROR }, - { { 10, 20, 40, 50 }, - "(8 + 2) * 5", 60, 70, NO_ERROR }, - { { 10, 20, 40, 50 }, - "(8 + 8) / 2", 18, 28, NO_ERROR }, - /* Errors */ - { { 10, 20, 40, 50 }, - "2 * foo", 0, 0, META_THEME_ERROR_UNKNOWN_VARIABLE }, - { { 10, 20, 40, 50 }, - "2 *", 0, 0, META_THEME_ERROR_FAILED }, - { { 10, 20, 40, 50 }, - "- width", 0, 0, META_THEME_ERROR_FAILED }, - { { 10, 20, 40, 50 }, - "5 % 1.0", 0, 0, META_THEME_ERROR_MOD_ON_FLOAT }, - { { 10, 20, 40, 50 }, - "1.0 % 5", 0, 0, META_THEME_ERROR_MOD_ON_FLOAT }, - { { 10, 20, 40, 50 }, - "! * 2", 0, 0, META_THEME_ERROR_BAD_CHARACTER }, - { { 10, 20, 40, 50 }, - " ", 0, 0, META_THEME_ERROR_FAILED }, - { { 10, 20, 40, 50 }, - "() () (( ) ()) ((()))", 0, 0, META_THEME_ERROR_FAILED }, - { { 10, 20, 40, 50 }, - "(*) () ((/) ()) ((()))", 0, 0, META_THEME_ERROR_FAILED }, - { { 10, 20, 40, 50 }, - "2 * 5 /", 0, 0, META_THEME_ERROR_FAILED }, - { { 10, 20, 40, 50 }, - "+ 2 * 5", 0, 0, META_THEME_ERROR_FAILED }, - { { 10, 20, 40, 50 }, - "+ 2 * 5", 0, 0, META_THEME_ERROR_FAILED } -}; - -static void -run_position_expression_tests (void) -{ -#if 0 - int i; - MetaPositionExprEnv env; - - i = 0; - while (i < (int) G_N_ELEMENTS (position_expression_tests)) - { - GError *err; - gboolean retval; - const PositionExpressionTest *test; - PosToken *tokens; - int n_tokens; - 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; - - env.rect = meta_rect (test->rect.x, test->rect.y, - test->rect.width, 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; - - if (err == NULL) - { - retval = meta_parse_position_expression (tokens, n_tokens, - &env, - &x, &y, - &err); - } - - if (retval && err) - 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 (((int) test->expected_error) != NO_ERROR) - { - if (err == NULL) - g_error (_("Error was expected but none given")); - if (err->code != (int) test->expected_error) - g_error (_("Error %d was expected but %d given"), - test->expected_error, err->code); - } - else - { - if (err) - g_error (_("Error not expected but one was returned: %s"), - err->message); - - if (x != test->expected_x) - g_error (_("x value was %d, %d was expected"), x, test->expected_x); - - if (y != test->expected_y) - g_error (_("y value was %d, %d was expected"), y, test->expected_y); - } - - if (err) - g_error_free (err); - - meta_pos_tokens_free (tokens, n_tokens); - ++i; - } -#endif -} - -#if 0 -static void -run_position_expression_timings (void) -{ - int i; - int iters; - 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; - env.y = test->rect.y; - env.width = test->rect.width; - 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); - - ++iters; - ++i; - if (i == G_N_ELEMENTS (position_expression_tests)) - i = 0; - } - - end = clock (); - - g_print (_("%d coordinate expressions parsed in %g seconds (%g seconds average)\n"), - ITERATIONS, - ((double)end - (double)start) / CLOCKS_PER_SEC, - ((double)end - (double)start) / CLOCKS_PER_SEC / (double) ITERATIONS); - -} -#endif diff --git a/src/ui/theme.c b/src/ui/theme.c deleted file mode 100644 index 58d28de64..000000000 --- a/src/ui/theme.c +++ /dev/null @@ -1,6228 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/* 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 - * 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. - */ - -/** - * SECTION:theme - * @short_description: Making Metacity look pretty - * - * The window decorations drawn by Metacity are described by files on disk - * known internally as "themes" (externally as "window border themes" on - * http://art.gnome.org/themes/metacity/ or "Metacity themes"). This file - * contains most of the code necessary to support themes; it does not - * contain the XML parser, which is in theme-parser.c. - * - * FIXME: This is a big file with lots of different subsystems, which might - * be better split out into separate files. - */ - -#include <config.h> -#include "uiframe.h" -#include "theme-private.h" -#include <meta/util.h> -#include <meta/prefs.h> -#include <gtk/gtk.h> -#include <string.h> -#include <stdlib.h> -#include <math.h> - -#define GDK_COLOR_RGBA(color) \ - ((guint32) (0xff | \ - ((int)((color).red * 255) << 24) | \ - ((int)((color).green * 255) << 16) | \ - ((int)((color).blue * 255) << 8))) - -#define GDK_COLOR_RGB(color) \ - ((guint32) (((int)((color).red * 255) << 16) | \ - ((int)((color).green * 255) << 8) | \ - ((int)((color).blue * 255)))) - -#define DEBUG_FILL_STRUCT(s) memset ((s), 0xef, sizeof (*(s))) -#define CLAMP_UCHAR(v) ((guchar) (CLAMP (((int)v), (int)0, (int)255))) -#define INTENSITY(r, g, b) ((r) * 0.30 + (g) * 0.59 + (b) * 0.11) - -static void gtk_style_shade (GdkRGBA *a, - GdkRGBA *b, - gdouble k); -static void rgb_to_hls (gdouble *r, - gdouble *g, - gdouble *b); -static void hls_to_rgb (gdouble *h, - gdouble *l, - gdouble *s); - -/* - * The current theme. (Themes are singleton.) - */ -static MetaTheme *meta_current_theme = NULL; - -static void -color_composite (const GdkRGBA *bg, - const GdkRGBA *fg, - double alpha, - GdkRGBA *color) -{ - *color = *bg; - color->red = color->red + (fg->red - color->red) * alpha; - color->green = color->green + (fg->green - color->green) * alpha; - color->blue = color->blue + (fg->blue - color->blue) * alpha; -} - -/** - * init_border: - * @border: The border whose fields should be reset. - * - * Sets all the fields of a border to dummy values. - */ -static void -init_border (GtkBorder *border) -{ - border->top = -1; - border->bottom = -1; - border->left = -1; - border->right = -1; -} - -/** - * meta_frame_layout_new: (skip) - * - * Creates a new, empty MetaFrameLayout. The fields will be set to dummy - * values. - * - * Returns: The newly created MetaFrameLayout. - */ -MetaFrameLayout* -meta_frame_layout_new (void) -{ - MetaFrameLayout *layout; - - layout = g_new0 (MetaFrameLayout, 1); - - layout->refcount = 1; - - init_border (&layout->title_border); - - layout->right_titlebar_edge = -1; - layout->left_titlebar_edge = -1; - - layout->button_sizing = META_BUTTON_SIZING_LAST; - layout->button_aspect = 1.0; - layout->button_width = -1; - layout->button_height = -1; - - layout->has_title = TRUE; - layout->title_scale = 1.0; - - 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; -} - -/** - * validate_geometry_value: - * @val: The value to check - * @name: The name to use in the error message - * @error: (out): Set to an error if val was not initialised - * - * Ensures that the theme supplied a particular dimension. When a - * #MetaFrameLayout is created, all its integer fields are set to -1 - * by meta_frame_layout_new(). After an instance of this type - * should have been initialised, this function checks that - * a given field is not still at -1. It is never called directly, but - * rather via the %CHECK_GEOMETRY_VALUE and %CHECK_GEOMETRY_BORDER - * macros. - */ -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_BORDER (title_border); - - CHECK_GEOMETRY_VALUE (right_titlebar_edge); - CHECK_GEOMETRY_VALUE (left_titlebar_edge); - - switch (layout->button_sizing) - { - case META_BUTTON_SIZING_ASPECT: - if (layout->button_aspect < (0.1) || - layout->button_aspect > (15.0)) - { - g_set_error (error, META_THEME_ERROR, - META_THEME_ERROR_FRAME_GEOMETRY, - _("Button aspect ratio %g is not reasonable"), - layout->button_aspect); - return FALSE; - } - break; - case META_BUTTON_SIZING_FIXED: - CHECK_GEOMETRY_VALUE (button_width); - CHECK_GEOMETRY_VALUE (button_height); - break; - case META_BUTTON_SIZING_LAST: - g_set_error (error, META_THEME_ERROR, - META_THEME_ERROR_FRAME_GEOMETRY, - _("Frame geometry does not specify size of buttons")); - return FALSE; - } - - 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_ref (MetaFrameLayout *layout) -{ - g_return_if_fail (layout != NULL); - - 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); - } -} - -static GtkStateFlags -get_style_flags (MetaFrameFlags flags) -{ - GtkStateFlags gtk_flags; - - gtk_flags = GTK_STATE_FLAG_NORMAL; - - if ((flags & META_FRAME_HAS_FOCUS) == 0) - gtk_flags |= GTK_STATE_FLAG_BACKDROP; - - return gtk_flags; -} - -static void -meta_frame_layout_get_borders (const MetaFrameLayout *layout, - GtkStyleContext *style_context, - MetaFrameFlags flags, - MetaFrameType type, - MetaFrameBorders *borders) -{ - int draggable_borders; - GtkBorder padding; - - meta_frame_borders_clear (borders); - - /* For a full-screen window, we don't have any borders, visible or not. */ - if (flags & META_FRAME_FULLSCREEN) - return; - - g_return_if_fail (layout != NULL); - - gtk_style_context_get_border (style_context, - get_style_flags (flags), - &borders->visible); - - gtk_style_context_get_padding (style_context, - get_style_flags (flags), - &padding); - - borders->visible.left += padding.left; - borders->visible.right += padding.right; - borders->visible.top += padding.top; - borders->visible.bottom += padding.bottom; - - draggable_borders = meta_prefs_get_draggable_border_width (); - - if (flags & META_FRAME_ALLOWS_HORIZONTAL_RESIZE) - { - borders->invisible.left = MAX (0, draggable_borders - borders->visible.left); - borders->invisible.right = MAX (0, draggable_borders - borders->visible.right); - } - - if (flags & META_FRAME_ALLOWS_VERTICAL_RESIZE) - { - borders->invisible.bottom = MAX (0, draggable_borders - borders->visible.bottom); - - /* borders.visible.top is the height of the *title bar*. We can't do the same - * algorithm here, titlebars are expectedly much bigger. Just subtract a couple - * pixels to get a proper feel. */ - if (type != META_FRAME_TYPE_ATTACHED) - borders->invisible.top = MAX (0, draggable_borders - 2); - } - - borders->total.left = borders->invisible.left + borders->visible.left; - borders->total.right = borders->invisible.right + borders->visible.right; - borders->total.bottom = borders->invisible.bottom + borders->visible.bottom; - borders->total.top = borders->invisible.top + borders->visible.top; -} - -static MetaButtonType -map_button_function_to_type (MetaButtonFunction function) -{ - switch (function) - { - case META_BUTTON_FUNCTION_SHADE: - return META_BUTTON_TYPE_SHADE; - case META_BUTTON_FUNCTION_ABOVE: - return META_BUTTON_TYPE_ABOVE; - case META_BUTTON_FUNCTION_STICK: - return META_BUTTON_TYPE_STICK; - case META_BUTTON_FUNCTION_UNSHADE: - return META_BUTTON_TYPE_UNSHADE; - case META_BUTTON_FUNCTION_UNABOVE: - return META_BUTTON_TYPE_UNABOVE; - case META_BUTTON_FUNCTION_UNSTICK: - return META_BUTTON_TYPE_UNSTICK; - case META_BUTTON_FUNCTION_MENU: - return META_BUTTON_TYPE_MENU; - case META_BUTTON_FUNCTION_MINIMIZE: - return META_BUTTON_TYPE_MINIMIZE; - case META_BUTTON_FUNCTION_MAXIMIZE: - return META_BUTTON_TYPE_MAXIMIZE; - case META_BUTTON_FUNCTION_CLOSE: - return META_BUTTON_TYPE_CLOSE; - case META_BUTTON_FUNCTION_LAST: - return META_BUTTON_TYPE_LAST; - } - - return META_BUTTON_TYPE_LAST; -} - -static MetaButtonSpace* -rect_for_function (MetaFrameGeometry *fgeom, - MetaFrameFlags flags, - MetaButtonFunction function, - MetaTheme *theme) -{ - - /* Firstly, check version-specific things. */ - - if (META_THEME_ALLOWS(theme, META_THEME_SHADE_STICK_ABOVE_BUTTONS)) - { - switch (function) - { - case META_BUTTON_FUNCTION_SHADE: - if ((flags & META_FRAME_ALLOWS_SHADE) && !(flags & META_FRAME_SHADED)) - return &fgeom->shade_rect; - else - return NULL; - case META_BUTTON_FUNCTION_ABOVE: - if (!(flags & META_FRAME_ABOVE)) - return &fgeom->above_rect; - else - return NULL; - case META_BUTTON_FUNCTION_STICK: - if (!(flags & META_FRAME_STUCK)) - return &fgeom->stick_rect; - else - return NULL; - case META_BUTTON_FUNCTION_UNSHADE: - if ((flags & META_FRAME_ALLOWS_SHADE) && (flags & META_FRAME_SHADED)) - return &fgeom->unshade_rect; - else - return NULL; - case META_BUTTON_FUNCTION_UNABOVE: - if (flags & META_FRAME_ABOVE) - return &fgeom->unabove_rect; - else - return NULL; - case META_BUTTON_FUNCTION_UNSTICK: - if (flags & META_FRAME_STUCK) - return &fgeom->unstick_rect; - default: - /* just go on to the next switch block */; - } - } - - /* now consider the buttons which exist in all versions */ - - switch (function) - { - case META_BUTTON_FUNCTION_MENU: - if (flags & META_FRAME_ALLOWS_MENU) - return &fgeom->menu_rect; - else - return NULL; - case META_BUTTON_FUNCTION_MINIMIZE: - if (flags & META_FRAME_ALLOWS_MINIMIZE) - return &fgeom->min_rect; - else - return NULL; - case META_BUTTON_FUNCTION_MAXIMIZE: - if (flags & META_FRAME_ALLOWS_MAXIMIZE) - return &fgeom->max_rect; - else - return NULL; - case META_BUTTON_FUNCTION_CLOSE: - if (flags & META_FRAME_ALLOWS_DELETE) - return &fgeom->close_rect; - else - return NULL; - case META_BUTTON_FUNCTION_STICK: - case META_BUTTON_FUNCTION_SHADE: - case META_BUTTON_FUNCTION_ABOVE: - case META_BUTTON_FUNCTION_UNSTICK: - case META_BUTTON_FUNCTION_UNSHADE: - case META_BUTTON_FUNCTION_UNABOVE: - /* we are being asked for a >v1 button which hasn't been handled yet, - * so obviously we're not in a theme which supports that version. - * therefore, we don't show the button. return NULL and all will - * be well. - */ - return NULL; - - case META_BUTTON_FUNCTION_LAST: - return NULL; - } - - return NULL; -} - -static gboolean -strip_button (MetaButtonSpace *func_rects[MAX_BUTTONS_PER_CORNER], - GdkRectangle *bg_rects[MAX_BUTTONS_PER_CORNER], - int *n_rects, - MetaButtonSpace *to_strip) -{ - int i; - - i = 0; - while (i < *n_rects) - { - if (func_rects[i] == to_strip) - { - *n_rects -= 1; - - /* shift the other rects back in the array */ - while (i < *n_rects) - { - func_rects[i] = func_rects[i+1]; - bg_rects[i] = bg_rects[i+1]; - - ++i; - } - - func_rects[i] = NULL; - bg_rects[i] = NULL; - - return TRUE; - } - - ++i; - } - - return FALSE; /* did not strip anything */ -} - -static void -meta_frame_layout_calc_geometry (const MetaFrameLayout *layout, - GtkStyleContext *ctx, - MetaFrameFlags flags, - int client_width, - int client_height, - const MetaButtonLayout *button_layout, - MetaFrameType type, - MetaFrameGeometry *fgeom, - MetaTheme *theme) -{ - int i, n_left, n_right, n_left_spacers, n_right_spacers; - int x; - int button_y; - int title_right_edge; - int width, height; - int button_width, button_height; - - /* the left/right rects in order; the max # of rects - * is the number of button functions - */ - MetaButtonSpace *left_func_rects[MAX_BUTTONS_PER_CORNER]; - MetaButtonSpace *right_func_rects[MAX_BUTTONS_PER_CORNER]; - GdkRectangle *left_bg_rects[MAX_BUTTONS_PER_CORNER]; - gboolean left_buttons_has_spacer[MAX_BUTTONS_PER_CORNER]; - GdkRectangle *right_bg_rects[MAX_BUTTONS_PER_CORNER]; - gboolean right_buttons_has_spacer[MAX_BUTTONS_PER_CORNER]; - - MetaFrameBorders borders; - - meta_frame_layout_get_borders (layout, ctx, flags, type, - &borders); - - fgeom->borders = borders; - - width = client_width + borders.total.left + borders.total.right; - - height = ((flags & META_FRAME_SHADED) ? 0: client_height) + - borders.total.top + borders.total.bottom; - - 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_titlebar_edge; - fgeom->right_titlebar_edge = layout->right_titlebar_edge; - - /* gcc warnings */ - button_width = -1; - button_height = -1; - - switch (layout->button_sizing) - { - case META_BUTTON_SIZING_ASPECT: - button_height = borders.visible.top - layout->button_border.top - layout->button_border.bottom; - button_width = button_height / layout->button_aspect; - break; - case META_BUTTON_SIZING_FIXED: - button_width = layout->button_width; - button_height = layout->button_height; - break; - case META_BUTTON_SIZING_LAST: - g_assert_not_reached (); - break; - } - - /* FIXME all this code sort of pretends that duplicate buttons - * with the same function are allowed, but that breaks the - * code in frames.c, so isn't really allowed right now. - * Would need left_close_rect, right_close_rect, etc. - */ - - /* Init all button rects to 0, lame hack */ - memset (ADDRESS_OF_BUTTON_RECTS (fgeom), '\0', - LENGTH_OF_BUTTON_RECTS); - - n_left = 0; - n_right = 0; - n_left_spacers = 0; - n_right_spacers = 0; - - if (!layout->hide_buttons) - { - /* Try to fill in rects */ - for (i = 0; i < MAX_BUTTONS_PER_CORNER && button_layout->left_buttons[i] != META_BUTTON_FUNCTION_LAST; i++) - { - left_func_rects[n_left] = rect_for_function (fgeom, flags, - button_layout->left_buttons[i], - theme); - if (left_func_rects[n_left] != NULL) - { - left_buttons_has_spacer[n_left] = button_layout->left_buttons_has_spacer[i]; - if (button_layout->left_buttons_has_spacer[i]) - ++n_left_spacers; - - ++n_left; - } - } - - for (i = 0; i < MAX_BUTTONS_PER_CORNER && button_layout->right_buttons[i] != META_BUTTON_FUNCTION_LAST; i++) - { - right_func_rects[n_right] = rect_for_function (fgeom, flags, - button_layout->right_buttons[i], - theme); - if (right_func_rects[n_right] != NULL) - { - right_buttons_has_spacer[n_right] = button_layout->right_buttons_has_spacer[i]; - if (button_layout->right_buttons_has_spacer[i]) - ++n_right_spacers; - - ++n_right; - } - } - } - - for (i = 0; i < MAX_BUTTONS_PER_CORNER; i++) - { - left_bg_rects[i] = NULL; - right_bg_rects[i] = NULL; - } - - for (i = 0; i < n_left; i++) - { - if (n_left == 1) - left_bg_rects[i] = &fgeom->left_single_background; - else if (i == 0) - left_bg_rects[i] = &fgeom->left_left_background; - else if (i == (n_left - 1)) - left_bg_rects[i] = &fgeom->left_right_background; - else - left_bg_rects[i] = &fgeom->left_middle_backgrounds[i - 1]; - } - - for (i = 0; i < n_right; i++) - { - if (n_right == 1) - right_bg_rects[i] = &fgeom->right_single_background; - else if (i == (n_right - 1)) - right_bg_rects[i] = &fgeom->right_right_background; - else if (i == 0) - right_bg_rects[i] = &fgeom->right_left_background; - else - right_bg_rects[i] = &fgeom->right_middle_backgrounds[i - 1]; - } - - /* Be sure buttons fit */ - while (n_left > 0 || n_right > 0) - { - int space_used_by_buttons; - int space_available; - - space_available = fgeom->width - layout->left_titlebar_edge - layout->right_titlebar_edge; - - space_used_by_buttons = 0; - - space_used_by_buttons += button_width * n_left; - space_used_by_buttons += (button_width * 0.75) * n_left_spacers; - space_used_by_buttons += layout->button_border.left * n_left; - space_used_by_buttons += layout->button_border.right * n_left; - - space_used_by_buttons += button_width * n_right; - space_used_by_buttons += (button_width * 0.75) * n_right_spacers; - space_used_by_buttons += layout->button_border.left * n_right; - space_used_by_buttons += layout->button_border.right * n_right; - - if (space_used_by_buttons <= space_available) - break; /* Everything fits, bail out */ - - /* First try to remove separators */ - if (n_left_spacers > 0) - { - left_buttons_has_spacer[--n_left_spacers] = FALSE; - continue; - } - else if (n_right_spacers > 0) - { - right_buttons_has_spacer[--n_right_spacers] = FALSE; - continue; - } - - /* Otherwise we need to shave out a button. Shave - * above, stick, shade, min, max, close, then menu (menu is most useful); - * prefer the default button locations. - */ - if (strip_button (left_func_rects, left_bg_rects, - &n_left, &fgeom->above_rect)) - continue; - else if (strip_button (right_func_rects, right_bg_rects, - &n_right, &fgeom->above_rect)) - continue; - else if (strip_button (left_func_rects, left_bg_rects, - &n_left, &fgeom->stick_rect)) - continue; - else if (strip_button (right_func_rects, right_bg_rects, - &n_right, &fgeom->stick_rect)) - continue; - else if (strip_button (left_func_rects, left_bg_rects, - &n_left, &fgeom->shade_rect)) - continue; - else if (strip_button (right_func_rects, right_bg_rects, - &n_right, &fgeom->shade_rect)) - continue; - else if (strip_button (left_func_rects, left_bg_rects, - &n_left, &fgeom->min_rect)) - continue; - else if (strip_button (right_func_rects, right_bg_rects, - &n_right, &fgeom->min_rect)) - continue; - else if (strip_button (left_func_rects, left_bg_rects, - &n_left, &fgeom->max_rect)) - continue; - else if (strip_button (right_func_rects, right_bg_rects, - &n_right, &fgeom->max_rect)) - continue; - else if (strip_button (left_func_rects, left_bg_rects, - &n_left, &fgeom->close_rect)) - continue; - else if (strip_button (right_func_rects, right_bg_rects, - &n_right, &fgeom->close_rect)) - continue; - else if (strip_button (right_func_rects, right_bg_rects, - &n_right, &fgeom->menu_rect)) - continue; - else if (strip_button (left_func_rects, left_bg_rects, - &n_left, &fgeom->menu_rect)) - continue; - else - { - meta_bug ("Could not find a button to strip. n_left = %d n_right = %d\n", - n_left, n_right); - } - } - - /* Save the button layout */ - fgeom->button_layout = *button_layout; - fgeom->n_left_buttons = n_left; - fgeom->n_right_buttons = n_right; - - /* center buttons vertically */ - button_y = (borders.visible.top - - (button_height + layout->button_border.top + layout->button_border.bottom)) / 2 + layout->button_border.top + borders.invisible.top; - - /* right edge of farthest-right button */ - x = width - layout->right_titlebar_edge - borders.invisible.right; - - i = n_right - 1; - while (i >= 0) - { - MetaButtonSpace *rect; - - if (x < 0) /* if we go negative, leave the buttons we don't get to as 0-width */ - break; - - rect = right_func_rects[i]; - rect->visible.x = x - layout->button_border.right - button_width; - if (right_buttons_has_spacer[i]) - rect->visible.x -= (button_width * 0.75); - - rect->visible.y = button_y; - rect->visible.width = button_width; - rect->visible.height = button_height; - - if (flags & META_FRAME_MAXIMIZED || - flags & META_FRAME_TILED_LEFT || - flags & META_FRAME_TILED_RIGHT) - { - rect->clickable.x = rect->visible.x; - rect->clickable.y = 0; - rect->clickable.width = rect->visible.width; - rect->clickable.height = button_height + button_y; - - if (i == n_right - 1) - rect->clickable.width += layout->right_titlebar_edge + borders.visible.right + layout->button_border.right; - - } - else - g_memmove (&(rect->clickable), &(rect->visible), sizeof(rect->clickable)); - - *(right_bg_rects[i]) = rect->visible; - - x = rect->visible.x - layout->button_border.left; - - --i; - } - - /* save right edge of titlebar for later use */ - title_right_edge = x - layout->title_border.right; - - /* Now x changes to be position from the left and we go through - * the left-side buttons - */ - x = layout->left_titlebar_edge + borders.invisible.left; - for (i = 0; i < n_left; i++) - { - MetaButtonSpace *rect; - - rect = left_func_rects[i]; - - rect->visible.x = x + layout->button_border.left; - rect->visible.y = button_y; - rect->visible.width = button_width; - rect->visible.height = button_height; - - if (flags & META_FRAME_MAXIMIZED) - { - if (i==0) - { - rect->clickable.x = 0; - rect->clickable.width = button_width + x; - } - else - { - rect->clickable.x = rect->visible.x; - rect->clickable.width = button_width; - } - - rect->clickable.y = 0; - rect->clickable.height = button_height + button_y; - } - else - g_memmove (&(rect->clickable), &(rect->visible), sizeof(rect->clickable)); - - x = rect->visible.x + rect->visible.width + layout->button_border.right; - if (left_buttons_has_spacer[i]) - x += (button_width * 0.75); - - *(left_bg_rects[i]) = rect->visible; - } - - /* We always fill as much vertical space as possible with title rect, - * rather than centering it like the buttons - */ - fgeom->title_rect.x = x + layout->title_border.left; - fgeom->title_rect.y = layout->title_border.top + borders.invisible.top; - fgeom->title_rect.width = title_right_edge - fgeom->title_rect.x; - fgeom->title_rect.height = borders.visible.top - layout->title_border.top - layout->title_border.bottom; - - /* Nuke title if it won't fit */ - if (fgeom->title_rect.width < 0 || - fgeom->title_rect.height < 0) - { - fgeom->title_rect.width = 0; - fgeom->title_rect.height = 0; - } -} - -/** - * meta_gradient_spec_new: (skip) - * - */ -MetaGradientSpec* -meta_gradient_spec_new (MetaGradientType type) -{ - MetaGradientSpec *spec; - - spec = g_new (MetaGradientSpec, 1); - - 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, free_color_spec, NULL); - g_slist_free (spec->color_specs); - - DEBUG_FILL_STRUCT (spec); - g_free (spec); -} - -static cairo_pattern_t * -meta_gradient_spec_pattern (const MetaGradientSpec *spec, - const MetaAlphaGradientSpec *alpha_spec, - GtkStyleContext *style) -{ - cairo_pattern_t *pattern; - int n_colors; - GSList *tmp; - int i; - - if (spec->type == META_GRADIENT_HORIZONTAL) - pattern = cairo_pattern_create_linear (0, 0, 1, 0); - if (spec->type == META_GRADIENT_VERTICAL) - pattern = cairo_pattern_create_linear (0, 0, 0, 1); - else if (spec->type == META_GRADIENT_DIAGONAL) - pattern = cairo_pattern_create_linear (0, 0, 1, 1); - else - g_assert_not_reached (); - - n_colors = g_slist_length (spec->color_specs); - - if (n_colors == 0) - return NULL; - - if (alpha_spec != NULL) - g_assert (n_colors == alpha_spec->n_alphas); - - i = 0; - tmp = spec->color_specs; - while (tmp != NULL) - { - GdkRGBA color; - - meta_color_spec_render (tmp->data, style, &color); - - if (alpha_spec != NULL) - cairo_pattern_add_color_stop_rgba (pattern, - i / (float)n_colors, - color.red, - color.green, - color.blue, - alpha_spec->alphas[i]); - else - cairo_pattern_add_color_stop_rgb (pattern, - i / (float)n_colors, - color.red, - color.green, - color.blue); - - tmp = tmp->next; - ++i; - } - - return pattern; -} - - -void -meta_gradient_spec_render (const MetaGradientSpec *spec, - const MetaAlphaGradientSpec *alpha_spec, - cairo_t *cr, - GtkStyleContext *style, - int x, - int y, - int width, - int height) -{ - cairo_pattern_t *pattern; - - cairo_save (cr); - - pattern = meta_gradient_spec_pattern (spec, alpha_spec, style); - if (pattern == NULL) - return; - - cairo_rectangle (cr, x, y, width, height); - - cairo_translate (cr, x, y); - cairo_scale (cr, width, height); - - cairo_set_source (cr, pattern); - cairo_fill (cr); - cairo_pattern_destroy (pattern); - - cairo_restore (cr); -} - -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; -} - -/** - * meta_alpha_gradient_spec_new: (skip) - * - */ -MetaAlphaGradientSpec* -meta_alpha_gradient_spec_new (MetaGradientType type, - int n_alphas) -{ - MetaAlphaGradientSpec *spec; - - g_return_val_if_fail (n_alphas > 0, NULL); - - spec = g_new0 (MetaAlphaGradientSpec, 1); - - spec->type = type; - spec->alphas = g_new0 (unsigned char, n_alphas); - spec->n_alphas = n_alphas; - - return spec; -} - -void -meta_alpha_gradient_spec_free (MetaAlphaGradientSpec *spec) -{ - g_return_if_fail (spec != NULL); - - g_free (spec->alphas); - g_free (spec); -} - -/** - * meta_color_spec_new: (skip) - * - */ -MetaColorSpec* -meta_color_spec_new (MetaColorSpecType type) -{ - MetaColorSpec *spec; - MetaColorSpec dummy; - int size; - - size = G_STRUCT_OFFSET (MetaColorSpec, data); - - switch (type) - { - case META_COLOR_SPEC_BASIC: - size += sizeof (dummy.data.basic); - break; - - case META_COLOR_SPEC_GTK: - size += sizeof (dummy.data.gtk); - break; - - case META_COLOR_SPEC_GTK_CUSTOM: - size += sizeof (dummy.data.gtkcustom); - break; - - case META_COLOR_SPEC_BLEND: - size += sizeof (dummy.data.blend); - break; - - case META_COLOR_SPEC_SHADE: - size += sizeof (dummy.data.shade); - break; - } - - spec = g_malloc0 (size); - - spec->type = type; - - return spec; -} - -void -meta_color_spec_free (MetaColorSpec *spec) -{ - g_return_if_fail (spec != NULL); - - switch (spec->type) - { - 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_GTK_CUSTOM: - if (spec->data.gtkcustom.color_name) - g_free (spec->data.gtkcustom.color_name); - if (spec->data.gtkcustom.fallback) - meta_color_spec_free (spec->data.gtkcustom.fallback); - DEBUG_FILL_STRUCT (&spec->data.gtkcustom); - break; - - case META_COLOR_SPEC_BLEND: - if (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; - - case META_COLOR_SPEC_SHADE: - if (spec->data.shade.base) - meta_color_spec_free (spec->data.shade.base); - DEBUG_FILL_STRUCT (&spec->data.shade); - break; - } - - g_free (spec); -} - -/** - * meta_color_spec_new_from_string: (skip) - * - */ -MetaColorSpec* -meta_color_spec_new_from_string (const char *str, - GError **err) -{ - MetaColorSpec *spec; - - spec = NULL; - - if (strncmp (str, "gtk:custom", 10) == 0) - { - const char *color_name_start, *fallback_str_start, *end; - char *color_name; - MetaColorSpec *fallback = NULL; - static gboolean debug, debug_set = FALSE; - - if (!debug_set) - { - debug = g_getenv ("MUTTER_DISABLE_FALLBACK_COLOR") != NULL; - debug_set = TRUE; - } - - if (str[10] != '(') - { - g_set_error (err, META_THEME_ERROR, - META_THEME_ERROR_FAILED, - _("GTK custom color specification must have color name and fallback in parentheses, e.g. gtk:custom(foo,bar); could not parse \"%s\""), - str); - return NULL; - } - - color_name_start = str + 11; - - fallback_str_start = color_name_start; - while (*fallback_str_start && *fallback_str_start != ',') - { - if (!(g_ascii_isalnum (*fallback_str_start) - || *fallback_str_start == '-' - || *fallback_str_start == '_')) - { - g_set_error (err, META_THEME_ERROR, - META_THEME_ERROR_FAILED, - _("Invalid character '%c' in color_name parameter of gtk:custom, only A-Za-z0-9-_ are valid"), - *fallback_str_start); - return NULL; - } - fallback_str_start++; - } - fallback_str_start++; - - end = strrchr (str, ')'); - - if (color_name_start == NULL || fallback_str_start == NULL || end == NULL) - { - g_set_error (err, META_THEME_ERROR, - META_THEME_ERROR_FAILED, - _("Gtk:custom format is \"gtk:custom(color_name,fallback)\", \"%s\" does not fit the format"), - str); - return NULL; - } - - if (!debug) - { - char *fallback_str; - fallback_str = g_strndup (fallback_str_start, - end - fallback_str_start); - fallback = meta_color_spec_new_from_string (fallback_str, err); - g_free (fallback_str); - } - else - { - fallback = meta_color_spec_new_from_string ("pink", err); - } - - if (fallback == NULL) - return NULL; - - color_name = g_strndup (color_name_start, - fallback_str_start - color_name_start - 1); - - spec = meta_color_spec_new (META_COLOR_SPEC_GTK_CUSTOM); - spec->data.gtkcustom.color_name = color_name; - spec->data.gtkcustom.fallback = fallback; - } - else if (strncmp (str, "gtk:", 4) == 0) - { - /* GTK color */ - const char *bracket; - const char *end_bracket; - char *tmp; - GtkStateFlags 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.component < META_GTK_COLOR_LAST); - } - else if (strncmp (str, "blend/", 6) == 0) - { - /* 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 if (strncmp (str, "shade/", 6) == 0) - { - /* shade */ - char **split; - double factor; - char *end; - MetaColorSpec *base; - - split = g_strsplit (str, "/", 3); - - if (split[0] == NULL || split[1] == NULL || - split[2] == NULL) - { - g_set_error (err, META_THEME_ERROR, - META_THEME_ERROR_FAILED, - _("Shade format is \"shade/base_color/factor\", \"%s\" does not fit the format"), - str); - g_strfreev (split); - return NULL; - } - - factor = g_ascii_strtod (split[2], &end); - if (end == split[2]) - { - g_set_error (err, META_THEME_ERROR, - META_THEME_ERROR_FAILED, - _("Could not parse shade factor \"%s\" in shaded color"), - split[2]); - g_strfreev (split); - return NULL; - } - - if (factor < (0.0 - 1e6)) - { - g_set_error (err, META_THEME_ERROR, - META_THEME_ERROR_FAILED, - _("Shade factor \"%s\" in shaded color is negative"), - split[2]); - g_strfreev (split); - return NULL; - } - - base = NULL; - - base = meta_color_spec_new_from_string (split[1], err); - if (base == NULL) - { - g_strfreev (split); - return NULL; - } - - g_strfreev (split); - - spec = meta_color_spec_new (META_COLOR_SPEC_SHADE); - spec->data.shade.factor = factor; - spec->data.shade.base = base; - } - else - { - spec = meta_color_spec_new (META_COLOR_SPEC_BASIC); - - if (!gdk_rgba_parse (&spec->data.basic.color, str)) - { - 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; -} - -/** - * meta_color_spec_new_gtk: (skip) - * - */ -MetaColorSpec* -meta_color_spec_new_gtk (MetaGtkColorComponent component, - GtkStateFlags state) -{ - MetaColorSpec *spec; - - spec = meta_color_spec_new (META_COLOR_SPEC_GTK); - - spec->data.gtk.component = component; - spec->data.gtk.state = state; - - return spec; -} - -/* Based on set_color() in gtkstyle.c */ -#define LIGHTNESS_MULT 1.3 -#define DARKNESS_MULT 0.7 -void -meta_gtk_style_get_light_color (GtkStyleContext *style, - GtkStateFlags state, - GdkRGBA *color) -{ - gtk_style_context_get_background_color (style, state, color); - gtk_style_shade (color, color, LIGHTNESS_MULT); -} - -void -meta_gtk_style_get_dark_color (GtkStyleContext *style, - GtkStateFlags state, - GdkRGBA *color) -{ - gtk_style_context_get_background_color (style, state, color); - gtk_style_shade (color, color, DARKNESS_MULT); -} - -static void -meta_set_color_from_style (GdkRGBA *color, - GtkStyleContext *context, - GtkStateFlags state, - MetaGtkColorComponent component) -{ - GdkRGBA other; - - switch (component) - { - case META_GTK_COLOR_BG: - case META_GTK_COLOR_BASE: - gtk_style_context_get_background_color (context, state, color); - break; - case META_GTK_COLOR_FG: - case META_GTK_COLOR_TEXT: - gtk_style_context_get_color (context, state, color); - break; - case META_GTK_COLOR_TEXT_AA: - gtk_style_context_get_color (context, state, color); - meta_set_color_from_style (&other, context, state, META_GTK_COLOR_BASE); - - color->red = (color->red + other.red) / 2; - color->green = (color->green + other.green) / 2; - color->blue = (color->blue + other.blue) / 2; - break; - case META_GTK_COLOR_MID: - meta_gtk_style_get_light_color (context, state, color); - meta_gtk_style_get_dark_color (context, state, &other); - - color->red = (color->red + other.red) / 2; - color->green = (color->green + other.green) / 2; - color->blue = (color->blue + other.blue) / 2; - break; - case META_GTK_COLOR_LIGHT: - meta_gtk_style_get_light_color (context, state, color); - break; - case META_GTK_COLOR_DARK: - meta_gtk_style_get_dark_color (context, state, color); - break; - case META_GTK_COLOR_LAST: - g_assert_not_reached (); - break; - } -} - -static void -meta_set_custom_color_from_style (GdkRGBA *color, - GtkStyleContext *context, - char *color_name, - MetaColorSpec *fallback) -{ - if (!gtk_style_context_lookup_color (context, color_name, color)) - meta_color_spec_render (fallback, context, color); -} - -void -meta_color_spec_render (MetaColorSpec *spec, - GtkStyleContext *context, - GdkRGBA *color) -{ - g_return_if_fail (spec != NULL); - g_return_if_fail (GTK_IS_STYLE_CONTEXT (context)); - - switch (spec->type) - { - case META_COLOR_SPEC_BASIC: - *color = spec->data.basic.color; - break; - - case META_COLOR_SPEC_GTK: - meta_set_color_from_style (color, - context, - spec->data.gtk.state, - spec->data.gtk.component); - break; - - case META_COLOR_SPEC_GTK_CUSTOM: - meta_set_custom_color_from_style (color, - context, - spec->data.gtkcustom.color_name, - spec->data.gtkcustom.fallback); - break; - - case META_COLOR_SPEC_BLEND: - { - GdkRGBA bg, fg; - - meta_color_spec_render (spec->data.blend.background, context, &bg); - meta_color_spec_render (spec->data.blend.foreground, context, &fg); - - color_composite (&bg, &fg, spec->data.blend.alpha, - &spec->data.blend.color); - - *color = spec->data.blend.color; - } - break; - - case META_COLOR_SPEC_SHADE: - { - meta_color_spec_render (spec->data.shade.base, context, - &spec->data.shade.color); - - gtk_style_shade (&spec->data.shade.color, - &spec->data.shade.color, spec->data.shade.factor); - - *color = spec->data.shade.color; - } - break; - } -} - -/** - * op_name: - * @type: an operation, such as addition - * - * Represents an operation as a string. - * - * Returns: a string, such as "+" - */ -static const char* -op_name (PosOperatorType type) -{ - switch (type) - { - case POS_OP_ADD: - return "+"; - case POS_OP_SUBTRACT: - return "-"; - case POS_OP_MULTIPLY: - return "*"; - case POS_OP_DIVIDE: - return "/"; - case POS_OP_MOD: - return "%"; - case POS_OP_MAX: - return "`max`"; - case POS_OP_MIN: - return "`min`"; - case POS_OP_NONE: - break; - } - - return "<unknown>"; -} - -/** - * op_from_string: - * @p: a pointer into a string representing an operation; part of an - * expression somewhere, so not null-terminated - * @len: set to the length of the string found. Set to 0 if none is. - * - * Parses a string and returns an operation. - * - * Returns: the operation found. If none was, returns %POS_OP_NONE. - */ -static PosOperatorType -op_from_string (const char *p, - int *len) -{ - *len = 0; - - switch (*p) - { - case '+': - *len = 1; - return POS_OP_ADD; - case '-': - *len = 1; - return POS_OP_SUBTRACT; - case '*': - *len = 1; - return POS_OP_MULTIPLY; - case '/': - *len = 1; - return POS_OP_DIVIDE; - case '%': - *len = 1; - return POS_OP_MOD; - - case '`': - if (strncmp (p, "`max`", 5) == 0) - { - *len = 5; - return POS_OP_MAX; - } - else if (strncmp (p, "`min`", 5) == 0) - { - *len = 5; - return POS_OP_MIN; - } - } - - return POS_OP_NONE; -} - -/** - * free_tokens: - * @tokens: an array of tokens to be freed - * @n_tokens: how many tokens are in the array. - * - * Frees an array of tokens. All the tokens and their associated memory - * will be freed. - */ -static void -free_tokens (PosToken *tokens, - int n_tokens) -{ - int i; - - /* n_tokens can be 0 since tokens may have been allocated more than - * it was initialized - */ - - for (i = 0; i < n_tokens; i++) - if (tokens[i].type == POS_TOKEN_VARIABLE) - g_free (tokens[i].d.v.name); - - g_free (tokens); -} - -/** - * parse_number: - * @p: a pointer into a string representing an operation; part of an - * expression somewhere, so not null-terminated - * @end_return: set to a pointer to the end of the number found; but - * not updated if no number was found at all - * @next: set to either an integer or a float token - * @err: (out): set to the problem if there was a problem - * - * Tokenises a number in an expression. - * - * FIXME: The "while (*start)..." part: what's wrong with strchr-ish things? - * FIXME: The name is wrong: it doesn't parse anything. - * - * Returns: %TRUE if a valid number was found, FALSE otherwise (and "err" will - * have been set) - */ -static gboolean -parse_number (const char *p, - const char **end_return, - PosToken *next, - GError **err) -{ - const char *start = p; - char *end; - gboolean is_float; - char *num_str; - - while (*p && (*p == '.' || g_ascii_isdigit (*p))) - ++p; - - if (p == start) - { - char buf[7] = { '\0' }; - buf[g_unichar_to_utf8 (g_utf8_get_char (p), buf)] = '\0'; - g_set_error (err, META_THEME_ERROR, - META_THEME_ERROR_BAD_CHARACTER, - _("Coordinate expression contains character '%s' which is not allowed"), - buf); - return FALSE; - } - - *end_return = p; - - /* we need this to exclude floats like "1e6" */ - num_str = g_strndup (start, p - start); - start = num_str; - is_float = FALSE; - while (*start) - { - if (*start == '.') - is_float = TRUE; - ++start; - } - - if (is_float) - { - next->type = POS_TOKEN_DOUBLE; - next->d.d.val = g_ascii_strtod (num_str, &end); - - if (end == num_str) - { - g_set_error (err, META_THEME_ERROR, - META_THEME_ERROR_FAILED, - _("Coordinate expression contains floating point number '%s' which could not be parsed"), - num_str); - g_free (num_str); - return FALSE; - } - } - else - { - next->type = POS_TOKEN_INT; - next->d.i.val = strtol (num_str, &end, 10); - if (end == num_str) - { - g_set_error (err, META_THEME_ERROR, - META_THEME_ERROR_FAILED, - _("Coordinate expression contains integer '%s' which could not be parsed"), - num_str); - g_free (num_str); - return FALSE; - } - } - - g_free (num_str); - - g_assert (next->type == POS_TOKEN_INT || next->type == POS_TOKEN_DOUBLE); - - return TRUE; -} - -/* - * Whether a variable can validly appear as part of the name of a variable. - */ -#define IS_VARIABLE_CHAR(c) (g_ascii_isalpha ((c)) || (c) == '_') - -#if 0 -static void -debug_print_tokens (PosToken *tokens, - int n_tokens) -{ - int i; - - for (i = 0; i < n_tokens; i++) - { - PosToken *t = &tokens[i]; - - g_print (" "); - - switch (t->type) - { - case POS_TOKEN_INT: - g_print ("\"%d\"", t->d.i.val); - break; - case POS_TOKEN_DOUBLE: - g_print ("\"%g\"", t->d.d.val); - break; - case POS_TOKEN_OPEN_PAREN: - g_print ("\"(\""); - break; - case POS_TOKEN_CLOSE_PAREN: - g_print ("\")\""); - break; - case POS_TOKEN_VARIABLE: - g_print ("\"%s\"", t->d.v.name); - break; - case POS_TOKEN_OPERATOR: - g_print ("\"%s\"", op_name (t->d.o.op)); - break; - } - } - - g_print ("\n"); -} -#endif - -/** - * pos_tokenize: - * @expr: The expression - * @tokens_p: (out) The resulting tokens - * @n_tokens_p: (out): The number of resulting tokens - * @err: (out): set to the problem if there was a problem - - * Tokenises an expression. - * - * Returns: %TRUE if the expression was successfully tokenised; %FALSE otherwise. - */ -static gboolean -pos_tokenize (const char *expr, - PosToken **tokens_p, - int *n_tokens_p, - GError **err) -{ - PosToken *tokens; - int n_tokens; - int allocated; - const char *p; - - *tokens_p = NULL; - *n_tokens_p = 0; - - allocated = 3; - n_tokens = 0; - tokens = g_new (PosToken, allocated); - - p = expr; - while (*p) - { - PosToken *next; - int len; - - if (n_tokens == allocated) - { - allocated *= 2; - tokens = g_renew (PosToken, tokens, allocated); - } - - next = &tokens[n_tokens]; - - switch (*p) - { - case '*': - case '/': - case '+': - case '-': /* negative numbers aren't allowed so this is easy */ - case '%': - case '`': - next->type = POS_TOKEN_OPERATOR; - next->d.o.op = op_from_string (p, &len); - if (next->d.o.op != POS_OP_NONE) - { - ++n_tokens; - p = p + (len - 1); /* -1 since we ++p later */ - } - else - { - g_set_error (err, META_THEME_ERROR, - META_THEME_ERROR_FAILED, - _("Coordinate expression contained unknown operator at the start of this text: \"%s\""), - p); - - goto error; - } - break; - - case '(': - next->type = POS_TOKEN_OPEN_PAREN; - ++n_tokens; - break; - - case ')': - next->type = POS_TOKEN_CLOSE_PAREN; - ++n_tokens; - break; - - case ' ': - case '\t': - case '\n': - break; - - default: - if (IS_VARIABLE_CHAR (*p)) - { - /* Assume variable */ - const char *start = p; - while (*p && IS_VARIABLE_CHAR (*p)) - ++p; - g_assert (p != start); - next->type = POS_TOKEN_VARIABLE; - next->d.v.name = g_strndup (start, p - start); - ++n_tokens; - --p; /* since we ++p again at the end of while loop */ - } - else - { - /* Assume number */ - const char *end; - - if (!parse_number (p, &end, next, err)) - goto error; - - ++n_tokens; - p = end - 1; /* -1 since we ++p again at the end of while loop */ - } - - break; - } - - ++p; - } - - if (n_tokens == 0) - { - g_set_error (err, META_THEME_ERROR, - META_THEME_ERROR_FAILED, - _("Coordinate expression was empty or not understood")); - - goto error; - } - - *tokens_p = tokens; - *n_tokens_p = n_tokens; - - return TRUE; - - error: - g_assert (err == NULL || *err != NULL); - - free_tokens (tokens, n_tokens); - return FALSE; -} - -/** - * PosExprType: - * - * The type of a PosExpr: either integer, double, or an operation. - */ -typedef enum -{ - POS_EXPR_INT, - POS_EXPR_DOUBLE, - POS_EXPR_OPERATOR -} PosExprType; - -/** - * PosExpr: - * - * Type and value of an expression in a parsed sequence. We don't - * keep expressions in a tree; if this is of type %POS_EXPR_OPERATOR, - * the arguments of the operator will be in the array positions - * immediately preceding and following this operator; they cannot - * themselves be operators. - * - * FIXME: operator is #gchar; it should really be of #PosOperatorType. - */ -typedef struct -{ - PosExprType type; - union - { - double double_val; - int int_val; - char operator; - } d; -} PosExpr; - -#if 0 -static void -debug_print_exprs (PosExpr *exprs, - int n_exprs) -{ - int i; - - for (i = 0; i < n_exprs; i++) - { - switch (exprs[i].type) - { - case POS_EXPR_INT: - g_print (" %d", exprs[i].d.int_val); - break; - case POS_EXPR_DOUBLE: - g_print (" %g", exprs[i].d.double_val); - break; - case POS_EXPR_OPERATOR: - g_print (" %s", op_name (exprs[i].d.operator)); - break; - } - } - g_print ("\n"); -} -#endif - -static gboolean -do_operation (PosExpr *a, - PosExpr *b, - PosOperatorType op, - GError **err) -{ - /* Promote types to double if required */ - if (a->type == POS_EXPR_DOUBLE || - b->type == POS_EXPR_DOUBLE) - { - if (a->type != POS_EXPR_DOUBLE) - { - a->type = POS_EXPR_DOUBLE; - a->d.double_val = a->d.int_val; - } - if (b->type != POS_EXPR_DOUBLE) - { - b->type = POS_EXPR_DOUBLE; - b->d.double_val = b->d.int_val; - } - } - - g_assert (a->type == b->type); - - if (a->type == POS_EXPR_INT) - { - switch (op) - { - case POS_OP_MULTIPLY: - a->d.int_val = a->d.int_val * b->d.int_val; - break; - case POS_OP_DIVIDE: - if (b->d.int_val == 0) - { - g_set_error (err, META_THEME_ERROR, - META_THEME_ERROR_DIVIDE_BY_ZERO, - _("Coordinate expression results in division by zero")); - return FALSE; - } - a->d.int_val = a->d.int_val / b->d.int_val; - break; - case POS_OP_MOD: - if (b->d.int_val == 0) - { - g_set_error (err, META_THEME_ERROR, - META_THEME_ERROR_DIVIDE_BY_ZERO, - _("Coordinate expression results in division by zero")); - return FALSE; - } - a->d.int_val = a->d.int_val % b->d.int_val; - break; - case POS_OP_ADD: - a->d.int_val = a->d.int_val + b->d.int_val; - break; - case POS_OP_SUBTRACT: - a->d.int_val = a->d.int_val - b->d.int_val; - break; - case POS_OP_MAX: - a->d.int_val = MAX (a->d.int_val, b->d.int_val); - break; - case POS_OP_MIN: - a->d.int_val = MIN (a->d.int_val, b->d.int_val); - break; - case POS_OP_NONE: - g_assert_not_reached (); - break; - } - } - else if (a->type == POS_EXPR_DOUBLE) - { - switch (op) - { - case POS_OP_MULTIPLY: - a->d.double_val = a->d.double_val * b->d.double_val; - break; - case POS_OP_DIVIDE: - if (b->d.double_val == 0.0) - { - g_set_error (err, META_THEME_ERROR, - META_THEME_ERROR_DIVIDE_BY_ZERO, - _("Coordinate expression results in division by zero")); - return FALSE; - } - a->d.double_val = a->d.double_val / b->d.double_val; - break; - case POS_OP_MOD: - g_set_error (err, META_THEME_ERROR, - META_THEME_ERROR_MOD_ON_FLOAT, - _("Coordinate expression tries to use mod operator on a floating-point number")); - return FALSE; - case POS_OP_ADD: - a->d.double_val = a->d.double_val + b->d.double_val; - break; - case POS_OP_SUBTRACT: - a->d.double_val = a->d.double_val - b->d.double_val; - break; - case POS_OP_MAX: - a->d.double_val = MAX (a->d.double_val, b->d.double_val); - break; - case POS_OP_MIN: - a->d.double_val = MIN (a->d.double_val, b->d.double_val); - break; - case POS_OP_NONE: - g_assert_not_reached (); - break; - } - } - else - g_assert_not_reached (); - - return TRUE; -} - -static gboolean -do_operations (PosExpr *exprs, - int *n_exprs, - int precedence, - GError **err) -{ - int i; - -#if 0 - 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] operator - * exprs[i+1] second operand - * - * we replace first operand with result of mul/div/mod, - * or skip over operator and second operand if we have - * an add/subtract - */ - - if (exprs[i-1].type == POS_EXPR_OPERATOR) - { - g_set_error (err, META_THEME_ERROR, - META_THEME_ERROR_FAILED, - _("Coordinate expression has an operator \"%s\" where an operand was expected"), - op_name (exprs[i-1].d.operator)); - return FALSE; - } - - if (exprs[i].type != POS_EXPR_OPERATOR) - { - g_set_error (err, META_THEME_ERROR, - META_THEME_ERROR_FAILED, - _("Coordinate expression had an operand where an operator was expected")); - return FALSE; - } - - if (i == (*n_exprs - 1)) - { - g_set_error (err, META_THEME_ERROR, - META_THEME_ERROR_FAILED, - _("Coordinate expression ended with an operator instead of an operand")); - return FALSE; - } - - g_assert ((i+1) < *n_exprs); - - if (exprs[i+1].type == POS_EXPR_OPERATOR) - { - g_set_error (err, META_THEME_ERROR, - META_THEME_ERROR_FAILED, - _("Coordinate expression has operator \"%c\" following operator \"%c\" with no operand in between"), - exprs[i+1].d.operator, - exprs[i].d.operator); - return FALSE; - } - - compress = FALSE; - - switch (precedence) - { - case 2: - switch (exprs[i].d.operator) - { - case POS_OP_DIVIDE: - case POS_OP_MOD: - case POS_OP_MULTIPLY: - compress = TRUE; - if (!do_operation (&exprs[i-1], &exprs[i+1], - exprs[i].d.operator, - err)) - return FALSE; - break; - } - 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 POS_OP_MAX: - case POS_OP_MIN: - compress = TRUE; - if (!do_operation (&exprs[i-1], &exprs[i+1], - exprs[i].d.operator, - err)) - return FALSE; - break; - } - break; - } - - if (compress) - { - /* exprs[i-1] first operand (now result) - * exprs[i] operator - * exprs[i+1] second operand - * exprs[i+2] new operator - * - * we move new operator just after first operand - */ - if ((i+2) < *n_exprs) - { - g_memmove (&exprs[i], &exprs[i+2], - sizeof (PosExpr) * (*n_exprs - i - 2)); - } - - *n_exprs -= 2; - } - else - { - /* Skip operator and next operand */ - i += 2; - } - } - - return TRUE; -} - -/** - * pos_eval_get_variable: - * @t: The token representing a variable - * @result: (out): The value of that variable; not set if the token did - * not represent a known variable - * @env: The environment within which t should be evaluated - * @err: (out): set to the problem if there was a problem - * - * There is a predefined set of variables which can appear in an expression. - * Here we take a token representing a variable, and return the current value - * of that variable in a particular environment. - * (The value is always an integer.) - * - * There are supposedly some circumstances in which this function can be - * called from outside Metacity, in which case env->theme will be %NULL, and - * therefore we can't use it to find out quark values, so we do the comparison - * using strcmp(), which is slower. - * - * FIXME: shouldn't @t be const? - * FIXME: we should perhaps consider some sort of lookup arrangement into an - * array; also, the duplication of code is unlovely; perhaps using glib - * string hashes instead of quarks would fix both problems? - * - * Returns: %TRUE if we found the variable asked for, %FALSE if we didn't - */ -static gboolean -pos_eval_get_variable (PosToken *t, - int *result, - const MetaPositionExprEnv *env, - GError **err) -{ - if (env->theme) - { - if (t->d.v.name_quark == env->theme->quark_width) - *result = env->rect.width; - else if (t->d.v.name_quark == env->theme->quark_height) - *result = env->rect.height; - else if (env->object_width >= 0 && - t->d.v.name_quark == env->theme->quark_object_width) - *result = env->object_width; - else if (env->object_height >= 0 && - t->d.v.name_quark == env->theme->quark_object_height) - *result = env->object_height; - else if (t->d.v.name_quark == env->theme->quark_left_width) - *result = env->left_width; - else if (t->d.v.name_quark == env->theme->quark_right_width) - *result = env->right_width; - else if (t->d.v.name_quark == env->theme->quark_top_height) - *result = env->top_height; - else if (t->d.v.name_quark == env->theme->quark_bottom_height) - *result = env->bottom_height; - else if (t->d.v.name_quark == env->theme->quark_mini_icon_width) - *result = env->mini_icon_width; - else if (t->d.v.name_quark == env->theme->quark_mini_icon_height) - *result = env->mini_icon_height; - else if (t->d.v.name_quark == env->theme->quark_icon_width) - *result = env->icon_width; - else if (t->d.v.name_quark == env->theme->quark_icon_height) - *result = env->icon_height; - else if (t->d.v.name_quark == env->theme->quark_title_width) - *result = env->title_width; - else if (t->d.v.name_quark == env->theme->quark_title_height) - *result = env->title_height; - else if (t->d.v.name_quark == env->theme->quark_frame_x_center) - *result = env->frame_x_center; - else if (t->d.v.name_quark == env->theme->quark_frame_y_center) - *result = env->frame_y_center; - else - { - g_set_error (err, META_THEME_ERROR, - META_THEME_ERROR_UNKNOWN_VARIABLE, - _("Coordinate expression had unknown variable or constant \"%s\""), - t->d.v.name); - return FALSE; - } - } - else - { - if (strcmp (t->d.v.name, "width") == 0) - *result = env->rect.width; - else if (strcmp (t->d.v.name, "height") == 0) - *result = env->rect.height; - else if (env->object_width >= 0 && - strcmp (t->d.v.name, "object_width") == 0) - *result = env->object_width; - else if (env->object_height >= 0 && - strcmp (t->d.v.name, "object_height") == 0) - *result = env->object_height; - else if (strcmp (t->d.v.name, "left_width") == 0) - *result = env->left_width; - else if (strcmp (t->d.v.name, "right_width") == 0) - *result = env->right_width; - else if (strcmp (t->d.v.name, "top_height") == 0) - *result = env->top_height; - else if (strcmp (t->d.v.name, "bottom_height") == 0) - *result = env->bottom_height; - else if (strcmp (t->d.v.name, "mini_icon_width") == 0) - *result = env->mini_icon_width; - else if (strcmp (t->d.v.name, "mini_icon_height") == 0) - *result = env->mini_icon_height; - else if (strcmp (t->d.v.name, "icon_width") == 0) - *result = env->icon_width; - else if (strcmp (t->d.v.name, "icon_height") == 0) - *result = env->icon_height; - else if (strcmp (t->d.v.name, "title_width") == 0) - *result = env->title_width; - else if (strcmp (t->d.v.name, "title_height") == 0) - *result = env->title_height; - else if (strcmp (t->d.v.name, "frame_x_center") == 0) - *result = env->frame_x_center; - else if (strcmp (t->d.v.name, "frame_y_center") == 0) - *result = env->frame_y_center; - else - { - g_set_error (err, META_THEME_ERROR, - META_THEME_ERROR_UNKNOWN_VARIABLE, - _("Coordinate expression had unknown variable or constant \"%s\""), - t->d.v.name); - return FALSE; - } - } - - return TRUE; -} - -/** - * pos_eval_helper: - * @tokens: A list of tokens to evaluate. - * @n_tokens: How many tokens are in the list. - * @env: The environment context in which to evaluate the expression. - * @result: (out): The current value of the expression - * - * Evaluates a sequence of tokens within a particular environment context, - * and returns the current value. May recur if parantheses are found. - * - * FIXME: Yes, we really do reparse the expression every time it's evaluated. - * We should keep the parse tree around all the time and just - * run the new values through it. - */ -static gboolean -pos_eval_helper (PosToken *tokens, - int n_tokens, - const MetaPositionExprEnv *env, - PosExpr *result, - GError **err) -{ - /* Lazy-ass hardcoded limit on number of terms in expression */ -#define MAX_EXPRS 32 - int paren_level; - int first_paren; - int i; - PosExpr exprs[MAX_EXPRS]; - int n_exprs; - int precedence; - - /* Our first goal is to get a list of PosExpr, essentially - * substituting variables and handling parentheses. - */ - - first_paren = 0; - paren_level = 0; - n_exprs = 0; - for (i = 0; i < n_tokens; i++) - { - PosToken *t = &tokens[i]; - - if (n_exprs >= MAX_EXPRS) - { - g_set_error (err, META_THEME_ERROR, - META_THEME_ERROR_FAILED, - _("Coordinate expression parser overflowed its buffer.")); - return FALSE; - } - - if (paren_level == 0) - { - switch (t->type) - { - case POS_TOKEN_INT: - exprs[n_exprs].type = POS_EXPR_INT; - exprs[n_exprs].d.int_val = t->d.i.val; - ++n_exprs; - break; - - case POS_TOKEN_DOUBLE: - exprs[n_exprs].type = POS_EXPR_DOUBLE; - exprs[n_exprs].d.double_val = t->d.d.val; - ++n_exprs; - break; - - case POS_TOKEN_OPEN_PAREN: - ++paren_level; - if (paren_level == 1) - first_paren = i; - break; - - case POS_TOKEN_CLOSE_PAREN: - g_set_error (err, META_THEME_ERROR, - META_THEME_ERROR_BAD_PARENS, - _("Coordinate expression had a close parenthesis with no open parenthesis")); - return FALSE; - - case POS_TOKEN_VARIABLE: - exprs[n_exprs].type = POS_EXPR_INT; - - /* FIXME we should just dump all this crap - * in a hash, maybe keep width/height out - * for optimization purposes - */ - if (!pos_eval_get_variable (t, &exprs[n_exprs].d.int_val, env, err)) - return FALSE; - - ++n_exprs; - break; - - case POS_TOKEN_OPERATOR: - exprs[n_exprs].type = POS_EXPR_OPERATOR; - exprs[n_exprs].d.operator = t->d.o.op; - ++n_exprs; - break; - } - } - else - { - g_assert (paren_level > 0); - - switch (t->type) - { - case POS_TOKEN_INT: - case POS_TOKEN_DOUBLE: - case POS_TOKEN_VARIABLE: - case POS_TOKEN_OPERATOR: - break; - - case POS_TOKEN_OPEN_PAREN: - ++paren_level; - break; - - case POS_TOKEN_CLOSE_PAREN: - if (paren_level == 1) - { - /* We closed a toplevel paren group, so recurse */ - if (!pos_eval_helper (&tokens[first_paren+1], - i - first_paren - 1, - env, - &exprs[n_exprs], - err)) - return FALSE; - - ++n_exprs; - } - - --paren_level; - break; - - } - } - } - - if (paren_level > 0) - { - g_set_error (err, META_THEME_ERROR, - META_THEME_ERROR_BAD_PARENS, - _("Coordinate expression had an open parenthesis with no close parenthesis")); - return FALSE; - } - - /* Now we have no parens and no vars; so we just do all the multiplies - * and divides, then all the add and subtract. - */ - if (n_exprs == 0) - { - g_set_error (err, META_THEME_ERROR, - META_THEME_ERROR_FAILED, - _("Coordinate expression doesn't seem to have any operators or operands")); - return FALSE; - } - - /* precedence 1 ops */ - precedence = 2; - while (precedence >= 0) - { - if (!do_operations (exprs, &n_exprs, precedence, err)) - return FALSE; - --precedence; - } - - g_assert (n_exprs == 1); - - *result = *exprs; - - return TRUE; -} - -/* - * expr = int | double | expr * expr | expr / expr | - * expr + expr | expr - expr | (expr) - * - * so very not worth fooling with bison, yet so very painful by hand. - */ - -/** - * pos_eval: - * @spec: The expression to evaluate. - * @env: The environment context to evaluate the expression in. - * @val_p: (out): The integer value of the expression; if the expression - * is of type float, this will be rounded. If we return - * %FALSE because the expression is invalid, this will be - * zero. - * @err: (out): The error, if anything went wrong. - * - * Evaluates an expression. - * - * FIXME: Shouldn't @spec be const? - * - * Returns: %TRUE if we evaluated the expression successfully; %FALSE otherwise. - */ -static gboolean -pos_eval (MetaDrawSpec *spec, - const MetaPositionExprEnv *env, - int *val_p, - GError **err) -{ - PosExpr expr; - - *val_p = 0; - - if (pos_eval_helper (spec->tokens, spec->n_tokens, env, &expr, err)) - { - switch (expr.type) - { - case POS_EXPR_INT: - *val_p = expr.d.int_val; - break; - case POS_EXPR_DOUBLE: - *val_p = expr.d.double_val; - break; - case POS_EXPR_OPERATOR: - g_assert_not_reached (); - break; - } - return TRUE; - } - else - { - return FALSE; - } -} - -/* We always return both X and Y, but only one will be meaningful in - * most contexts. - */ - -/** - * meta_parse_position_expression: (skip) - * - */ -gboolean -meta_parse_position_expression (MetaDrawSpec *spec, - const MetaPositionExprEnv *env, - int *x_return, - int *y_return, - GError **err) -{ - /* All positions are in a coordinate system with x, y at the origin. - * The expression can have -, +, *, / as operators, floating point - * or integer constants, and the variables "width" and "height" and - * optionally "object_width" and object_height". Negative numbers - * aren't allowed. - */ - int val; - - if (spec->constant) - val = spec->value; - else - { - if (pos_eval (spec, env, &spec->value, err) == FALSE) - { - g_assert (err == NULL || *err != NULL); - return FALSE; - } - - val = spec->value; - } - - if (x_return) - *x_return = env->rect.x + val; - if (y_return) - *y_return = env->rect.y + val; - - return TRUE; -} - - -/** - * meta_parse_size_expression: (skip) - * - */ -gboolean -meta_parse_size_expression (MetaDrawSpec *spec, - const MetaPositionExprEnv *env, - int *val_return, - GError **err) -{ - int val; - - if (spec->constant) - val = spec->value; - else - { - if (pos_eval (spec, env, &spec->value, err) == FALSE) - { - g_assert (err == NULL || *err != NULL); - return FALSE; - } - - val = spec->value; - } - - if (val_return) - *val_return = MAX (val, 1); /* require that sizes be at least 1x1 */ - - return TRUE; -} - -/* To do this we tokenize, replace variable tokens - * that are constants, then reassemble. The purpose - * here is to optimize expressions so we don't do hash - * lookups to eval them. Obviously it's a tradeoff that - * slows down theme load times. - */ -gboolean -meta_theme_replace_constants (MetaTheme *theme, - PosToken *tokens, - int n_tokens, - GError **err) -{ - int i; - double dval; - int ival; - gboolean is_constant = TRUE; - - /* Loop through tokenized string looking for variables to replace */ - for (i = 0; i < n_tokens; i++) - { - PosToken *t = &tokens[i]; - - if (t->type == POS_TOKEN_VARIABLE) - { - if (meta_theme_lookup_int_constant (theme, t->d.v.name, &ival)) - { - g_free (t->d.v.name); - t->type = POS_TOKEN_INT; - t->d.i.val = ival; - } - else if (meta_theme_lookup_float_constant (theme, t->d.v.name, &dval)) - { - g_free (t->d.v.name); - t->type = POS_TOKEN_DOUBLE; - t->d.d.val = dval; - } - else - { - /* If we've found a variable that cannot be replaced then the - expression is not a constant expression and we want to - replace it with a GQuark */ - - t->d.v.name_quark = g_quark_from_string (t->d.v.name); - is_constant = FALSE; - } - } - } - - return is_constant; -} - -static int -parse_x_position_unchecked (MetaDrawSpec *spec, - const MetaPositionExprEnv *env) -{ - int retval; - GError *error; - - retval = 0; - error = NULL; - if (!meta_parse_position_expression (spec, env, &retval, NULL, &error)) - { - meta_warning (_("Theme contained an expression that resulted in an error: %s\n"), - error->message); - - g_error_free (error); - } - - return retval; -} - -static int -parse_y_position_unchecked (MetaDrawSpec *spec, - const MetaPositionExprEnv *env) -{ - int retval; - GError *error; - - retval = 0; - error = NULL; - if (!meta_parse_position_expression (spec, env, NULL, &retval, &error)) - { - meta_warning (_("Theme contained an expression that resulted in an error: %s\n"), - error->message); - - g_error_free (error); - } - - return retval; -} - -static int -parse_size_unchecked (MetaDrawSpec *spec, - MetaPositionExprEnv *env) -{ - int retval; - GError *error; - - retval = 0; - error = NULL; - if (!meta_parse_size_expression (spec, env, &retval, &error)) - { - meta_warning (_("Theme contained an expression that resulted in an error: %s\n"), - error->message); - - g_error_free (error); - } - - return retval; -} - -void -meta_draw_spec_free (MetaDrawSpec *spec) -{ - if (!spec) return; - free_tokens (spec->tokens, spec->n_tokens); - g_slice_free (MetaDrawSpec, spec); -} - -/** - * meta_draw_spec_new: (skip) - * - */ -MetaDrawSpec * -meta_draw_spec_new (MetaTheme *theme, - const char *expr, - GError **error) -{ - MetaDrawSpec *spec; - - spec = g_slice_new0 (MetaDrawSpec); - - pos_tokenize (expr, &spec->tokens, &spec->n_tokens, NULL); - - spec->constant = meta_theme_replace_constants (theme, spec->tokens, - spec->n_tokens, NULL); - if (spec->constant) - { - gboolean result; - - result = pos_eval (spec, NULL, &spec->value, error); - if (result == FALSE) - { - meta_draw_spec_free (spec); - return NULL; - } - } - - return spec; -} - -/** - * meta_draw_op_new: (skip) - * - */ -MetaDrawOp* -meta_draw_op_new (MetaDrawType type) -{ - MetaDrawOp *op; - MetaDrawOp dummy; - int size; - - size = G_STRUCT_OFFSET (MetaDrawOp, data); - - switch (type) - { - case META_DRAW_LINE: - size += sizeof (dummy.data.line); - break; - - case META_DRAW_RECTANGLE: - size += sizeof (dummy.data.rectangle); - break; - - case META_DRAW_ARC: - 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; - - case META_DRAW_GRADIENT: - size += sizeof (dummy.data.gradient); - break; - - case META_DRAW_IMAGE: - size += sizeof (dummy.data.image); - break; - - case META_DRAW_GTK_ARROW: - size += sizeof (dummy.data.gtk_arrow); - break; - - case META_DRAW_GTK_BOX: - size += sizeof (dummy.data.gtk_box); - break; - - 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; - case META_DRAW_TILE: - size += sizeof (dummy.data.tile); - break; - } - - op = g_malloc0 (size); - - op->type = type; - - return op; -} - -void -meta_draw_op_free (MetaDrawOp *op) -{ - g_return_if_fail (op != NULL); - - switch (op->type) - { - case META_DRAW_LINE: - if (op->data.line.color_spec) - meta_color_spec_free (op->data.line.color_spec); - - meta_draw_spec_free (op->data.line.x1); - meta_draw_spec_free (op->data.line.y1); - meta_draw_spec_free (op->data.line.x2); - meta_draw_spec_free (op->data.line.y2); - break; - - case META_DRAW_RECTANGLE: - if (op->data.rectangle.color_spec) - g_free (op->data.rectangle.color_spec); - - meta_draw_spec_free (op->data.rectangle.x); - meta_draw_spec_free (op->data.rectangle.y); - meta_draw_spec_free (op->data.rectangle.width); - meta_draw_spec_free (op->data.rectangle.height); - break; - - case META_DRAW_ARC: - if (op->data.arc.color_spec) - g_free (op->data.arc.color_spec); - - meta_draw_spec_free (op->data.arc.x); - meta_draw_spec_free (op->data.arc.y); - meta_draw_spec_free (op->data.arc.width); - meta_draw_spec_free (op->data.arc.height); - break; - - case META_DRAW_CLIP: - meta_draw_spec_free (op->data.clip.x); - meta_draw_spec_free (op->data.clip.y); - meta_draw_spec_free (op->data.clip.width); - meta_draw_spec_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); - - if (op->data.tint.alpha_spec) - meta_alpha_gradient_spec_free (op->data.tint.alpha_spec); - - meta_draw_spec_free (op->data.tint.x); - meta_draw_spec_free (op->data.tint.y); - meta_draw_spec_free (op->data.tint.width); - meta_draw_spec_free (op->data.tint.height); - break; - - case META_DRAW_GRADIENT: - if (op->data.gradient.gradient_spec) - meta_gradient_spec_free (op->data.gradient.gradient_spec); - - if (op->data.gradient.alpha_spec) - meta_alpha_gradient_spec_free (op->data.gradient.alpha_spec); - - meta_draw_spec_free (op->data.gradient.x); - meta_draw_spec_free (op->data.gradient.y); - meta_draw_spec_free (op->data.gradient.width); - meta_draw_spec_free (op->data.gradient.height); - break; - - case META_DRAW_IMAGE: - if (op->data.image.pixbuf) - g_object_unref (G_OBJECT (op->data.image.pixbuf)); - - meta_draw_spec_free (op->data.image.x); - meta_draw_spec_free (op->data.image.y); - meta_draw_spec_free (op->data.image.width); - meta_draw_spec_free (op->data.image.height); - break; - - case META_DRAW_GTK_ARROW: - meta_draw_spec_free (op->data.gtk_arrow.x); - meta_draw_spec_free (op->data.gtk_arrow.y); - meta_draw_spec_free (op->data.gtk_arrow.width); - meta_draw_spec_free (op->data.gtk_arrow.height); - break; - - case META_DRAW_GTK_BOX: - meta_draw_spec_free (op->data.gtk_box.x); - meta_draw_spec_free (op->data.gtk_box.y); - meta_draw_spec_free (op->data.gtk_box.width); - meta_draw_spec_free (op->data.gtk_box.height); - break; - - case META_DRAW_GTK_VLINE: - meta_draw_spec_free (op->data.gtk_vline.x); - meta_draw_spec_free (op->data.gtk_vline.y1); - meta_draw_spec_free (op->data.gtk_vline.y2); - break; - - case META_DRAW_ICON: - meta_draw_spec_free (op->data.icon.x); - meta_draw_spec_free (op->data.icon.y); - meta_draw_spec_free (op->data.icon.width); - meta_draw_spec_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); - - meta_draw_spec_free (op->data.title.x); - meta_draw_spec_free (op->data.title.y); - if (op->data.title.ellipsize_width) - meta_draw_spec_free (op->data.title.ellipsize_width); - break; - - case META_DRAW_OP_LIST: - if (op->data.op_list.op_list) - meta_draw_op_list_unref (op->data.op_list.op_list); - - meta_draw_spec_free (op->data.op_list.x); - meta_draw_spec_free (op->data.op_list.y); - meta_draw_spec_free (op->data.op_list.width); - meta_draw_spec_free (op->data.op_list.height); - break; - - case META_DRAW_TILE: - if (op->data.tile.op_list) - meta_draw_op_list_unref (op->data.tile.op_list); - - meta_draw_spec_free (op->data.tile.x); - meta_draw_spec_free (op->data.tile.y); - meta_draw_spec_free (op->data.tile.width); - meta_draw_spec_free (op->data.tile.height); - meta_draw_spec_free (op->data.tile.tile_xoffset); - meta_draw_spec_free (op->data.tile.tile_yoffset); - meta_draw_spec_free (op->data.tile.tile_width); - meta_draw_spec_free (op->data.tile.tile_height); - break; - } - - g_free (op); -} - -static void -draw_image (cairo_t *cr, - GdkPixbuf *src, - MetaImageFillType fill_type, - int x, - int y, - int width, - int height) -{ - cairo_save (cr); - - cairo_rectangle (cr, x, y, width, height); - - if (fill_type == META_IMAGE_FILL_TILE) - { - gdk_cairo_set_source_pixbuf (cr, src, 0, 0); - - cairo_pattern_set_extend (cairo_get_source (cr), - CAIRO_EXTEND_REPEAT); - } - else - { - float pixbuf_width, pixbuf_height; - - pixbuf_width = gdk_pixbuf_get_width (src); - pixbuf_height = gdk_pixbuf_get_height (src); - - cairo_translate (cr, x, y); - cairo_scale (cr, - pixbuf_width / width, - pixbuf_height / height); - - gdk_cairo_set_source_pixbuf (cr, src, 0, 0); - } - - cairo_fill (cr); - - cairo_restore (cr); -} - -static void -fill_env (MetaPositionExprEnv *env, - const MetaDrawInfo *info, - MetaRectangle logical_region) -{ - /* FIXME this stuff could be raised into draw_op_list_draw() probably - */ - env->rect = logical_region; - env->object_width = -1; - env->object_height = -1; - if (info->fgeom) - { - env->left_width = info->fgeom->borders.visible.left; - env->right_width = info->fgeom->borders.visible.right; - env->top_height = info->fgeom->borders.visible.top; - env->bottom_height = info->fgeom->borders.visible.bottom; - env->frame_x_center = info->fgeom->width / 2 - logical_region.x; - env->frame_y_center = info->fgeom->height / 2 - logical_region.y; - } - else - { - env->left_width = 0; - env->right_width = 0; - env->top_height = 0; - env->bottom_height = 0; - env->frame_x_center = 0; - env->frame_y_center = 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 = meta_current_theme; -} - - -/* This code was originally rendering anti-aliased using X primitives, and - * now has been switched to draw anti-aliased using cairo. In general, the - * closest correspondence between X rendering and cairo rendering is given - * by offsetting the geometry by 0.5 pixels in both directions before rendering - * with cairo. This is because X samples at the upper left corner of the - * pixel while cairo averages over the entire pixel. However, in the cases - * where the X rendering was an exact rectangle with no "jaggies" - * we need to be a bit careful about applying the offset. We want to produce - * the exact same pixel-aligned rectangle, rather than a rectangle with - * fuzz around the edges. - */ -static void -meta_draw_op_draw_with_env (const MetaDrawOp *op, - GtkStyleContext *style_gtk, - cairo_t *cr, - const MetaDrawInfo *info, - MetaRectangle rect, - MetaPositionExprEnv *env) -{ - GdkRGBA color; - - cairo_save (cr); - gtk_style_context_save (style_gtk); - - cairo_set_line_width (cr, 1.0); - - switch (op->type) - { - case META_DRAW_LINE: - { - int x1, x2, y1, y2; - - meta_color_spec_render (op->data.line.color_spec, style_gtk, &color); - gdk_cairo_set_source_rgba (cr, &color); - - if (op->data.line.width > 0) - cairo_set_line_width (cr, op->data.line.width); - - if (op->data.line.dash_on_length > 0 && - op->data.line.dash_off_length > 0) - { - double dash_list[2]; - dash_list[0] = op->data.line.dash_on_length; - dash_list[1] = op->data.line.dash_off_length; - cairo_set_dash (cr, dash_list, 2, 0); - } - - x1 = parse_x_position_unchecked (op->data.line.x1, env); - y1 = parse_y_position_unchecked (op->data.line.y1, env); - - if (!op->data.line.x2 && - !op->data.line.y2 && - op->data.line.width==0) - { - cairo_rectangle (cr, x1, y1, 1, 1); - cairo_fill (cr); - } - else - { - if (op->data.line.x2) - x2 = parse_x_position_unchecked (op->data.line.x2, env); - else - x2 = x1; - - if (op->data.line.y2) - y2 = parse_y_position_unchecked (op->data.line.y2, env); - else - y2 = y1; - - /* This is one of the cases where we are matching the exact - * pixel aligned rectangle produced by X; for zero-width lines - * the generic algorithm produces the right result so we don't - * need to handle them here. - */ - if ((y1 == y2 || x1 == x2) && op->data.line.width != 0) - { - double offset = op->data.line.width % 2 ? .5 : 0; - - if (y1 == y2) - { - cairo_move_to (cr, x1, y1 + offset); - cairo_line_to (cr, x2, y2 + offset); - } - else - { - cairo_move_to (cr, x1 + offset, y1); - cairo_line_to (cr, x2 + offset, y2); - } - } - else - { - /* zero-width lines include both end-points in X, unlike wide lines */ - if (op->data.line.width == 0) - cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE); - - cairo_move_to (cr, x1 + .5, y1 + .5); - cairo_line_to (cr, x2 + .5, y2 + .5); - } - cairo_stroke (cr); - } - } - break; - - case META_DRAW_RECTANGLE: - { - int rx, ry, rwidth, rheight; - - meta_color_spec_render (op->data.rectangle.color_spec, - style_gtk, &color); - gdk_cairo_set_source_rgba (cr, &color); - - rx = parse_x_position_unchecked (op->data.rectangle.x, env); - ry = parse_y_position_unchecked (op->data.rectangle.y, env); - rwidth = parse_size_unchecked (op->data.rectangle.width, env); - rheight = parse_size_unchecked (op->data.rectangle.height, env); - - /* Filled and stroked rectangles are the other cases - * we pixel-align to X rasterization - */ - if (op->data.rectangle.filled) - { - cairo_rectangle (cr, rx, ry, rwidth, rheight); - cairo_fill (cr); - } - else - { - cairo_rectangle (cr, rx + .5, ry + .5, rwidth, rheight); - cairo_stroke (cr); - } - } - break; - - case META_DRAW_ARC: - { - int rx, ry, rwidth, rheight; - double start_angle, end_angle; - double center_x, center_y; - - meta_color_spec_render (op->data.arc.color_spec, style_gtk, &color); - gdk_cairo_set_source_rgba (cr, &color); - - rx = parse_x_position_unchecked (op->data.arc.x, env); - ry = parse_y_position_unchecked (op->data.arc.y, env); - rwidth = parse_size_unchecked (op->data.arc.width, env); - rheight = parse_size_unchecked (op->data.arc.height, env); - - start_angle = op->data.arc.start_angle * (M_PI / 180.) - - (.5 * M_PI); /* start at 12 instead of 3 oclock */ - end_angle = start_angle + op->data.arc.extent_angle * (M_PI / 180.); - center_x = rx + (double)rwidth / 2. + .5; - center_y = ry + (double)rheight / 2. + .5; - - cairo_save (cr); - - cairo_translate (cr, center_x, center_y); - cairo_scale (cr, (double)rwidth / 2., (double)rheight / 2.); - - if (op->data.arc.extent_angle >= 0) - cairo_arc (cr, 0, 0, 1, start_angle, end_angle); - else - cairo_arc_negative (cr, 0, 0, 1, start_angle, end_angle); - - cairo_restore (cr); - - if (op->data.arc.filled) - { - cairo_line_to (cr, center_x, center_y); - cairo_fill (cr); - } - else - cairo_stroke (cr); - } - 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); - - meta_color_spec_render (op->data.tint.color_spec, - style_gtk, &color); - - if (op->data.tint.alpha_spec && - op->data.tint.alpha_spec->n_alphas == 1) - color.alpha = op->data.tint.alpha_spec->alphas[0]; - - gdk_cairo_set_source_rgba (cr, &color); - - cairo_rectangle (cr, rx, ry, rwidth, rheight); - cairo_fill (cr); - } - break; - - case META_DRAW_GRADIENT: - { - int rx, ry, rwidth, rheight; - - 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); - - meta_gradient_spec_render (op->data.gradient.gradient_spec, - op->data.gradient.alpha_spec, - cr, style_gtk, - rx, ry, rwidth, rheight); - } - break; - - case META_DRAW_IMAGE: - { - int rx, ry, rwidth, rheight; - - if (op->data.image.pixbuf == NULL) - break; - - env->object_width = gdk_pixbuf_get_width (op->data.image.pixbuf); - env->object_height = gdk_pixbuf_get_height (op->data.image.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); - - draw_image (cr, - op->data.image.pixbuf, - op->data.image.fill_type, - rwidth, rheight, rx, ry); - } - break; - - case META_DRAW_GTK_ARROW: - { - int rx, ry, rwidth, rheight; - double angle = 0, size; - - rx = parse_x_position_unchecked (op->data.gtk_arrow.x, env); - ry = parse_y_position_unchecked (op->data.gtk_arrow.y, env); - rwidth = parse_size_unchecked (op->data.gtk_arrow.width, env); - rheight = parse_size_unchecked (op->data.gtk_arrow.height, env); - - size = MAX(rwidth, rheight); - - switch (op->data.gtk_arrow.arrow) - { - case GTK_ARROW_UP: - angle = 0; - break; - case GTK_ARROW_RIGHT: - angle = M_PI / 2; - break; - case GTK_ARROW_DOWN: - angle = M_PI; - break; - case GTK_ARROW_LEFT: - angle = 3 * M_PI / 2; - break; - case GTK_ARROW_NONE: - return; - } - - gtk_style_context_set_state (style_gtk, op->data.gtk_arrow.state); - gtk_render_arrow (style_gtk, cr, angle, rx, ry, size); - } - break; - - 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); - - gtk_style_context_set_state (style_gtk, op->data.gtk_box.state); - gtk_render_background (style_gtk, cr, rx, ry, rwidth, rheight); - gtk_render_frame (style_gtk, cr, rx, ry, rwidth, rheight); - } - break; - - 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); - - gtk_style_context_set_state (style_gtk, op->data.gtk_vline.state); - gtk_render_line (style_gtk, cr, rx, ry1, rx, ry2); - } - break; - - case META_DRAW_ICON: - { - int rx, ry, rwidth, rheight; - GdkPixbuf *src; - - rwidth = parse_size_unchecked (op->data.icon.width, env); - rheight = parse_size_unchecked (op->data.icon.height, env); - - if (info->mini_icon && - rwidth < gdk_pixbuf_get_width (info->mini_icon) && - rheight < gdk_pixbuf_get_height (info->mini_icon)) - src = info->mini_icon; - else if (info->icon) - src = info->icon; - else - break; - - rx = parse_x_position_unchecked (op->data.icon.x, env); - ry = parse_y_position_unchecked (op->data.icon.y, env); - - draw_image (cr, src, - op->data.icon.fill_type, - rwidth, rheight, rx, ry); - } - break; - - case META_DRAW_TITLE: - if (info->title_layout) - { - int rx, ry; - PangoRectangle ink_rect, logical_rect; - - meta_color_spec_render (op->data.title.color_spec, - style_gtk, &color); - gdk_cairo_set_source_rgba (cr, &color); - - rx = parse_x_position_unchecked (op->data.title.x, env); - ry = parse_y_position_unchecked (op->data.title.y, env); - - if (op->data.title.ellipsize_width) - { - int ellipsize_width; - int right_bearing; - - ellipsize_width = parse_x_position_unchecked (op->data.title.ellipsize_width, env); - /* HACK: parse_x_position_unchecked adds in env->rect.x, subtract out again */ - ellipsize_width -= env->rect.x; - - pango_layout_set_width (info->title_layout, -1); - pango_layout_get_pixel_extents (info->title_layout, - &ink_rect, &logical_rect); - - /* Pango's idea of ellipsization is with respect to the logical rect. - * correct for this, by reducing the ellipsization width by the overflow - * of the un-ellipsized text on the right... it's always the visual - * right we want regardless of bidi, since since the X we pass in to - * cairo_move_to() is always the left edge of the line. - */ - right_bearing = (ink_rect.x + ink_rect.width) - (logical_rect.x + logical_rect.width); - right_bearing = MAX (right_bearing, 0); - - ellipsize_width -= right_bearing; - ellipsize_width = MAX (ellipsize_width, 0); - - /* Only ellipsizing when necessary is a performance optimization - - * pango_layout_set_width() will force a relayout if it isn't the - * same as the current width of -1. - */ - if (ellipsize_width < logical_rect.width) - pango_layout_set_width (info->title_layout, PANGO_SCALE * ellipsize_width); - } - - cairo_move_to (cr, rx, ry); - pango_cairo_show_layout (cr, info->title_layout); - - /* Remove any ellipsization we might have set; will short-circuit - * if the width is already -1 */ - pango_layout_set_width (info->title_layout, -1); - } - break; - - case META_DRAW_OP_LIST: - { - MetaRectangle d_rect; - - d_rect.x = parse_x_position_unchecked (op->data.op_list.x, env); - d_rect.y = parse_y_position_unchecked (op->data.op_list.y, env); - d_rect.width = parse_size_unchecked (op->data.op_list.width, env); - d_rect.height = parse_size_unchecked (op->data.op_list.height, env); - - meta_draw_op_list_draw_with_style (op->data.op_list.op_list, - style_gtk, cr, info, d_rect); - } - break; - - case META_DRAW_TILE: - { - int rx, ry, rwidth, rheight; - int tile_xoffset, tile_yoffset; - MetaRectangle tile; - - rx = parse_x_position_unchecked (op->data.tile.x, env); - ry = parse_y_position_unchecked (op->data.tile.y, env); - rwidth = parse_size_unchecked (op->data.tile.width, env); - rheight = parse_size_unchecked (op->data.tile.height, env); - - cairo_save (cr); - - cairo_rectangle (cr, rx, ry, rwidth, rheight); - cairo_clip (cr); - - tile_xoffset = parse_x_position_unchecked (op->data.tile.tile_xoffset, env); - tile_yoffset = parse_y_position_unchecked (op->data.tile.tile_yoffset, env); - /* tile offset should not include x/y */ - tile_xoffset -= rect.x; - tile_yoffset -= rect.y; - - tile.width = parse_size_unchecked (op->data.tile.tile_width, env); - tile.height = parse_size_unchecked (op->data.tile.tile_height, env); - - tile.x = rx - tile_xoffset; - - while (tile.x < (rx + rwidth)) - { - tile.y = ry - tile_yoffset; - while (tile.y < (ry + rheight)) - { - meta_draw_op_list_draw_with_style (op->data.tile.op_list, - style_gtk, cr, info, tile); - - tile.y += tile.height; - } - - tile.x += tile.width; - } - cairo_restore (cr); - - } - break; - } - - cairo_restore (cr); - gtk_style_context_restore (style_gtk); -} - -void -meta_draw_op_draw_with_style (const MetaDrawOp *op, - GtkStyleContext *style_gtk, - cairo_t *cr, - const MetaDrawInfo *info, - MetaRectangle logical_region) -{ - MetaPositionExprEnv env; - - fill_env (&env, info, logical_region); - - meta_draw_op_draw_with_env (op, style_gtk, cr, - info, logical_region, - &env); - -} - -/** - * meta_draw_op_list_new: (skip) - * - */ -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; -} - -void -meta_draw_op_list_ref (MetaDrawOpList *op_list) -{ - g_return_if_fail (op_list != NULL); - - op_list->refcount += 1; -} - -void -meta_draw_op_list_unref (MetaDrawOpList *op_list) -{ - g_return_if_fail (op_list != NULL); - g_return_if_fail (op_list->refcount > 0); - - op_list->refcount -= 1; - - if (op_list->refcount == 0) - { - int i; - - for (i = 0; i < op_list->n_ops; i++) - meta_draw_op_free (op_list->ops[i]); - - g_free (op_list->ops); - - DEBUG_FILL_STRUCT (op_list); - g_free (op_list); - } -} - -void -meta_draw_op_list_draw_with_style (const MetaDrawOpList *op_list, - GtkStyleContext *style_gtk, - cairo_t *cr, - const MetaDrawInfo *info, - MetaRectangle rect) -{ - int i; - MetaPositionExprEnv env; - - if (op_list->n_ops == 0) - return; - - fill_env (&env, info, rect); - - cairo_save (cr); - - for (i = 0; i < op_list->n_ops; i++) - { - MetaDrawOp *op = op_list->ops[i]; - - if (op->type == META_DRAW_CLIP) - { - cairo_restore (cr); - - cairo_rectangle (cr, - parse_x_position_unchecked (op->data.clip.x, &env), - parse_y_position_unchecked (op->data.clip.y, &env), - parse_size_unchecked (op->data.clip.width, &env), - parse_size_unchecked (op->data.clip.height, &env)); - cairo_clip (cr); - - cairo_save (cr); - } - else if (gdk_cairo_get_clip_rectangle (cr, NULL)) - { - meta_draw_op_draw_with_env (op, - style_gtk, cr, info, - rect, - &env); - } - } - - cairo_restore (cr); -} - -void -meta_draw_op_list_append (MetaDrawOpList *op_list, - MetaDrawOp *op) -{ - if (op_list->n_ops == op_list->n_allocated) - { - op_list->n_allocated *= 2; - op_list->ops = g_renew (MetaDrawOp*, op_list->ops, op_list->n_allocated); - } - - op_list->ops[op_list->n_ops] = op; - 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 */ - - for (i = 0; i < op_list->n_ops; i++) - { - 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; - } - else if (op_list->ops[i]->type == META_DRAW_TILE) - { - if (op_list->ops[i]->data.tile.op_list == child) - return TRUE; - - if (meta_draw_op_list_contains (op_list->ops[i]->data.tile.op_list, - child)) - return TRUE; - } - } - - return FALSE; -} - -/** - * meta_frame_style_new: - * @parent: The parent style. Data not filled in here will be - * looked for in the parent style, and in its parent - * style, and so on. - * - * Constructor for a MetaFrameStyle. - * - * Returns: (transfer full): The newly-constructed style. - */ -MetaFrameStyle* -meta_frame_style_new (MetaFrameStyle *parent) -{ - MetaFrameStyle *style; - - style = g_new0 (MetaFrameStyle, 1); - - style->refcount = 1; - - /* Default alpha is fully opaque */ - style->window_background_alpha = 255; - - style->parent = parent; - if (parent) - meta_frame_style_ref (parent); - - return style; -} - -/** - * meta_frame_style_ref: - * @style: The style. - * - * Increases the reference count of a frame style. - */ -void -meta_frame_style_ref (MetaFrameStyle *style) -{ - g_return_if_fail (style != NULL); - - style->refcount += 1; -} - -static void -free_button_ops (MetaDrawOpList *op_lists[META_BUTTON_TYPE_LAST][META_BUTTON_STATE_LAST]) -{ - int i, j; - - for (i = 0; i < META_BUTTON_TYPE_LAST; i++) - for (j = 0; j < META_BUTTON_STATE_LAST; j++) - if (op_lists[i][j]) - meta_draw_op_list_unref (op_lists[i][j]); -} - -void -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->buttons); - - for (i = 0; i < META_FRAME_PIECE_LAST; i++) - if (style->pieces[i]) - meta_draw_op_list_unref (style->pieces[i]); - - if (style->layout) - meta_frame_layout_unref (style->layout); - - if (style->window_background_color) - meta_color_spec_free (style->window_background_color); - - /* 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 MetaButtonState -map_button_state (MetaButtonType button_type, - const MetaFrameGeometry *fgeom, - int middle_bg_offset, - MetaButtonState button_states[META_BUTTON_TYPE_LAST]) -{ - MetaButtonFunction function = META_BUTTON_FUNCTION_LAST; - - switch (button_type) - { - /* First hande functions, which map directly */ - case META_BUTTON_TYPE_SHADE: - case META_BUTTON_TYPE_ABOVE: - case META_BUTTON_TYPE_STICK: - case META_BUTTON_TYPE_UNSHADE: - case META_BUTTON_TYPE_UNABOVE: - case META_BUTTON_TYPE_UNSTICK: - case META_BUTTON_TYPE_MENU: - case META_BUTTON_TYPE_MINIMIZE: - case META_BUTTON_TYPE_MAXIMIZE: - case META_BUTTON_TYPE_CLOSE: - return button_states[button_type]; - - /* Map position buttons to the corresponding function */ - case META_BUTTON_TYPE_RIGHT_LEFT_BACKGROUND: - case META_BUTTON_TYPE_RIGHT_SINGLE_BACKGROUND: - if (fgeom->n_right_buttons > 0) - function = fgeom->button_layout.right_buttons[0]; - break; - case META_BUTTON_TYPE_RIGHT_RIGHT_BACKGROUND: - if (fgeom->n_right_buttons > 0) - function = fgeom->button_layout.right_buttons[fgeom->n_right_buttons - 1]; - break; - case META_BUTTON_TYPE_RIGHT_MIDDLE_BACKGROUND: - if (middle_bg_offset + 1 < fgeom->n_right_buttons) - function = fgeom->button_layout.right_buttons[middle_bg_offset + 1]; - break; - case META_BUTTON_TYPE_LEFT_LEFT_BACKGROUND: - case META_BUTTON_TYPE_LEFT_SINGLE_BACKGROUND: - if (fgeom->n_left_buttons > 0) - function = fgeom->button_layout.left_buttons[0]; - break; - case META_BUTTON_TYPE_LEFT_RIGHT_BACKGROUND: - if (fgeom->n_left_buttons > 0) - function = fgeom->button_layout.left_buttons[fgeom->n_left_buttons - 1]; - break; - case META_BUTTON_TYPE_LEFT_MIDDLE_BACKGROUND: - if (middle_bg_offset + 1 < fgeom->n_left_buttons) - function = fgeom->button_layout.left_buttons[middle_bg_offset + 1]; - break; - case META_BUTTON_TYPE_LAST: - break; - } - - if (function != META_BUTTON_FUNCTION_LAST) - return button_states[map_button_function_to_type (function)]; - - return META_BUTTON_STATE_LAST; -} - -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 the side buttons if we don't have - * single button backgrounds, and to middle button - * backgrounds if we don't have the ones on the sides - */ - - if (op_list == NULL && - type == META_BUTTON_TYPE_LEFT_SINGLE_BACKGROUND) - return get_button (style, META_BUTTON_TYPE_LEFT_LEFT_BACKGROUND, state); - - if (op_list == NULL && - type == META_BUTTON_TYPE_RIGHT_SINGLE_BACKGROUND) - return get_button (style, META_BUTTON_TYPE_RIGHT_RIGHT_BACKGROUND, state); - - if (op_list == NULL && - (type == META_BUTTON_TYPE_LEFT_LEFT_BACKGROUND || - type == META_BUTTON_TYPE_LEFT_RIGHT_BACKGROUND)) - return get_button (style, META_BUTTON_TYPE_LEFT_MIDDLE_BACKGROUND, - state); - - if (op_list == NULL && - (type == META_BUTTON_TYPE_RIGHT_LEFT_BACKGROUND || - type == META_BUTTON_TYPE_RIGHT_RIGHT_BACKGROUND)) - return get_button (style, META_BUTTON_TYPE_RIGHT_MIDDLE_BACKGROUND, - state); - - /* 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, - guint current_theme_version, - GError **error) -{ - int i, j; - - g_return_val_if_fail (style != NULL, FALSE); - g_return_val_if_fail (style->layout != NULL, FALSE); - - for (i = 0; i < META_BUTTON_TYPE_LAST; i++) - { - /* for now the "positional" buttons are optional */ - if (i >= META_BUTTON_TYPE_CLOSE) - { - for (j = 0; j < META_BUTTON_STATE_LAST; j++) - { - if (get_button (style, i, j) == NULL && - meta_theme_earliest_version_with_button (i) <= current_theme_version - ) - { - 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; - } - } - } - } - - return TRUE; -} - -static void -button_rect (MetaButtonType type, - const MetaFrameGeometry *fgeom, - int middle_background_offset, - GdkRectangle *rect) -{ - switch (type) - { - case META_BUTTON_TYPE_LEFT_LEFT_BACKGROUND: - *rect = fgeom->left_left_background; - break; - - case META_BUTTON_TYPE_LEFT_MIDDLE_BACKGROUND: - *rect = fgeom->left_middle_backgrounds[middle_background_offset]; - break; - - case META_BUTTON_TYPE_LEFT_RIGHT_BACKGROUND: - *rect = fgeom->left_right_background; - break; - - case META_BUTTON_TYPE_LEFT_SINGLE_BACKGROUND: - *rect = fgeom->left_single_background; - break; - - case META_BUTTON_TYPE_RIGHT_LEFT_BACKGROUND: - *rect = fgeom->right_left_background; - break; - - case META_BUTTON_TYPE_RIGHT_MIDDLE_BACKGROUND: - *rect = fgeom->right_middle_backgrounds[middle_background_offset]; - break; - - case META_BUTTON_TYPE_RIGHT_RIGHT_BACKGROUND: - *rect = fgeom->right_right_background; - break; - - case META_BUTTON_TYPE_RIGHT_SINGLE_BACKGROUND: - *rect = fgeom->right_single_background; - break; - - case META_BUTTON_TYPE_CLOSE: - *rect = fgeom->close_rect.visible; - break; - - case META_BUTTON_TYPE_SHADE: - *rect = fgeom->shade_rect.visible; - break; - - case META_BUTTON_TYPE_UNSHADE: - *rect = fgeom->unshade_rect.visible; - break; - - case META_BUTTON_TYPE_ABOVE: - *rect = fgeom->above_rect.visible; - break; - - case META_BUTTON_TYPE_UNABOVE: - *rect = fgeom->unabove_rect.visible; - break; - - case META_BUTTON_TYPE_STICK: - *rect = fgeom->stick_rect.visible; - break; - - case META_BUTTON_TYPE_UNSTICK: - *rect = fgeom->unstick_rect.visible; - break; - - case META_BUTTON_TYPE_MAXIMIZE: - *rect = fgeom->max_rect.visible; - break; - - case META_BUTTON_TYPE_MINIMIZE: - *rect = fgeom->min_rect.visible; - break; - - case META_BUTTON_TYPE_MENU: - *rect = fgeom->menu_rect.visible; - break; - - case META_BUTTON_TYPE_LAST: - g_assert_not_reached (); - break; - } -} - -void -meta_theme_render_background (GtkStyleContext *style_gtk, - cairo_t *cr, - MetaFrameFlags flags, - const MetaFrameGeometry *fgeom) -{ - GdkRectangle visible_rect; - const MetaFrameBorders *borders; - - borders = &fgeom->borders; - - visible_rect.x = borders->invisible.left; - visible_rect.y = borders->invisible.top; - visible_rect.width = fgeom->width - borders->invisible.left - borders->invisible.right; - visible_rect.height = fgeom->height - borders->invisible.top - borders->invisible.bottom; - - gtk_style_context_save (style_gtk); - - gtk_style_context_set_state (style_gtk, get_style_flags (flags)); - - gtk_render_background (style_gtk, cr, - visible_rect.x, - visible_rect.y, - visible_rect.width, - visible_rect.height); - - gtk_render_frame (style_gtk, cr, - visible_rect.x, - visible_rect.y, - visible_rect.width, - visible_rect.height); - - gtk_style_context_restore (style_gtk); -} - -static void -meta_frame_style_draw_with_style (MetaFrameStyle *style, - GtkStyleContext *style_gtk, - MetaFrameFlags flags, - cairo_t *cr, - const MetaFrameGeometry *fgeom, - int client_width, - int client_height, - MetaButtonState button_states[META_BUTTON_TYPE_LAST], - GdkPixbuf *mini_icon, - GdkPixbuf *icon) -{ - meta_theme_render_background (style_gtk, cr, flags, fgeom); - -#if 0 - int i, j; - 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; - MetaDrawInfo draw_info; - - titlebar_rect.x = visible_rect.x; - titlebar_rect.y = visible_rect.y; - titlebar_rect.width = visible_rect.width; - titlebar_rect.height = borders->visible.top; - - 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; - - 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.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; - - 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; - - left_edge.x = visible_rect.x; - left_edge.y = visible_rect.y + borders->visible.top; - left_edge.width = borders->visible.left; - left_edge.height = visible_rect.height - borders->visible.top - borders->visible.bottom; - - right_edge.x = visible_rect.x + visible_rect.width - borders->visible.right; - right_edge.y = visible_rect.y + borders->visible.top; - right_edge.width = borders->visible.right; - right_edge.height = visible_rect.height - borders->visible.top - borders->visible.bottom; - - bottom_edge.x = visible_rect.x; - bottom_edge.y = visible_rect.y + visible_rect.height - borders->visible.bottom; - bottom_edge.width = visible_rect.width; - bottom_edge.height = borders->visible.bottom; - - draw_info.mini_icon = mini_icon; - draw_info.icon = icon; - draw_info.title_layout = title_layout; - draw_info.title_layout_width = title_layout ? logical_rect.width : 0; - draw_info.title_layout_height = title_layout ? logical_rect.height : 0; - draw_info.fgeom = fgeom; - - /* The enum is in the order the pieces should be rendered. */ - i = 0; - while (i < META_FRAME_PIECE_LAST) - { - GdkRectangle rect; - - switch ((MetaFramePiece) i) - { - case META_FRAME_PIECE_ENTIRE_BACKGROUND: - rect = visible_rect; - break; - - case META_FRAME_PIECE_TITLEBAR: - rect = titlebar_rect; - break; - - case META_FRAME_PIECE_LEFT_TITLEBAR_EDGE: - rect = left_titlebar_edge; - break; - - case META_FRAME_PIECE_RIGHT_TITLEBAR_EDGE: - rect = right_titlebar_edge; - break; - - case META_FRAME_PIECE_TOP_TITLEBAR_EDGE: - rect = top_titlebar_edge; - break; - - case META_FRAME_PIECE_BOTTOM_TITLEBAR_EDGE: - rect = bottom_titlebar_edge; - break; - - case META_FRAME_PIECE_TITLEBAR_MIDDLE: - rect.x = left_titlebar_edge.x + left_titlebar_edge.width; - rect.y = top_titlebar_edge.y + top_titlebar_edge.height; - rect.width = titlebar_rect.width - left_titlebar_edge.width - - right_titlebar_edge.width; - rect.height = titlebar_rect.height - top_titlebar_edge.height - bottom_titlebar_edge.height; - break; - - case META_FRAME_PIECE_TITLE: - rect = fgeom->title_rect; - break; - - case META_FRAME_PIECE_LEFT_EDGE: - rect = left_edge; - break; - - case META_FRAME_PIECE_RIGHT_EDGE: - rect = right_edge; - break; - - case META_FRAME_PIECE_BOTTOM_EDGE: - rect = bottom_edge; - break; - - case META_FRAME_PIECE_OVERLAY: - rect = visible_rect; - break; - - case META_FRAME_PIECE_LAST: - g_assert_not_reached (); - break; - } - - cairo_save (cr); - - gdk_cairo_rectangle (cr, &rect); - cairo_clip (cr); - - if (gdk_cairo_get_clip_rectangle (cr, NULL)) - { - MetaDrawOpList *op_list; - MetaFrameStyle *parent; - - parent = style; - op_list = NULL; - while (parent && op_list == NULL) - { - op_list = parent->pieces[i]; - parent = parent->parent; - } - - if (op_list) - { - MetaRectangle m_rect; - m_rect = meta_rect (rect.x, rect.y, rect.width, rect.height); - meta_draw_op_list_draw_with_style (op_list, - style_gtk, - cr, - &draw_info, - m_rect); - } - } - - cairo_restore (cr); - - /* Draw buttons just before overlay */ - if ((i + 1) == META_FRAME_PIECE_OVERLAY) - { - MetaDrawOpList *op_list; - int middle_bg_offset; - - middle_bg_offset = 0; - j = 0; - while (j < META_BUTTON_TYPE_LAST) - { - MetaButtonState button_state; - - button_rect (j, fgeom, middle_bg_offset, &rect); - - button_state = map_button_state (j, fgeom, middle_bg_offset, button_states); - - op_list = get_button (style, j, button_state); - - if (op_list) - { - cairo_save (cr); - gdk_cairo_rectangle (cr, &rect); - cairo_clip (cr); - - if (gdk_cairo_get_clip_rectangle (cr, NULL)) - { - MetaRectangle m_rect; - - m_rect = meta_rect (rect.x, rect.y, - rect.width, rect.height); - - meta_draw_op_list_draw_with_style (op_list, - style_gtk, - cr, - &draw_info, - m_rect); - } - - cairo_restore (cr); - } - - /* MIDDLE_BACKGROUND type may get drawn more than once */ - if ((j == META_BUTTON_TYPE_RIGHT_MIDDLE_BACKGROUND || - j == META_BUTTON_TYPE_LEFT_MIDDLE_BACKGROUND) && - middle_bg_offset < MAX_MIDDLE_BACKGROUNDS) - { - ++middle_bg_offset; - } - else - { - middle_bg_offset = 0; - ++j; - } - } - } - - ++i; - } -#endif -} - -MetaFrameStyleSet* -meta_frame_style_set_new (MetaFrameStyleSet *parent) -{ - MetaFrameStyleSet *style_set; - - style_set = g_new0 (MetaFrameStyleSet, 1); - - style_set->parent = parent; - if (parent) - meta_frame_style_set_ref (parent); - - style_set->refcount = 1; - - return style_set; -} - -static void -free_focus_styles (MetaFrameStyle *focus_styles[META_FRAME_FOCUS_LAST]) -{ - int i; - - for (i = 0; i < META_FRAME_FOCUS_LAST; i++) - if (focus_styles[i]) - meta_frame_style_unref (focus_styles[i]); -} - -void -meta_frame_style_set_ref (MetaFrameStyleSet *style_set) -{ - g_return_if_fail (style_set != NULL); - - style_set->refcount += 1; -} - -void -meta_frame_style_set_unref (MetaFrameStyleSet *style_set) -{ - g_return_if_fail (style_set != NULL); - g_return_if_fail (style_set->refcount > 0); - - style_set->refcount -= 1; - - if (style_set->refcount == 0) - { - int i; - - for (i = 0; i < META_FRAME_RESIZE_LAST; i++) - { - free_focus_styles (style_set->normal_styles[i]); - free_focus_styles (style_set->shaded_styles[i]); - } - - free_focus_styles (style_set->maximized_styles); - free_focus_styles (style_set->tiled_left_styles); - free_focus_styles (style_set->tiled_right_styles); - free_focus_styles (style_set->maximized_and_shaded_styles); - free_focus_styles (style_set->tiled_left_and_shaded_styles); - free_focus_styles (style_set->tiled_right_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; - - switch (state) - { - case META_FRAME_STATE_NORMAL: - case META_FRAME_STATE_SHADED: - { - if (state == META_FRAME_STATE_SHADED) - style = style_set->shaded_styles[resize][focus]; - else - 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); - } - break; - default: - { - MetaFrameStyle **styles; - - styles = NULL; - - switch (state) - { - case META_FRAME_STATE_MAXIMIZED: - styles = style_set->maximized_styles; - break; - case META_FRAME_STATE_TILED_LEFT: - styles = style_set->tiled_left_styles; - break; - case META_FRAME_STATE_TILED_RIGHT: - styles = style_set->tiled_right_styles; - break; - case META_FRAME_STATE_MAXIMIZED_AND_SHADED: - styles = style_set->maximized_and_shaded_styles; - break; - case META_FRAME_STATE_TILED_LEFT_AND_SHADED: - styles = style_set->tiled_left_and_shaded_styles; - break; - case META_FRAME_STATE_TILED_RIGHT_AND_SHADED: - styles = style_set->tiled_right_and_shaded_styles; - break; - case META_FRAME_STATE_NORMAL: - case META_FRAME_STATE_SHADED: - case META_FRAME_STATE_LAST: - g_assert_not_reached (); - break; - } - - style = styles[focus]; - - /* Tiled states are optional, try falling back to non-tiled states */ - if (style == NULL) - { - if (state == META_FRAME_STATE_TILED_LEFT || - state == META_FRAME_STATE_TILED_RIGHT) - style = get_style (style_set, META_FRAME_STATE_NORMAL, - resize, focus); - else if (state == META_FRAME_STATE_TILED_LEFT_AND_SHADED || - state == META_FRAME_STATE_TILED_RIGHT_AND_SHADED) - style = get_style (style_set, META_FRAME_STATE_SHADED, - resize, 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; - - for (i = 0; i < META_FRAME_FOCUS_LAST; i++) - { - if (get_style (style_set, state, - META_FRAME_RESIZE_NONE, i) == NULL) - { - /* Translators: This error occurs when a <frame> tag is missing - * in theme XML. The "<frame ...>" is intended as a noun phrase, - * and the "missing" qualifies it. You should translate "whatever". - */ - 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; - } - } - - 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); - - for (i = 0; i < META_FRAME_RESIZE_LAST; i++) - for (j = 0; j < META_FRAME_FOCUS_LAST; j++) - 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; - } - - 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; -} - -/** - * meta_theme_get_current: (skip) - * - */ -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_topic (META_DEBUG_THEMES, "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\n"), - name, err->message); - g_error_free (err); - } - else - { - if (meta_current_theme) - meta_theme_free (meta_current_theme); - - meta_current_theme = new_theme; - - meta_topic (META_DEBUG_THEMES, "New theme is \"%s\"\n", meta_current_theme->name); - } -} - -/* owns style context */ -static MetaThemeVariant * -meta_theme_variant_new (MetaTheme *theme, - GtkStyleContext *style_context) -{ - MetaThemeVariant *tv = g_slice_new (MetaThemeVariant); - tv->theme = theme; - tv->style_context = style_context; - return tv; -} - -static void -meta_theme_variant_free (gpointer data) -{ - MetaThemeVariant *tv = data; - - g_object_unref (tv->style_context); - g_slice_free (MetaThemeVariant, tv); -} - -static GtkStyleContext * -create_style_context (gchar *variant) -{ - GtkStyleContext *style; - GdkScreen *screen; - GtkWidgetPath *path; - char *theme_name; - - screen = gdk_screen_get_default (); - g_object_get (gtk_settings_get_for_screen (screen), - "gtk-theme-name", &theme_name, - NULL); - - path = gtk_widget_path_new (); - gtk_widget_path_append_type (path, META_TYPE_UIFRAME); - - style = gtk_style_context_new (); - gtk_style_context_set_path (style, path); - - gtk_widget_path_unref (path); - - if (theme_name && *theme_name) - { - GtkCssProvider *provider; - - provider = gtk_css_provider_get_named (theme_name, variant); - gtk_style_context_add_provider (style, - GTK_STYLE_PROVIDER (provider), - GTK_STYLE_PROVIDER_PRIORITY_THEME); - } - - g_free (theme_name); - - return style; -} - -MetaThemeVariant * -meta_theme_get_variant (MetaTheme *theme, - gchar *variant) -{ - MetaThemeVariant *tv; - - if (variant == NULL || strcmp (variant, "normal") == 0) - return theme->normal_variant; - - tv = g_hash_table_lookup (theme->theme_variants, variant); - if (tv == NULL) - { - GtkStyleContext *style = create_style_context (variant); - tv = meta_theme_variant_new (theme, style); - g_hash_table_insert (theme->theme_variants, g_strdup (variant), tv); - } - - return tv; -} - -/** - * meta_theme_new: (skip) - * - */ -MetaTheme* -meta_theme_new (void) -{ - MetaTheme *theme; - - theme = g_new0 (MetaTheme, 1); - - 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); - - theme->normal_variant = meta_theme_variant_new (theme, create_style_context (NULL)); - theme->theme_variants = g_hash_table_new_full (g_str_hash, g_str_equal, - g_free, meta_theme_variant_free); - - /* Create our variable quarks so we can look up variables without - having to strcmp for the names */ - theme->quark_width = g_quark_from_static_string ("width"); - theme->quark_height = g_quark_from_static_string ("height"); - theme->quark_object_width = g_quark_from_static_string ("object_width"); - theme->quark_object_height = g_quark_from_static_string ("object_height"); - theme->quark_left_width = g_quark_from_static_string ("left_width"); - theme->quark_right_width = g_quark_from_static_string ("right_width"); - theme->quark_top_height = g_quark_from_static_string ("top_height"); - theme->quark_bottom_height = g_quark_from_static_string ("bottom_height"); - theme->quark_mini_icon_width = g_quark_from_static_string ("mini_icon_width"); - theme->quark_mini_icon_height = g_quark_from_static_string ("mini_icon_height"); - theme->quark_icon_width = g_quark_from_static_string ("icon_width"); - theme->quark_icon_height = g_quark_from_static_string ("icon_height"); - theme->quark_frame_x_center = g_quark_from_static_string ("frame_x_center"); - theme->quark_frame_y_center = g_quark_from_static_string ("frame_y_center"); - return theme; -} - - -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); - - /* be more careful when destroying the theme hash tables, - since they are only constructed as needed, and may be NULL. */ - if (theme->integer_constants) - g_hash_table_destroy (theme->integer_constants); - if (theme->images_by_filename) - g_hash_table_destroy (theme->images_by_filename); - if (theme->layouts_by_name) - g_hash_table_destroy (theme->layouts_by_name); - if (theme->draw_op_lists_by_name) - g_hash_table_destroy (theme->draw_op_lists_by_name); - if (theme->styles_by_name) - g_hash_table_destroy (theme->styles_by_name); - if (theme->style_sets_by_name) - g_hash_table_destroy (theme->style_sets_by_name); - - for (i = 0; i < META_FRAME_TYPE_LAST; i++) - if (theme->style_sets_by_type[i]) - meta_frame_style_set_unref (theme->style_sets_by_type[i]); - - DEBUG_FILL_STRUCT (theme); - g_free (theme); -} - -gboolean -meta_theme_validate (MetaTheme *theme, - GError **error) -{ - int i; - - g_return_val_if_fail (theme != NULL, FALSE); - - /* FIXME what else should be checked? */ - - g_assert (theme->name); - - if (theme->readable_name == NULL) - { - /* Translators: This error means that a necessary XML tag (whose name - * is given in angle brackets) was not found in a given theme (whose - * name is given second, in quotation marks). - */ - g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED, - _("No <%s> set for theme \"%s\""), "name", theme->name); - return FALSE; - } - - if (theme->author == NULL) - { - g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED, - _("No <%s> set for theme \"%s\""), "author", theme->name); - return FALSE; - } - - if (theme->date == NULL) - { - g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED, - _("No <%s> set for theme \"%s\""), "date", theme->name); - return FALSE; - } - - if (theme->description == NULL) - { - g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED, - _("No <%s> set for theme \"%s\""), "description", theme->name); - return FALSE; - } - - if (theme->copyright == NULL) - { - g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED, - _("No <%s> set for theme \"%s\""), "copyright", theme->name); - return FALSE; - } - - for (i = 0; i < (int)META_FRAME_TYPE_LAST; i++) - if (i != (int)META_FRAME_TYPE_ATTACHED && 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; - } - - return TRUE; -} - -/** - * meta_theme_load_image: (skip) - * - */ -GdkPixbuf* -meta_theme_load_image (MetaTheme *theme, - const char *filename, - guint size_of_theme_icons, - GError **error) -{ - GdkPixbuf *pixbuf; - - pixbuf = g_hash_table_lookup (theme->images_by_filename, - filename); - - if (pixbuf == NULL) - { - - if (g_str_has_prefix (filename, "theme:") && - META_THEME_ALLOWS (theme, META_THEME_IMAGES_FROM_ICON_THEMES)) - { - pixbuf = gtk_icon_theme_load_icon ( - gtk_icon_theme_get_default (), - filename+6, - size_of_theme_icons, - 0, - error); - if (pixbuf == NULL) return NULL; - } - else - { - 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]; - - if (style_set == NULL && type == META_FRAME_TYPE_ATTACHED) - style_set = theme->style_sets_by_type[META_FRAME_TYPE_BORDER]; - - /* Right now the parser forces a style set for all other 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 | - META_FRAME_TILED_LEFT | META_FRAME_TILED_RIGHT)) - { - case 0: - state = META_FRAME_STATE_NORMAL; - break; - case META_FRAME_MAXIMIZED: - state = META_FRAME_STATE_MAXIMIZED; - break; - case META_FRAME_TILED_LEFT: - state = META_FRAME_STATE_TILED_LEFT; - break; - case META_FRAME_TILED_RIGHT: - state = META_FRAME_STATE_TILED_RIGHT; - 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; - case (META_FRAME_TILED_LEFT | META_FRAME_SHADED): - state = META_FRAME_STATE_TILED_LEFT_AND_SHADED; - break; - case (META_FRAME_TILED_RIGHT | META_FRAME_SHADED): - state = META_FRAME_STATE_TILED_RIGHT_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; - } - - /* re invert the styles used for focus/unfocussed while flashing a frame */ - if (((flags & META_FRAME_HAS_FOCUS) && !(flags & META_FRAME_IS_FLASHING)) - || (!(flags & META_FRAME_HAS_FOCUS) && (flags & META_FRAME_IS_FLASHING))) - 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_with_style (MetaTheme *theme, - GtkStyleContext *style_gtk, - cairo_t *cr, - MetaFrameType type, - MetaFrameFlags flags, - int client_width, - int client_height, - const MetaButtonLayout *button_layout, - 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, - style_gtk, - flags, - client_width, client_height, - button_layout, - type, - &fgeom, - theme); - - meta_frame_style_draw_with_style (style, - style_gtk, - flags, - cr, - &fgeom, - client_width, client_height, - button_states, - mini_icon, icon); -} - -void -meta_theme_get_frame_borders (MetaTheme *theme, - GtkStyleContext *style_context, - MetaFrameType type, - MetaFrameFlags flags, - MetaFrameBorders *borders) -{ - MetaFrameStyle *style; - - g_return_if_fail (type < META_FRAME_TYPE_LAST); - - style = theme_get_style (theme, type, flags); - - meta_frame_borders_clear (borders); - - /* Parser is not supposed to allow this currently */ - if (style == NULL) - return; - - meta_frame_layout_get_borders (style->layout, - style_context, - flags, type, - borders); -} - -void -meta_theme_calc_geometry (MetaTheme *theme, - GtkStyleContext *ctx, - MetaFrameType type, - MetaFrameFlags flags, - int client_width, - int client_height, - const MetaButtonLayout *button_layout, - 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, - ctx, - flags, - client_width, client_height, - button_layout, - type, - fgeom, - theme); -} - -MetaFrameLayout* -meta_theme_lookup_layout (MetaTheme *theme, - const char *name) -{ - return g_hash_table_lookup (theme->layouts_by_name, name); -} - -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); -} - -MetaDrawOpList* -meta_theme_lookup_draw_op_list (MetaTheme *theme, - const char *name) -{ - return g_hash_table_lookup (theme->draw_op_lists_by_name, name); -} - -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); -} - -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)); - - return TRUE; -} - -gboolean -meta_theme_lookup_int_constant (MetaTheme *theme, - const char *name, - int *value) -{ - gpointer old_value; - - *value = 0; - - 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); - - return TRUE; -} - -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; - } -} - -gboolean -meta_theme_define_color_constant (MetaTheme *theme, - const char *name, - const char *value, - GError **error) -{ - if (theme->color_constants == NULL) - theme->color_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->color_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->color_constants, - g_strdup (name), - g_strdup (value)); - - return TRUE; -} - -/** - * meta_theme_lookup_color_constant: - * @theme: the theme containing the constant - * @name: the name of the constant - * @value: (out): the string representation of the colour, or %NULL if it - * doesn't exist - * - * Looks up a colour constant. - * - * Returns: %TRUE if it exists, %FALSE otherwise - */ -gboolean -meta_theme_lookup_color_constant (MetaTheme *theme, - const char *name, - char **value) -{ - char *result; - - *value = NULL; - - if (theme->color_constants == NULL) - return FALSE; - - result = g_hash_table_lookup (theme->color_constants, name); - - if (result) - { - *value = result; - return TRUE; - } - else - { - return FALSE; - } -} - - -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, MetaTheme *theme) -{ - if (META_THEME_ALLOWS(theme, META_THEME_SHADE_STICK_ABOVE_BUTTONS)) - { - if (strcmp ("shade", str) == 0) - return META_BUTTON_TYPE_SHADE; - else if (strcmp ("above", str) == 0) - return META_BUTTON_TYPE_ABOVE; - else if (strcmp ("stick", str) == 0) - return META_BUTTON_TYPE_STICK; - else if (strcmp ("unshade", str) == 0) - return META_BUTTON_TYPE_UNSHADE; - else if (strcmp ("unabove", str) == 0) - return META_BUTTON_TYPE_UNABOVE; - else if (strcmp ("unstick", str) == 0) - return META_BUTTON_TYPE_UNSTICK; - } - - 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 if (strcmp ("left_left_background", str) == 0) - return META_BUTTON_TYPE_LEFT_LEFT_BACKGROUND; - else if (strcmp ("left_middle_background", str) == 0) - return META_BUTTON_TYPE_LEFT_MIDDLE_BACKGROUND; - else if (strcmp ("left_right_background", str) == 0) - return META_BUTTON_TYPE_LEFT_RIGHT_BACKGROUND; - else if (strcmp ("left_single_background", str) == 0) - return META_BUTTON_TYPE_LEFT_SINGLE_BACKGROUND; - else if (strcmp ("right_left_background", str) == 0) - return META_BUTTON_TYPE_RIGHT_LEFT_BACKGROUND; - else if (strcmp ("right_middle_background", str) == 0) - return META_BUTTON_TYPE_RIGHT_MIDDLE_BACKGROUND; - else if (strcmp ("right_right_background", str) == 0) - return META_BUTTON_TYPE_RIGHT_RIGHT_BACKGROUND; - else if (strcmp ("right_single_background", str) == 0) - return META_BUTTON_TYPE_RIGHT_SINGLE_BACKGROUND; - 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_SHADE: - return "shade"; - case META_BUTTON_TYPE_ABOVE: - return "above"; - case META_BUTTON_TYPE_STICK: - return "stick"; - case META_BUTTON_TYPE_UNSHADE: - return "unshade"; - case META_BUTTON_TYPE_UNABOVE: - return "unabove"; - case META_BUTTON_TYPE_UNSTICK: - return "unstick"; - case META_BUTTON_TYPE_MENU: - return "menu"; - case META_BUTTON_TYPE_LEFT_LEFT_BACKGROUND: - return "left_left_background"; - case META_BUTTON_TYPE_LEFT_MIDDLE_BACKGROUND: - return "left_middle_background"; - case META_BUTTON_TYPE_LEFT_RIGHT_BACKGROUND: - return "left_right_background"; - case META_BUTTON_TYPE_LEFT_SINGLE_BACKGROUND: - return "left_single_background"; - case META_BUTTON_TYPE_RIGHT_LEFT_BACKGROUND: - return "right_left_background"; - case META_BUTTON_TYPE_RIGHT_MIDDLE_BACKGROUND: - return "right_middle_background"; - case META_BUTTON_TYPE_RIGHT_RIGHT_BACKGROUND: - return "right_right_background"; - case META_BUTTON_TYPE_RIGHT_SINGLE_BACKGROUND: - return "right_single_background"; - case META_BUTTON_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 ("tiled_left", str) == 0) - return META_FRAME_STATE_TILED_LEFT; - else if (strcmp ("tiled_right", str) == 0) - return META_FRAME_STATE_TILED_RIGHT; - 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 if (strcmp ("tiled_left_and_shaded", str) == 0) - return META_FRAME_STATE_TILED_LEFT_AND_SHADED; - else if (strcmp ("tiled_right_and_shaded", str) == 0) - return META_FRAME_STATE_TILED_RIGHT_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_TILED_LEFT: - return "tiled_left"; - case META_FRAME_STATE_TILED_RIGHT: - return "tiled_right"; - case META_FRAME_STATE_SHADED: - return "shaded"; - case META_FRAME_STATE_MAXIMIZED_AND_SHADED: - return "maximized_and_shaded"; - case META_FRAME_STATE_TILED_LEFT_AND_SHADED: - return "tiled_left_and_shaded"; - case META_FRAME_STATE_TILED_RIGHT_AND_SHADED: - return "tiled_right_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; - else if (strcmp ("border", str) == 0) - return META_FRAME_TYPE_BORDER; - else if (strcmp ("attached", str) == 0) - return META_FRAME_TYPE_ATTACHED; -#if 0 - else if (strcmp ("toolbar", str) == 0) - return META_FRAME_TYPE_TOOLBAR; -#endif - else - return META_FRAME_TYPE_LAST; -} - -/** - * meta_frame_type_to_string: - * - * Converts a frame type enum value to the name string that would - * appear in the theme definition file. - * - * Return value: the string value - */ -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"; - case META_FRAME_TYPE_BORDER: - return "border"; - case META_FRAME_TYPE_ATTACHED: - return "attached"; -#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>"; -} - -GtkStateFlags -meta_gtk_state_from_string (const char *str) -{ - if (g_ascii_strcasecmp ("normal", str) == 0) - return GTK_STATE_FLAG_NORMAL; - else if (g_ascii_strcasecmp ("prelight", str) == 0) - return GTK_STATE_FLAG_PRELIGHT; - else if (g_ascii_strcasecmp ("active", str) == 0) - return GTK_STATE_FLAG_ACTIVE; - else if (g_ascii_strcasecmp ("selected", str) == 0) - return GTK_STATE_FLAG_SELECTED; - else if (g_ascii_strcasecmp ("insensitive", str) == 0) - return GTK_STATE_FLAG_INSENSITIVE; - else if (g_ascii_strcasecmp ("inconsistent", str) == 0) - return GTK_STATE_FLAG_INCONSISTENT; - else if (g_ascii_strcasecmp ("focused", str) == 0) - return GTK_STATE_FLAG_FOCUSED; - else if (g_ascii_strcasecmp ("backdrop", str) == 0) - return GTK_STATE_FLAG_BACKDROP; - else - return -1; /* hack */ -} - -const char* -meta_gtk_state_to_string (GtkStateFlags state) -{ - switch (state) - { - case GTK_STATE_FLAG_NORMAL: - return "NORMAL"; - case GTK_STATE_FLAG_PRELIGHT: - return "PRELIGHT"; - case GTK_STATE_FLAG_ACTIVE: - return "ACTIVE"; - case GTK_STATE_FLAG_SELECTED: - return "SELECTED"; - case GTK_STATE_FLAG_INSENSITIVE: - return "INSENSITIVE"; - case GTK_STATE_FLAG_INCONSISTENT: - return "INCONSISTENT"; - case GTK_STATE_FLAG_FOCUSED: - return "FOCUSED"; - case GTK_STATE_FLAG_BACKDROP: - return "BACKDROP"; - } - - 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 if (strcmp ("none", str) == 0) - return GTK_ARROW_NONE; - 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"; - case GTK_ARROW_NONE: - return "none"; - } - - return "<unknown>"; -} - -/** - * meta_image_fill_type_from_string: - * @str: a string representing a fill_type - * - * Returns a fill_type from a string. The inverse of - * meta_image_fill_type_to_string(). - * - * Returns: the fill type, or -1 if it represents no fill type. - */ -MetaImageFillType -meta_image_fill_type_from_string (const char *str) -{ - if (strcmp ("tile", str) == 0) - return META_IMAGE_FILL_TILE; - else if (strcmp ("scale", str) == 0) - return META_IMAGE_FILL_SCALE; - else - return -1; -} - -/** - * meta_image_fill_type_to_string: - * @fill_type: the fill type - * - * Returns a string representation of a fill_type. The inverse of - * meta_image_fill_type_from_string(). - * - * Returns: a string representing that type - */ -const char* -meta_image_fill_type_to_string (MetaImageFillType fill_type) -{ - switch (fill_type) - { - case META_IMAGE_FILL_TILE: - return "tile"; - case META_IMAGE_FILL_SCALE: - return "scale"; - } - - return "<unknown>"; -} - -/** - * gtk_style_shade: - * @a: the starting colour - * @b: (out): the resulting colour - * @k: amount to scale lightness and saturation by - * - * Takes a colour "a", scales the lightness and saturation by a certain amount, - * and sets "b" to the resulting colour. - * gtkstyle.c cut-and-pastage. - */ -static void -gtk_style_shade (GdkRGBA *a, - GdkRGBA *b, - gdouble k) -{ - gdouble red; - gdouble green; - gdouble blue; - - red = a->red; - green = a->green; - blue = a->blue; - - rgb_to_hls (&red, &green, &blue); - - green *= k; - if (green > 1.0) - green = 1.0; - else if (green < 0.0) - green = 0.0; - - blue *= k; - if (blue > 1.0) - blue = 1.0; - else if (blue < 0.0) - blue = 0.0; - - hls_to_rgb (&red, &green, &blue); - - b->red = red; - b->green = green; - b->blue = blue; -} - -/** - * rgb_to_hls: - * @r: on input, red; on output, hue - * @g: on input, green; on output, lightness - * @b: on input, blue; on output, saturation - * - * Converts a red/green/blue triplet to a hue/lightness/saturation triplet. - */ -static void -rgb_to_hls (gdouble *r, - gdouble *g, - gdouble *b) -{ - gdouble min; - gdouble max; - gdouble red; - gdouble green; - gdouble blue; - gdouble h, l, s; - gdouble delta; - - red = *r; - green = *g; - blue = *b; - - if (red > green) - { - if (red > blue) - max = red; - else - max = blue; - - if (green < blue) - min = green; - else - min = blue; - } - else - { - if (green > blue) - max = green; - else - max = blue; - - if (red < blue) - min = red; - else - min = blue; - } - - l = (max + min) / 2; - s = 0; - h = 0; - - if (max != min) - { - if (l <= 0.5) - s = (max - min) / (max + min); - else - s = (max - min) / (2 - max - min); - - delta = max -min; - if (red == max) - h = (green - blue) / delta; - else if (green == max) - h = 2 + (blue - red) / delta; - else if (blue == max) - h = 4 + (red - green) / delta; - - h *= 60; - if (h < 0.0) - h += 360; - } - - *r = h; - *g = l; - *b = s; -} - -/** - * hls_to_rgb: - * @h: on input, hue; on output, red - * @l: on input, lightness; on output, green - * @s: on input, saturation; on output, blue - * - * Converts a hue/lightness/saturation triplet to a red/green/blue triplet. - */ -static void -hls_to_rgb (gdouble *h, - gdouble *l, - gdouble *s) -{ - gdouble hue; - gdouble lightness; - gdouble saturation; - gdouble m1, m2; - gdouble r, g, b; - - lightness = *l; - saturation = *s; - - if (lightness <= 0.5) - m2 = lightness * (1 + saturation); - else - m2 = lightness + saturation - lightness * saturation; - m1 = 2 * lightness - m2; - - if (saturation == 0) - { - *h = lightness; - *l = lightness; - *s = lightness; - } - else - { - hue = *h + 120; - while (hue > 360) - hue -= 360; - while (hue < 0) - hue += 360; - - if (hue < 60) - r = m1 + (m2 - m1) * hue / 60; - else if (hue < 180) - r = m2; - else if (hue < 240) - r = m1 + (m2 - m1) * (240 - hue) / 60; - else - r = m1; - - hue = *h; - while (hue > 360) - hue -= 360; - while (hue < 0) - hue += 360; - - if (hue < 60) - g = m1 + (m2 - m1) * hue / 60; - else if (hue < 180) - g = m2; - else if (hue < 240) - g = m1 + (m2 - m1) * (240 - hue) / 60; - else - g = m1; - - hue = *h - 120; - while (hue > 360) - hue -= 360; - while (hue < 0) - hue += 360; - - if (hue < 60) - b = m1 + (m2 - m1) * hue / 60; - else if (hue < 180) - b = m2; - else if (hue < 240) - b = m1 + (m2 - m1) * (240 - hue) / 60; - else - b = m1; - - *h = r; - *l = g; - *s = b; - } -} - -/** - * meta_theme_earliest_version_with_button: - * @type: the button type - * - * Returns the earliest version of the theme format which required support - * for a particular button. (For example, "shade" first appeared in v2, and - * "close" in v1.) - * - * Returns: the number of the theme format - */ -guint -meta_theme_earliest_version_with_button (MetaButtonType type) -{ - switch (type) - { - case META_BUTTON_TYPE_CLOSE: - case META_BUTTON_TYPE_MAXIMIZE: - case META_BUTTON_TYPE_MINIMIZE: - case META_BUTTON_TYPE_MENU: - case META_BUTTON_TYPE_LEFT_LEFT_BACKGROUND: - case META_BUTTON_TYPE_LEFT_MIDDLE_BACKGROUND: - case META_BUTTON_TYPE_LEFT_RIGHT_BACKGROUND: - case META_BUTTON_TYPE_RIGHT_LEFT_BACKGROUND: - case META_BUTTON_TYPE_RIGHT_MIDDLE_BACKGROUND: - case META_BUTTON_TYPE_RIGHT_RIGHT_BACKGROUND: - return 1000; - - case META_BUTTON_TYPE_SHADE: - case META_BUTTON_TYPE_ABOVE: - case META_BUTTON_TYPE_STICK: - case META_BUTTON_TYPE_UNSHADE: - case META_BUTTON_TYPE_UNABOVE: - case META_BUTTON_TYPE_UNSTICK: - return 2000; - - case META_BUTTON_TYPE_LEFT_SINGLE_BACKGROUND: - case META_BUTTON_TYPE_RIGHT_SINGLE_BACKGROUND: - return 3003; - - default: - meta_warning("Unknown button %d\n", type); - return 1000; - } -} diff --git a/src/ui/ui.c b/src/ui/ui.c index 26c87b777..5692b905b 100644 --- a/src/ui/ui.c +++ b/src/ui/ui.c @@ -29,7 +29,6 @@ #include <meta/util.h> #include "menu.h" #include "core.h" -#include "theme-private.h" #include "inlinepixbufs.h" @@ -367,32 +366,12 @@ meta_ui_get_frame_borders (MetaUI *ui, Window xwindow, MetaFrameBorders *borders) { - MetaFrameFlags flags; - MetaUIFrame *frame; - MetaFrameType type; - - frame = meta_ui_lookup_window (ui, xwindow); + MetaUIFrame *frame = meta_ui_lookup_window (ui, xwindow); if (frame == NULL) meta_bug ("No such frame 0x%lx\n", xwindow); - meta_core_get (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), frame->xwindow, - META_CORE_GET_FRAME_FLAGS, &flags, - META_CORE_GET_FRAME_TYPE, &type, - META_CORE_GET_END); - - 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_theme_get_frame_borders (frame->tv->theme, - frame->tv->style_context, - type, - flags, - borders); + meta_uiframe_get_frame_borders (frame, borders); } void @@ -400,18 +379,8 @@ meta_ui_render_background (MetaUI *ui, Window xwindow, cairo_t *cr) { - MetaUIFrame *frame; - MetaFrameGeometry fgeom; - MetaFrameFlags flags; - - frame = meta_ui_lookup_window (ui, xwindow); - - meta_core_get (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), frame->xwindow, - META_CORE_GET_FRAME_FLAGS, &flags, - META_CORE_GET_END); - - meta_uiframe_calc_geometry (frame, &fgeom); - meta_theme_render_background (frame->tv->style_context, cr, flags, &fgeom); + MetaUIFrame *frame = meta_ui_lookup_window (ui, xwindow); + meta_uiframe_paint (frame, cr); } Window @@ -590,15 +559,6 @@ meta_ui_unmap_frame (MetaUI *ui, } void -meta_ui_update_frame_style (MetaUI *ui, - Window xwindow) -{ - MetaUIFrame *frame = meta_ui_lookup_window (ui, xwindow); - meta_uiframe_attach_style (frame); - gtk_widget_queue_draw (GTK_WIDGET (frame)); -} - -void meta_ui_queue_frame_draw (MetaUI *ui, Window xwindow) { @@ -825,20 +785,6 @@ 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); - meta_invalidate_default_icons (); -} - -gboolean -meta_ui_have_a_theme (void) -{ - return meta_theme_get_current () != NULL; -} - static void meta_ui_accelerator_parse (const char *accel, guint *keysym, diff --git a/src/ui/ui.h b/src/ui/ui.h index 89b0ec590..433191fbc 100644 --- a/src/ui/ui.h +++ b/src/ui/ui.h @@ -97,12 +97,6 @@ void meta_ui_set_frame_title (MetaUI *ui, Window xwindow, const char *title); -void meta_ui_update_frame_style (MetaUI *ui, - Window window); - -void meta_ui_repaint_frame (MetaUI *ui, - Window xwindow); - MetaWindowMenu* meta_ui_window_menu_new (MetaUI *ui, Display *display, gint screen_no, @@ -137,10 +131,6 @@ 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); - /* Not a real key symbol but means "key above the tab key"; this is * used as the default keybinding for cycle_group. * 0x2xxxxxxx is a range not used by GDK or X. the remaining digits are diff --git a/src/ui/uiframe.c b/src/ui/uiframe.c index 88a053e48..e94b1a97a 100644 --- a/src/ui/uiframe.c +++ b/src/ui/uiframe.c @@ -31,27 +31,139 @@ #include <meta/util.h> #include "core.h" #include "menu.h" -#include <meta/theme.h> #include <meta/prefs.h> #include "ui.h" -#include "theme-private.h" - #ifdef HAVE_SHAPE #include <X11/extensions/shape.h> #endif -static void meta_uiframe_update_prelit_control (MetaUIFrame *frame, - MetaFrameControl control); - -static GdkRectangle* control_rect (MetaFrameControl control, - MetaFrameGeometry *fgeom); static MetaFrameControl get_control (MetaUIFrame *frame, int x, int y); G_DEFINE_TYPE (MetaUIFrame, meta_uiframe, GTK_TYPE_WINDOW); + +static void +initialize_style_context (MetaUIFrame *frame) +{ + GtkWidget *widget; + GtkCssProvider *provider; + GdkScreen *screen; + char *theme_name, *variant; + + if (G_LIKELY (frame->style_context_initialized)) + return; + + widget = GTK_WIDGET (frame); + + screen = gtk_widget_get_screen (widget); + g_object_get (gtk_settings_get_for_screen (screen), + "gtk-theme-name", &theme_name, + NULL); + + meta_core_get (GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (frame))), + frame->xwindow, + META_CORE_GET_THEME_VARIANT, &variant, + META_CORE_GET_END); + + provider = gtk_css_provider_get_named (theme_name, variant); + gtk_style_context_add_provider (gtk_widget_get_style_context (widget), + GTK_STYLE_PROVIDER (provider), + GTK_STYLE_PROVIDER_PRIORITY_THEME); + + g_free (theme_name); + + frame->style_context_initialized = TRUE; +} + +static void +sync_state_flags (MetaUIFrame *frame) +{ + MetaFrameFlags flags; + GtkStateFlags gtk_flags; + + initialize_style_context (frame); + + meta_core_get (GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (frame))), + frame->xwindow, + META_CORE_GET_FRAME_FLAGS, &flags, + META_CORE_GET_END); + + gtk_flags = GTK_STATE_FLAG_NORMAL; + + if ((flags & META_FRAME_HAS_FOCUS) == 0) + gtk_flags |= GTK_STATE_FLAG_BACKDROP; + + gtk_widget_set_state_flags (GTK_WIDGET (frame), gtk_flags, TRUE); +} + +void +meta_uiframe_get_frame_borders (MetaUIFrame *frame, + MetaFrameBorders *borders) +{ + GtkWidget *widget = GTK_WIDGET (frame); + GtkBorder padding; + GtkStyleContext *style_context; + MetaFrameType type; + MetaFrameFlags flags; + int draggable_borders; + + meta_core_get (GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (frame))), + frame->xwindow, + META_CORE_GET_FRAME_TYPE, &type, + META_CORE_GET_FRAME_FLAGS, &flags, + META_CORE_GET_END); + + /* For a full-screen window, we don't have any borders, visible or not. */ + if (flags & META_FRAME_FULLSCREEN) + return; + + sync_state_flags (frame); + + meta_frame_borders_clear (borders); + + style_context = gtk_widget_get_style_context (widget); + + gtk_style_context_get_border (style_context, + gtk_widget_get_state_flags (widget), + &borders->visible); + + gtk_style_context_get_padding (style_context, + gtk_widget_get_state_flags (widget), + &padding); + + borders->visible.left += padding.left; + borders->visible.right += padding.right; + borders->visible.top += padding.top; + borders->visible.bottom += padding.bottom; + + draggable_borders = meta_prefs_get_draggable_border_width (); + + if (flags & META_FRAME_ALLOWS_HORIZONTAL_RESIZE) + { + borders->invisible.left = MAX (0, draggable_borders - borders->visible.left); + borders->invisible.right = MAX (0, draggable_borders - borders->visible.right); + } + + if (flags & META_FRAME_ALLOWS_VERTICAL_RESIZE) + { + borders->invisible.bottom = MAX (0, draggable_borders - borders->visible.bottom); + + /* borders.visible.top is the height of the *title bar*. We can't do the same + * algorithm here, titlebars are expectedly much bigger. Just subtract a couple + * pixels to get a proper feel. */ + if (type != META_FRAME_TYPE_ATTACHED) + borders->invisible.top = MAX (0, draggable_borders - 2); + } + + borders->total.left = borders->invisible.left + borders->visible.left; + borders->total.right = borders->invisible.right + borders->visible.right; + borders->total.bottom = borders->invisible.bottom + borders->visible.bottom; + borders->total.top = borders->invisible.top + borders->visible.top; +} + static void meta_uiframe_finalize (GObject *obj) { @@ -68,6 +180,7 @@ meta_uiframe_init (MetaUIFrame *frame) frame->container = container = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); frame->label = label = gtk_label_new (""); + frame->style_context_initialized = FALSE; gtk_container_add (GTK_CONTAINER (frame), container); gtk_container_add (GTK_CONTAINER (container), frame->label); @@ -80,66 +193,19 @@ meta_uiframe_init (MetaUIFrame *frame) gtk_widget_show_all (GTK_WIDGET (container)); } -/* In order to use a style with a window it has to be attached to that - * window. Actually, the colormaps just have to match, but since GTK+ - * already takes care of making sure that its cheap to attach a style - * to multiple windows with the same colormap, we can just go ahead - * and attach separately for each window. - */ -void -meta_uiframe_attach_style (MetaUIFrame *frame) -{ - char *variant = NULL; - - meta_core_get (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), - frame->xwindow, - META_CORE_GET_THEME_VARIANT, &variant, - META_CORE_GET_END); - - frame->tv = meta_theme_get_variant (meta_theme_get_current (), - variant); -} - -void -meta_uiframe_calc_geometry (MetaUIFrame *frame, - MetaFrameGeometry *fgeom) -{ - int width, height; - MetaFrameFlags flags; - MetaFrameType type; - MetaButtonLayout button_layout; - - meta_core_get (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), frame->xwindow, - META_CORE_GET_CLIENT_WIDTH, &width, - META_CORE_GET_CLIENT_HEIGHT, &height, - META_CORE_GET_FRAME_FLAGS, &flags, - META_CORE_GET_FRAME_TYPE, &type, - META_CORE_GET_END); - - meta_prefs_get_button_layout (&button_layout); - - meta_theme_calc_geometry (frame->tv->theme, - frame->tv->style_context, - type, - flags, - width, height, - &button_layout, - fgeom); -} - /* The client rectangle surrounds client window; it subtracts both * the visible and invisible borders from the frame window's size. */ static void -get_client_rect (MetaFrameGeometry *fgeom, +get_client_rect (MetaFrameBorders *borders, int window_width, int window_height, cairo_rectangle_int_t *rect) { - rect->x = fgeom->borders.total.left; - rect->y = fgeom->borders.total.top; - rect->width = window_width - fgeom->borders.total.right - rect->x; - rect->height = window_height - fgeom->borders.total.bottom - rect->y; + rect->x = borders->total.left; + rect->y = borders->total.top; + rect->width = window_width - borders->total.right - rect->x; + rect->height = window_height - borders->total.bottom - rect->y; } void @@ -149,20 +215,6 @@ meta_uiframe_set_title (MetaUIFrame *frame, gtk_label_set_text (GTK_LABEL (frame->label), title); } -static void -redraw_control (MetaUIFrame *frame, - MetaFrameControl control) -{ - MetaFrameGeometry fgeom; - GdkRectangle *rect; - - meta_uiframe_calc_geometry (frame, &fgeom); - - rect = control_rect (control, &fgeom); - - gdk_window_invalidate_rect (frame->window, rect, FALSE); -} - static gboolean meta_frame_titlebar_event (MetaUIFrame *frame, GdkEventButton *event, @@ -172,7 +224,7 @@ meta_frame_titlebar_event (MetaUIFrame *frame, Display *display; display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); - + switch (action) { case G_DESKTOP_TITLEBAR_ACTION_TOGGLE_SHADE: @@ -412,35 +464,6 @@ meta_uiframe_button_press_event (GtkWidget *widget, event->time, event->x_root, event->y_root); - - frame->prelit_control = control; - redraw_control (frame, control); - - if (op == META_GRAB_OP_CLICKING_MENU) - { - MetaFrameGeometry fgeom; - GdkRectangle *rect; - int dx, dy; - - meta_uiframe_calc_geometry (frame, &fgeom); - - rect = control_rect (META_FRAME_CONTROL_MENU, &fgeom); - - /* get delta to convert to root coords */ - dx = event->x_root - event->x; - dy = event->y_root - event->y; - - /* Align to the right end of the menu rectangle if RTL */ - if (meta_ui_get_direction() == META_UI_DIRECTION_RTL) - dx += rect->width; - - meta_core_show_window_menu (display, - frame->xwindow, - rect->x + dx, - rect->y + rect->height + dy, - event->button, - event->time); - } } else if (event->button == 1 && (control == META_FRAME_CONTROL_RESIZE_SE || @@ -644,213 +667,12 @@ meta_uiframe_button_release_event (GtkWidget *widget, default: break; } - - /* Update the prelit control regardless of what button the mouse - * was released over; needed so that the new button can become - * prelit so to let the user know that it can now be pressed. - * :) - */ - meta_uiframe_update_prelit_control (frame, control); } return TRUE; } static void -meta_uiframe_update_prelit_control (MetaUIFrame *frame, - MetaFrameControl control) -{ - MetaFrameControl old_control; - MetaCursor cursor; - - - meta_verbose ("Updating prelit control from %u to %u\n", - frame->prelit_control, control); - - cursor = META_CURSOR_DEFAULT; - - switch (control) - { - case META_FRAME_CONTROL_CLIENT_AREA: - break; - case META_FRAME_CONTROL_NONE: - break; - case META_FRAME_CONTROL_TITLE: - break; - case META_FRAME_CONTROL_DELETE: - break; - case META_FRAME_CONTROL_MENU: - break; - case META_FRAME_CONTROL_MINIMIZE: - break; - case META_FRAME_CONTROL_MAXIMIZE: - break; - case META_FRAME_CONTROL_UNMAXIMIZE: - break; - case META_FRAME_CONTROL_SHADE: - break; - case META_FRAME_CONTROL_UNSHADE: - break; - case META_FRAME_CONTROL_ABOVE: - break; - case META_FRAME_CONTROL_UNABOVE: - break; - case META_FRAME_CONTROL_STICK: - break; - case META_FRAME_CONTROL_UNSTICK: - break; - case META_FRAME_CONTROL_RESIZE_SE: - cursor = META_CURSOR_SE_RESIZE; - break; - case META_FRAME_CONTROL_RESIZE_S: - cursor = META_CURSOR_SOUTH_RESIZE; - break; - case META_FRAME_CONTROL_RESIZE_SW: - cursor = META_CURSOR_SW_RESIZE; - break; - case META_FRAME_CONTROL_RESIZE_N: - cursor = META_CURSOR_NORTH_RESIZE; - break; - case META_FRAME_CONTROL_RESIZE_NE: - cursor = META_CURSOR_NE_RESIZE; - break; - case META_FRAME_CONTROL_RESIZE_NW: - cursor = META_CURSOR_NW_RESIZE; - break; - case META_FRAME_CONTROL_RESIZE_W: - cursor = META_CURSOR_WEST_RESIZE; - break; - case META_FRAME_CONTROL_RESIZE_E: - cursor = META_CURSOR_EAST_RESIZE; - break; - } - - /* set/unset the prelight cursor */ - meta_core_set_screen_cursor (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), - frame->xwindow, - cursor); - - switch (control) - { - case META_FRAME_CONTROL_MENU: - case META_FRAME_CONTROL_MINIMIZE: - case META_FRAME_CONTROL_MAXIMIZE: - case META_FRAME_CONTROL_DELETE: - case META_FRAME_CONTROL_SHADE: - case META_FRAME_CONTROL_UNSHADE: - case META_FRAME_CONTROL_ABOVE: - case META_FRAME_CONTROL_UNABOVE: - case META_FRAME_CONTROL_STICK: - case META_FRAME_CONTROL_UNSTICK: - case META_FRAME_CONTROL_UNMAXIMIZE: - /* leave control set */ - break; - default: - /* Only prelight buttons */ - control = META_FRAME_CONTROL_NONE; - break; - } - - if (control == frame->prelit_control) - return; - - /* Save the old control so we can unprelight it */ - old_control = frame->prelit_control; - - frame->prelit_control = control; - - redraw_control (frame, old_control); - redraw_control (frame, control); -} - -static gboolean -meta_uiframe_motion_notify_event (GtkWidget *widget, - GdkEventMotion *event) -{ - MetaUIFrame *frame; - MetaGrabOp grab_op; - Display *display; - - frame = META_UIFRAME (widget); - display = GDK_DISPLAY_XDISPLAY (gdk_window_get_display (event->window)); - - grab_op = meta_core_get_grab_op (display); - - switch (grab_op) - { - case META_GRAB_OP_CLICKING_MENU: - case META_GRAB_OP_CLICKING_DELETE: - case META_GRAB_OP_CLICKING_MINIMIZE: - case META_GRAB_OP_CLICKING_MAXIMIZE: - case META_GRAB_OP_CLICKING_UNMAXIMIZE: - case META_GRAB_OP_CLICKING_SHADE: - case META_GRAB_OP_CLICKING_UNSHADE: - case META_GRAB_OP_CLICKING_ABOVE: - case META_GRAB_OP_CLICKING_UNABOVE: - case META_GRAB_OP_CLICKING_STICK: - case META_GRAB_OP_CLICKING_UNSTICK: - { - MetaFrameControl control; - int x, y; - - gdk_window_get_device_position (frame->window, event->device, - &x, &y, NULL); - - /* Control is set to none unless it matches - * the current grab - */ - control = get_control (frame, x, y); - if (! ((control == META_FRAME_CONTROL_MENU && - grab_op == META_GRAB_OP_CLICKING_MENU) || - (control == META_FRAME_CONTROL_DELETE && - grab_op == META_GRAB_OP_CLICKING_DELETE) || - (control == META_FRAME_CONTROL_MINIMIZE && - grab_op == META_GRAB_OP_CLICKING_MINIMIZE) || - ((control == META_FRAME_CONTROL_MAXIMIZE || - control == META_FRAME_CONTROL_UNMAXIMIZE) && - (grab_op == META_GRAB_OP_CLICKING_MAXIMIZE || - grab_op == META_GRAB_OP_CLICKING_UNMAXIMIZE)) || - (control == META_FRAME_CONTROL_SHADE && - grab_op == META_GRAB_OP_CLICKING_SHADE) || - (control == META_FRAME_CONTROL_UNSHADE && - grab_op == META_GRAB_OP_CLICKING_UNSHADE) || - (control == META_FRAME_CONTROL_ABOVE && - grab_op == META_GRAB_OP_CLICKING_ABOVE) || - (control == META_FRAME_CONTROL_UNABOVE && - grab_op == META_GRAB_OP_CLICKING_UNABOVE) || - (control == META_FRAME_CONTROL_STICK && - grab_op == META_GRAB_OP_CLICKING_STICK) || - (control == META_FRAME_CONTROL_UNSTICK && - grab_op == META_GRAB_OP_CLICKING_UNSTICK))) - control = META_FRAME_CONTROL_NONE; - - /* Update prelit control and cursor */ - meta_uiframe_update_prelit_control (frame, control); - } - break; - case META_GRAB_OP_NONE: - { - MetaFrameControl control; - int x, y; - - gdk_window_get_device_position (frame->window, event->device, - &x, &y, NULL); - - control = get_control (frame, x, y); - - /* Update prelit control and cursor */ - meta_uiframe_update_prelit_control (frame, control); - } - break; - - default: - break; - } - - return TRUE; -} - -static void clip_to_screen (cairo_region_t *region, MetaUIFrame *frame) { @@ -890,8 +712,6 @@ subtract_client_area (cairo_region_t *region, MetaUIFrame *frame) { cairo_rectangle_int_t area; - MetaFrameFlags flags; - MetaFrameType type; MetaFrameBorders borders; cairo_region_t *tmp_region; Display *display; @@ -899,15 +719,11 @@ subtract_client_area (cairo_region_t *region, display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); meta_core_get (display, frame->xwindow, - META_CORE_GET_FRAME_FLAGS, &flags, - META_CORE_GET_FRAME_TYPE, &type, META_CORE_GET_CLIENT_WIDTH, &area.width, META_CORE_GET_CLIENT_HEIGHT, &area.height, META_CORE_GET_END); - meta_theme_get_frame_borders (frame->tv->theme, - frame->tv->style_context, - type, flags, - &borders); + + meta_uiframe_get_frame_borders (frame, &borders); area.x = borders.total.left; area.y = borders.total.top; @@ -917,125 +733,41 @@ subtract_client_area (cairo_region_t *region, cairo_region_destroy (tmp_region); } -static void +void meta_uiframe_paint (MetaUIFrame *frame, cairo_t *cr) { - MetaFrameFlags flags; - MetaFrameType type; - GdkPixbuf *mini_icon; - GdkPixbuf *icon; - int w, h; - MetaButtonState button_states[META_BUTTON_TYPE_LAST]; - Window grab_frame; - int i; - MetaButtonLayout button_layout; - MetaGrabOp grab_op; - Display *display; + GtkWidget *widget = GTK_WIDGET (frame); + GtkStyleContext *style_gtk = gtk_widget_get_style_context (widget); + GdkRectangle visible_rect; + MetaFrameBorders borders; - display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); + meta_uiframe_get_frame_borders (frame, &borders); + gtk_widget_get_allocation (widget, &visible_rect); - for (i = 0; i < META_BUTTON_TYPE_LAST; i++) - button_states[i] = META_BUTTON_STATE_NORMAL; + visible_rect.x += borders.invisible.left; + visible_rect.y += borders.invisible.top; + visible_rect.width -= borders.invisible.left + borders.invisible.right; + visible_rect.height -= borders.invisible.top - borders.invisible.bottom; - grab_frame = meta_core_get_grab_frame (display); - grab_op = meta_core_get_grab_op (display); - if (grab_frame != frame->xwindow) - grab_op = META_GRAB_OP_NONE; - - /* Set prelight state */ - switch (frame->prelit_control) - { - case META_FRAME_CONTROL_MENU: - if (grab_op == META_GRAB_OP_CLICKING_MENU) - button_states[META_BUTTON_TYPE_MENU] = META_BUTTON_STATE_PRESSED; - else - button_states[META_BUTTON_TYPE_MENU] = META_BUTTON_STATE_PRELIGHT; - break; - case META_FRAME_CONTROL_MINIMIZE: - if (grab_op == META_GRAB_OP_CLICKING_MINIMIZE) - button_states[META_BUTTON_TYPE_MINIMIZE] = META_BUTTON_STATE_PRESSED; - else - button_states[META_BUTTON_TYPE_MINIMIZE] = META_BUTTON_STATE_PRELIGHT; - break; - case META_FRAME_CONTROL_MAXIMIZE: - if (grab_op == META_GRAB_OP_CLICKING_MAXIMIZE) - button_states[META_BUTTON_TYPE_MAXIMIZE] = META_BUTTON_STATE_PRESSED; - else - button_states[META_BUTTON_TYPE_MAXIMIZE] = META_BUTTON_STATE_PRELIGHT; - break; - case META_FRAME_CONTROL_UNMAXIMIZE: - if (grab_op == META_GRAB_OP_CLICKING_UNMAXIMIZE) - button_states[META_BUTTON_TYPE_MAXIMIZE] = META_BUTTON_STATE_PRESSED; - else - button_states[META_BUTTON_TYPE_MAXIMIZE] = META_BUTTON_STATE_PRELIGHT; - break; - case META_FRAME_CONTROL_SHADE: - if (grab_op == META_GRAB_OP_CLICKING_SHADE) - button_states[META_BUTTON_TYPE_SHADE] = META_BUTTON_STATE_PRESSED; - else - button_states[META_BUTTON_TYPE_SHADE] = META_BUTTON_STATE_PRELIGHT; - break; - case META_FRAME_CONTROL_UNSHADE: - if (grab_op == META_GRAB_OP_CLICKING_UNSHADE) - button_states[META_BUTTON_TYPE_UNSHADE] = META_BUTTON_STATE_PRESSED; - else - button_states[META_BUTTON_TYPE_UNSHADE] = META_BUTTON_STATE_PRELIGHT; - break; - case META_FRAME_CONTROL_ABOVE: - if (grab_op == META_GRAB_OP_CLICKING_ABOVE) - button_states[META_BUTTON_TYPE_ABOVE] = META_BUTTON_STATE_PRESSED; - else - button_states[META_BUTTON_TYPE_ABOVE] = META_BUTTON_STATE_PRELIGHT; - break; - case META_FRAME_CONTROL_UNABOVE: - if (grab_op == META_GRAB_OP_CLICKING_UNABOVE) - button_states[META_BUTTON_TYPE_UNABOVE] = META_BUTTON_STATE_PRESSED; - else - button_states[META_BUTTON_TYPE_UNABOVE] = META_BUTTON_STATE_PRELIGHT; - break; - case META_FRAME_CONTROL_STICK: - if (grab_op == META_GRAB_OP_CLICKING_STICK) - button_states[META_BUTTON_TYPE_STICK] = META_BUTTON_STATE_PRESSED; - else - button_states[META_BUTTON_TYPE_STICK] = META_BUTTON_STATE_PRELIGHT; - break; - case META_FRAME_CONTROL_UNSTICK: - if (grab_op == META_GRAB_OP_CLICKING_UNSTICK) - button_states[META_BUTTON_TYPE_UNSTICK] = META_BUTTON_STATE_PRESSED; - else - button_states[META_BUTTON_TYPE_UNSTICK] = META_BUTTON_STATE_PRELIGHT; - break; - case META_FRAME_CONTROL_DELETE: - if (grab_op == META_GRAB_OP_CLICKING_DELETE) - button_states[META_BUTTON_TYPE_CLOSE] = META_BUTTON_STATE_PRESSED; - else - button_states[META_BUTTON_TYPE_CLOSE] = META_BUTTON_STATE_PRELIGHT; - break; - default: - break; - } - - meta_core_get (display, frame->xwindow, - META_CORE_GET_FRAME_FLAGS, &flags, - META_CORE_GET_FRAME_TYPE, &type, - META_CORE_GET_MINI_ICON, &mini_icon, - META_CORE_GET_ICON, &icon, - META_CORE_GET_CLIENT_WIDTH, &w, - META_CORE_GET_CLIENT_HEIGHT, &h, - META_CORE_GET_END); + sync_state_flags (frame); + + gtk_render_background (style_gtk, cr, + visible_rect.x, + visible_rect.y, + visible_rect.width, + visible_rect.height); - meta_prefs_get_button_layout (&button_layout); - - meta_theme_draw_frame_with_style (frame->tv->theme, - frame->tv->style_context, - cr, - type, - flags, - w, h, - &button_layout, - button_states, - mini_icon, icon); + gtk_render_frame (style_gtk, cr, + visible_rect.x, + visible_rect.y, + visible_rect.width, + visible_rect.height); + + /* We chain up to paint the contents here so that the mask + * we paint with meta_frame_render_background can be accurate + * with children. */ + GTK_WIDGET_CLASS (meta_uiframe_parent_class)->draw (widget, cr); } static gboolean @@ -1068,11 +800,9 @@ meta_uiframe_draw (GtkWidget *widget, meta_uiframe_paint (frame, cr); - GTK_WIDGET_CLASS (meta_uiframe_parent_class)->draw (widget, cr); - out: cairo_region_destroy (region); - + return TRUE; } @@ -1081,199 +811,52 @@ meta_uiframe_style_updated (GtkWidget *widget) { MetaUIFrame *frame = META_UIFRAME (widget); GtkWidget *container = frame->container; - MetaFrameGeometry fgeom; - - meta_uiframe_calc_geometry (frame, &fgeom); - - gtk_widget_set_margin_left (container, fgeom.borders.total.left); - gtk_widget_set_margin_right (container, fgeom.borders.total.right); - gtk_widget_set_margin_top (container, fgeom.borders.invisible.top); - gtk_widget_set_size_request (container, -1, fgeom.borders.visible.top); -} - -static gboolean -meta_uiframe_enter_notify_event (GtkWidget *widget, - GdkEventCrossing *event) -{ - MetaUIFrame *frame; - MetaFrameControl control; - - frame = META_UIFRAME (widget); - - control = get_control (frame, event->x, event->y); - meta_uiframe_update_prelit_control (frame, control); - - return TRUE; -} - -static gboolean -meta_uiframe_leave_notify_event (GtkWidget *widget, - GdkEventCrossing *event) -{ - MetaUIFrame *frame = META_UIFRAME (widget); - - meta_uiframe_update_prelit_control (frame, META_FRAME_CONTROL_NONE); - - return TRUE; -} + MetaFrameBorders borders; -static GdkRectangle* -control_rect (MetaFrameControl control, - MetaFrameGeometry *fgeom) -{ - GdkRectangle *rect; - - rect = NULL; - switch (control) - { - case META_FRAME_CONTROL_TITLE: - rect = &fgeom->title_rect; - break; - case META_FRAME_CONTROL_DELETE: - rect = &fgeom->close_rect.visible; - break; - case META_FRAME_CONTROL_MENU: - rect = &fgeom->menu_rect.visible; - break; - case META_FRAME_CONTROL_MINIMIZE: - rect = &fgeom->min_rect.visible; - break; - case META_FRAME_CONTROL_MAXIMIZE: - case META_FRAME_CONTROL_UNMAXIMIZE: - rect = &fgeom->max_rect.visible; - break; - case META_FRAME_CONTROL_SHADE: - rect = &fgeom->shade_rect.visible; - break; - case META_FRAME_CONTROL_UNSHADE: - rect = &fgeom->unshade_rect.visible; - break; - case META_FRAME_CONTROL_ABOVE: - rect = &fgeom->above_rect.visible; - break; - case META_FRAME_CONTROL_UNABOVE: - rect = &fgeom->unabove_rect.visible; - break; - case META_FRAME_CONTROL_STICK: - rect = &fgeom->stick_rect.visible; - break; - case META_FRAME_CONTROL_UNSTICK: - rect = &fgeom->unstick_rect.visible; - break; - case META_FRAME_CONTROL_RESIZE_SE: - break; - case META_FRAME_CONTROL_RESIZE_S: - break; - case META_FRAME_CONTROL_RESIZE_SW: - break; - case META_FRAME_CONTROL_RESIZE_N: - break; - case META_FRAME_CONTROL_RESIZE_NE: - break; - case META_FRAME_CONTROL_RESIZE_NW: - break; - case META_FRAME_CONTROL_RESIZE_W: - break; - case META_FRAME_CONTROL_RESIZE_E: - break; - case META_FRAME_CONTROL_NONE: - break; - case META_FRAME_CONTROL_CLIENT_AREA: - break; - } + meta_uiframe_get_frame_borders (frame, &borders); - return rect; + gtk_widget_set_margin_left (container, borders.total.left); + gtk_widget_set_margin_right (container, borders.total.right); + gtk_widget_set_margin_top (container, borders.invisible.top); + gtk_widget_set_size_request (container, -1, borders.visible.top); } -#define TOP_RESIZE_HEIGHT 4 static MetaFrameControl get_control (MetaUIFrame *frame, int x, int y) { - MetaFrameGeometry fgeom; + MetaFrameBorders borders; MetaFrameFlags flags; MetaFrameType type; gboolean has_vert, has_horiz; gboolean has_north_resize; cairo_rectangle_int_t client; - - meta_uiframe_calc_geometry (frame, &fgeom); - get_client_rect (&fgeom, fgeom.width, fgeom.height, &client); - - if (POINT_IN_RECT (x, y, client)) - return META_FRAME_CONTROL_CLIENT_AREA; - - if (POINT_IN_RECT (x, y, fgeom.close_rect.clickable)) - return META_FRAME_CONTROL_DELETE; - - if (POINT_IN_RECT (x, y, fgeom.min_rect.clickable)) - return META_FRAME_CONTROL_MINIMIZE; - - if (POINT_IN_RECT (x, y, fgeom.menu_rect.clickable)) - return META_FRAME_CONTROL_MENU; + int width, height; meta_core_get (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), frame->xwindow, META_CORE_GET_FRAME_FLAGS, &flags, + META_CORE_GET_CLIENT_WIDTH, &width, + META_CORE_GET_CLIENT_HEIGHT, &height, META_CORE_GET_FRAME_TYPE, &type, META_CORE_GET_END); + meta_uiframe_get_frame_borders (frame, &borders); + get_client_rect (&borders, width, height, &client); + + if (POINT_IN_RECT (x, y, client)) + return META_FRAME_CONTROL_CLIENT_AREA; + has_north_resize = (type != META_FRAME_TYPE_ATTACHED); has_vert = (flags & META_FRAME_ALLOWS_VERTICAL_RESIZE) != 0; has_horiz = (flags & META_FRAME_ALLOWS_HORIZONTAL_RESIZE) != 0; - if (POINT_IN_RECT (x, y, fgeom.title_rect)) - { - if (has_vert && y <= TOP_RESIZE_HEIGHT && has_north_resize) - return META_FRAME_CONTROL_RESIZE_N; - else - return META_FRAME_CONTROL_TITLE; - } - - if (POINT_IN_RECT (x, y, fgeom.max_rect.clickable)) - { - if (flags & META_FRAME_MAXIMIZED) - return META_FRAME_CONTROL_UNMAXIMIZE; - else - return META_FRAME_CONTROL_MAXIMIZE; - } - - if (POINT_IN_RECT (x, y, fgeom.shade_rect.clickable)) - { - return META_FRAME_CONTROL_SHADE; - } - - if (POINT_IN_RECT (x, y, fgeom.unshade_rect.clickable)) - { - return META_FRAME_CONTROL_UNSHADE; - } - - if (POINT_IN_RECT (x, y, fgeom.above_rect.clickable)) - { - return META_FRAME_CONTROL_ABOVE; - } - - if (POINT_IN_RECT (x, y, fgeom.unabove_rect.clickable)) - { - return META_FRAME_CONTROL_UNABOVE; - } - - if (POINT_IN_RECT (x, y, fgeom.stick_rect.clickable)) - { - return META_FRAME_CONTROL_STICK; - } - - if (POINT_IN_RECT (x, y, fgeom.unstick_rect.clickable)) - { - return META_FRAME_CONTROL_UNSTICK; - } - /* South resize always has priority over north resize, * in case of overlap. */ - if (y >= (fgeom.height - fgeom.borders.total.bottom) && - x >= (fgeom.width - fgeom.borders.total.right)) + if (y >= (height - borders.total.bottom) && + x >= (width - borders.total.right)) { if (has_vert && has_horiz) return META_FRAME_CONTROL_RESIZE_SE; @@ -1282,8 +865,8 @@ get_control (MetaUIFrame *frame, else if (has_horiz) return META_FRAME_CONTROL_RESIZE_E; } - else if (y >= (fgeom.height - fgeom.borders.total.bottom) && - x <= fgeom.borders.total.left) + else if (y >= (height - borders.total.bottom) && + x <= borders.total.left) { if (has_vert && has_horiz) return META_FRAME_CONTROL_RESIZE_SW; @@ -1292,8 +875,8 @@ get_control (MetaUIFrame *frame, else if (has_horiz) return META_FRAME_CONTROL_RESIZE_W; } - else if (y < (fgeom.borders.invisible.top) && - x <= fgeom.borders.total.left && has_north_resize) + else if (y < (borders.invisible.top) && + x <= borders.total.left && has_north_resize) { if (has_vert && has_horiz) return META_FRAME_CONTROL_RESIZE_NW; @@ -1302,8 +885,8 @@ get_control (MetaUIFrame *frame, else if (has_horiz) return META_FRAME_CONTROL_RESIZE_W; } - else if (y < (fgeom.borders.invisible.top) && - x >= fgeom.width - fgeom.borders.total.right && has_north_resize) + else if (y < (borders.invisible.top) && + x >= width - borders.total.right && has_north_resize) { if (has_vert && has_horiz) return META_FRAME_CONTROL_RESIZE_NE; @@ -1312,28 +895,28 @@ get_control (MetaUIFrame *frame, else if (has_horiz) return META_FRAME_CONTROL_RESIZE_E; } - else if (y < (fgeom.borders.invisible.top + TOP_RESIZE_HEIGHT)) + else if (y < borders.invisible.top) { if (has_vert && has_north_resize) return META_FRAME_CONTROL_RESIZE_N; } - else if (y >= (fgeom.height - fgeom.borders.total.bottom)) + else if (y >= (height - borders.total.bottom)) { if (has_vert) return META_FRAME_CONTROL_RESIZE_S; } - else if (x <= fgeom.borders.total.left) + else if (x <= borders.total.left) { if (has_horiz) return META_FRAME_CONTROL_RESIZE_W; } - else if (x >= (fgeom.width - fgeom.borders.total.right)) + else if (x >= (width - borders.total.right)) { if (has_horiz) return META_FRAME_CONTROL_RESIZE_E; } - if (y >= fgeom.borders.total.top) + if (y >= borders.total.top) return META_FRAME_CONTROL_NONE; else return META_FRAME_CONTROL_TITLE; @@ -1354,7 +937,4 @@ meta_uiframe_class_init (MetaUIFrameClass *class) widget_class->style_updated = meta_uiframe_style_updated; widget_class->button_press_event = meta_uiframe_button_press_event; widget_class->button_release_event = meta_uiframe_button_release_event; - widget_class->motion_notify_event = meta_uiframe_motion_notify_event; - widget_class->enter_notify_event = meta_uiframe_enter_notify_event; - widget_class->leave_notify_event = meta_uiframe_leave_notify_event; } diff --git a/src/ui/uiframe.h b/src/ui/uiframe.h index 1d8e37716..6f00b5eab 100644 --- a/src/ui/uiframe.h +++ b/src/ui/uiframe.h @@ -27,7 +27,6 @@ #include <gtk/gtk.h> #include <gdk/gdkx.h> #include <meta/common.h> -#include "theme-private.h" typedef enum { @@ -75,11 +74,12 @@ struct _MetaUIFrame Window xwindow; GdkWindow *window; - MetaThemeVariant *tv; MetaFrameControl prelit_control; GtkWidget *label; GtkWidget *container; + + gboolean style_context_initialized; }; struct _MetaUIFrameClass @@ -92,9 +92,10 @@ GType meta_uiframe_get_type (void) G_GNUC_CONST; void meta_uiframe_set_title (MetaUIFrame *frame, const char *title); -void meta_uiframe_attach_style (MetaUIFrame *frame); +void meta_uiframe_paint (MetaUIFrame *frame, + cairo_t *cr); -void meta_uiframe_calc_geometry (MetaUIFrame *frame, - MetaFrameGeometry *fgeom); +void meta_uiframe_get_frame_borders (MetaUIFrame *frame, + MetaFrameBorders *borders); #endif |