diff --git a/configure.ac b/configure.ac index 478968bb5..94d97a305 100644 --- a/configure.ac +++ b/configure.ac @@ -861,6 +861,7 @@ AC_E_OPTIONAL_MODULE([illume2], true) AC_E_OPTIONAL_MODULE([syscon], true) AC_E_OPTIONAL_MODULE([everything], true) AC_E_OPTIONAL_MODULE([systray], true) +AC_E_OPTIONAL_MODULE([appmenu], true) AC_E_OPTIONAL_MODULE([comp], true) AC_E_OPTIONAL_MODULE([physics], true, [CHECK_MODULE_PHYSICS]) AC_E_OPTIONAL_MODULE([quickaccess], true) @@ -970,6 +971,7 @@ src/modules/syscon/module.desktop src/modules/everything/module.desktop src/modules/everything/everything.pc src/modules/systray/module.desktop +src/modules/appmenu/module.desktop src/modules/comp/module.desktop src/modules/physics/module.desktop src/modules/quickaccess/module.desktop diff --git a/data/themes/default.edc b/data/themes/default.edc index 2c0ba9427..9c3213e6c 100644 --- a/data/themes/default.edc +++ b/data/themes/default.edc @@ -47,6 +47,7 @@ collections { #include "edc/connman.edc" #include "edc/music_control.edc" #include "edc/systray.edc" +#include "edc/appmenu.edc" #include "edc/clock.edc" #include "edc/ibar-ibox.edc" #include "edc/colors.edc" diff --git a/data/themes/edc/appmenu.edc b/data/themes/edc/appmenu.edc new file mode 100644 index 000000000..2372eb186 --- /dev/null +++ b/data/themes/edc/appmenu.edc @@ -0,0 +1,77 @@ +group { name: "e/modules/appmenu/item"; + images.image: "vgrad_med_dark.png" COMP; + data.item: "padding_horizontal" "5"; + data.item: "padding_vertical" "5"; + parts { + part { + name: "event_area"; + type: RECT; + mouse_events: 1; + description { + state: "default" 0.0; + color: 0 0 0 0; + } + } + part { + name: "background"; + type: IMAGE; + mouse_events: 0; + description { + state: "default" 0.0; + image.normal: "vgrad_med_dark.png"; + fill.smooth: 0; + TILED_HORIZ(120) + visible: 0; + } + description { + state: "selected" 0.0; + inherit: "default" 0.0; + visible: 1; + } + } + part { + name: "text"; + type: TEXT; + mouse_events: 0; + effect: SHADOW BOTTOM; + scale: 1; + description { + state: "default" 0.0; + rel1.offset: 2 2; + rel2.offset: -3 -3; + color: 255 255 255 255; + color3: 0 0 0 128; + text { + font: "Sans"; + size: 10; + min: 1 1; + align: 0.5 0.5; + text_class: "label"; + } + } + description { + state: "selected" 0.0; + inherit: "default" 0.0; + color: 51 153 255 255; + color2: 51 153 255 24; + color3: 51 153 255 18; + } + } + } + programs { + program { + signal: "mouse,in"; + source: "event_area"; + action: STATE_SET "selected" 0.0; + target: "background"; + target: "text"; + } + program { + signal: "mouse,out"; + source: "event_area"; + action: STATE_SET "default" 0.0; + target: "background"; + target: "text"; + } + } +} \ No newline at end of file diff --git a/src/bin/e_module.c b/src/bin/e_module.c index e354bb43a..4ca995bc8 100644 --- a/src/bin/e_module.c +++ b/src/bin/e_module.c @@ -707,7 +707,7 @@ _e_module_whitelist_check(void) "echievements", "music-control", "conf2", - + "appmenu", NULL // end marker }; diff --git a/src/modules/Makefile.am b/src/modules/Makefile.am index 3f7209d85..35f69cf97 100644 --- a/src/modules/Makefile.am +++ b/src/modules/Makefile.am @@ -194,6 +194,10 @@ if USE_MODULE_SYSTRAY include Makefile_systray.am endif +if USE_MODULE_APPMENU +include Makefile_appmenu.am +endif + if USE_MODULE_COMP include Makefile_comp.am endif diff --git a/src/modules/Makefile_appmenu.am b/src/modules/Makefile_appmenu.am new file mode 100644 index 000000000..3981284f0 --- /dev/null +++ b/src/modules/Makefile_appmenu.am @@ -0,0 +1,17 @@ +appmenudir = $(MDIR)/appmenu +appmenu_DATA = appmenu/module.desktop + +EXTRA_DIST += $(appmenu2_DATA) + +appmenupkgdir = $(MDIR)/appmenu/$(MODULE_ARCH) +appmenupkg_LTLIBRARIES = appmenu/module.la + +appmenu_module_la_SOURCES = appmenu/e_mod_main.c \ + appmenu/e_mod_dbus_registrar_server.c \ + appmenu/e_mod_appmenu_render.c \ + appmenu/e_mod_appmenu_private.h + + +.PHONY: appmenu install-appmenu +appmenu: $(appmenupkg_LTLIBRARIES) $(appmenu_DATA) +install-appmenu: install-appmenuDATA install-appmenupkgLTLIBRARIES diff --git a/src/modules/appmenu/e_mod_appmenu_private.h b/src/modules/appmenu/e_mod_appmenu_private.h new file mode 100644 index 000000000..7bb5a16ef --- /dev/null +++ b/src/modules/appmenu/e_mod_appmenu_private.h @@ -0,0 +1,44 @@ +#ifndef APPMENU_PRIVATE_H +#define APPMENU_PRIVATE_H + +#include "e.h" + +typedef struct _E_AppMenu_Window E_AppMenu_Window; + +typedef struct _E_AppMenu_Context +{ + Eina_List *instances; + EDBus_Connection *conn; + EDBus_Service_Interface *iface; + Eina_List *windows; + unsigned window_with_focus; + E_AppMenu_Window *window; + Ecore_Event_Handler *events[2]; +} E_AppMenu_Context; + +typedef struct _E_AppMenu_Instance +{ + Evas_Object *box; + Evas *evas; + E_Gadcon_Client *gcc; + E_AppMenu_Context *ctx; + Eina_Bool orientation_horizontal; +} E_AppMenu_Instance; + +struct _E_AppMenu_Window +{ + unsigned window_id; + const char *bus_id; + const char *path; + E_DBusMenu_Ctx *dbus_menu; + E_AppMenu_Context *ctxt; + E_DBusMenu_Item *root_item; +}; + +void appmenu_window_free(E_AppMenu_Window *window); +void appmenu_dbus_registrar_server_init(E_AppMenu_Context *ctx); +void appmenu_application_monitor(void *data, const char *bus, const char *old, const char *new); +void appmenu_menu_render(E_AppMenu_Context *ctxt EINA_UNUSED, E_AppMenu_Window *w); +void appmenu_menu_of_instance_render(E_AppMenu_Instance *inst, E_AppMenu_Window *window); + +#endif diff --git a/src/modules/appmenu/e_mod_appmenu_render.c b/src/modules/appmenu/e_mod_appmenu_render.c new file mode 100644 index 000000000..8842f690f --- /dev/null +++ b/src/modules/appmenu/e_mod_appmenu_render.c @@ -0,0 +1,169 @@ +#include "e_mod_appmenu_private.h" + +static void +menu_deactive(E_Menu *m) +{ + Eina_List *iter; + E_Menu_Item *mi; + EINA_LIST_FOREACH(m->items, iter, mi) + { + if (mi->submenu) + { + menu_deactive(mi->submenu); + e_menu_deactivate(mi->submenu); + } + } + e_object_del(E_OBJECT(m)); +} + +static void +menu_post_deactivate(void *data, E_Menu *m) +{ + E_Gadcon *gadcon = data; + + e_gadcon_locked_set(gadcon, 0); + menu_deactive(m); +} + +static void +sub_item_clicked_cb(void *data, E_Menu *m EINA_UNUSED, E_Menu_Item *mi EINA_UNUSED) +{ + E_DBusMenu_Item *item = data; + e_dbusmenu_event_send(item, E_DBUSMENU_ITEM_EVENT_CLICKED); +} + +static E_Menu * +item_submenu_new(E_DBusMenu_Item *item, E_Menu_Item *mi) +{ + E_Menu *m; + E_DBusMenu_Item *child; + + m = e_menu_new(); + EINA_SAFETY_ON_NULL_RETURN_VAL(m, NULL); + if (mi) + e_menu_item_submenu_set(mi, m); + + EINA_INLIST_FOREACH(item->sub_items, child) + { + E_Menu_Item *submi; + if (!child->visible) + continue; + submi = e_menu_item_new(m); + if (child->type == E_DBUSMENU_ITEM_TYPE_SEPARATOR) + { + e_menu_item_separator_set(submi, 1); + continue; + } + e_menu_item_label_set(submi, child->label); + e_menu_item_callback_set(submi, sub_item_clicked_cb, child); + if (!child->enabled) + e_menu_item_disabled_set(submi, 1); + if (child->toggle_type) + { + if (child->toggle_type == E_DBUSMENU_ITEM_TOGGLE_TYPE_CHECKMARK) + e_menu_item_check_set(submi, 1); + else if (child->toggle_type == E_DBUSMENU_ITEM_TOGGLE_TYPE_RADIO) + e_menu_item_radio_set(submi, 1); + e_menu_item_toggle_set(submi, child->toggle_state); + } + if (eina_inlist_count(child->sub_items)) + item_submenu_new(child, submi); + e_util_menu_item_theme_icon_set(submi, child->icon_name); + } + return m; +} + +static void +item_menu_open(E_DBusMenu_Item *item, E_Gadcon *gadcon) +{ + E_Menu *m = item_submenu_new(item, NULL); + E_Zone *zone; + int x, y; + + EINA_SAFETY_ON_NULL_RETURN(m); + e_gadcon_locked_set(gadcon, 1); + e_menu_post_deactivate_callback_set(m, menu_post_deactivate, gadcon); + + zone = e_util_zone_current_get(e_manager_current_get()); + ecore_x_pointer_xy_get(zone->container->win, &x, &y); + e_menu_activate_mouse(m, zone, x, y, 1, 1, E_MENU_POP_DIRECTION_DOWN, + ecore_x_current_time_get()); +} + +static void +clicked_toolbar_item(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj, void *event EINA_UNUSED) +{ + E_DBusMenu_Item *item = data; + E_Gadcon *gadcon = evas_object_data_get(obj, "gadcon"); + item_menu_open(item, gadcon); +} + +void +appmenu_menu_of_instance_render(E_AppMenu_Instance *inst, E_AppMenu_Window *window) +{ + E_DBusMenu_Item *child; + Evas_Coord w, h, sum_w = 0, sum_h = 0, padding = 0; + Eina_List *l; + Evas_Object *item; + + l = evas_object_box_children_get(inst->box); + EINA_LIST_FREE(l, item) + evas_object_del(item); + e_gadcon_client_min_size_set(inst->gcc, 0, 0); + + if (!window || !window->root_item) + return; + + EINA_INLIST_FOREACH(window->root_item->sub_items, child) + { + if (!child->label) + continue; + + item = edje_object_add(inst->evas); + e_theme_edje_object_set(item, "base/themes", "e/modules/appmenu/item"); + edje_object_part_text_set(item, "text", child->label); + evas_object_box_append(inst->box, item); + edje_object_size_min_calc(item, &w, &h); + + if (!padding) + { + const char *padding_txt; + if (inst->orientation_horizontal) + padding_txt = edje_object_data_get(item, "padding_horizontal"); + else + padding_txt = edje_object_data_get(item, "padding_vertical"); + padding = atoi(padding_txt); + } + + if (inst->orientation_horizontal) + { + h = inst->gcc->gadcon->shelf->h; + w = w + padding; + sum_w = sum_w + w; + } + else + { + w = inst->gcc->gadcon->shelf->w; + h = h + padding; + sum_h = sum_h + h; + } + evas_object_resize(item, w, h); + evas_object_show(item); + evas_object_data_set(item, "gadcon", inst->gcc->gadcon); + evas_object_event_callback_add(item, EVAS_CALLBACK_MOUSE_DOWN, + clicked_toolbar_item, child); + } + e_gadcon_client_min_size_set(inst->gcc, sum_w, sum_h); +} + +void +appmenu_menu_render(E_AppMenu_Context *ctxt, E_AppMenu_Window *window) +{ + Eina_List *list; + E_AppMenu_Instance *inst; + + ctxt->window = window; + + EINA_LIST_FOREACH(ctxt->instances, list, inst) + appmenu_menu_of_instance_render(inst, window); +} diff --git a/src/modules/appmenu/e_mod_dbus_registrar_server.c b/src/modules/appmenu/e_mod_dbus_registrar_server.c new file mode 100644 index 000000000..bc2b5b1a4 --- /dev/null +++ b/src/modules/appmenu/e_mod_dbus_registrar_server.c @@ -0,0 +1,194 @@ +#include "e_mod_appmenu_private.h" + +#define REGISTRAR_BUS "com.canonical.AppMenu.Registrar" +#define REGISTRAR_PATH "/com/canonical/AppMenu/Registrar" +#define REGISTRAR_IFACE "com.canonical.AppMenu.Registrar" + +#define ERROR_WINDOW_NOT_FOUND "com.canonical.AppMenu.Registrar.WindowNotFound" + +void +appmenu_application_monitor(void *data, const char *bus EINA_UNUSED, const char *old __UNUSED__, const char *new __UNUSED__) +{ + E_AppMenu_Window *window = data; + edbus_service_signal_emit(window->ctxt->iface, 1, window->window_id); + appmenu_window_free(window); +} + +static void +menu_update_cb(void *data, E_DBusMenu_Item *item) +{ + E_AppMenu_Window *window = data; + window->root_item = item; + if (!item) + return; + if (window->window_id == window->ctxt->window_with_focus) + appmenu_menu_render(window->ctxt, window); +} + +static void +menu_pop_cb(void *data EINA_UNUSED, const E_DBusMenu_Item *new_root_item EINA_UNUSED) +{ + //TODO +} + +static EDBus_Message * +_on_register_window(const EDBus_Service_Interface *iface, const EDBus_Message *msg) +{ + EDBus_Connection *conn = edbus_service_connection_get(iface); + E_AppMenu_Context *ctxt = edbus_service_object_data_get(iface, "ctxt"); + unsigned window_id; + const char *path, *bus_id; + E_AppMenu_Window *window; + + if (!edbus_message_arguments_get(msg, "uo", &window_id, &path)) + { + ERR("Error reading message"); + return NULL; + } + + window = calloc(1, sizeof(E_AppMenu_Window)); + EINA_SAFETY_ON_NULL_RETURN_VAL(window, NULL); + + + bus_id = edbus_message_sender_get(msg); + + window->window_id = window_id; + window->dbus_menu = e_dbusmenu_load(conn, bus_id, path, window); + e_dbusmenu_update_cb_set(window->dbus_menu, menu_update_cb); + e_dbusmenu_pop_request_cb_set(window->dbus_menu, menu_pop_cb); + window->bus_id = eina_stringshare_add(bus_id); + window->path = eina_stringshare_add(path); + + edbus_name_owner_changed_callback_add(conn, bus_id, appmenu_application_monitor, + window, EINA_FALSE); + ctxt->windows = eina_list_append(ctxt->windows, window); + window->ctxt = ctxt; + + edbus_service_signal_emit(iface, 0, window_id, bus_id, path); + return edbus_message_method_return_new(msg); +} + +static E_AppMenu_Window * +window_find(E_AppMenu_Context *ctxt, unsigned window_id) +{ + Eina_List *l; + E_AppMenu_Window *w; + EINA_LIST_FOREACH(ctxt->windows, l, w) + { + if (w->window_id == window_id) + return w; + } + return NULL; +} + +static EDBus_Message * +_on_unregister_window(const EDBus_Service_Interface *iface, const EDBus_Message *msg) +{ + E_AppMenu_Context *ctxt = edbus_service_object_data_get(iface, "ctxt"); + E_AppMenu_Window *w; + unsigned window_id; + + if (!edbus_message_arguments_get(msg, "u", &window_id)) + { + ERR("Error reading message."); + return NULL; + } + + w = window_find(ctxt, window_id); + if (w) + appmenu_window_free(w); + edbus_service_signal_emit(iface, 1, window_id); + return edbus_message_method_return_new(msg); +} + +static EDBus_Message * +_on_getmenu(const EDBus_Service_Interface *iface, const EDBus_Message *msg) +{ + unsigned window_id; + Eina_List *l; + E_AppMenu_Window *w; + E_AppMenu_Context *ctxt = edbus_service_object_data_get(iface, "ctxt"); + + if (!edbus_message_arguments_get(msg, "u", &window_id)) + { + ERR("Error reading message"); + return NULL; + } + EINA_LIST_FOREACH(ctxt->windows, l, w) + { + if (w->window_id == window_id) + { + EDBus_Message *reply; + reply = edbus_message_method_return_new(msg); + edbus_message_arguments_append(reply, "so", w->bus_id, w->path); + return reply; + } + } + return edbus_message_error_new(msg, ERROR_WINDOW_NOT_FOUND, ""); +} + +static EDBus_Message * +_on_getmenus(const EDBus_Service_Interface *iface, const EDBus_Message *msg) +{ + Eina_List *l; + E_AppMenu_Window *w; + E_AppMenu_Context *ctxt = edbus_service_object_data_get(iface, "ctxt"); + EDBus_Message *reply; + EDBus_Message_Iter *array, *main_iter; + + reply = edbus_message_method_return_new(msg); + main_iter = edbus_message_iter_get(reply); + edbus_message_iter_arguments_append(main_iter, "a(uso)", &array); + + EINA_LIST_FOREACH(ctxt->windows, l, w) + { + EDBus_Message_Iter *entry; + edbus_message_iter_arguments_append(array, "(uso)", &entry); + edbus_message_iter_arguments_append(entry, "uso", w->window_id, + w->bus_id, w->path); + edbus_message_iter_container_close(array, entry); + } + + edbus_message_iter_container_close(main_iter, array); + return reply; +} + +static const EDBus_Method registrar_methods[] = { + { + "RegisterWindow", EDBUS_ARGS({"u", "windowId"},{"o", "menuObjectPath"}), + NULL, _on_register_window, 0 + }, + { + "UnregisterWindow", EDBUS_ARGS({"u", "windowId"}), + NULL, _on_unregister_window, 0 + }, + { + "GetMenuForWindow", EDBUS_ARGS({"u", "windowId"}), + EDBUS_ARGS({"s", "bus_id"},{"o", "menu_path"}), _on_getmenu, 0 + }, + { + "GetMenus", NULL, EDBUS_ARGS({"a(uso)", "array_of_menu"}), _on_getmenus, 0 + }, + { NULL, NULL, NULL, NULL, 0 } +}; + +static const EDBus_Signal registrar_signals[] = { + { "WindowRegistered", EDBUS_ARGS({"u", "windowId"}, {"s", "bus_id"}, {"o", "menu_path"}), 0 }, + { "WindowUnregistered", EDBUS_ARGS({"u", "windowId"}), 0 }, + { NULL, NULL, 0 } +}; + +static const EDBus_Service_Interface_Desc registrar_iface = { + REGISTRAR_IFACE, registrar_methods, registrar_signals, NULL, NULL, NULL +}; + +void +appmenu_dbus_registrar_server_init(E_AppMenu_Context *ctx) +{ + ctx->iface = edbus_service_interface_register(ctx->conn, + REGISTRAR_PATH, + ®istrar_iface); + edbus_service_object_data_set(ctx->iface, "ctxt", ctx); + edbus_name_request(ctx->conn, REGISTRAR_BUS, + EDBUS_NAME_REQUEST_FLAG_REPLACE_EXISTING, NULL, NULL); +} diff --git a/src/modules/appmenu/e_mod_main.c b/src/modules/appmenu/e_mod_main.c new file mode 100644 index 000000000..a5d423fcc --- /dev/null +++ b/src/modules/appmenu/e_mod_main.c @@ -0,0 +1,207 @@ +#include "e_mod_appmenu_private.h" + +static E_Module *appmenu_module = NULL; + +static void +_gc_orient(E_Gadcon_Client *gcc, E_Gadcon_Orient orient) +{ + E_AppMenu_Instance *inst = gcc->data; + switch (orient) + { + case E_GADCON_ORIENT_HORIZ: + case E_GADCON_ORIENT_TOP: + case E_GADCON_ORIENT_BOTTOM: + case E_GADCON_ORIENT_CORNER_TL: + case E_GADCON_ORIENT_CORNER_TR: + case E_GADCON_ORIENT_CORNER_BL: + case E_GADCON_ORIENT_CORNER_BR: + inst->orientation_horizontal = EINA_TRUE; + break; + case E_GADCON_ORIENT_VERT: + case E_GADCON_ORIENT_LEFT: + case E_GADCON_ORIENT_RIGHT: + case E_GADCON_ORIENT_CORNER_LT: + case E_GADCON_ORIENT_CORNER_RT: + case E_GADCON_ORIENT_CORNER_LB: + case E_GADCON_ORIENT_CORNER_RB: + default: + inst->orientation_horizontal = EINA_FALSE; + break; + } + if (inst->orientation_horizontal) + evas_object_box_layout_set(inst->box, evas_object_box_layout_horizontal, NULL, NULL); + else + evas_object_box_layout_set(inst->box, evas_object_box_layout_vertical, NULL, NULL); + appmenu_menu_of_instance_render(inst, inst->ctx->window); +} + +/* Gadcon Api Functions */ +static E_Gadcon_Client * +_gc_init(E_Gadcon *gc, const char *name, const char *id, const char *style) +{ + E_AppMenu_Instance *inst; + E_AppMenu_Context *ctxt; + + EINA_SAFETY_ON_NULL_RETURN_VAL(appmenu_module, NULL); + inst = calloc(1, sizeof(E_AppMenu_Instance)); + EINA_SAFETY_ON_NULL_RETURN_VAL(inst, NULL); + + ctxt = appmenu_module->data; + ctxt->instances = eina_list_append(ctxt->instances, inst); + inst->evas = gc->evas; + inst->ctx = ctxt; + + inst->box = evas_object_box_add(inst->evas); + evas_object_show(inst->box); + + inst->gcc = e_gadcon_client_new(gc, name, id, style, inst->box); + if (!inst->gcc) + { + evas_object_del(inst->box); + ctxt->instances = eina_list_remove(ctxt->instances, inst); + free(inst); + return NULL; + } + inst->gcc->data = inst; + _gc_orient(inst->gcc, inst->gcc->gadcon->orient); + return inst->gcc; +} + +static void +_gc_shutdown(E_Gadcon_Client *gcc) +{ + E_AppMenu_Instance *inst = gcc->data; + evas_object_del(inst->box); + inst->ctx->instances = eina_list_remove(inst->ctx->instances, inst); + free(inst); +} + +static const char * +_gc_label(const E_Gadcon_Client_Class *client_class EINA_UNUSED) +{ + return "Application Menu"; +} + +static Evas_Object * +_gc_icon(const E_Gadcon_Client_Class *client_class EINA_UNUSED, Evas *evas EINA_UNUSED) +{ + return NULL; +} + +static char tmpbuf[64]; /* general purpose buffer, just use immediately */ + +static const char * +_gc_id_new(const E_Gadcon_Client_Class *client_class EINA_UNUSED) +{ + E_AppMenu_Context *ctxt; + EINA_SAFETY_ON_NULL_RETURN_VAL(appmenu_module, NULL); + ctxt = appmenu_module->data; + snprintf(tmpbuf, sizeof(tmpbuf), "appmenu.%d", + eina_list_count(ctxt->instances)); + return tmpbuf; +} + +EAPI E_Module_Api e_modapi = { E_MODULE_API_VERSION, "AppMenu" }; + +static Eina_Bool +cb_focus_in(void *data, int type __UNUSED__, void *event) +{ + E_AppMenu_Context *ctxt = data; + E_Event_Border_Focus_In *ev = event; + Eina_List *l; + E_AppMenu_Window *w, *found = NULL; + ctxt->window_with_focus = ev->border->client.win; + + EINA_LIST_FOREACH(ctxt->windows, l, w) + { + if (w->window_id == ev->border->client.win) + { + found = w; + break; + } + } + appmenu_menu_render(ctxt, found); + return EINA_TRUE; +} + +static Eina_Bool +cb_focus_out(void *data, int type __UNUSED__, void *event EINA_UNUSED) +{ + E_AppMenu_Context *ctxt = data; + appmenu_menu_render(ctxt, NULL); + return EINA_TRUE; +} + +static const E_Gadcon_Client_Class _gc_class = +{ + GADCON_CLIENT_CLASS_VERSION, "appmenu", + { + _gc_init, _gc_shutdown, _gc_orient, _gc_label, _gc_icon, _gc_id_new, NULL, + e_gadcon_site_is_not_toolbar + }, + E_GADCON_CLIENT_STYLE_PLAIN +}; + +EAPI void * +e_modapi_init(E_Module *m) +{ + E_AppMenu_Context *ctxt; + Ecore_Event_Handler *event; + + ctxt = calloc(1, sizeof(E_AppMenu_Context)); + EINA_SAFETY_ON_NULL_RETURN_VAL(ctxt, NULL); + + appmenu_module = m; + + edbus_init(); + ctxt->conn = edbus_connection_get(EDBUS_CONNECTION_TYPE_SESSION); + appmenu_dbus_registrar_server_init(ctxt); + + event = ecore_event_handler_add(E_EVENT_BORDER_FOCUS_IN, cb_focus_in, ctxt); + ctxt->events[0] = event; + event = ecore_event_handler_add(E_EVENT_BORDER_FOCUS_OUT, cb_focus_out, ctxt); + ctxt->events[1] = event; + + e_gadcon_provider_register(&_gc_class); + + return ctxt; +} + +EAPI int +e_modapi_save(E_Module *m EINA_UNUSED) +{ + return 1; +} + +EAPI int +e_modapi_shutdown(E_Module *m) +{ + E_AppMenu_Context *ctxt = m->data; + E_AppMenu_Window *w; + Eina_List *l, *l2; + + ecore_event_handler_del(ctxt->events[0]); + ecore_event_handler_del(ctxt->events[1]); + + EINA_SAFETY_ON_NULL_RETURN_VAL(ctxt, 0); + EINA_LIST_FOREACH_SAFE(ctxt->windows, l, l2, w) + appmenu_window_free(w); + + edbus_service_interface_unregister(ctxt->iface); + edbus_connection_unref(ctxt->conn); + edbus_shutdown(); + free(ctxt); + return 1; +} + +void +appmenu_window_free(E_AppMenu_Window *window) +{ + window->ctxt->windows = eina_list_remove(window->ctxt->windows, window); + e_dbusmenu_unload(window->dbus_menu); + edbus_name_owner_changed_callback_del(window->ctxt->conn, window->bus_id, + appmenu_application_monitor, window); + eina_stringshare_del(window->bus_id); + eina_stringshare_del(window->path); + free(window); +} diff --git a/src/modules/appmenu/module.desktop.in b/src/modules/appmenu/module.desktop.in new file mode 100644 index 000000000..2031d5c4c --- /dev/null +++ b/src/modules/appmenu/module.desktop.in @@ -0,0 +1,6 @@ +[Desktop Entry] +Type=Link +Name=Application Menu +Icon=e-module-appmenu +Comment=Gadget that hold the toolbar of the foreground application. +X-Enlightenment-ModuleType=utils