/* 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 #include "util.h" #include "theme.h" #include "theme-parser.h" #include "preview-widget.h" #include #include #include #define CLIENT_WIDTH 200 #define CLIENT_HEIGHT 200 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] = { NULL, }; static void run_position_expression_tests (void); static void run_position_expression_timings (void); static void run_theme_benchmark (int client_width, int client_height); static GtkItemFactoryEntry menu_items[] = { { "/_Windows", NULL, NULL, 0, "" }, { "/Windows/tearoff", NULL, NULL, 0, "" }, { "/Windows/_Dialog", "d", NULL, 0, NULL }, { "/Windows/_Modal dialog", NULL, NULL, 0, NULL }, { "/Windows/_Utility", "u", NULL, 0, NULL }, { "/Windows/_Splashscreen", "s", NULL, 0, NULL }, { "/Windows/_Top dock", NULL, NULL, 0, NULL }, { "/Windows/_Bottom dock", NULL, NULL, 0, NULL }, { "/Windows/_Left dock", NULL, NULL, 0, NULL }, { "/Windows/_Right dock", NULL, NULL, 0, NULL }, { "/Windows/_All docks", NULL, NULL, 0, NULL }, { "/Windows/Des_ktop", NULL, NULL, 0, NULL } }; static GtkWidget * normal_contents (void) { GtkWidget *table; GtkWidget *toolbar; GtkWidget *handlebox; GtkWidget *statusbar; GtkWidget *contents; GtkWidget *sw; GtkTextBuffer *buffer; GtkItemFactory *item_factory; table = gtk_table_new (1, 4, FALSE); /* Create the menubar */ item_factory = gtk_item_factory_new (GTK_TYPE_MENU_BAR, "
", NULL); /* Set up item factory to go away */ g_object_ref (item_factory); gtk_object_sink (GTK_OBJECT (item_factory)); g_object_set_data_full (G_OBJECT (table), "
", item_factory, (GDestroyNotify) g_object_unref); /* create menu items */ gtk_item_factory_create_items (item_factory, G_N_ELEMENTS (menu_items), menu_items, NULL); gtk_table_attach (GTK_TABLE (table), gtk_item_factory_get_widget (item_factory, "
"), /* X direction */ /* Y direction */ 0, 1, 0, 1, GTK_EXPAND | GTK_FILL, 0, 0, 0); /* Create the toolbar */ toolbar = gtk_toolbar_new (); gtk_toolbar_insert_stock (GTK_TOOLBAR (toolbar), GTK_STOCK_NEW, "Open another one of these windows", NULL, NULL, NULL, -1); /* -1 means "append" */ gtk_toolbar_insert_stock (GTK_TOOLBAR (toolbar), GTK_STOCK_OPEN, "This is a demo button with an 'open' icon", NULL, NULL, NULL, -1); /* -1 means "append" */ gtk_toolbar_insert_stock (GTK_TOOLBAR (toolbar), GTK_STOCK_QUIT, "This is a demo button with a 'quit' icon", NULL, NULL, NULL, -1); /* -1 means "append" */ handlebox = gtk_handle_box_new (); gtk_container_add (GTK_CONTAINER (handlebox), toolbar); gtk_table_attach (GTK_TABLE (table), handlebox, /* X direction */ /* Y direction */ 0, 1, 1, 2, GTK_EXPAND | GTK_FILL, 0, 0, 0); /* 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_table_attach (GTK_TABLE (table), sw, /* X direction */ /* Y direction */ 0, 1, 2, 3, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); 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_table_attach (GTK_TABLE (table), statusbar, /* X direction */ /* Y direction */ 0, 1, 3, 4, GTK_EXPAND | GTK_FILL, 0, 0, 0); /* Show text widget info in the statusbar */ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (contents)); gtk_widget_show_all (table); return table; } 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_vbox_new (FALSE, 0); action_area = gtk_hbutton_box_new (); 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_hbox_new (FALSE, 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 *table; GtkWidget *button; int i, j; table = gtk_table_new (3, 4, FALSE); 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_table_attach (GTK_TABLE (table), button, /* X direction */ /* Y direction */ i, i+1, j, j+1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); ++j; } ++i; } gtk_widget_show_all (table); return table; } 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_vbox_new (FALSE, 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; GdkColor color; event_box = gtk_event_box_new (); color.red = 40000; color.green = 0; color.blue = 40000; gtk_widget_modify_bg (event_box, GTK_STATE_NORMAL, &color); vbox = gtk_vbox_new (FALSE, 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 (); break; case META_FRAME_TYPE_DIALOG: *title = "Dialog Box"; return dialog_contents (); break; case META_FRAME_TYPE_MODAL_DIALOG: *title = "Modal Dialog Box"; return dialog_contents (); break; case META_FRAME_TYPE_UTILITY: *title = "Utility Palette"; return utility_contents (); break; case META_FRAME_TYPE_MENU: *title = "Torn-off Menu"; return menu_contents (); break; case META_FRAME_TYPE_BORDER: *title = "Border"; return border_only_contents (); break; 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_LAST: g_assert_not_reached (); break; } return flags; } static GtkWidget* preview_collection (int font_size, PangoFontDescription *base_desc) { GtkWidget *box; GtkWidget *sw; GdkColor 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_vbox_new (FALSE, 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 = 0x5144; desktop_color.green = 0x75D6; desktop_color.blue = 0xA699; gtk_widget_modify_bg (eventbox, GTK_STATE_NORMAL, &desktop_color); i = 0; while (i < META_FRAME_TYPE_LAST) { const char *title; 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; } int main (int argc, char **argv) { GtkWidget *window; GtkWidget *collection; GError *err; clock_t start, end; GtkWidget *notebook; int i; bindtextdomain (GETTEXT_PACKAGE, METACITY_LOCALEDIR); run_position_expression_tests (); #if 0 run_position_expression_timings (); #endif gtk_init (&argc, &argv); if (g_getenv ("METACITY_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 (CLIENT_WIDTH, CLIENT_HEIGHT); window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_default_size (GTK_WINDOW (window), 350, 350); g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (gtk_main_quit), NULL); gtk_widget_realize (window); g_assert (window->style); g_assert (window->style->font_desc); notebook = gtk_notebook_new (); gtk_container_add (GTK_CONTAINER (window), notebook); collection = preview_collection (FONT_SIZE_NORMAL, window->style->font_desc); gtk_notebook_append_page (GTK_NOTEBOOK (notebook), collection, gtk_label_new ("Normal Title Font")); collection = preview_collection (FONT_SIZE_SMALL, window->style->font_desc); gtk_notebook_append_page (GTK_NOTEBOOK (notebook), collection, gtk_label_new ("Small Title Font")); collection = preview_collection (FONT_SIZE_LARGE, window->style->font_desc); gtk_notebook_append_page (GTK_NOTEBOOK (notebook), collection, gtk_label_new ("Large Title Font")); 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) { return meta_pango_font_desc_get_text_height (widget->style->font_desc, gtk_widget_get_pango_context (widget)); } 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 (int client_width, int client_height) { GtkWidget* widget; GdkPixmap *pixmap; int top_height, bottom_height, left_width, right_width; 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; int i; #define ITERATIONS 100 widget = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_widget_realize (widget); meta_theme_get_frame_borders (global_theme, META_FRAME_TYPE_NORMAL, get_text_height (widget), get_flags (widget), &top_height, &bottom_height, &left_width, &right_width); pixmap = gdk_pixmap_new (widget->window, client_width + left_width + right_width, client_height + top_height + bottom_height, -1); layout = create_title_layout (widget); start = clock (); i = 0; while (i < ITERATIONS) { meta_theme_draw_frame (global_theme, widget, pixmap, NULL, 0, 0, META_FRAME_TYPE_NORMAL, get_flags (widget), client_width, client_height, layout, get_text_height (widget), button_states, meta_preview_get_mini_icon (), meta_preview_get_icon ()); ++i; } end = clock (); g_print ("Drew %d frames for %dx%d clients in %g seconds (%g seconds per frame)\n", ITERATIONS, client_width, client_height, ((double)end - (double)start) / CLOCKS_PER_SEC, ((double)end - (double)start) / CLOCKS_PER_SEC / (double) ITERATIONS); g_object_unref (G_OBJECT (layout)); g_object_unref (G_OBJECT (pixmap)); 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) { int i; MetaPositionExprEnv env; i = 0; while (i < (int) G_N_ELEMENTS (position_expression_tests)) { GError *err; gboolean retval; const PositionExpressionTest *test; int x, y; test = &position_expression_tests[i]; if (g_getenv ("META_PRINT_TESTS") != NULL) g_print ("Test expression: \"%s\" expecting x = %d y = %d", test->expr, test->expected_x, test->expected_y); err = NULL; 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; retval = meta_parse_position_expression (test->expr, &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); ++i; } } 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); }