#include "e_mod_notifier_host_private.h" #define WATCHER_BUS "org.kde.StatusNotifierWatcher" #define WATCHER_PATH "/StatusNotifierWatcher" #define WATCHER_IFACE "org.kde.StatusNotifierWatcher" #define ITEM_IFACE "org.kde.StatusNotifierItem" const char *Category_Names[] = { "unknown", "SystemServices", NULL }; const char *Status_Names[] = { "unknown", "Active", "Passive", "NeedsAttention", NULL }; static Context_Notifier_Host *ctx = NULL; void systray_notifier_item_free(Notifier_Item *item) { Eldbus_Object *obj; Eldbus_Signal_Handler *sig; Instance_Notifier_Host *host_inst; EINA_INLIST_FOREACH(ctx->instances, host_inst) { Notifier_Item_Icon *ii; EINA_INLIST_FOREACH(host_inst->ii_list, ii) { if (ii->item == item) break; } if (!ii) continue; host_inst->ii_list = eina_inlist_remove(host_inst->ii_list, EINA_INLIST_GET(ii)); evas_object_del(ii->icon); free(ii); systray_size_updated(host_inst->inst); } if (item->menu_path) e_dbusmenu_unload(item->menu_data); eina_stringshare_del(item->bus_id); eina_stringshare_del(item->path); free(item->imgdata); free(item->attnimgdata); if (item->attention_icon_name) eina_stringshare_del(item->attention_icon_name); if (item->icon_name) eina_stringshare_del(item->icon_name); if (item->icon_path) eina_stringshare_del(item->icon_path); if (item->id) eina_stringshare_del(item->id); if (item->menu_path) eina_stringshare_del(item->menu_path); if (item->title) eina_stringshare_del(item->title); EINA_LIST_FREE(item->signals, sig) eldbus_signal_handler_del(sig); obj = eldbus_proxy_object_get(item->proxy); eldbus_proxy_unref(item->proxy); eldbus_object_unref(obj); ctx->item_list = eina_inlist_remove(ctx->item_list, EINA_INLIST_GET(item)); free(item); } static void image_load(const char *name, const char *path, uint32_t *imgdata, int w, int h, Evas_Object *image) { const char **ext, *exts[] = { ".png", ".jpg", NULL }; if (path && path[0]) { char buf[PATH_MAX]; const char **theme, *themes[] = { e_config->icon_theme, "hicolor", NULL }; for (theme = themes; *theme; theme++) { struct stat st; unsigned int *i, sizes[] = { 16, 22, 24, 32, 36, 40, 48, 64, 72, 96, 128, 192, 256, 512, 0 }; snprintf(buf, sizeof(buf), "%s/%s", path, *theme); if (stat(buf, &st)) continue; for (i = sizes; *i; i++) { snprintf(buf, sizeof(buf), "%s/%s/%ux%u", path, *theme, *i, *i); if (stat(buf, &st)) continue; for (ext = exts; *ext; ext++) { snprintf(buf, sizeof(buf), "%s/%s/%ux%u/apps/%s%s", path, *theme, *i, *i, name, *ext); if (e_icon_file_set(image, buf)) return; } } } } if (name && name[0] && e_util_icon_theme_set(image, name)) return; if (imgdata) { Evas_Object *o; o = evas_object_image_filled_add(evas_object_evas_get(image)); evas_object_image_alpha_set(o, 1); evas_object_image_size_set(o, w, h); evas_object_image_data_set(o, imgdata); e_icon_image_object_set(image, o); } else e_util_icon_theme_set(image, "dialog-error"); } 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 void _menu_post_deactivate(void *data, E_Menu *m) { Eina_List *iter; E_Menu_Item *mi; E_Gadcon *gadcon = data; if (gadcon) e_gadcon_locked_set(gadcon, 0); EINA_LIST_FOREACH(m->items, iter, mi) { if (mi->submenu) e_menu_deactivate(mi->submenu); } e_object_del(E_OBJECT(m)); } static E_Menu * _item_submenu_new(E_DBusMenu_Item *item, E_Menu_Item *mi) { E_DBusMenu_Item *child; E_Menu *m; E_Menu_Item *submi; m = e_menu_new(); e_menu_post_deactivate_callback_set(m, _menu_post_deactivate, NULL); if (mi) e_menu_item_submenu_set(mi, m); EINA_INLIST_FOREACH(item->sub_items, child) { 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); else { 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 == 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); if (child->toggle_type) e_menu_item_toggle_set(submi, child->toggle_state); if (child->sub_items) _item_submenu_new(child, submi); e_util_menu_item_theme_icon_set(submi, child->icon_name); } } return m; } void _clicked_item_cb(void *data, Evas *evas, Evas_Object *obj EINA_UNUSED, void *event) { Notifier_Item_Icon *ii = data; Evas_Event_Mouse_Down *ev = event; E_DBusMenu_Item *root_item; E_Menu *m; E_Zone *zone; int x, y; E_Gadcon *gadcon = evas_object_data_get(ii->icon, "gadcon"); if (ev->button != 1) return; EINA_SAFETY_ON_NULL_RETURN(gadcon); if (!ii->item->dbus_item) return; root_item = ii->item->dbus_item; EINA_SAFETY_ON_FALSE_RETURN(root_item->is_submenu); m = _item_submenu_new(root_item, NULL); e_gadcon_locked_set(gadcon, 1); e_menu_post_deactivate_callback_set(m, _menu_post_deactivate, gadcon); zone = e_gadcon_zone_get(gadcon); evas_pointer_canvas_xy_get(e_comp->evas, &x, &y); e_menu_activate_mouse(m, zone, x, y, 1, 1, E_MENU_POP_DIRECTION_DOWN, ev->timestamp); evas_event_feed_mouse_up(evas, ev->button, EVAS_BUTTON_NONE, ev->timestamp, NULL); } void systray_notifier_update_menu(void *data, E_DBusMenu_Item *new_root_item) { Notifier_Item *item = data; item->dbus_item = new_root_item; } static void image_scale(Instance_Notifier_Host *notifier_inst, Notifier_Item_Icon *ii) { Evas_Coord sz; switch (systray_gadcon_get(notifier_inst->inst)->orient) { case E_GADCON_ORIENT_FLOAT: 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: if (systray_gadcon_get(notifier_inst->inst)->shelf) sz = systray_gadcon_get(notifier_inst->inst)->shelf->h; else evas_object_geometry_get(notifier_inst->inst->gcc->o_frame ?: notifier_inst->inst->gcc->o_base, NULL, NULL, NULL, &sz); 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: if (systray_gadcon_get(notifier_inst->inst)->shelf) sz = systray_gadcon_get(notifier_inst->inst)->shelf->w; else evas_object_geometry_get(notifier_inst->inst->gcc->o_frame ?: notifier_inst->inst->gcc->o_base, NULL, NULL, &sz, NULL); break; } sz = sz - 5; evas_object_resize(ii->icon, sz, sz); } static void _systray_notifier_inst_item_update(Instance_Notifier_Host *host_inst, Notifier_Item *item, Eina_Bool search) { Notifier_Item_Icon *ii = NULL; if (!search) goto jump_search; EINA_INLIST_FOREACH(host_inst->ii_list, ii) { if (ii->item == item) break; } jump_search: if (!ii) { ii = calloc(1, sizeof(Notifier_Item_Icon)); ii->item = item; host_inst->ii_list = eina_inlist_append(host_inst->ii_list, EINA_INLIST_GET(ii)); } if (!ii->icon) { ii->icon = e_icon_add(evas_object_evas_get(host_inst->edje)); EINA_SAFETY_ON_NULL_RETURN(ii->icon); image_scale(host_inst, ii); evas_object_data_set(ii->icon, "gadcon", host_inst->gadcon); evas_object_event_callback_add(ii->icon, EVAS_CALLBACK_MOUSE_DOWN, _clicked_item_cb, ii); } switch (item->status) { case STATUS_ACTIVE: { image_load(item->icon_name, item->icon_path, item->imgdata, item->imgw, item->imgh, ii->icon); if (!evas_object_visible_get(ii->icon)) { systray_edje_box_append(host_inst->inst, ii->icon); evas_object_show(ii->icon); } break; } case STATUS_PASSIVE: { if (evas_object_visible_get(ii->icon)) { systray_edje_box_remove(host_inst->inst, ii->icon); evas_object_hide(ii->icon); } break; } case STATUS_ATTENTION: { image_load(item->attention_icon_name, item->icon_path, item->attnimgdata, item->attnimgw, item->attnimgh, ii->icon); if (!evas_object_visible_get(ii->icon)) { systray_edje_box_append(host_inst->inst, ii->icon); evas_object_show(ii->icon); } break; } default: { WRN("unhandled status"); break; } } systray_size_updated(host_inst->inst); } void systray_notifier_item_update(Notifier_Item *item) { Instance_Notifier_Host *inst; EINA_INLIST_FOREACH(ctx->instances, inst) _systray_notifier_inst_item_update(inst, item, EINA_TRUE); } Instance_Notifier_Host * systray_notifier_host_new(Instance *inst, E_Gadcon *gadcon) { Instance_Notifier_Host *host_inst = NULL; Notifier_Item *item; host_inst = calloc(1, sizeof(Instance_Notifier_Host)); EINA_SAFETY_ON_NULL_RETURN_VAL(host_inst, NULL); host_inst->inst = inst; host_inst->edje = systray_edje_get(inst); host_inst->gadcon = gadcon; ctx->instances = eina_inlist_append(ctx->instances, EINA_INLIST_GET(host_inst)); EINA_INLIST_FOREACH(ctx->item_list, item) _systray_notifier_inst_item_update(host_inst, item, EINA_FALSE); return host_inst; } void systray_notifier_host_free(Instance_Notifier_Host *notifier) { while (notifier->ii_list) { Notifier_Item_Icon *ii = EINA_INLIST_CONTAINER_GET(notifier->ii_list, Notifier_Item_Icon); notifier->ii_list = eina_inlist_remove(notifier->ii_list, notifier->ii_list); free(ii); } ctx->instances = eina_inlist_remove(ctx->instances, EINA_INLIST_GET(notifier)); free(notifier); } void systray_notifier_host_init(void) { ctx = calloc(1, sizeof(Context_Notifier_Host)); EINA_SAFETY_ON_NULL_RETURN(ctx); systray_notifier_dbus_init(ctx); } void systray_notifier_host_shutdown(void) { Eldbus_Pending *p; EINA_LIST_FREE(ctx->pending, p) eldbus_pending_cancel(p); systray_notifier_dbus_shutdown(ctx); free(ctx); ctx = NULL; }