#ifdef HAVE_CONFIG_H # include "elementary_config.h" #endif #define EFL_ACCESS_OBJECT_PROTECTED #define EFL_ACCESS_WIDGET_ACTION_PROTECTED #include #include "elm_priv.h" #define MY_CLASS ELM_ACCESS_CLASS #define MY_CLASS_NAME "Elm_Access" #define MY_CLASS_NAME_LEGACY "elm_access" struct _Func_Data { void *user_data; /* Holds user data to CB */ Elm_Access_Action_Cb cb; }; typedef struct _Func_Data Func_Data; struct _Action_Info { Evas_Object *obj; Func_Data fn[ELM_ACCESS_ACTION_LAST + 1]; /* Callback for specific action */ }; typedef struct _Action_Info Action_Info; static Eina_Bool mouse_event_enable = EINA_TRUE; static Eina_Bool auto_highlight = EINA_FALSE; static Elm_Access_Action_Type action_by = ELM_ACCESS_ACTION_FIRST; static Evas_Object * _elm_access_add(Evas_Object *parent); static void _access_object_unregister(Evas_Object *obj); static const char SIG_ACTIVATED[] = "access,activated"; static const Evas_Smart_Cb_Description _smart_callbacks[] = { {SIG_ACTIVATED, ""}, {NULL, NULL} }; EOLIAN static void _elm_access_efl_canvas_group_group_add(Eo *obj, void *_pd EINA_UNUSED) { efl_canvas_group_add(efl_super(obj, MY_CLASS)); } static Eina_Bool _access_action_callback_call(Evas_Object *obj, Elm_Access_Action_Type type, Elm_Access_Action_Info *action_info) { Elm_Access_Action_Info *ai = NULL; Action_Info *a; Eina_Bool ret; ret = EINA_FALSE; a = evas_object_data_get(obj, "_elm_access_action_info"); if (!action_info) { ai = calloc(1, sizeof(Elm_Access_Action_Info)); action_info = ai; } action_info->action_type = type; if ((type == ELM_ACCESS_ACTION_HIGHLIGHT) && (action_by != ELM_ACCESS_ACTION_FIRST)) action_info->action_by = action_by; if (a && (a->fn[type].cb)) ret = a->fn[type].cb(a->fn[type].user_data, obj, action_info); free(ai); return ret; } EOLIAN static Eina_Bool _elm_access_efl_ui_widget_on_access_activate(Eo *obj, void *_pd EINA_UNUSED, Efl_Ui_Activate act) { int type = ELM_ACCESS_ACTION_FIRST; Action_Info *a; a = evas_object_data_get(obj, "_elm_access_action_info"); switch (act) { case EFL_UI_ACTIVATE_DEFAULT: type = ELM_ACCESS_ACTION_ACTIVATE; break; case EFL_UI_ACTIVATE_UP: type = ELM_ACCESS_ACTION_UP; break; case EFL_UI_ACTIVATE_DOWN: type = ELM_ACCESS_ACTION_DOWN; break; case EFL_UI_ACTIVATE_RIGHT: break; case EFL_UI_ACTIVATE_LEFT: break; case EFL_UI_ACTIVATE_BACK: type = ELM_ACCESS_ACTION_BACK; break; default: break; } if (type == ELM_ACCESS_ACTION_FIRST) return EINA_FALSE; /* if an access object has a callback, it would have the intention to do something. so, check here and return EINA_TRUE. */ if ((a) && (type > ELM_ACCESS_ACTION_FIRST) && (type < ELM_ACCESS_ACTION_LAST) && (a->fn[type].cb)) { _access_action_callback_call(obj, type, NULL); return EINA_TRUE; } /* TODO: deprecate below? */ if (act != EFL_UI_ACTIVATE_DEFAULT) return EINA_FALSE; Elm_Access_Info *ac = evas_object_data_get(obj, "_elm_access"); if (!ac) return EINA_FALSE; if (ac->activate) ac->activate(ac->activate_data, ac->part_object, ac->widget_item->eo_obj); return EINA_TRUE; } EOLIAN static Eina_Bool _elm_access_efl_ui_focus_object_on_focus_update(Eo *obj, void *_pd EINA_UNUSED) { evas_object_focus_set(obj, efl_ui_focus_object_focus_get(obj)); return EINA_TRUE; } typedef struct _Mod_Api Mod_Api; struct _Mod_Api { void (*out_read) (const char *txt); void (*out_read_done) (void); void (*out_cancel) (void); void (*out_done_callback_set) (void (*func) (void *data), const void *data); }; static int initted = 0; static Mod_Api *mapi = NULL; static void _access_init(void) { Elm_Module *m; if (initted > 0) return; if (!(m = _elm_module_find_as("access/api"))) return; if (m->init_func(m) < 0) return; initted++; m->api = malloc(sizeof(Mod_Api)); if (!m->api) return; ((Mod_Api *)(m->api) )->out_read = // called to read out some text _elm_module_symbol_get(m, "out_read"); ((Mod_Api *)(m->api) )->out_read_done = // called to set a done marker so when it is reached the done callback is called _elm_module_symbol_get(m, "out_read_done"); ((Mod_Api *)(m->api) )->out_cancel = // called to read out some text _elm_module_symbol_get(m, "out_cancel"); ((Mod_Api *)(m->api) )->out_done_callback_set = // called when last read done _elm_module_symbol_get(m, "out_done_callback_set"); mapi = m->api; } static void _access_shutdown(void) { Elm_Module *m; if (initted == 0) return; if (!(m = _elm_module_find_as("access/api"))) return; m->shutdown_func(m); initted = 0; /* _elm_module_unload(); could access m->api and try to free(); */ ELM_SAFE_FREE(m->api, free); mapi = NULL; } static Elm_Access_Item * _access_add_set(Elm_Access_Info *ac, int type) { Elm_Access_Item *ai; Eina_List *l; if (!ac) return NULL; EINA_LIST_FOREACH(ac->items, l, ai) { if (ai->type == type) { if (!ai->func) { eina_stringshare_del(ai->data); } ai->func = NULL; ai->data = NULL; return ai; } } ai = calloc(1, sizeof(Elm_Access_Item)); ai->type = type; ac->items = eina_list_prepend(ac->items, ai); return ai; } static Evas_Object * _access_highlight_object_get(Evas_Object *obj) { Evas_Object *o, *ho; o = evas_object_name_find(evas_object_evas_get(obj), "_elm_access_disp"); if (!o) return NULL; ho = evas_object_data_get(o, "_elm_access_target"); return ho; } static void _access_highlight_read(Elm_Access_Info *ac, Evas_Object *obj) { int type; char *txt = NULL; Eina_Strbuf *strbuf; strbuf = eina_strbuf_new(); if (_elm_config->access_mode != ELM_ACCESS_MODE_OFF) { if (ac->on_highlight) ac->on_highlight(ac->on_highlight_data); _elm_access_object_highlight(obj); for (type = ELM_ACCESS_INFO_FIRST + 1; type < ELM_ACCESS_INFO_LAST; type++) { txt = _elm_access_text_get(ac, type, obj); if (txt && (strlen(txt) > 0)) { if (eina_strbuf_length_get(strbuf) > 0) eina_strbuf_append_printf(strbuf, ", %s", txt); else eina_strbuf_append(strbuf, txt); } free(txt); } } txt = eina_strbuf_string_steal(strbuf); eina_strbuf_free(strbuf); _elm_access_say(txt); free(txt); } static Eina_Bool _access_obj_over_timeout_cb(void *data) { Elm_Access_Info *ac; Evas_Object *ho; if (!data) return EINA_FALSE; ac = evas_object_data_get(data, "_elm_access"); if (!ac) return EINA_FALSE; ho = _access_highlight_object_get(data); if (ho != data) _access_highlight_read(ac, data); ac->delay_timer = NULL; return EINA_FALSE; } static void _access_hover_mouse_in_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { Elm_Access_Info *ac; if (!mouse_event_enable) return; ac = evas_object_data_get(data, "_elm_access"); if (!ac) return; ELM_SAFE_FREE(ac->delay_timer, ecore_timer_del); if (_elm_config->access_mode != ELM_ACCESS_MODE_OFF) ac->delay_timer = ecore_timer_add(0.2, _access_obj_over_timeout_cb, data); } static void _access_hover_mouse_out_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { Elm_Access_Info *ac; if (!mouse_event_enable) return; ac = evas_object_data_get(data, "_elm_access"); if (!ac) return; _elm_access_object_unhighlight(data); ELM_SAFE_FREE(ac->delay_timer, ecore_timer_del); } static void _access_read_done(void *data EINA_UNUSED) { DBG("read done"); // FIXME: produce event here } static void _access_2nd_click_del_cb(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED) { Ecore_Timer *t; evas_object_event_callback_del_full(obj, EVAS_CALLBACK_DEL, _access_2nd_click_del_cb, NULL); t = evas_object_data_get(obj, "_elm_2nd_timeout"); if (t) { ecore_timer_del(t); evas_object_data_del(obj, "_elm_2nd_timeout"); } } static Eina_Bool _access_2nd_click_timeout_cb(void *data) { evas_object_event_callback_del_full(data, EVAS_CALLBACK_DEL, _access_2nd_click_del_cb, NULL); evas_object_data_del(data, "_elm_2nd_timeout"); return EINA_FALSE; } static void _access_obj_hilight_del_cb(void *data EINA_UNUSED, Evas *e, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { _elm_access_object_highlight_disable(e); } static void _access_obj_hilight_hide_cb(void *data EINA_UNUSED, Evas *e, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { _elm_access_object_highlight_disable(e); } static void _access_obj_hilight_move_cb(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED) { Evas_Coord x, y; Evas_Object *o; o = evas_object_name_find(evas_object_evas_get(obj), "_elm_access_disp"); if (!o) return; evas_object_geometry_get(obj, &x, &y, NULL, NULL); evas_object_move(o, x, y); } static void _access_obj_hilight_resize_cb(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED) { Evas_Coord w, h; Evas_Object *o; o = evas_object_name_find(evas_object_evas_get(obj), "_elm_access_disp"); if (!o) return; evas_object_geometry_get(obj, NULL, NULL, &w, &h); evas_object_resize(o, w, h); } void _elm_access_mouse_event_enabled_set(Eina_Bool enabled) { enabled = !!enabled; if (mouse_event_enable == enabled) return; mouse_event_enable = enabled; } void _elm_access_auto_highlight_set(Eina_Bool enabled) { enabled = !!enabled; if (auto_highlight == enabled) return; auto_highlight = enabled; } Eina_Bool _elm_access_auto_highlight_get(void) { return auto_highlight; } void _elm_access_shutdown() { _access_shutdown(); } static void _access_order_del_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED) { Elm_Widget_Item_Data *item = data; item->access_order = eina_list_remove(item->access_order, obj); } void _elm_access_widget_item_access_order_set(Elm_Widget_Item_Data *item, Eina_List *objs) { Eina_List *l; Evas_Object *o; if (!item) return; _elm_access_widget_item_access_order_unset(item); EINA_LIST_FOREACH(objs, l, o) { evas_object_event_callback_add(o, EVAS_CALLBACK_DEL, _access_order_del_cb, item); } item->access_order = objs; } const Eina_List * _elm_access_widget_item_access_order_get(const Elm_Widget_Item_Data *item) { if (!item) return NULL; return item->access_order; } void _elm_access_widget_item_access_order_unset(Elm_Widget_Item_Data *item) { Eina_List *l, *l_next; Evas_Object *o; if (!item) return; EINA_LIST_FOREACH_SAFE(item->access_order, l, l_next, o) { evas_object_event_callback_del_full (o, EVAS_CALLBACK_DEL, _access_order_del_cb, item); item->access_order = eina_list_remove_list(item->access_order, l); } } static Eina_Bool _access_highlight_next_get(Evas_Object *obj, Elm_Focus_Direction dir) { int type; Evas_Object *ho, *parent, *target; Eina_Bool ret; target = NULL; ret = EINA_FALSE; if (!elm_widget_is(obj)) return ret; ho = _access_highlight_object_get(obj); if (!ho) ho = obj; parent = ho; /* find highlight root */ do { ELM_WIDGET_DATA_GET_OR_RETURN(parent, wd, ret); if (wd->highlight_root) { /* change highlight root */ obj = parent; break; } parent = elm_widget_parent_get(parent); } while (parent); _elm_access_auto_highlight_set(EINA_TRUE); if (dir == ELM_FOCUS_NEXT) type = ELM_ACCESS_ACTION_HIGHLIGHT_NEXT; else type = ELM_ACCESS_ACTION_HIGHLIGHT_PREV; /* this value is used in _elm_access_object_highlight(); to inform the target object of how to get highlight */ action_by = type; if (!_access_action_callback_call(ho, type, NULL)) { if (ho) { Elm_Access_Info *info = _elm_access_info_get(ho); if (type == ELM_ACCESS_ACTION_HIGHLIGHT_NEXT && info->next) target = info->next; else if (type == ELM_ACCESS_ACTION_HIGHLIGHT_PREV && info->prev) target = info->prev; } if (target) { _elm_access_highlight_set(target); elm_widget_focus_region_show(target); ret = EINA_TRUE; } else { Efl_Ui_Focus_Relations *rel; rel = efl_ui_focus_manager_fetch(efl_ui_focus_object_focus_manager_get(obj), obj); if (rel) { if (dir == ELM_FOCUS_NEXT) _elm_access_highlight_set(rel->next); else _elm_access_highlight_set(rel->prev); free(rel); } } } action_by = ELM_ACCESS_ACTION_FIRST; _elm_access_auto_highlight_set(EINA_FALSE); return ret; } //-------------------------------------------------------------------------// EAPI void _elm_access_highlight_set(Evas_Object* obj) { Elm_Access_Info *ac; Evas_Object *ho; if (!obj) return; ho = _access_highlight_object_get(obj); if (ho == obj) return; ac = evas_object_data_get(obj, "_elm_access"); if (!ac) return; _access_highlight_read(ac, obj); } EAPI void _elm_access_clear(Elm_Access_Info *ac) { Elm_Access_Item *ai; if (!ac) return; ELM_SAFE_FREE(ac->delay_timer, ecore_timer_del); EINA_LIST_FREE(ac->items, ai) { if (!ai->func) { eina_stringshare_del(ai->data); } free(ai); } } EAPI void _elm_access_text_set(Elm_Access_Info *ac, int type, const char *text) { Elm_Access_Item *ai = _access_add_set(ac, type); if (!ai) return; ai->func = NULL; ai->data = eina_stringshare_add(text); } EAPI void _elm_access_callback_set(Elm_Access_Info *ac, int type, Elm_Access_Info_Cb func, const void *data) { Elm_Access_Item *ai = _access_add_set(ac, type); if (!ai) return; ai->func = func; ai->data = data; } EAPI void _elm_access_on_highlight_hook_set(Elm_Access_Info *ac, Elm_Access_On_Highlight_Cb func, void *data) { if (!ac) return; ac->on_highlight = func; ac->on_highlight_data = data; } EAPI void _elm_access_activate_callback_set(Elm_Access_Info *ac, Elm_Access_Activate_Cb func, void *data) { if (!ac) return; ac->activate = func; ac->activate_data = data; } EAPI void _elm_access_highlight_object_activate(Evas_Object *obj, Efl_Ui_Activate act) { Evas_Object *highlight; highlight = _access_highlight_object_get(obj); if (!highlight) return; _elm_access_auto_highlight_set(EINA_FALSE); if (!elm_object_focus_get(highlight)) elm_object_focus_set(highlight, EINA_TRUE); elm_widget_activate(highlight, act); return; } EAPI void _elm_access_highlight_cycle(Evas_Object *obj, Elm_Focus_Direction dir) { int type; Evas_Object *ho, *parent; ho = _access_highlight_object_get(obj); if (!ho) return; parent = ho; /* find highlight root */ do { ELM_WIDGET_DATA_GET_OR_RETURN(parent, wd); if (wd->highlight_root) { /* change highlight root */ obj = parent; break; } parent = elm_widget_parent_get(parent); } while (parent); _elm_access_auto_highlight_set(EINA_TRUE); if (dir == ELM_FOCUS_NEXT) type = ELM_ACCESS_ACTION_HIGHLIGHT_NEXT; else type = ELM_ACCESS_ACTION_HIGHLIGHT_PREV; action_by = type; if (!_access_action_callback_call(ho, type, NULL)) { Elm_Access_Info *info = _elm_access_info_get(ho); Evas_Object *comming = NULL; if (type == ELM_ACCESS_ACTION_HIGHLIGHT_NEXT) { if ((info) && (info->next)) comming = info->next; } else { if ((info) && (info->prev)) comming = info->prev; } if (comming) { _elm_access_highlight_set(comming); elm_widget_focus_region_show(comming); } else { efl_ui_focus_util_focus(obj); efl_ui_focus_manager_move(elm_widget_top_get(obj), dir); } } action_by = ELM_ACCESS_ACTION_FIRST; _elm_access_auto_highlight_set(EINA_FALSE); } EAPI char * _elm_access_text_get(const Elm_Access_Info *ac, int type, const Evas_Object *obj) { Elm_Access_Item *ai; Eina_List *l; if (!ac) return NULL; EINA_LIST_FOREACH(ac->items, l, ai) { if (ai->type == type) { if (ai->func) return ai->func((void *)(ai->data), (Evas_Object *)obj); else if (ai->data) return strdup(ai->data); return NULL; } } return NULL; } EAPI void _elm_access_read(Elm_Access_Info *ac, int type, const Evas_Object *obj) { char *txt = _elm_access_text_get(ac, type, obj); _access_init(); if (mapi) { if (mapi->out_done_callback_set) mapi->out_done_callback_set(_access_read_done, NULL); if (type == ELM_ACCESS_DONE) { if (mapi->out_read_done) mapi->out_read_done(); } else if (type == ELM_ACCESS_CANCEL) { if (mapi->out_cancel) mapi->out_cancel(); } else { if (txt) { if (mapi->out_read) mapi->out_read(txt); if (mapi->out_read) mapi->out_read(".\n"); } } } free(txt); } EAPI void _elm_access_say(const char *txt) { if (!_elm_config->access_mode) return; _access_init(); if (mapi) { if (mapi->out_done_callback_set) mapi->out_done_callback_set(_access_read_done, NULL); if (mapi->out_cancel) mapi->out_cancel(); if (txt) { if (mapi->out_read) mapi->out_read(txt); if (mapi->out_read) mapi->out_read(".\n"); } if (mapi->out_read_done) mapi->out_read_done(); } } EAPI Elm_Access_Info * _elm_access_info_get(const Evas_Object *obj) { return evas_object_data_get(obj, "_elm_access"); } EAPI Elm_Access_Info * _elm_access_object_get(const Evas_Object *obj) { return _elm_access_info_get(obj); } static Evas_Object * _elm_access_widget_target_get(Evas_Object *obj) { Evas_Object *o = obj; do { if (elm_widget_is(o)) break; else { o = elm_widget_parent_widget_get(o); if (!o) o = evas_object_smart_parent_get(o); } } while (o); return o; } EAPI void _elm_access_object_highlight(Evas_Object *obj) { Evas_Object *o, *widget; Evas_Coord x, y, w, h; Eina_Bool in_theme = EINA_FALSE; o = evas_object_name_find(evas_object_evas_get(obj), "_elm_access_disp"); if (!o) { o = edje_object_add(evas_object_evas_get(obj)); evas_object_name_set(o, "_elm_access_disp"); evas_object_layer_set(o, ELM_OBJECT_LAYER_TOOLTIP); } else { Evas_Object *ptarget = evas_object_data_get(o, "_elm_access_target"); if (ptarget) { evas_object_data_del(o, "_elm_access_target"); elm_widget_parent_highlight_set(ptarget, EINA_FALSE); evas_object_event_callback_del_full(ptarget, EVAS_CALLBACK_DEL, _access_obj_hilight_del_cb, NULL); evas_object_event_callback_del_full(ptarget, EVAS_CALLBACK_HIDE, _access_obj_hilight_hide_cb, NULL); evas_object_event_callback_del_full(ptarget, EVAS_CALLBACK_MOVE, _access_obj_hilight_move_cb, NULL); evas_object_event_callback_del_full(ptarget, EVAS_CALLBACK_RESIZE, _access_obj_hilight_resize_cb, NULL); widget = _elm_access_widget_target_get(ptarget); if (widget) { if (elm_widget_access_highlight_in_theme_get(widget)) { elm_widget_signal_emit(widget, "elm,action,access_highlight,hide", "elm"); } } } } evas_object_data_set(o, "_elm_access_target", obj); elm_widget_parent_highlight_set(obj, EINA_TRUE); elm_widget_theme_object_set(obj, o, "access", "base", "default"); evas_object_event_callback_add(obj, EVAS_CALLBACK_DEL, _access_obj_hilight_del_cb, NULL); evas_object_event_callback_add(obj, EVAS_CALLBACK_HIDE, _access_obj_hilight_hide_cb, NULL); evas_object_event_callback_add(obj, EVAS_CALLBACK_MOVE, _access_obj_hilight_move_cb, NULL); evas_object_event_callback_add(obj, EVAS_CALLBACK_RESIZE, _access_obj_hilight_resize_cb, NULL); evas_object_raise(o); evas_object_geometry_get(obj, &x, &y, &w, &h); evas_object_geometry_set(o, x, y, w, h); widget = _elm_access_widget_target_get(obj); if (widget) { if (elm_widget_access_highlight_in_theme_get(widget)) { in_theme = EINA_TRUE; elm_widget_signal_emit(widget, "elm,action,access_highlight,show", "elm"); } } /* use callback, should an access object do below every time when * a window gets a client message ECORE_X_ATOM_E_ILLMUE_ACTION_READ? */ if (!in_theme && !_access_action_callback_call(obj, ELM_ACCESS_ACTION_HIGHLIGHT, NULL)) evas_object_show(o); else evas_object_hide(o); } EAPI void _elm_access_object_unhighlight(Evas_Object *obj) { Evas_Object *o, *ptarget; o = evas_object_name_find(evas_object_evas_get(obj), "_elm_access_disp"); if (!o) return; ptarget = evas_object_data_get(o, "_elm_access_target"); if (ptarget == obj) { evas_object_event_callback_del_full(ptarget, EVAS_CALLBACK_DEL, _access_obj_hilight_del_cb, NULL); evas_object_event_callback_del_full(ptarget, EVAS_CALLBACK_HIDE, _access_obj_hilight_hide_cb, NULL); evas_object_event_callback_del_full(ptarget, EVAS_CALLBACK_MOVE, _access_obj_hilight_move_cb, NULL); evas_object_event_callback_del_full(ptarget, EVAS_CALLBACK_RESIZE, _access_obj_hilight_resize_cb, NULL); evas_object_del(o); elm_widget_parent_highlight_set(ptarget, EINA_FALSE); } } static void _content_resize(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED) { Evas_Object *accessobj; Evas_Coord w, h; accessobj = data; if (!accessobj) return; evas_object_geometry_get(obj, NULL, NULL, &w, &h); evas_object_resize(accessobj, w, h); } static void _content_move(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED) { Evas_Object *accessobj; Evas_Coord x, y; accessobj = data; if (!accessobj) return; evas_object_geometry_get(obj, &x, &y, NULL, NULL); evas_object_move(accessobj, x, y); } static Evas_Object * _access_object_register(Evas_Object *obj, Evas_Object *parent) { Evas_Object *ao; Elm_Access_Info *ac; Evas_Coord x, y, w, h; if (!obj) return NULL; /* check previous access object */ ao = evas_object_data_get(obj, "_part_access_obj"); if (ao) _access_object_unregister(obj); /* create access object */ ao = _elm_access_add(parent); if (!ao) return NULL; evas_object_event_callback_add(obj, EVAS_CALLBACK_RESIZE, _content_resize, ao); evas_object_event_callback_add(obj, EVAS_CALLBACK_MOVE, _content_move, ao); evas_object_geometry_get(obj, &x, &y, &w, &h); evas_object_geometry_set(ao, x, y, w, h); evas_object_show(ao); /* register access object */ _elm_access_object_register(ao, obj); /* set access object */ evas_object_data_set(obj, "_part_access_obj", ao); /* set owner part object */ ac = evas_object_data_get(ao, "_elm_access"); ac->part_object = obj; return ao; } static void _access_object_unregister(Evas_Object *obj) { Elm_Access_Info *ac; Evas_Object *ao; if (!obj) return; ao = evas_object_data_get(obj, "_part_access_obj"); if (ao) { /* delete callbacks and unregister access object in _access_obj_del_cb*/ evas_object_del(ao); } else { /* button, check, label etc. */ ac = evas_object_data_get(obj, "_elm_access"); if (ac && ac->hoverobj) _elm_access_object_unregister(obj, ac->hoverobj); } } EAPI Evas_Object * _elm_access_edje_object_part_object_register(Evas_Object* obj, const Evas_Object *eobj, const char* part) { Evas_Object *ao, *po; edje_object_freeze((Evas_Object *)eobj); po = (Evas_Object *)edje_object_part_object_get(eobj, part); edje_object_thaw((Evas_Object *)eobj); if (!obj || !po) return NULL; /* check previous access object */ ao = evas_object_data_get(po, "_part_access_obj"); if (ao) _elm_access_edje_object_part_object_unregister(obj, eobj, part); ao = _access_object_register(po, obj); return ao; } //FIXME: unused obj should be removed from here and each widget. EAPI void _elm_access_edje_object_part_object_unregister(Evas_Object* obj EINA_UNUSED, const Evas_Object *eobj, const char* part) { Evas_Object *po; edje_object_freeze((Evas_Object *)eobj); po = (Evas_Object *)edje_object_part_object_get(eobj, part); edje_object_thaw((Evas_Object *)eobj); if (!po) return; _access_object_unregister(po); } EAPI void _elm_access_object_highlight_disable(Evas *e) { Evas_Object *o, *ptarget; o = evas_object_name_find(e, "_elm_access_disp"); if (!o) return; ptarget = evas_object_data_get(o, "_elm_access_target"); if (ptarget) { evas_object_event_callback_del_full(ptarget, EVAS_CALLBACK_DEL, _access_obj_hilight_del_cb, NULL); evas_object_event_callback_del_full(ptarget, EVAS_CALLBACK_HIDE, _access_obj_hilight_hide_cb, NULL); evas_object_event_callback_del_full(ptarget, EVAS_CALLBACK_MOVE, _access_obj_hilight_move_cb, NULL); evas_object_event_callback_del_full(ptarget, EVAS_CALLBACK_RESIZE, _access_obj_hilight_resize_cb, NULL); } evas_object_del(o); elm_widget_parent_highlight_set(ptarget, EINA_FALSE); } static void _access_obj_del_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED) { Ecore_Job *ao_del_job = NULL; evas_object_event_callback_del(obj, EVAS_CALLBACK_DEL, _access_obj_del_cb); if (data) /* hover object */ { evas_object_event_callback_del_full(data, EVAS_CALLBACK_RESIZE, _content_resize, obj); evas_object_event_callback_del_full(data, EVAS_CALLBACK_MOVE, _content_move, obj); _elm_access_object_unregister(obj, data); } ao_del_job = evas_object_data_get(obj, "_access_obj_del_job"); if (ao_del_job) { ecore_job_del(ao_del_job); evas_object_data_del(obj, "_access_obj_del_job"); } } static void _access_obj_del_job(void *data) { if (!data) return; evas_object_data_del(data, "_access_obj_del_job"); evas_object_event_callback_del(data, EVAS_CALLBACK_DEL, _access_obj_del_cb); evas_object_del(data); } static void _access_hover_del_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED) { Ecore_Job *ao_del_job = NULL; /* data - access object - could be NULL */ if (!data) return; evas_object_event_callback_del_full(obj, EVAS_CALLBACK_RESIZE, _content_resize, data); evas_object_event_callback_del_full(obj, EVAS_CALLBACK_MOVE, _content_move, data); _elm_access_object_unregister(data, obj); /* delete access object in job */ ao_del_job = evas_object_data_get(data, "_access_obj_del_job"); if (ao_del_job) { ecore_job_del(ao_del_job); evas_object_data_del(data, "_access_obj_del_job"); } ao_del_job = ecore_job_add(_access_obj_del_job, data); evas_object_data_set(data, "_access_obj_del_job", ao_del_job); } EAPI void _elm_access_object_register(Evas_Object *obj, Evas_Object *hoverobj) { Elm_Access_Info *ac; evas_object_event_callback_add(hoverobj, EVAS_CALLBACK_MOUSE_IN, _access_hover_mouse_in_cb, obj); evas_object_event_callback_add(hoverobj, EVAS_CALLBACK_MOUSE_OUT, _access_hover_mouse_out_cb, obj); evas_object_event_callback_add(hoverobj, EVAS_CALLBACK_DEL, _access_hover_del_cb, obj); evas_object_event_callback_add(obj, EVAS_CALLBACK_DEL, _access_obj_del_cb, hoverobj); ac = calloc(1, sizeof(Elm_Access_Info)); evas_object_data_set(obj, "_elm_access", ac); ac->hoverobj = hoverobj; } EAPI void _elm_access_object_unregister(Evas_Object *obj, Evas_Object *hoverobj) { Elm_Access_Info *ac; Evas_Object *ao; evas_object_event_callback_del_full(hoverobj, EVAS_CALLBACK_MOUSE_IN, _access_hover_mouse_in_cb, obj); evas_object_event_callback_del_full(hoverobj, EVAS_CALLBACK_MOUSE_OUT, _access_hover_mouse_out_cb, obj); evas_object_event_callback_del_full(hoverobj, EVAS_CALLBACK_DEL, _access_hover_del_cb, obj); /* _access_obj_del_cb and _access_hover_del_cb calls this function, both do not need _part_access_obj data, so delete the data here. */ ao = evas_object_data_get(hoverobj, "_part_access_obj"); if (ao) evas_object_data_del(hoverobj, "_part_access_obj"); ac = evas_object_data_get(obj, "_elm_access"); evas_object_data_del(obj, "_elm_access"); if (ac) { _elm_access_clear(ac); free(ac); } Action_Info *a; a = evas_object_data_get(obj, "_elm_access_action_info"); evas_object_data_del(obj, "_elm_access_action_info"); free(a); } EAPI void _elm_access_widget_item_register(Elm_Widget_Item_Data *item) { Evas_Object *ao, *ho; Evas_Coord x, y, w, h; Elm_Access_Info *ac; ELM_WIDGET_ITEM_CHECK_OR_RETURN(item); /* check previous access object */ if (item->access_obj) _elm_access_widget_item_unregister(item); // create access object ho = item->view; ao = _elm_access_add(item->widget); if (!ao) return; evas_object_event_callback_add(ho, EVAS_CALLBACK_RESIZE, _content_resize, ao); evas_object_event_callback_add(ho, EVAS_CALLBACK_MOVE, _content_move, ao); evas_object_geometry_get(ho, &x, &y, &w, &h); evas_object_geometry_set(ao, x, y, w, h); evas_object_show(ao); // register access object _elm_access_object_register(ao, ho); item->access_obj = ao; /* set owner widget item */ ac = evas_object_data_get(ao, "_elm_access"); ac->widget_item = item; } EAPI void _elm_access_widget_item_unregister(Elm_Widget_Item_Data *item) { Evas_Object *ao; ELM_WIDGET_ITEM_CHECK_OR_RETURN(item); if (!item->access_obj) return; /* delete callbacks and unregister access object in _access_obj_del_cb*/ ao = item->access_obj; item->access_obj = NULL; evas_object_del(ao); } EAPI Eina_Bool _elm_access_2nd_click_timeout(Evas_Object *obj) { Ecore_Timer *t; t = evas_object_data_get(obj, "_elm_2nd_timeout"); if (t) { ecore_timer_del(t); evas_object_data_del(obj, "_elm_2nd_timeout"); evas_object_event_callback_del_full(obj, EVAS_CALLBACK_DEL, _access_2nd_click_del_cb, NULL); return EINA_TRUE; } t = ecore_timer_add(0.3, _access_2nd_click_timeout_cb, obj); evas_object_data_set(obj, "_elm_2nd_timeout", t); evas_object_event_callback_add(obj, EVAS_CALLBACK_DEL, _access_2nd_click_del_cb, NULL); return EINA_FALSE; } static Evas_Object * _elm_access_add(Evas_Object *parent) { EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL); return elm_legacy_add(MY_CLASS, parent); } EOLIAN static Eo * _elm_access_efl_object_constructor(Eo *obj, void *_pd EINA_UNUSED) { obj = efl_constructor(efl_super(obj, MY_CLASS)); efl_canvas_object_type_set(obj, MY_CLASS_NAME_LEGACY); evas_object_smart_callbacks_descriptions_set(obj, _smart_callbacks); return obj; } EAPI Evas_Object * elm_access_object_register(Evas_Object *obj, Evas_Object *parent) { return _access_object_register(obj, parent); } EAPI void elm_access_object_unregister(Evas_Object *obj) { _access_object_unregister(obj); } EAPI Evas_Object * elm_access_object_get(const Evas_Object *obj) { return evas_object_data_get(obj, "_part_access_obj"); } EAPI void elm_access_info_set(Evas_Object *obj, int type, const char *text) { _elm_access_text_set(_elm_access_info_get(obj), type, text); } EAPI char * elm_access_info_get(const Evas_Object *obj, int type) { return _elm_access_text_get(_elm_access_info_get(obj), type, obj); } EAPI void elm_access_info_cb_set(Evas_Object *obj, int type, Elm_Access_Info_Cb func, const void *data) { _elm_access_callback_set(_elm_access_info_get(obj), type, func, data); } EAPI void elm_access_activate_cb_set(Evas_Object *obj, Elm_Access_Activate_Cb func, void *data) { Elm_Access_Info *ac; ac = _elm_access_info_get(obj); if (!ac) return; ac->activate = func; ac->activate_data = data; } EAPI void elm_access_say(const char *text) { if (!text) return; _elm_access_say(text); } EAPI void elm_access_highlight_set(Evas_Object* obj) { _elm_access_highlight_set(obj); } EAPI Eina_Bool elm_access_action(Evas_Object *obj, const Elm_Access_Action_Type type, Elm_Access_Action_Info *action_info) { Evas *evas; Evas_Object *ho; Elm_Access_Action_Info *a = action_info; switch (type) { case ELM_ACCESS_ACTION_READ: case ELM_ACCESS_ACTION_HIGHLIGHT: evas = evas_object_evas_get(obj); if (!evas) return EINA_FALSE; evas_event_feed_mouse_in(evas, 0, NULL); _elm_access_mouse_event_enabled_set(EINA_TRUE); evas_event_feed_mouse_move(evas, a->x, a->y, 0, NULL); _elm_access_mouse_event_enabled_set(EINA_FALSE); ho = _access_highlight_object_get(obj); if (ho) _access_action_callback_call(ho, ELM_ACCESS_ACTION_READ, a); break; case ELM_ACCESS_ACTION_UNHIGHLIGHT: evas = evas_object_evas_get(obj); if (!evas) return EINA_FALSE; _elm_access_object_highlight_disable(evas); break; case ELM_ACCESS_ACTION_HIGHLIGHT_NEXT: if (a->highlight_cycle) _elm_access_highlight_cycle(obj, ELM_FOCUS_NEXT); else return _access_highlight_next_get(obj, ELM_FOCUS_NEXT); break; case ELM_ACCESS_ACTION_HIGHLIGHT_PREV: if (a->highlight_cycle) _elm_access_highlight_cycle(obj, ELM_FOCUS_PREVIOUS); else return _access_highlight_next_get(obj, ELM_FOCUS_PREVIOUS); break; case ELM_ACCESS_ACTION_ACTIVATE: _elm_access_highlight_object_activate(obj, EFL_UI_ACTIVATE_DEFAULT); break; case ELM_ACCESS_ACTION_UP: _elm_access_highlight_object_activate(obj, EFL_UI_ACTIVATE_UP); break; case ELM_ACCESS_ACTION_DOWN: _elm_access_highlight_object_activate(obj, EFL_UI_ACTIVATE_DOWN); break; case ELM_ACCESS_ACTION_SCROLL: //TODO: SCROLL HIGHLIGHT OBJECT break; case ELM_ACCESS_ACTION_BACK: break; default: break; } return EINA_TRUE; } EAPI void elm_access_action_cb_set(Evas_Object *obj, const Elm_Access_Action_Type type, const Elm_Access_Action_Cb cb, const void *data) { Action_Info *a; a = evas_object_data_get(obj, "_elm_access_action_info"); if (!a) { a = calloc(1, sizeof(Action_Info)); evas_object_data_set(obj, "_elm_access_action_info", a); } a->obj = obj; a->fn[type].cb = cb; a->fn[type].user_data = (void *)data; } EAPI void elm_access_external_info_set(Evas_Object *obj, const char *text) { _elm_access_text_set (_elm_access_info_get(obj), ELM_ACCESS_CONTEXT_INFO, text); } EAPI char * elm_access_external_info_get(const Evas_Object *obj) { Elm_Access_Info *ac; ac = _elm_access_info_get(obj); return _elm_access_text_get(ac, ELM_ACCESS_CONTEXT_INFO, obj); } EAPI void elm_access_highlight_next_set(Evas_Object *obj, Elm_Highlight_Direction dir, Evas_Object *next) { EINA_SAFETY_ON_FALSE_RETURN(obj); EINA_SAFETY_ON_FALSE_RETURN(next); Elm_Access_Info *info = _elm_access_info_get(obj); Elm_Access_Info *info_next = _elm_access_info_get(next); if (!info || !info_next) { ERR("There is no access information"); return; } if (dir == ELM_HIGHLIGHT_DIR_NEXT) { info_next->prev = obj; info->next = next; } else if (dir == ELM_HIGHLIGHT_DIR_PREVIOUS) { info_next->next = obj; info->prev = next; } else ERR("Not supported focus direction for access highlight [%d]", dir); } EOLIAN static void _elm_access_class_constructor(Efl_Class *klass) { evas_smart_legacy_type_register(MY_CLASS_NAME_LEGACY, klass); } static Eina_Bool _access_atspi_action_do(Evas_Object *obj, const char *params) { Eina_Bool ret; ret = EINA_FALSE; if (!strcmp(params, "highlight")) ret = _access_action_callback_call(obj, ELM_ACCESS_ACTION_HIGHLIGHT, NULL); else if (!strcmp(params, "unhighlight")) ret = _access_action_callback_call(obj, ELM_ACCESS_ACTION_UNHIGHLIGHT, NULL); else if (!strcmp(params, "highlight,next")) ret = _access_action_callback_call(obj, ELM_ACCESS_ACTION_HIGHLIGHT_NEXT, NULL); else if (!strcmp(params, "highlight,prev")) ret = _access_action_callback_call(obj, ELM_ACCESS_ACTION_HIGHLIGHT_PREV, NULL); else if (!strcmp(params, "activate")) { evas_object_smart_callback_call(obj, SIG_ACTIVATED, NULL); ret = _access_action_callback_call(obj, ELM_ACCESS_ACTION_ACTIVATE, NULL); } else if (!strcmp(params, "value,up")) ret = _access_action_callback_call(obj, ELM_ACCESS_ACTION_UP, NULL); else if (!strcmp(params, "value,down")) ret = _access_action_callback_call(obj, ELM_ACCESS_ACTION_DOWN, NULL); else if (!strcmp(params, "read")) ret = _access_action_callback_call(obj, ELM_ACCESS_ACTION_READ, NULL); return ret; } EOLIAN const Efl_Access_Action_Data * _elm_access_efl_access_widget_action_elm_actions_get(const Eo *obj EINA_UNUSED, void *pd EINA_UNUSED) { static Efl_Access_Action_Data atspi_actions[] = { { "highlight", NULL, "highlight", _access_atspi_action_do}, { "unhighlight", NULL, "unhighlight", _access_atspi_action_do}, { "highlight,next", NULL, "highlight,next", _access_atspi_action_do}, { "highlight,prev", NULL, "highlight,prev", _access_atspi_action_do}, { "activate", NULL, "activate", _access_atspi_action_do}, { "value,up", NULL, "value,up", _access_atspi_action_do}, { "value,down", NULL, "value,down", _access_atspi_action_do}, { "read", NULL, "read", _access_atspi_action_do}, { NULL, NULL, NULL, NULL } }; return &atspi_actions[0]; } EOLIAN static Efl_Access_State_Set _elm_access_efl_access_object_state_set_get(const Eo *obj, void *pd EINA_UNUSED) { Efl_Access_State_Set ret; ret = efl_access_object_state_set_get(efl_super(obj, ELM_ACCESS_CLASS)); Elm_Access_Info *info = _elm_access_info_get(obj); if (info && !evas_object_visible_get(info->part_object)) { STATE_TYPE_UNSET(ret, EFL_ACCESS_STATE_TYPE_VISIBLE); STATE_TYPE_UNSET(ret, EFL_ACCESS_STATE_TYPE_SHOWING); } return ret; } /* Internal EO APIs and hidden overrides */ #define ELM_ACCESS_EXTRA_OPS \ EFL_CANVAS_GROUP_ADD_OPS(elm_access) #include "elm_access_eo.c"