#include <gio/gio.h>
+#include "gdbus-sessionbus.h"
+
+/* Markup printing {{{1 */
+
+/* This used to be part of GLib, but it was removed before the stable
+ * release because it wasn't generally useful. We want it here, though.
+ */
+static void
+indent_string (GString *string,
+ gint indent)
+{
+ while (indent--)
+ g_string_append_c (string, ' ');
+}
+
+static GString *
+g_menu_markup_print_string (GString *string,
+ GMenuModel *model,
+ gint indent,
+ gint tabstop)
+{
+ gboolean need_nl = FALSE;
+ gint i, n;
+
+ if G_UNLIKELY (string == NULL)
+ string = g_string_new (NULL);
+
+ n = g_menu_model_get_n_items (model);
+
+ for (i = 0; i < n; i++)
+ {
+ GMenuAttributeIter *attr_iter;
+ GMenuLinkIter *link_iter;
+ GString *contents;
+ GString *attrs;
+
+ attr_iter = g_menu_model_iterate_item_attributes (model, i);
+ link_iter = g_menu_model_iterate_item_links (model, i);
+ contents = g_string_new (NULL);
+ attrs = g_string_new (NULL);
+
+ while (g_menu_attribute_iter_next (attr_iter))
+ {
+ const char *name = g_menu_attribute_iter_get_name (attr_iter);
+ GVariant *value = g_menu_attribute_iter_get_value (attr_iter);
+
+ if (g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
+ {
+ gchar *str;
+ str = g_markup_printf_escaped (" %s='%s'", name, g_variant_get_string (value, NULL));
+ g_string_append (attrs, str);
+ g_free (str);
+ }
+
+ else
+ {
+ gchar *printed;
+ gchar *str;
+ const gchar *type;
+
+ printed = g_variant_print (value, TRUE);
+ type = g_variant_type_peek_string (g_variant_get_type (value));
+ str = g_markup_printf_escaped ("<attribute name='%s' type='%s'>%s</attribute>\n", name, type, printed);
+ indent_string (contents, indent + tabstop);
+ g_string_append (contents, str);
+ g_free (printed);
+ g_free (str);
+ }
+
+ g_variant_unref (value);
+ }
+ g_object_unref (attr_iter);
+
+ while (g_menu_link_iter_next (link_iter))
+ {
+ const gchar *name = g_menu_link_iter_get_name (link_iter);
+ GMenuModel *menu = g_menu_link_iter_get_value (link_iter);
+ gchar *str;
+
+ if (contents->str[0])
+ g_string_append_c (contents, '\n');
+
+ str = g_markup_printf_escaped ("<link name='%s'>\n", name);
+ indent_string (contents, indent + tabstop);
+ g_string_append (contents, str);
+ g_free (str);
+
+ g_menu_markup_print_string (contents, menu, indent + 2 * tabstop, tabstop);
+
+ indent_string (contents, indent + tabstop);
+ g_string_append (contents, "</link>\n");
+ g_object_unref (menu);
+ }
+ g_object_unref (link_iter);
+
+ if (contents->str[0])
+ {
+ indent_string (string, indent);
+ g_string_append_printf (string, "<item%s>\n", attrs->str);
+ g_string_append (string, contents->str);
+ indent_string (string, indent);
+ g_string_append (string, "</item>\n");
+ need_nl = TRUE;
+ }
+
+ else
+ {
+ if (need_nl)
+ g_string_append_c (string, '\n');
+
+ indent_string (string, indent);
+ g_string_append_printf (string, "<item%s/>\n", attrs->str);
+ need_nl = FALSE;
+ }
+
+ g_string_free (contents, TRUE);
+ g_string_free (attrs, TRUE);
+ }
+
+ return string;
+}
+
/* TestItem {{{1 */
/* This utility struct is used by both the RandomMenu and MirrorMenu
}
}
+static void
+assert_menuitem_equal (GMenuItem *item,
+ GMenuModel *model,
+ gint index)
+{
+ GMenuAttributeIter *attr_iter;
+ GMenuLinkIter *link_iter;
+ const gchar *name;
+ GVariant *value;
+ GMenuModel *linked_model;
+
+ /* NOTE we can't yet test whether item has attributes or links that
+ * are not in the model, because there's no iterator API for menu
+ * items */
+
+ attr_iter = g_menu_model_iterate_item_attributes (model, index);
+ while (g_menu_attribute_iter_get_next (attr_iter, &name, &value))
+ {
+ GVariant *item_value;
+
+ item_value = g_menu_item_get_attribute_value (item, name, g_variant_get_type (value));
+ g_assert (item_value && g_variant_equal (item_value, value));
+
+ g_variant_unref (item_value);
+ g_variant_unref (value);
+ }
+
+ link_iter = g_menu_model_iterate_item_links (model, index);
+ while (g_menu_link_iter_get_next (link_iter, &name, &linked_model))
+ {
+ GMenuModel *item_linked_model;
+
+ item_linked_model = g_menu_item_get_link (item, name);
+ g_assert (linked_model == item_linked_model);
+
+ g_object_unref (item_linked_model);
+ g_object_unref (linked_model);
+ }
+
+ g_object_unref (attr_iter);
+ g_object_unref (link_iter);
+}
+
/* Test cases {{{1 */
static void
test_equality (void)
g_object_unref (menu);
g_main_loop_unref (loop);
+ g_object_unref (bus);
}
static gpointer
g_object_unref (menu[i]);
}
-typedef struct {
- GMenu *menu;
- GHashTable *objects;
-} ParserData;
-
-static void
-start_element (GMarkupParseContext *context,
- const gchar *element_name,
- const gchar **attribute_names,
- const gchar **attribute_values,
- gpointer user_data,
- GError **error)
-{
- ParserData *data = user_data;
-
- if (g_strcmp0 (element_name, "menu") == 0)
- g_menu_markup_parser_start_menu (context, "domain", data->objects);
-}
-
-static void
-end_element (GMarkupParseContext *context,
- const gchar *element_name,
- gpointer user_data,
- GError **error)
-{
- ParserData *data = user_data;
-
- if (g_strcmp0 (element_name, "menu") == 0)
- data->menu = g_menu_markup_parser_end_menu (context);
-}
-
-static GMenuModel *
-parse_menu_string (const gchar *string, GHashTable *objects, GError **error)
-{
- const GMarkupParser parser = {
- start_element, end_element, NULL, NULL, NULL
- };
- GMarkupParseContext *context;
- ParserData data;
-
- data.menu = NULL;
- data.objects = objects;
-
- context = g_markup_parse_context_new (&parser, 0, &data, NULL);
- g_markup_parse_context_parse (context, string, -1, error);
- g_markup_parse_context_free (context);
-
- return (GMenuModel*)data.menu;
-}
-
-static gchar *
-menu_to_string (GMenuModel *menu)
-{
- GString *s;
-
- s = g_string_new ("<menu>\n");
- g_menu_markup_print_string (s, menu, 2, 2);
- g_string_append (s, "</menu>\n");
-
- return g_string_free (s, FALSE);
-}
-
-const gchar menu_data[] =
- "<menu id='edit-menu'>\n"
- " <section>\n"
- " <item action='undo'>\n"
- " <attribute name='label' translatable='yes' context='Stock label'>'_Undo'</attribute>\n"
- " </item>\n"
- " <item label='Redo' action='redo'/>\n"
- " </section>\n"
- " <section></section>\n"
- " <section label='Copy & Paste'>\n"
- " <item label='Cut' action='cut'/>\n"
- " <item label='Copy' action='copy'/>\n"
- " <item label='Paste' action='paste'/>\n"
- " </section>\n"
- " <item><link name='section' id='blargh'>\n"
- " <item label='Bold' action='bold'/>\n"
- " <submenu label='Language'>\n"
- " <item label='Latin' action='lang' target='latin'/>\n"
- " <item label='Greek' action='lang' target='greek'/>\n"
- " <item label='Urdu' action='lang' target='urdu'/>\n"
- " </submenu>\n"
- " <item name='test unusual attributes'>\n"
- " <attribute name='action' type='s'>'quite-some-action'</attribute>\n"
- " <attribute name='target' type='i'>36</attribute>\n"
- " <attribute name='chocolate-thunda' type='as'>['a','b']</attribute>\n"
- " <attribute name='thing1' type='g'>'s(uu)'</attribute>\n"
- " <attribute name='icon' type='s'>'small blue thing'</attribute>\n"
- " </item>\n"
- " </link></item>\n"
- "</menu>\n";
-
-static void
-test_markup_roundtrip (void)
-{
- GError *error = NULL;
- GMenuModel *a;
- GMenuModel *b;
- gchar *s;
- gchar *s2;
-
- a = parse_menu_string (menu_data, NULL, &error);
- g_assert_no_error (error);
- g_assert (G_IS_MENU_MODEL (a));
-
- /* normalized representation */
- s = menu_to_string (a);
-
- b = parse_menu_string (s, NULL, &error);
- g_assert_no_error (error);
- g_assert (G_IS_MENU_MODEL (b));
-
- assert_menus_equal (G_MENU_MODEL (a), G_MENU_MODEL (b));
-
- s2 = menu_to_string (b);
-
- g_assert_cmpstr (s, ==, s2);
-
- g_object_unref (a);
- g_object_unref (b);
- g_free (s);
- g_free (s2);
-}
-
-static void
-test_markup_objects (void)
-{
- GMenuModel *a, *b;
- GHashTable *objects;
- GError *error = NULL;
-
- objects = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
- a = parse_menu_string (menu_data, objects, &error);
- g_assert_no_error (error);
- g_assert (G_IS_MENU_MODEL (a));
- g_assert_cmpint (g_hash_table_size (objects), ==, 1);
- b = g_hash_table_lookup (objects, "blargh");
- g_assert (G_IS_MENU_MODEL (b));
- g_object_unref (a);
- g_hash_table_unref (objects);
-}
-
-const gchar menu_data2[] =
- "<menu>"
- " <section>"
- " <item label='Redo' action='redo'/>"
- " </section>"
- " <section></section>\n"
- " <section label='Copy & Paste'>"
- " <item label='Cut' action='cut'/>"
- " </section>"
- " <section id='section1'>"
- " <item label='Bold' action='bold'/>"
- " <submenu label='Language' id='submenu1'>"
- " <section id='section2'>"
- " <item label='Urdu' action='lang' target='urdu'/>"
- " </section>"
- " </submenu>"
- " </section>"
- "</menu>";
-static void
-test_markup_ids (void)
-{
- GMenuModel *a, *b;
- GHashTable *objects;
- GError *error = NULL;
-
- objects = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
- a = parse_menu_string (menu_data2, objects, &error);
- g_assert_no_error (error);
- g_assert (G_IS_MENU_MODEL (a));
- g_assert_cmpint (g_hash_table_size (objects), ==, 3);
- b = g_hash_table_lookup (objects, "section1");
- g_assert (G_IS_MENU_MODEL (b));
- b = g_hash_table_lookup (objects, "section2");
- g_assert (G_IS_MENU_MODEL (b));
- b = g_hash_table_lookup (objects, "submenu1");
- g_assert (G_IS_MENU_MODEL (b));
- g_object_unref (a);
- g_hash_table_unref (objects);
-}
-
static void
test_attributes (void)
{
item = g_menu_item_new ("test", NULL);
g_menu_item_set_attribute_value (item, "boolean", g_variant_new_boolean (FALSE));
g_menu_item_set_attribute_value (item, "string", g_variant_new_string ("bla"));
- g_menu_item_set_attribute_value (item, "double", g_variant_new_double (1.5));
+
+ g_menu_item_set_attribute (item, "double", "d", 1.5);
v = g_variant_new_parsed ("[('one', 1), ('two', %i), (%s, 3)]", 2, "three");
g_menu_item_set_attribute_value (item, "complex", v);
g_menu_item_set_attribute_value (item, "test-123", g_variant_new_string ("test-123"));
g_menu_append_item (menu, item);
+ g_menu_item_set_attribute (item, "double", "d", G_PI);
+
g_assert_cmpint (g_menu_model_get_n_items (G_MENU_MODEL (menu)), ==, 1);
v = g_menu_model_get_item_attribute_value (G_MENU_MODEL (menu), 0, "boolean", NULL);
g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE("a(si)")));
g_variant_unref (v);
+ g_menu_remove_all (menu);
+
g_object_unref (menu);
+ g_object_unref (item);
}
static void
menu = g_menu_new ();
- item = g_menu_item_new ("test1", NULL);
- g_menu_item_set_link (item, "section", m);
- g_menu_append_item (menu, item);
-
item = g_menu_item_new ("test2", NULL);
g_menu_item_set_link (item, "submenu", m);
- g_menu_append_item (menu, item);
+ g_menu_prepend_item (menu, item);
+
+ item = g_menu_item_new ("test1", NULL);
+ g_menu_item_set_link (item, "section", m);
+ g_menu_insert_item (menu, 0, item);
item = g_menu_item_new ("test3", NULL);
g_menu_item_set_link (item, "wallet", m);
- g_menu_append_item (menu, item);
+ g_menu_insert_item (menu, 1000, item);
item = g_menu_item_new ("test4", NULL);
g_menu_item_set_link (item, "purse", m);
}
static void
-test_misc (void)
+test_convenience (void)
{
- /* trying to use most of the GMenu api for constructing the
- * same menu two different ways
- */
- GMenu *a, *m, *m2;
- GMenuModel *b;
+ GMenu *m1, *m2;
+ GMenu *sub;
GMenuItem *item;
- const gchar *s;
- a = g_menu_new ();
- item = g_menu_item_new ("test1", "action1::target1");
- g_menu_prepend_item (a, item);
+ m1 = g_menu_new ();
+ m2 = g_menu_new ();
+ sub = g_menu_new ();
+
+ g_menu_prepend (m1, "label1", "do::something");
+ g_menu_insert (m2, 0, "label1", "do::something");
+
+ g_menu_append (m1, "label2", "do::somethingelse");
+ g_menu_insert (m2, -1, "label2", "do::somethingelse");
+
+ g_menu_insert_section (m1, 10, "label3", G_MENU_MODEL (sub));
+ item = g_menu_item_new_section ("label3", G_MENU_MODEL (sub));
+ g_menu_insert_item (m2, 10, item);
g_object_unref (item);
- m = g_menu_new ();
- g_menu_prepend (m, "test2a", "action2");
- g_menu_append (m, "test2c", NULL);
- g_menu_insert (m, 1, "test2b", NULL);
+ g_menu_prepend_section (m1, "label4", G_MENU_MODEL (sub));
+ g_menu_insert_section (m2, 0, "label4", G_MENU_MODEL (sub));
- item = g_menu_item_new_submenu ("test2", G_MENU_MODEL (m));
- g_menu_append_item (a, item);
+ g_menu_append_section (m1, "label5", G_MENU_MODEL (sub));
+ g_menu_insert_section (m2, -1, "label5", G_MENU_MODEL (sub));
+
+ g_menu_insert_submenu (m1, 5, "label6", G_MENU_MODEL (sub));
+ item = g_menu_item_new_submenu ("label6", G_MENU_MODEL (sub));
+ g_menu_insert_item (m2, 5, item);
g_object_unref (item);
- g_object_unref (m);
- m = g_menu_new ();
+ g_menu_prepend_submenu (m1, "label7", G_MENU_MODEL (sub));
+ g_menu_insert_submenu (m2, 0, "label7", G_MENU_MODEL (sub));
- m2 = g_menu_new ();
- g_menu_append (m2, "x", NULL);
- g_menu_prepend_section (m, "test3a", G_MENU_MODEL (m2));
+ g_menu_append_submenu (m1, "label8", G_MENU_MODEL (sub));
+ g_menu_insert_submenu (m2, -1, "label8", G_MENU_MODEL (sub));
+
+ assert_menus_equal (G_MENU_MODEL (m1), G_MENU_MODEL (m2));
+
+ g_object_unref (m1);
g_object_unref (m2);
+}
+
+static void
+test_menuitem (void)
+{
+ GMenu *menu;
+ GMenu *submenu;
+ GMenuItem *item;
+ GIcon *icon;
+ gboolean b;
+ gchar *s;
+
+ menu = g_menu_new ();
+ submenu = g_menu_new ();
+
+ item = g_menu_item_new ("label", "action");
+ g_menu_item_set_attribute (item, "attribute", "b", TRUE);
+ g_menu_item_set_link (item, G_MENU_LINK_SUBMENU, G_MENU_MODEL (submenu));
+ g_menu_append_item (menu, item);
+
+ icon = g_themed_icon_new ("bla");
+ g_menu_item_set_icon (item, icon);
+ g_object_unref (icon);
+
+ g_assert (g_menu_item_get_attribute (item, "attribute", "b", &b));
+ g_assert (b);
+
+ g_menu_item_set_action_and_target (item, "action", "(bs)", TRUE, "string");
+ g_assert (g_menu_item_get_attribute (item, "target", "(bs)", &b, &s));
+ g_assert (b);
+ g_assert_cmpstr (s, ==, "string");
+ g_free (s);
- item = g_menu_item_new_section ("test3", G_MENU_MODEL (m));
- g_menu_insert_item (a, -1, item);
g_object_unref (item);
- g_object_unref (m);
- s = ""
-"<menu>"
-" <item target='target1' action='action1' label='test1'/>"
-" <item label='test2'>"
-" <link name='submenu'>"
-" <item action='action2' label='test2a'/>"
-" <item label='test2b'/>"
-" <item label='test2c'/>"
-" </link>"
-" </item>"
-" <item label='test3'>"
-" <link name='section'>"
-" <item label='test3a'>"
-" <link name='section'>"
-" <item label='x'/>"
-" </link>"
-" </item>"
-" </link>"
-" </item>"
-"</menu>";
-
- b = parse_menu_string (s, NULL, NULL);
-
- assert_menus_equal (G_MENU_MODEL (a), G_MENU_MODEL (b));
- g_object_unref (a);
- g_object_unref (b);
+ item = g_menu_item_new_from_model (G_MENU_MODEL (menu), 0);
+ assert_menuitem_equal (item, G_MENU_MODEL (menu), 0);
+ g_object_unref (item);
+
+ g_object_unref (menu);
+ g_object_unref (submenu);
}
- /* Epilogue {{{1 */
+/* Epilogue {{{1 */
int
main (int argc, char **argv)
{
+ gboolean ret;
+
g_test_init (&argc, &argv, NULL);
- g_type_init ();
+ session_bus_up ();
g_test_add_func ("/gmenu/equality", test_equality);
g_test_add_func ("/gmenu/random", test_random);
g_test_add_func ("/gmenu/dbus/roundtrip", test_dbus_roundtrip);
g_test_add_func ("/gmenu/dbus/subscriptions", test_dbus_subscriptions);
g_test_add_func ("/gmenu/dbus/threaded", test_dbus_threaded);
- g_test_add_func ("/gmenu/markup/roundtrip", test_markup_roundtrip);
- g_test_add_func ("/gmenu/markup/objects", test_markup_objects);
- g_test_add_func ("/gmenu/markup/ids", test_markup_ids);
g_test_add_func ("/gmenu/attributes", test_attributes);
g_test_add_func ("/gmenu/links", test_links);
g_test_add_func ("/gmenu/mutable", test_mutable);
- g_test_add_func ("/gmenu/misc", test_misc);
+ g_test_add_func ("/gmenu/convenience", test_convenience);
+ g_test_add_func ("/gmenu/menuitem", test_menuitem);
+
+ ret = g_test_run ();
+
+ session_bus_down ();
- return g_test_run ();
+ return ret;
}
/* vim:set foldmethod=marker: */