mutter/src/ui/theme-viewer.c

1366 lines
37 KiB
C

/* -*- 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_container_add (GTK_CONTAINER (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_container_add (GTK_CONTAINER (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;
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 = meta_theme_create_style_context (gtk_widget_get_screen (window), NULL);
gtk_style_context_get (style, GTK_STATE_FLAG_NORMAL, "font", &font_desc, NULL);
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")));
pango_font_description_free (font_desc);
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 int
get_text_height (GtkWidget *widget,
GtkStyleContext *style)
{
PangoFontDescription *font_desc;
int text_height;
gtk_style_context_get (style, GTK_STATE_FLAG_NORMAL, "font", &font_desc, NULL);
text_height = meta_pango_font_desc_get_text_height (font_desc,
gtk_widget_get_pango_context (widget));
pango_font_description_free (font_desc);
return text_height;
}
static PangoLayout*
create_title_layout (GtkWidget *widget)
{
PangoLayout *layout;
layout = gtk_widget_create_pango_layout (widget, _("Window Title Goes Here"));
return layout;
}
static void
run_theme_benchmark (void)
{
GtkWidget* widget;
GtkStyleContext *style_context;
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
};
PangoLayout *layout;
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);
style_context = meta_theme_create_style_context (gtk_widget_get_screen (widget), NULL);
meta_theme_get_frame_borders (global_theme,
META_FRAME_TYPE_NORMAL,
get_text_height (widget, style_context),
get_flags (widget),
&borders);
layout = create_title_layout (widget);
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 (global_theme,
style_context,
cr,
META_FRAME_TYPE_NORMAL,
get_flags (widget),
client_width, client_height,
layout,
get_text_height (widget, style_context),
&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);
g_object_unref (G_OBJECT (layout));
g_object_unref (style_context);
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