diff --git a/legacy/elementary/ChangeLog b/legacy/elementary/ChangeLog index 68c9bad0d2..49553afc5b 100644 --- a/legacy/elementary/ChangeLog +++ b/legacy/elementary/ChangeLog @@ -839,3 +839,9 @@ * elm_plug now emits "image,resized" on server-side changes. * elm_plug typo fixed "image.deleted" to "image,deleted" ('.' -> ',') + +2012-12-20 Henrique Dante de Almeida (hdante) + + * Add elm_dbus_menu to support D-Bus external menus. + * Add main menu to elm_win, which can be exported via D-Bus + * Add configuration: ELM_EXTERNAL_MENU, to switch between internal and external menus diff --git a/legacy/elementary/NEWS b/legacy/elementary/NEWS index 56c9638f87..9a4c6f1ae9 100644 --- a/legacy/elementary/NEWS +++ b/legacy/elementary/NEWS @@ -23,6 +23,8 @@ Additions: * Add elm_sys_notify.[ch] * Add elm_need_elocation() and Elocation.h support with ELM_ELOCATION macro to integrate elocation. * Add elm_plug signals "image,deleted" (fixed typo) and "image,resized". + * Add elm_dbus_menu to support D-Bus external menus. + * Add configuration: ELM_EXTERNAL_MENU, to switch between internal and extenal menus Improvements: @@ -40,6 +42,7 @@ Improvements: * Conformant widget handles displaymode change related with keypad. * Conformant widget handles indicator service. * Elm_Transit image animation effects supports elm_image object type. + * Include a main menu in elm_win, which can be exported via D-Bus. Fixes: diff --git a/legacy/elementary/src/bin/Makefile.am b/legacy/elementary/src/bin/Makefile.am index 5a033d6c4f..657c6472ec 100644 --- a/legacy/elementary/src/bin/Makefile.am +++ b/legacy/elementary/src/bin/Makefile.am @@ -100,6 +100,7 @@ test_launcher.c \ test_layout.c \ test_list.c \ test_map.c \ +test_main_menu.c \ test_menu.c \ test_multi.c \ test_multibuttonentry.c \ diff --git a/legacy/elementary/src/bin/test.c b/legacy/elementary/src/bin/test.c index b2d1dbe2d4..e571fea913 100644 --- a/legacy/elementary/src/bin/test.c +++ b/legacy/elementary/src/bin/test.c @@ -153,6 +153,7 @@ void test_label2(void *data, Evas_Object *obj, void *event_info); void test_conformant(void *data, Evas_Object *obj, void *event_info); void test_conformant2(void *data, Evas_Object *obj, void *event_info); void test_conformant_indicator(void *data, Evas_Object *obj, void *event_info); +void test_main_menu(void *data, Evas_Object *obj, void *event_info); void test_multi(void *data, Evas_Object *obj, void *event_info); void test_floating(void *data, Evas_Object *obj, void *event_info); void test_launcher(void *data, Evas_Object *obj, void *event_info); @@ -643,6 +644,8 @@ add_tests: ADD_TEST(NULL, "Selectors", "Radios", test_radio); ADD_TEST(NULL, "Selectors", "Flip Selector", test_flipselector); ADD_TEST(NULL, "Selectors", "Dayselector", test_dayselector); + ADD_TEST(NULL, "Selectors", "Main menu", test_main_menu); + //------------------------------// ADD_TEST(NULL, "Cursors", "Cursor", test_cursor); diff --git a/legacy/elementary/src/bin/test_main_menu.c b/legacy/elementary/src/bin/test_main_menu.c new file mode 100644 index 0000000000..45636766b3 --- /dev/null +++ b/legacy/elementary/src/bin/test_main_menu.c @@ -0,0 +1,86 @@ +#include +#ifdef HAVE_CONFIG_H +# include "elementary_config.h" +#endif +#ifndef ELM_LIB_QUICKLAUNCH + +static void +_click_me(void *data __UNUSED__, Evas_Object *obj __UNUSED__, + void *event_info __UNUSED__) +{ + fputs(":-)\n", stderr); +} + +void +test_main_menu(void *data __UNUSED__, + Evas_Object *obj __UNUSED__, + void *event_info __UNUSED__) +{ + Evas_Object *win, *bg, *menu, *label, *bx; + Elm_Object_Item *menu_it, *menu_it1; + char *s; + Eina_Bool enabled = EINA_FALSE; + + win = elm_win_add(NULL, "menu", ELM_WIN_BASIC); + elm_win_title_set(win, "Menu"); + elm_win_autodel_set(win, EINA_TRUE); + elm_policy_set(ELM_POLICY_QUIT, ELM_POLICY_QUIT_LAST_WINDOW_CLOSED); + + bg = elm_bg_add(win); + evas_object_size_hint_weight_set(bg, EVAS_HINT_EXPAND, + EVAS_HINT_EXPAND); + + elm_win_resize_object_add(win, bg); + evas_object_show(bg); + + bx = elm_box_add(win); + evas_object_size_hint_weight_set(bx, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + elm_win_resize_object_add(win, bx); + evas_object_show(bx); + + label = elm_label_add(win); + elm_object_text_set(label, "Note: this example requires support from the " + "desktop environment to display the application menu"); + evas_object_size_hint_weight_set(label, EVAS_HINT_EXPAND, + EVAS_HINT_EXPAND); + elm_win_resize_object_add(win, label); + elm_box_pack_end(bx, label); + evas_object_show(label); + + s = getenv("ELM_EXTERNAL_MENU"); + if (s) + enabled = !!atoi(s); + + if (!enabled) + { + label = elm_label_add(win); + elm_object_text_set(label, "(ELM_EXTERNAL_MENU environment variable not " + "set. Test won't display it)"); + evas_object_size_hint_weight_set(label, EVAS_HINT_EXPAND, + EVAS_HINT_EXPAND); + elm_win_resize_object_add(win, label); + elm_box_pack_end(bx, label); + evas_object_show(label); + } + + menu = elm_win_main_menu_get(win); + + menu_it = elm_menu_item_add(menu, NULL, NULL, "first item", NULL, NULL); + elm_menu_item_add(menu, menu_it, NULL, "first item", NULL, NULL); + menu_it1 = elm_menu_item_add(menu, menu_it, NULL, "submenu", NULL, NULL); + elm_menu_item_add(menu, menu_it1, NULL, "first item", NULL, NULL); + elm_menu_item_add(menu, menu_it1, NULL, "second item", NULL, NULL); + + menu_it = elm_menu_item_add(menu, NULL, NULL, "second item", NULL, NULL); + menu_it1 = elm_menu_item_add(menu, menu_it, NULL, "disabled item", NULL, NULL); + elm_object_item_disabled_set(menu_it1, EINA_TRUE); + elm_menu_item_add(menu, menu_it, NULL, "click me :-)", _click_me, NULL); + elm_menu_item_add(menu, menu_it, NULL, "third item", NULL, NULL); + menu_it1 = elm_menu_item_add(menu, menu_it, NULL, "sub menu", NULL, NULL); + elm_menu_item_add(menu, menu_it1, NULL, "first item", NULL, NULL); + + evas_object_resize(win, 250, 350); + evas_object_show(win); +} + +#endif diff --git a/legacy/elementary/src/lib/Makefile.am b/legacy/elementary/src/lib/Makefile.am index 8cc584eb9e..b984aa4998 100644 --- a/legacy/elementary/src/lib/Makefile.am +++ b/legacy/elementary/src/lib/Makefile.am @@ -238,6 +238,7 @@ elm_conform.c \ elm_container.c \ elm_datetime.c \ elm_dayselector.c \ +elm_dbus_menu.c \ elm_diskselector.c \ elm_entry.c \ elm_flip.c \ diff --git a/legacy/elementary/src/lib/elm_config.c b/legacy/elementary/src/lib/elm_config.c index f0ec41ba10..64d7f9edf4 100644 --- a/legacy/elementary/src/lib/elm_config.c +++ b/legacy/elementary/src/lib/elm_config.c @@ -424,6 +424,7 @@ _desc_init(void) ELM_CONFIG_VAL(D, T, indicator_service_90, T_STRING); ELM_CONFIG_VAL(D, T, indicator_service_180, T_STRING); ELM_CONFIG_VAL(D, T, indicator_service_270, T_STRING); + ELM_CONFIG_VAL(D, T, external_menu, T_UCHAR); #undef T #undef D #undef T_INT @@ -1123,6 +1124,7 @@ _config_load(void) _elm_config->indicator_service_90 = eina_stringshare_add("elm_indicator_landscape"); _elm_config->indicator_service_180 = eina_stringshare_add("elm_indicator_portrait"); _elm_config->indicator_service_270 = eina_stringshare_add("elm_indicator_landscape"); + _elm_config->external_menu = EINA_FALSE; } static const char * @@ -1654,6 +1656,8 @@ _env_get(void) if (s) eina_stringshare_replace(&_elm_config->indicator_service_180, s); s = getenv("ELM_INDICATOR_SERVICE_270"); if (s) eina_stringshare_replace(&_elm_config->indicator_service_270, s); + s = getenv("ELM_EXTERNAL_MENU"); + if (s) _elm_config->external_menu = !!atoi(s); } EAPI Eina_Bool @@ -2167,6 +2171,18 @@ elm_config_softcursor_mode_get(void) return _elm_config->softcursor_mode; } +EAPI Eina_Bool +elm_config_external_menu_get(void) +{ + return _elm_config->external_menu; +} + +EAPI void +elm_config_external_menu_set(Eina_Bool enable) +{ + _elm_config->external_menu = !!enable; +} + EAPI void elm_config_all_flush(void) { diff --git a/legacy/elementary/src/lib/elm_dbus_menu.c b/legacy/elementary/src/lib/elm_dbus_menu.c new file mode 100644 index 0000000000..f80cdd3bc2 --- /dev/null +++ b/legacy/elementary/src/lib/elm_dbus_menu.c @@ -0,0 +1,937 @@ +#include +#include +#include "elm_priv.h" +#include "elm_widget_menu.h" + +#ifdef ELM_EDBUS2 + +#define DBUS_PATH "/com/canonical/dbusmenu" +#define DBUS_INTERFACE "com.canonical.dbusmenu" +#define DBUS_MENU_VERSION 3u + +#define REGISTRAR_NAME "com.canonical.AppMenu.Registrar" +#define REGISTRAR_PATH "/com/canonical/AppMenu/Registrar" +#define REGISTRAR_INTERFACE REGISTRAR_NAME + +#define DBUS_DATA_KEY "_Elm_DBus_Menu" +#endif + +struct _Elm_DBus_Menu +{ +#ifdef ELM_EDBUS2 + Eo *menu; + EDBus_Connection *bus; + EDBus_Service_Interface *iface; + unsigned timestamp; + Eina_Hash *elements; + Ecore_Idler *signal_idler; + Ecore_X_Window xid; +#endif +}; + +#ifdef ELM_EDBUS2 +static const EDBus_Service_Interface_Desc _interface; +static unsigned last_object_path; + +typedef enum _Elm_DBus_Property +{ + ELM_DBUS_PROPERTY_LABEL, + ELM_DBUS_PROPERTY_CHILDREN_DISPLAY, + ELM_DBUS_PROPERTY_ENABLED, + ELM_DBUS_PROPERTY_VISIBLE, + ELM_DBUS_PROPERTY_UNKNOWN, +} Elm_DBus_Property; + +enum +{ + ELM_DBUS_SIGNAL_LAYOUT_UPDATED, + ELM_DBUS_SIGNAL_ITEM_ACTIVATION_REQUESTED, +}; + +static Eina_Bool +_menu_add_recursive(Elm_DBus_Menu *dbus_menu, Elm_Menu_Item *item) +{ + int32_t id; + Eina_List *l; + Elm_Menu_Item *subitem; + + id = ++dbus_menu->timestamp; + if (!eina_hash_add(dbus_menu->elements, &id, item)) + return EINA_FALSE; + + item->dbus_idx = id; + + EINA_LIST_FOREACH (item->submenu.items, l, subitem) + { + if (!_menu_add_recursive(dbus_menu, subitem)) + return EINA_FALSE; + } + + return EINA_TRUE; +} + +static Eina_Bool +_layout_idler(void *data) +{ + Elm_DBus_Menu *dbus_menu = data; + + edbus_service_signal_emit(dbus_menu->iface, ELM_DBUS_SIGNAL_LAYOUT_UPDATED, + dbus_menu->timestamp, 0); + + dbus_menu->signal_idler = NULL; + return ECORE_CALLBACK_CANCEL; +} + +static void +_layout_signal(Elm_DBus_Menu *dbus_menu) +{ + if (!dbus_menu->bus) return; + if (dbus_menu->signal_idler) return; + + dbus_menu->signal_idler = ecore_idler_add(_layout_idler, dbus_menu); +} + +static Elm_DBus_Property +_str_to_property(const char *str) +{ + if (!strcmp(str, "label")) + return ELM_DBUS_PROPERTY_LABEL; + else if (!strcmp(str, "children-display")) + return ELM_DBUS_PROPERTY_CHILDREN_DISPLAY; + else if (!strcmp(str, "enabled")) + return ELM_DBUS_PROPERTY_ENABLED; + else if (!strcmp(str, "visible")) + return ELM_DBUS_PROPERTY_VISIBLE; + + return ELM_DBUS_PROPERTY_UNKNOWN; +} + +// Ad-hoc dbusmenu property dictionary subset implementation +static void +_property_append(Elm_Menu_Item *item, + Elm_DBus_Property property, + EDBus_Message_Iter *iter) +{ + EDBus_Message_Iter *variant = NULL; + Elm_Object_Item *item_obj = (Elm_Object_Item *)item; + const char *t; + Eina_Bool b; + + switch (property) + { + case ELM_DBUS_PROPERTY_LABEL: + variant = edbus_message_iter_container_new(iter, 'v', "s"); + t = elm_object_item_part_text_get(item_obj, NULL); + if (!t) t = ""; + edbus_message_iter_basic_append(variant, 's', t); + break; + + case ELM_DBUS_PROPERTY_CHILDREN_DISPLAY: + variant = edbus_message_iter_container_new(iter, 'v', "s"); + + if (eina_list_count(item->submenu.items)) + t = "submenu"; + else + t = ""; + + edbus_message_iter_basic_append(variant, 's', t); + break; + + case ELM_DBUS_PROPERTY_ENABLED: + variant = edbus_message_iter_container_new(iter, 'v', "b"); + b = !elm_object_item_disabled_get(item_obj); + edbus_message_iter_basic_append(variant, 'b', b); + break; + + case ELM_DBUS_PROPERTY_VISIBLE: + variant = edbus_message_iter_container_new(iter, 'v', "b"); + edbus_message_iter_basic_append(variant, 'b', EINA_TRUE); + break; + + case ELM_DBUS_PROPERTY_UNKNOWN: + ERR("Invalid code path"); + return; + } + + edbus_message_iter_container_close(iter, variant); +} + +static void +_property_dict_build(Elm_Menu_Item *item, + Eina_List *property_list, EDBus_Message_Iter *iter) +{ + char *propstr; + Elm_DBus_Property property; + EDBus_Message_Iter *array, *pair; + Eina_List *l; + + array = edbus_message_iter_container_new(iter, 'a', "{sv}"); + + EINA_LIST_FOREACH (property_list, l, propstr) + { + property = _str_to_property(propstr); + + if (property == ELM_DBUS_PROPERTY_UNKNOWN) continue; + + pair = edbus_message_iter_container_new(array, 'e', NULL); + edbus_message_iter_basic_append(pair, 's', propstr); + _property_append(item, property, pair); + edbus_message_iter_container_close(array, pair); + } + + edbus_message_iter_container_close(iter, array); +} + +static void +_layout_build_recursive(Elm_Menu_Item *item, + Eina_List *property_list, unsigned recursion_depth, + EDBus_Message_Iter *iter) +{ + Eina_List *l; + Elm_Menu_Item *subitem; + EDBus_Message_Iter *layout, *array, *variant; + + layout = edbus_message_iter_container_new(iter, 'r', NULL); + edbus_message_iter_basic_append(layout, 'i', item->dbus_idx); + _property_dict_build(item, property_list, layout); + array = edbus_message_iter_container_new(layout, 'a', "v"); + + if (recursion_depth > 0) + { + EINA_LIST_FOREACH (item->submenu.items, l, subitem) + { + if (subitem->separator) continue; + + variant = edbus_message_iter_container_new(array, 'v', + "(ia{sv}av)"); + _layout_build_recursive(subitem, property_list, + recursion_depth - 1, variant); + edbus_message_iter_container_close(array, variant); + } + } + + edbus_message_iter_container_close(layout, array); + edbus_message_iter_container_close(iter, layout); +} + +static void +_root_layout_build(Elm_DBus_Menu *dbus_menu, Eina_List *property_list, + unsigned recursion_depth, EDBus_Message_Iter *iter) +{ + char *property; + EDBus_Message_Iter *layout, *array, *pair, *variant; + const Eina_List *ret = NULL; + Eina_List *items; + Eina_List *l; + Elm_Menu_Item *item; + + layout = edbus_message_iter_container_new(iter, 'r', NULL); + edbus_message_iter_basic_append(layout, 'i', 0); + array = edbus_message_iter_container_new(layout, 'a', "{sv}"); + + EINA_LIST_FOREACH (property_list, l, property) + { + if (!strcmp(property, "children-display")) + { + pair = edbus_message_iter_container_new(array, 'e', NULL); + edbus_message_iter_basic_append(pair, 's', property); + variant = edbus_message_iter_container_new(pair, 'v', "s"); + edbus_message_iter_basic_append(variant, 's', "submenu"); + edbus_message_iter_container_close(pair, variant); + edbus_message_iter_container_close(array, pair); + break; + } + } + + edbus_message_iter_container_close(layout, array); + array = edbus_message_iter_container_new(layout, 'a', "v"); + + if (recursion_depth > 0) + { + eo_do(dbus_menu->menu, elm_obj_menu_items_get(&ret)); + items = (Eina_List *)ret; + EINA_LIST_FOREACH (items, l, item) + { + variant = edbus_message_iter_container_new(array, 'v', + "(ia{sv}av)"); + _layout_build_recursive(item, property_list, + recursion_depth - 1, variant); + edbus_message_iter_container_close(array, variant); + } + } + + edbus_message_iter_container_close(layout, array); + edbus_message_iter_container_close(iter, layout); +} + +static Eina_List * +_empty_properties_handle(Eina_List *property_list) +{ + if (!eina_list_count(property_list)) + { + property_list = eina_list_append(property_list, "label"); + property_list = eina_list_append(property_list, "children-display"); + property_list = eina_list_append(property_list, "enabled"); + property_list = eina_list_append(property_list, "visible"); + } + return property_list; +} + +static Eina_Bool +_event_handle(Elm_DBus_Menu *dbus_menu, EDBus_Message_Iter *iter, int *error_id) +{ + Elm_Menu_Item *item; + const char *event; + int id; + int32_t i; + EDBus_Message_Iter *data; + unsigned *timestamp; + + edbus_message_iter_arguments_get(iter, "isvu", &id, &event, &data, + ×tamp); + i = id; + item = eina_hash_find(dbus_menu->elements, &i); + if (!item) + { + if (error_id) *error_id = id; + return EINA_FALSE; + } + + if (!strcmp(event, "clicked")) + _elm_dbus_menu_item_select_cb((Elm_Object_Item *)item); + + return EINA_TRUE; +} + +static Elm_DBus_Menu * +_elm_dbus_menu_add(Eo *menu) +{ + Elm_DBus_Menu *dbus_menu; + const Eina_List *ret = NULL; + Eina_List *items, *l; + Elm_Menu_Item *item; + + ELM_MENU_CHECK(menu) NULL; + + dbus_menu = calloc(1, sizeof(Elm_DBus_Menu)); + if (!dbus_menu) + { + ERR("Unable to allocate D-Bus data"); + return NULL; + } + + dbus_menu->elements = eina_hash_int32_new(NULL); + if (!dbus_menu->elements) + { + ERR("Unable to allocate hash table"); + goto error_menu; + } + + dbus_menu->menu = menu; + + eo_do(menu, elm_obj_menu_items_get(&ret)); + items = (Eina_List *)ret; + EINA_LIST_FOREACH (items, l, item) + { + if (!_menu_add_recursive(dbus_menu, item)) + { + ERR("Unable to add menu item"); + goto error_hash; + } + } + + return dbus_menu; + +error_hash: + eina_hash_free(dbus_menu->elements); +error_menu: + free(dbus_menu); + return NULL; +} + +// ============================================================================= +// com.canonical.dbusmenu +// ============================================================================= +// ============================================================================= +// Methods +// ============================================================================= +static EDBus_Message * +_method_layout_get(const EDBus_Service_Interface *iface, + const EDBus_Message *msg) +{ + int parent_id; + int32_t id; + int r; + unsigned recursion_depth; + char *property; + Eina_List *property_list = NULL; + EDBus_Message *reply; + EDBus_Message_Iter *iter, *array; + Elm_DBus_Menu *dbus_menu; + Elm_Menu_Item *item = NULL; + + dbus_menu = edbus_service_object_data_get(iface, DBUS_DATA_KEY); + + if (!edbus_message_arguments_get(msg, "iias", &parent_id, &r, &array)) + ERR("Invalid arguments in D-Bus message"); + + recursion_depth = r; + + while (edbus_message_iter_get_and_next(array, 's', &property)) + property_list = eina_list_append(property_list, property); + + property_list = _empty_properties_handle(property_list); + + if (parent_id) + { + id = parent_id; + item = eina_hash_find(dbus_menu->elements, &id); + if (!item) + { + reply = edbus_message_error_new(msg, DBUS_INTERFACE ".Error", + "Invalid parent"); + return reply; + } + } + + reply = edbus_message_method_return_new(msg); + iter = edbus_message_iter_get(reply); + edbus_message_iter_basic_append(iter, 'u', dbus_menu->timestamp); + + if (parent_id) + _layout_build_recursive(item, property_list, recursion_depth, iter); + else + _root_layout_build(dbus_menu, property_list, recursion_depth, iter); + + eina_list_free(property_list); + return reply; +} + +static EDBus_Message * +_method_group_properties_get(const EDBus_Service_Interface *iface, + const EDBus_Message *msg) +{ + Eina_Iterator *hash_iter; + EDBus_Message *reply; + EDBus_Message_Iter *ids, *property_names; + EDBus_Message_Iter *iter, *array, *tuple; + Eina_List *property_list = NULL; + Elm_DBus_Menu *dbus_menu; + Elm_Menu_Item *item; + char *property; + int id; + int32_t i; + void *data; + + dbus_menu = edbus_service_object_data_get(iface, DBUS_DATA_KEY); + + if (!edbus_message_arguments_get(msg, "aias", &ids, &property_names)) + ERR("Invalid arguments in D-Bus message"); + + while (edbus_message_iter_get_and_next(property_names, 's', &property)) + property_list = eina_list_append(property_list, property); + + property_list = _empty_properties_handle(property_list); + + reply = edbus_message_method_return_new(msg); + iter = edbus_message_iter_get(reply); + array = edbus_message_iter_container_new(iter, 'a', "(ia{sv})"); + + if (!edbus_message_iter_get_and_next(ids, 'i', &id)) + { + hash_iter = eina_hash_iterator_data_new(dbus_menu->elements); + + while (eina_iterator_next(hash_iter, &data)) + { + item = data; + tuple = edbus_message_iter_container_new(array, 'r', NULL); + edbus_message_iter_basic_append(tuple, 'i', item->dbus_idx); + _property_dict_build(item, property_list, tuple); + edbus_message_iter_container_close(array, tuple); + } + + eina_iterator_free(hash_iter); + } + else + do + { + i = id; + item = eina_hash_find(dbus_menu->elements, &i); + if (!item) continue; + + tuple = edbus_message_iter_container_new(array, 'r', NULL); + edbus_message_iter_basic_append(tuple, 'i', item->dbus_idx); + _property_dict_build(item, property_list, tuple); + edbus_message_iter_container_close(array, tuple); + } + while (edbus_message_iter_get_and_next(ids, 'i', &id)); + + edbus_message_iter_container_close(iter, array); + eina_list_free(property_list); + + return reply; +} + +static EDBus_Message * +_method_property_get(const EDBus_Service_Interface *iface, + const EDBus_Message *msg) +{ + EDBus_Message *reply; + EDBus_Message_Iter *iter, *variant; + Elm_DBus_Property property; + Elm_DBus_Menu *dbus_menu; + Elm_Menu_Item *item; + int id; + int32_t i; + char *name; + + dbus_menu = edbus_service_object_data_get(iface, DBUS_DATA_KEY); + + if (!edbus_message_arguments_get(msg, "is", &id, &name)) + ERR("Invalid arguments in D-Bus message"); + + property = _str_to_property(name); + + if (property == ELM_DBUS_PROPERTY_UNKNOWN) + { + reply = edbus_message_error_new(msg, DBUS_INTERFACE ".Error", + "Property not found"); + return reply; + } + + if (!id) + { + if (property != ELM_DBUS_PROPERTY_CHILDREN_DISPLAY) + reply = edbus_message_error_new(msg, DBUS_INTERFACE ".Error", + "Property not found"); + else + { + reply = edbus_message_method_return_new(msg); + iter = edbus_message_iter_get(reply); + variant = edbus_message_iter_container_new(iter, 'v', "s"); + edbus_message_iter_basic_append(variant, 's', "submenu"); + edbus_message_iter_container_close(iter, variant); + } + + return reply; + } + + i = id; + item = eina_hash_find(dbus_menu->elements, &i); + + if (!item) + { + reply = edbus_message_error_new(msg, DBUS_INTERFACE ".Error", + "Invalid menu identifier"); + return reply; + } + + reply = edbus_message_method_return_new(msg); + iter = edbus_message_iter_get(reply); + _property_append(item, property, iter); + + return reply; +} + +static EDBus_Message * +_method_event(const EDBus_Service_Interface *iface, + const EDBus_Message *msg) +{ + Elm_DBus_Menu *dbus_menu; + EDBus_Message *reply; + + reply = edbus_message_method_return_new(msg); + dbus_menu = edbus_service_object_data_get(iface, DBUS_DATA_KEY); + + if (!_event_handle(dbus_menu, edbus_message_iter_get(msg), NULL)) + reply = edbus_message_error_new(msg, DBUS_INTERFACE ".Error", + "Invalid menu"); + else + reply = edbus_message_method_return_new(msg); + + return reply; +} + +static EDBus_Message * +_method_event_group(const EDBus_Service_Interface *iface, + const EDBus_Message *msg) +{ + EDBus_Message *reply; + EDBus_Message_Iter *iter, *array, *tuple, *errors; + int id; + Elm_DBus_Menu *dbus_menu; + Eina_Bool return_error = EINA_TRUE; + + dbus_menu = edbus_service_object_data_get(iface, DBUS_DATA_KEY); + + if (!edbus_message_arguments_get(msg, "a(isvu)", &array)) + ERR("Invalid arguments in D-Bus message"); + + reply = edbus_message_method_return_new(msg); + iter = edbus_message_iter_get(reply); + errors = edbus_message_iter_container_new(iter, 'a', "i"); + + while (edbus_message_iter_get_and_next(array, 'r', &tuple)) + { + if (_event_handle(dbus_menu, tuple, &id)) + return_error = EINA_FALSE; + else + edbus_message_iter_basic_append(errors, 'i', id); + } + + if (return_error) + { + edbus_message_unref(reply); + reply = edbus_message_error_new(msg, DBUS_INTERFACE ".Error", + "Invalid menu identifiers"); + } + else + edbus_message_iter_container_close(iter, errors); + + return reply; +} + +static EDBus_Message * +_method_about_to_show(const EDBus_Service_Interface *iface EINA_UNUSED, + const EDBus_Message *msg) +{ + EDBus_Message *reply = edbus_message_method_return_new(msg); + edbus_message_arguments_append(reply, "b", EINA_TRUE); + + return reply; +} + +static EDBus_Message * +_method_about_to_show_group(const EDBus_Service_Interface *iface EINA_UNUSED, + const EDBus_Message *msg) +{ + EDBus_Message *reply = edbus_message_method_return_new(msg); + EDBus_Message_Iter *iter, *array; + + iter = edbus_message_iter_get(reply); + array = edbus_message_iter_container_new(iter, 'a', "i"); + edbus_message_iter_container_close(iter, array); + array = edbus_message_iter_container_new(iter, 'a', "i"); + edbus_message_iter_container_close(iter, array); + + return reply; +} + +static const EDBus_Method _methods[] = { + { + "GetLayout", + EDBUS_ARGS({"i", "parentId"}, + {"i", "recursionDepth"}, + {"as", "propertyNames"}), + EDBUS_ARGS({"u", "revision"}, {"(ia{sv}av)", "layout"}), + _method_layout_get, + 0 + }, + { + "GetGroupProperties", + EDBUS_ARGS({"ai", "ids"}, {"as", "propertyNames"}), + EDBUS_ARGS({"a(ia{sv})", "properties"}), + _method_group_properties_get, + 0 + }, + { + "GetProperty", + EDBUS_ARGS({"i", "id"}, {"s", "name"}), + EDBUS_ARGS({"v", "value"}), + _method_property_get, + 0 + }, + { + "Event", + EDBUS_ARGS({"i", "id"}, + {"s", "eventId"}, + {"v", "data"}, + {"u", "timestamp"}), + NULL, + _method_event, + 0 + }, + { + "EventGroup", + EDBUS_ARGS({"a(isvu)", "events"}), + EDBUS_ARGS({"ai", "idErrors"}), + _method_event_group, + 0 + }, + { + "AboutToShow", + EDBUS_ARGS({"i", "id"}), + EDBUS_ARGS({"b", "needUpdate"}), + _method_about_to_show, + 0 + }, + { + "AboutToShowGroup", + EDBUS_ARGS({"ai", "ids"}), + EDBUS_ARGS({"ai", "updatesNeeded"}, {"ai", "idErrors"}), + _method_about_to_show_group, + 0 + }, + + {NULL, NULL, NULL, NULL, 0} +}; + +// ============================================================================= +// Signals +// ============================================================================= +static const EDBus_Signal _signals[] = { + [ELM_DBUS_SIGNAL_LAYOUT_UPDATED] = { + "LayoutUpdated", EDBUS_ARGS({"u", "revision"}, {"i", "parent"}), 0 + }, + [ELM_DBUS_SIGNAL_ITEM_ACTIVATION_REQUESTED] = { + "ItemActivationRequested", EDBUS_ARGS({"i", "id"}, {"u", "timestamp"}), 0 + }, + {NULL, NULL, 0} +}; + +// ============================================================================= +// Properties +// ============================================================================= +static Eina_Bool +_prop_version_get(const EDBus_Service_Interface *iface EINA_UNUSED, + const char *propname EINA_UNUSED, + EDBus_Message_Iter *iter, + const EDBus_Message *request_msg EINA_UNUSED, + EDBus_Message **error EINA_UNUSED) +{ + edbus_message_iter_basic_append(iter, 'u', DBUS_MENU_VERSION); + + return EINA_TRUE; +} + +static Eina_Bool +_prop_text_direction_get(const EDBus_Service_Interface *iface EINA_UNUSED, + const char *propname EINA_UNUSED, + EDBus_Message_Iter *iter, + const EDBus_Message *request_msg EINA_UNUSED, + EDBus_Message **error EINA_UNUSED) +{ + if (_elm_config->is_mirrored) + edbus_message_iter_basic_append(iter, 's', "rtl"); + else + edbus_message_iter_basic_append(iter, 's', "ltr"); + + return EINA_TRUE; +} + +static Eina_Bool +_prop_status_get(const EDBus_Service_Interface *iface EINA_UNUSED, + const char *propname EINA_UNUSED, + EDBus_Message_Iter *iter, + const EDBus_Message *request_msg EINA_UNUSED, + EDBus_Message **error EINA_UNUSED) +{ + static const char *normal = "normal"; + edbus_message_iter_basic_append(iter, 's', normal); + + return EINA_TRUE; +} + +static Eina_Bool +_prop_icon_theme_path_get(const EDBus_Service_Interface *iface EINA_UNUSED, + const char *propname EINA_UNUSED, + EDBus_Message_Iter *iter, + const EDBus_Message *request_msg EINA_UNUSED, + EDBus_Message **error EINA_UNUSED) +{ + EDBus_Message_Iter *actions; + edbus_message_iter_arguments_append(iter, "as", &actions); + edbus_message_iter_container_close(iter, actions); + + return EINA_TRUE; +} + +static const EDBus_Property _properties[] = { + { "Version", "u", _prop_version_get, NULL, 0 }, + { "TextDirection", "s", _prop_text_direction_get, NULL, 0 }, + { "Status", "s", _prop_status_get, NULL, 0 }, + { "IconThemePath", "as", _prop_icon_theme_path_get, NULL, 0 }, + { NULL, NULL, NULL, NULL, 0 }, +}; + +static const EDBus_Service_Interface_Desc _interface = { + DBUS_INTERFACE, _methods, _signals, _properties, NULL, NULL +}; +// ============================================================================= + +const char * +_elm_dbus_menu_register(Eo *obj) +{ + char buf[60]; + ELM_MENU_CHECK(obj) NULL; + ELM_MENU_DATA_GET(obj, sd); + + elm_need_edbus(); + + if (sd->dbus_menu) + goto end; + + sd->dbus_menu = _elm_dbus_menu_add(obj); + sd->dbus_menu->bus = edbus_connection_get(EDBUS_CONNECTION_TYPE_SESSION); + snprintf(buf, sizeof(buf), "%s/%u", DBUS_PATH, ++last_object_path); + sd->dbus_menu->iface = edbus_service_interface_register(sd->dbus_menu->bus, + buf, + &_interface); + edbus_service_object_data_set(sd->dbus_menu->iface, DBUS_DATA_KEY, + sd->dbus_menu); + +end: + return edbus_service_object_path_get(sd->dbus_menu->iface); +} + +void +_elm_dbus_menu_unregister(Eo *obj) +{ + // TODO: support refcounting object paths + + ELM_MENU_CHECK(obj); + ELM_MENU_DATA_GET(obj, sd); + + if (!sd->dbus_menu) return; + + if (sd->dbus_menu->xid) + _elm_dbus_menu_app_menu_unregister(sd->dbus_menu->menu); + edbus_service_interface_unregister(sd->dbus_menu->iface); + edbus_connection_unref(sd->dbus_menu->bus); + if (sd->dbus_menu->signal_idler) + ecore_idler_del(sd->dbus_menu->signal_idler); + + eina_hash_free(sd->dbus_menu->elements); + free(sd->dbus_menu); + sd->dbus_menu = NULL; +} + +void +_elm_dbus_menu_app_menu_register(Ecore_X_Window xid, Eo *obj) +{ + EDBus_Message *msg; + const char *obj_path; + + ELM_MENU_CHECK(obj); + ELM_MENU_DATA_GET(obj, sd); + + if (!sd->dbus_menu || !sd->dbus_menu->bus) + { + ERR("D-Bus is inactive for menu: %p", obj); + return; + } + + msg = edbus_message_method_call_new(REGISTRAR_NAME, REGISTRAR_PATH, + REGISTRAR_INTERFACE, "RegisterWindow"); + obj_path = edbus_service_object_path_get(sd->dbus_menu->iface); + edbus_message_arguments_append(msg, "uo", (unsigned)xid, + obj_path); + edbus_connection_send(sd->dbus_menu->bus, msg, NULL, NULL, -1); + edbus_message_unref(msg); + sd->dbus_menu->xid = xid; +} + +void +_elm_dbus_menu_app_menu_unregister(Eo *obj) +{ + EDBus_Message *msg; + + ELM_MENU_CHECK(obj); + ELM_MENU_DATA_GET(obj, sd); + + if (!sd->dbus_menu || !sd->dbus_menu->bus) + { + ERR("D-Bus is inactive for menu: %p", obj); + return; + } + + if (!sd->dbus_menu->xid) return; + + msg = edbus_message_method_call_new(REGISTRAR_NAME, REGISTRAR_PATH, + REGISTRAR_INTERFACE, "UnregisterWindow"); + edbus_message_arguments_append(msg, "u", (unsigned)sd->dbus_menu->xid); + edbus_connection_send(sd->dbus_menu->bus, msg, NULL, NULL, -1); + edbus_message_unref(msg); + sd->dbus_menu->xid = 0; +} + +int +_elm_dbus_menu_item_add(Elm_DBus_Menu *dbus_menu, Elm_Object_Item *item_obj) +{ + Elm_Menu_Item *item = (Elm_Menu_Item *)item_obj; + int32_t id = dbus_menu->timestamp + 1; + + if (!eina_hash_add(dbus_menu->elements, &id, item)) + { + ERR("Unable to add menu"); + return -1; + } + + _layout_signal(dbus_menu); + return ++dbus_menu->timestamp; +} + +void +_elm_dbus_menu_item_delete(Elm_DBus_Menu *dbus_menu, int id) +{ + int32_t i; + + i = id; + + if (!eina_hash_del_by_key(dbus_menu->elements, &i)) + { + ERR("Invalid menu ID: %d", id); + return; + } + + dbus_menu->timestamp++; + _layout_signal(dbus_menu); +} + +void +_elm_dbus_menu_update(Elm_DBus_Menu *dbus_menu) +{ + _layout_signal(dbus_menu); +} + +#else + +const char * +_elm_dbus_menu_register(Eo *obj EINA_UNUSED) +{ + return NULL; +} + +void +_elm_dbus_menu_unregister(Eo *obj EINA_UNUSED) +{ +} + +void +_elm_dbus_menu_app_menu_register(Ecore_X_Window xid EINA_UNUSED, Eo *obj EINA_UNUSED) +{ +} + +void +_elm_dbus_menu_app_menu_unregister(Eo *obj EINA_UNUSED) +{ +} + +int +_elm_dbus_menu_item_add(Elm_DBus_Menu *dbus_menu EINA_UNUSED, + Elm_Object_Item *item_obj EINA_UNUSED) +{ + return -1; +} + +void +_elm_dbus_menu_item_delete(Elm_DBus_Menu *dbus_menu EINA_UNUSED, int id EINA_UNUSED) +{ +} + +void +_elm_dbus_menu_update(Elm_DBus_Menu *dbus_menu EINA_UNUSED) +{ +} + +#endif diff --git a/legacy/elementary/src/lib/elm_menu.c b/legacy/elementary/src/lib/elm_menu.c index 8aedb77ae6..fd64c3224c 100644 --- a/legacy/elementary/src/lib/elm_menu.c +++ b/legacy/elementary/src/lib/elm_menu.c @@ -372,6 +372,14 @@ _submenu_open_cb(void *data, _submenu_sizing_eval(item); } +void +_elm_dbus_menu_item_select_cb(Elm_Object_Item *obj_item) +{ + Elm_Menu_Item *item = (Elm_Menu_Item *)obj_item; + + if (item->func) item->func((void *)(item->base.data), WIDGET(item), item); +} + static void _menu_item_select_cb(void *data, Evas_Object *obj __UNUSED__, @@ -549,6 +557,8 @@ _elm_menu_smart_del(Eo *obj, void *_pd, va_list *list EINA_UNUSED) Elm_Menu_Item *item; Elm_Menu_Smart_Data *sd = _pd; + _elm_dbus_menu_unregister(obj); + evas_object_event_callback_del_full (sd->parent, EVAS_CALLBACK_RESIZE, _parent_resize_cb, obj); evas_object_event_callback_del_full @@ -779,6 +789,8 @@ _item_del_pre_hook(Elm_Object_Item *it) Elm_Menu_Item *item = (Elm_Menu_Item *)it; Elm_Object_Item *_item; + ELM_MENU_DATA_GET(WIDGET(item), sd); + EINA_LIST_FREE (item->submenu.items, _item) elm_object_item_del(_item); if (item->label) eina_stringshare_del(item->label); @@ -790,10 +802,10 @@ _item_del_pre_hook(Elm_Object_Item *it) item->parent->submenu.items = eina_list_remove(item->parent->submenu.items, item); else - { - ELM_MENU_DATA_GET(WIDGET(item), sd); - sd->items = eina_list_remove(sd->items, item); - } + sd->items = eina_list_remove(sd->items, item); + + if (sd->dbus_menu) + _elm_dbus_menu_item_delete(sd->dbus_menu, item->dbus_idx); return EINA_TRUE; } @@ -861,6 +873,9 @@ _item_add(Eo *obj, void *_pd, va_list *list) _elm_menu_item_add_helper(obj, (Elm_Menu_Item *)parent, subitem, sd); + if (sd->dbus_menu) + subitem->dbus_idx = _elm_dbus_menu_item_add(sd->dbus_menu, (Elm_Object_Item *)subitem); + *ret = (Elm_Object_Item *)subitem; } diff --git a/legacy/elementary/src/lib/elm_priv.h b/legacy/elementary/src/lib/elm_priv.h index 4063a34fe2..acd13a5755 100644 --- a/legacy/elementary/src/lib/elm_priv.h +++ b/legacy/elementary/src/lib/elm_priv.h @@ -234,6 +234,7 @@ struct _Elm_Config const char *indicator_service_180; const char *indicator_service_270; unsigned char selection_clear_enable; + unsigned char external_menu; /* Not part of the EET file */ Eina_Bool is_mirrored : 1; @@ -397,6 +398,19 @@ void _elm_config_color_set(const char *palette_name, int a); void _elm_config_colors_free(const char *palette_name); +typedef struct _Elm_DBus_Menu Elm_DBus_Menu; + +const char *_elm_dbus_menu_register(Eo *obj); +void _elm_dbus_menu_unregister(Eo *obj); +int _elm_dbus_menu_item_add(Elm_DBus_Menu *dbus_menu, + Elm_Object_Item *item); +void _elm_dbus_menu_item_delete(Elm_DBus_Menu *dbus_menu, + int id); + +void _elm_dbus_menu_app_menu_register(Ecore_X_ID xid, Eo *obj); +void _elm_dbus_menu_app_menu_unregister(Eo *obj); +void _elm_dbus_menu_item_select_cb(Elm_Object_Item *obj_item); + /* DEPRECATED, will be removed on next release */ void _elm_icon_signal_emit(Evas_Object *obj, const char *emission, diff --git a/legacy/elementary/src/lib/elm_widget_menu.h b/legacy/elementary/src/lib/elm_widget_menu.h index 45befaeaf2..fbb5cd84d3 100644 --- a/legacy/elementary/src/lib/elm_widget_menu.h +++ b/legacy/elementary/src/lib/elm_widget_menu.h @@ -24,6 +24,8 @@ struct _Elm_Menu_Smart_Data Eina_List *items; Evas_Coord xloc, yloc; + Elm_DBus_Menu *dbus_menu; + }; typedef struct _Elm_Menu_Item Elm_Menu_Item; @@ -37,6 +39,7 @@ struct _Elm_Menu_Item const char *label; Evas_Smart_Cb func; unsigned int idx; + int dbus_idx; struct { diff --git a/legacy/elementary/src/lib/elm_win.c b/legacy/elementary/src/lib/elm_win.c index 185d1d447a..4363570014 100644 --- a/legacy/elementary/src/lib/elm_win.c +++ b/legacy/elementary/src/lib/elm_win.c @@ -130,6 +130,8 @@ struct _Elm_Win_Smart_Data const char *icon_name; const char *role; + Evas_Object *main_menu; + struct { const char *name; @@ -1479,6 +1481,8 @@ _elm_win_smart_del(Eo *obj, void *_pd, va_list *list EINA_UNUSED) if (sd->role) eina_stringshare_del(sd->role); if (sd->icon) evas_object_del(sd->icon); + if (sd->main_menu) evas_object_del(sd->main_menu); + _elm_win_profile_del(sd); _elm_win_available_profiles_del(sd); @@ -2482,7 +2486,7 @@ elm_win_add(Evas_Object *parent, } static void -_win_constructor(Eo *obj, void *_pd EINA_UNUSED, va_list *list) +_win_constructor(Eo *obj, void *_pd, va_list *list) { Elm_Win_Smart_Data *sd = _pd; sd->obj = obj; // in ctor @@ -3533,6 +3537,43 @@ _fullscreen_get(Eo *obj EINA_UNUSED, void *_pd, va_list *list) } } +EAPI Evas_Object * +elm_win_main_menu_get(const Evas_Object *obj) +{ + ELM_WIN_CHECK(obj) NULL; + Evas_Object *ret; + eo_do((Eo *) obj, elm_obj_win_main_menu_get(&ret)); + return ret; +} + +static void +_main_menu_get(Eo *obj, void *_pd, va_list *list) +{ + Eo **ret = va_arg(*list, Eo **); + Elm_Win_Smart_Data *sd = _pd; + + if (sd->main_menu) goto end; + + sd->main_menu = elm_menu_add(obj); + + if (_elm_config->external_menu) + { +#ifdef HAVE_ELEMENTARY_X + if (sd->x.xwin) + { + _elm_dbus_menu_register(sd->main_menu); + _elm_dbus_menu_app_menu_register(sd->x.xwin, sd->main_menu); + } +#endif + } + else + { + //TODO: Local version. + } +end: + *ret = sd->main_menu; +} + EAPI void elm_win_maximized_set(Evas_Object *obj, Eina_Bool maximized) @@ -5143,6 +5184,7 @@ _class_constructor(Eo_Class *klass) EO_OP_FUNC(ELM_OBJ_WIN_ID(ELM_OBJ_WIN_SUB_ID_OVERRIDE_GET), _override_get), EO_OP_FUNC(ELM_OBJ_WIN_ID(ELM_OBJ_WIN_SUB_ID_FULLSCREEN_SET), _fullscreen_set), EO_OP_FUNC(ELM_OBJ_WIN_ID(ELM_OBJ_WIN_SUB_ID_FULLSCREEN_GET), _fullscreen_get), + EO_OP_FUNC(ELM_OBJ_WIN_ID(ELM_OBJ_WIN_SUB_ID_MAIN_MENU_GET), _main_menu_get), EO_OP_FUNC(ELM_OBJ_WIN_ID(ELM_OBJ_WIN_SUB_ID_MAXIMIZED_SET), _maximized_set), EO_OP_FUNC(ELM_OBJ_WIN_ID(ELM_OBJ_WIN_SUB_ID_MAXIMIZED_GET), _maximized_get), EO_OP_FUNC(ELM_OBJ_WIN_ID(ELM_OBJ_WIN_SUB_ID_ICONIFIED_SET), _iconified_set), @@ -5240,6 +5282,7 @@ static const Eo_Op_Description op_desc[] = { EO_OP_DESCRIPTION(ELM_OBJ_WIN_SUB_ID_OVERRIDE_GET, "Get the override state of a window."), EO_OP_DESCRIPTION(ELM_OBJ_WIN_SUB_ID_FULLSCREEN_SET, "Set the fullscreen state of a window."), EO_OP_DESCRIPTION(ELM_OBJ_WIN_SUB_ID_FULLSCREEN_GET, "Get the fullscreen state of a window."), + EO_OP_DESCRIPTION(ELM_OBJ_WIN_SUB_ID_MAIN_MENU_GET, "Get the Main Menu of a window."), EO_OP_DESCRIPTION(ELM_OBJ_WIN_SUB_ID_MAXIMIZED_SET, "Set the maximized state of a window."), EO_OP_DESCRIPTION(ELM_OBJ_WIN_SUB_ID_MAXIMIZED_GET, "Get the maximized state of a window."), EO_OP_DESCRIPTION(ELM_OBJ_WIN_SUB_ID_ICONIFIED_SET, "Set the iconified state of a window."), diff --git a/legacy/elementary/src/lib/elm_win.h b/legacy/elementary/src/lib/elm_win.h index d3b753ef34..2a89ba0118 100644 --- a/legacy/elementary/src/lib/elm_win.h +++ b/legacy/elementary/src/lib/elm_win.h @@ -128,6 +128,7 @@ enum ELM_OBJ_WIN_SUB_ID_OVERRIDE_GET, ELM_OBJ_WIN_SUB_ID_FULLSCREEN_SET, ELM_OBJ_WIN_SUB_ID_FULLSCREEN_GET, + ELM_OBJ_WIN_SUB_ID_MAIN_MENU_GET, ELM_OBJ_WIN_SUB_ID_MAXIMIZED_SET, ELM_OBJ_WIN_SUB_ID_MAXIMIZED_GET, ELM_OBJ_WIN_SUB_ID_ICONIFIED_SET, @@ -519,6 +520,20 @@ enum */ #define elm_obj_win_fullscreen_get(ret) ELM_OBJ_WIN_ID(ELM_OBJ_WIN_SUB_ID_FULLSCREEN_GET), EO_TYPECHECK(Eina_Bool *, ret) +/** + * @def elm_obj_win_main_menu_get + * @since 1.8 + * + * Get the Main Menu of a window. + * + * @param[out] ret Main menu. + * + * @see elm_win_main_menu_get + */ +#define elm_obj_win_main_menu_get(ret) \ + ELM_OBJ_WIN_ID(ELM_OBJ_WIN_SUB_ID_MAIN_MENU_GET), \ + EO_TYPECHECK(Eo **, ret) + /** * @def elm_obj_win_maximized_set * @since 1.8 @@ -1849,6 +1864,16 @@ EAPI void elm_win_fullscreen_set(Evas_Object *obj, Eina_Bool fu */ EAPI Eina_Bool elm_win_fullscreen_get(const Evas_Object *obj); +/** + * Get the Main Menu of a window. + * + * @param obj The window object + * @return The Main Menu of the window (NULL if error). + * + * @ingroup Win + */ +EAPI Evas_Object *elm_win_main_menu_get(const Evas_Object *obj); + /** * Set the maximized state of a window. *