[access] support back gesture
[framework/uifw/elementary.git] / src / lib / elm_access.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3 #include "elm_interface_scrollable.h"
4
5 static const char ACCESS_SMART_NAME[] = "elm_access";
6
7 ELM_INTERNAL_SMART_SUBCLASS_NEW
8   (ACCESS_SMART_NAME, _elm_access, Elm_Widget_Smart_Class,
9   Elm_Widget_Smart_Class, elm_widget_smart_class_get, NULL);
10
11 struct _Func_Data
12 {
13    void                *user_data; /* Holds user data to CB */
14    Elm_Access_Action_Cb cb;
15 };
16
17 typedef struct _Func_Data Func_Data;
18
19 struct _Action_Info
20 {
21    Evas_Object      *obj;
22    Func_Data         fn[ELM_ACCESS_ACTION_LAST + 1]; /* Callback for specific action */
23 };
24
25 typedef struct _Action_Info Action_Info;
26
27 static Eina_Bool mouse_event_enable = EINA_TRUE;
28 static Eina_Bool read_mode = EINA_FALSE;
29 static Evas_Coord_Point offset;
30 static Evas_Object *s_parent; /* scrollable parent */
31 static Elm_Access_Action_Type action_type = ELM_ACCESS_ACTION_FIRST;
32
33 static Evas_Object * _elm_access_add(Evas_Object *parent);
34
35 static void
36 _elm_access_smart_add(Evas_Object *obj)
37 {
38    EVAS_SMART_DATA_ALLOC(obj, Elm_Widget_Smart_Data);
39    ELM_WIDGET_CLASS(_elm_access_parent_sc)->base.add(obj);
40
41    elm_widget_can_focus_set(obj, EINA_TRUE);
42 }
43
44 static Eina_Bool
45 _elm_access_smart_on_focus(Evas_Object *obj)
46 {
47    evas_object_focus_set(obj, elm_widget_focus_get(obj));
48
49    return EINA_TRUE;
50 }
51
52 static Eina_Bool
53 _elm_access_smart_activate(Evas_Object *obj, Elm_Activate act)
54 {
55    int type = ELM_ACCESS_ACTION_FIRST;
56
57    Action_Info *a;
58    a = evas_object_data_get(obj, "_elm_access_action_info");
59
60    switch (act)
61      {
62       case ELM_ACTIVATE_DEFAULT:
63         type = ELM_ACCESS_ACTION_ACTIVATE;
64         break;
65
66       case ELM_ACTIVATE_UP:
67         type = ELM_ACCESS_ACTION_VALUE_CHANGE;
68         break;
69
70       case ELM_ACTIVATE_DOWN:
71         type = ELM_ACCESS_ACTION_VALUE_CHANGE;
72         break;
73
74       case ELM_ACTIVATE_RIGHT:
75         break;
76
77       case ELM_ACTIVATE_LEFT:
78         break;
79
80       case ELM_ACTIVATE_BACK:
81         type = ELM_ACCESS_ACTION_BACK;
82         break;
83
84       default:
85         break;
86      }
87
88    if ((a) && (type > ELM_ACCESS_ACTION_FIRST) &&
89               (type < ELM_ACCESS_ACTION_LAST) &&
90               (a->fn[type].cb))
91      {
92         a->fn[type].cb(a->fn[type].user_data, obj, NULL);
93         return EINA_TRUE;
94      }
95
96    /* TODO: deprecate below? and change above with _access_action_callback_call(); */
97    if (act != ELM_ACTIVATE_DEFAULT) return EINA_FALSE;
98
99    Elm_Access_Info *ac = evas_object_data_get(obj, "_elm_access");
100    if (!ac) return EINA_FALSE;
101
102    if (ac->activate)
103      ac->activate(ac->activate_data, ac->part_object,
104                   (Elm_Object_Item *)ac->widget_item);
105
106    return EINA_TRUE;
107 }
108
109 static void
110 _elm_access_smart_set_user(Elm_Widget_Smart_Class *sc)
111 {
112    sc->base.add = _elm_access_smart_add;
113
114    /* not a 'focus chain manager' */
115    sc->focus_next = NULL;
116    sc->focus_direction = NULL;
117    sc->on_focus = _elm_access_smart_on_focus;
118    sc->activate = _elm_access_smart_activate;
119
120    return;
121 }
122
123 typedef struct _Mod_Api Mod_Api;
124
125 struct _Mod_Api
126 {
127    void (*out_read) (const char *txt);
128    void (*out_read_done) (void);
129    void (*out_cancel) (void);
130    void (*out_done_callback_set) (void (*func) (void *data), const void *data);
131 };
132
133 static int initted = 0;
134 static Mod_Api *mapi = NULL;
135
136 static void
137 _access_init(void)
138 {
139    Elm_Module *m;
140    initted++;
141    if (initted > 1) return;
142    if (!(m = _elm_module_find_as("access/api"))) return;
143    m->api = malloc(sizeof(Mod_Api));
144    if (!m->api) return;
145    m->init_func(m);
146    ((Mod_Api *)(m->api)      )->out_read = // called to read out some text
147       _elm_module_symbol_get(m, "out_read");
148    ((Mod_Api *)(m->api)      )->out_read_done = // called to set a done marker so when it is reached the done callback is called
149       _elm_module_symbol_get(m, "out_read_done");
150    ((Mod_Api *)(m->api)      )->out_cancel = // called to read out some text
151       _elm_module_symbol_get(m, "out_cancel");
152    ((Mod_Api *)(m->api)      )->out_done_callback_set = // called when last read done
153       _elm_module_symbol_get(m, "out_done_callback_set");
154    mapi = m->api;
155 }
156
157 static void
158 _access_shutdown(void)
159 {
160    Elm_Module *m;
161    if (initted == 0) return;
162    if (!(m = _elm_module_find_as("access/api"))) return;
163
164    m->shutdown_func(m);
165
166    initted = 0;
167
168    free(mapi);
169    mapi = NULL;
170 }
171
172 static Elm_Access_Item *
173 _access_add_set(Elm_Access_Info *ac, int type)
174 {
175    Elm_Access_Item *ai;
176    Eina_List *l;
177
178    if (!ac) return NULL;
179    EINA_LIST_FOREACH(ac->items, l, ai)
180      {
181         if (ai->type == type)
182           {
183              if (!ai->func)
184                {
185                   if (ai->data) eina_stringshare_del(ai->data);
186                }
187              ai->func = NULL;
188              ai->data = NULL;
189              return ai;
190           }
191      }
192    ai = calloc(1, sizeof(Elm_Access_Item));
193    ai->type = type;
194    ac->items = eina_list_prepend(ac->items, ai);
195    return ai;
196 }
197
198 static Evas_Object *
199 _access_highlight_object_get(Evas_Object *obj)
200 {
201    Evas_Object *o, *ho;
202
203    o = evas_object_name_find(evas_object_evas_get(obj), "_elm_access_disp");
204    if (!o) return NULL;
205
206    ho = evas_object_data_get(o, "_elm_access_target");
207
208    return ho;
209 }
210
211 static void
212 _access_highlight_read(Elm_Access_Info *ac, Evas_Object *obj)
213 {
214    int type;
215    char *txt = NULL;
216    Eina_Strbuf *strbuf;
217
218    /*FIXME: TIZEN only - read at once because Tizen TTS engine performance is not good */
219    strbuf = eina_strbuf_new();
220
221    if (_elm_config->access_mode != ELM_ACCESS_MODE_OFF)
222      {
223
224         if (ac->on_highlight) ac->on_highlight(ac->on_highlight_data);
225         _elm_access_object_hilight(obj);
226
227         for (type = ELM_ACCESS_INFO_FIRST + 1; type < ELM_ACCESS_INFO_LAST; type++)
228           {
229              txt = _elm_access_text_get(ac, type, obj);
230              if (txt && (strlen(txt) > 0))
231                {
232                   if (eina_strbuf_length_get(strbuf) > 0)
233                     eina_strbuf_append_printf(strbuf, ", %s", txt);
234                   else
235                     eina_strbuf_append(strbuf, txt);
236
237                   free(txt);
238                }
239           }
240      }
241
242    txt = eina_strbuf_string_steal(strbuf);
243    eina_strbuf_free(strbuf);
244
245    _elm_access_say(txt);
246    free(txt);
247 }
248
249 static Eina_Bool
250 _access_obj_over_timeout_cb(void *data)
251 {
252    Elm_Access_Info *ac;
253    Evas_Object *ho;
254
255    if (!data) return EINA_FALSE;
256
257    ac = evas_object_data_get(data, "_elm_access");
258    if (!ac) return EINA_FALSE;
259
260    ho = _access_highlight_object_get(data);
261    if (ho != data) _access_highlight_read(ac, data);
262
263    ac->delay_timer = NULL;
264    return EINA_FALSE;
265 }
266
267 static void
268 _access_obj_mouse_in_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info  __UNUSED__)
269 {
270    Elm_Access_Info *ac;
271    if (!mouse_event_enable) return;
272
273    ac = evas_object_data_get(data, "_elm_access");
274    if (!ac) return;
275
276    if (ac->delay_timer)
277      {
278         ecore_timer_del(ac->delay_timer);
279         ac->delay_timer = NULL;
280      }
281    if (_elm_config->access_mode != ELM_ACCESS_MODE_OFF)
282       ac->delay_timer = ecore_timer_add(0.2, _access_obj_over_timeout_cb, data);
283 }
284
285 static void
286 _access_obj_mouse_out_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
287 {
288    Elm_Access_Info *ac;
289    if (!mouse_event_enable) return;
290
291    ac = evas_object_data_get(data, "_elm_access");
292    if (!ac) return;
293
294    _elm_access_object_unhilight(data);
295    if (ac->delay_timer)
296      {
297         ecore_timer_del(ac->delay_timer);
298         ac->delay_timer = NULL;
299      }
300 }
301
302 static void
303 _access_obj_del_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
304 {
305    _elm_access_object_unregister(data, obj);
306
307    // _elm_access_edje_object_part_object_register(); set below object data
308    evas_object_data_del(obj, "_part_access_obj");
309 }
310
311 static void
312 _access_read_done(void *data __UNUSED__)
313 {
314    printf("read done\n");
315    // FIXME: produce event here
316 }
317
318 static void
319 _access_2nd_click_del_cb(void *data __UNUSED__, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
320 {
321    Ecore_Timer *t;
322
323    evas_object_event_callback_del_full(obj, EVAS_CALLBACK_DEL,
324                                        _access_2nd_click_del_cb, NULL);
325    t = evas_object_data_get(obj, "_elm_2nd_timeout");
326    if (t)
327      {
328         ecore_timer_del(t);
329         evas_object_data_del(obj, "_elm_2nd_timeout");
330      }
331 }
332
333 static Eina_Bool
334 _access_2nd_click_timeout_cb(void *data)
335 {
336    evas_object_event_callback_del_full(data, EVAS_CALLBACK_DEL,
337                                        _access_2nd_click_del_cb, NULL);
338    evas_object_data_del(data, "_elm_2nd_timeout");
339    return EINA_FALSE;
340 }
341
342 static void
343 _access_obj_hilight_del_cb(void *data __UNUSED__, Evas *e, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
344 {
345    _elm_access_object_hilight_disable(e);
346 }
347
348 static void
349 _access_obj_hilight_hide_cb(void *data __UNUSED__, Evas *e, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
350 {
351    _elm_access_object_hilight_disable(e);
352 }
353
354 static void
355 _access_obj_hilight_move_cb(void *data __UNUSED__, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
356 {
357    Evas_Coord x, y;
358    Evas_Object *o;
359
360    o = evas_object_name_find(evas_object_evas_get(obj), "_elm_access_disp");
361    if (!o) return;
362    evas_object_geometry_get(obj, &x, &y, NULL, NULL);
363    evas_object_move(o, x, y);
364 }
365
366 static void
367 _access_obj_hilight_resize_cb(void *data __UNUSED__, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
368 {
369    Evas_Coord w, h;
370    Evas_Object *o;
371
372    o = evas_object_name_find(evas_object_evas_get(obj), "_elm_access_disp");
373    if (!o) return;
374    evas_object_geometry_get(obj, NULL, NULL, &w, &h);
375    evas_object_resize(o, w, h);
376 }
377
378 void
379 _elm_access_mouse_event_enabled_set(Eina_Bool enabled)
380 {
381    enabled = !!enabled;
382    if (mouse_event_enable == enabled) return;
383    mouse_event_enable = enabled;
384 }
385
386 void
387 _elm_access_read_mode_set(Eina_Bool enabled)
388 {
389    enabled = !!enabled;
390    if (read_mode == enabled) return;
391    read_mode = enabled;
392 }
393
394 Eina_Bool
395 _elm_access_read_mode_get()
396 {
397    return read_mode;
398 }
399
400 void
401 _elm_access_shutdown()
402 {
403    _access_shutdown();
404 }
405
406 static void
407 _access_order_del_cb(void *data,
408                      Evas *e __UNUSED__,
409                      Evas_Object *obj,
410                      void *event_info __UNUSED__)
411 {
412    Elm_Widget_Item *item = data;
413
414    item->access_order = eina_list_remove(item->access_order, obj);
415 }
416
417 void
418 _elm_access_widget_item_access_order_set(Elm_Widget_Item *item,
419                                               Eina_List *objs)
420 {
421    Eina_List *l;
422    Evas_Object *o;
423
424    if (!item) return;
425
426    _elm_access_widget_item_access_order_unset(item);
427
428    EINA_LIST_FOREACH(objs, l, o)
429      {
430         evas_object_event_callback_add(o, EVAS_CALLBACK_DEL,
431                                        _access_order_del_cb, item);
432      }
433
434    item->access_order = objs;
435 }
436
437 const Eina_List *
438 _elm_access_widget_item_access_order_get(const Elm_Widget_Item *item)
439 {
440    if (!item) return NULL;
441    return item->access_order;
442 }
443
444 void
445 _elm_access_widget_item_access_order_unset(Elm_Widget_Item *item)
446 {
447    Eina_List *l, *l_next;
448    Evas_Object *o;
449
450    if (!item) return;
451
452    EINA_LIST_FOREACH_SAFE(item->access_order, l, l_next, o)
453      {
454         evas_object_event_callback_del_full
455           (o, EVAS_CALLBACK_DEL, _access_order_del_cb, item);
456         item->access_order = eina_list_remove_list(item->access_order, l);
457      }
458 }
459
460 void
461 _elm_access_highlight_object_scroll(Evas_Object *obj, int type, int x, int y)
462 {
463    Evas *evas;
464    Evas_Object *ho;
465    Evas_Coord_Rectangle ho_area;
466
467    if (!obj) return;
468
469    evas = evas_object_evas_get(obj);
470    if (!evas) return;
471
472    switch (type)
473      {
474       case 0:
475         ho = _access_highlight_object_get(obj);
476         if (!ho)
477           {
478              s_parent = NULL;
479              return;
480           }
481         else
482           {
483              /* find scrollable parent */
484              s_parent = elm_widget_parent_get(ho);
485              while (s_parent)
486                {
487                   if(!!evas_object_smart_interface_get(s_parent, ELM_SCROLLABLE_IFACE_NAME))
488                     break;
489                   s_parent = elm_widget_parent_get(s_parent);
490                }
491
492               if (!s_parent) return;
493
494               ELM_SCROLLABLE_IFACE_GET(s_parent, s_iface);
495               s_iface->repeat_events_set(s_parent, EINA_FALSE);
496
497               evas_object_geometry_get
498                 (ho, &ho_area.x, &ho_area.y, &ho_area.w, &ho_area.h);
499
500               offset.x = x - (ho_area.x + (ho_area.w / 2));
501               offset.y = y - (ho_area.y + (ho_area.h / 2));
502           }
503
504         evas_event_feed_mouse_in(evas, 0, NULL);
505         evas_event_feed_mouse_move(evas, x - offset.x, y - offset.y, 0, NULL);
506         evas_event_feed_mouse_down(evas, 1, EVAS_BUTTON_NONE, 0, NULL);
507         break;
508
509       case 1:
510         if (!s_parent) return;
511         evas_event_feed_mouse_move(evas, x - offset.x, y - offset.y, 0, NULL);
512         break;
513
514       case 2:
515         if (!s_parent) return;
516         evas_event_feed_mouse_up(evas, 1, EVAS_BUTTON_NONE, 0, NULL);
517
518         ELM_SCROLLABLE_IFACE_GET(s_parent, s_iface);
519         s_iface->repeat_events_set(s_parent, EINA_TRUE);
520         break;
521
522       default:
523         break;
524      }
525 }
526
527 static Eina_Bool
528 _access_action_callback_call(Evas_Object *obj,
529                              Elm_Access_Action_Type type,
530                              void *data)
531 {
532    Action_Info *a;
533    Eina_Bool ret;
534
535    ret = EINA_FALSE;
536    a = evas_object_data_get(obj, "_elm_access_action_info");
537
538    if (a && (a->fn[type].cb))
539      ret = a->fn[type].cb(a->fn[type].user_data, obj, data);
540
541    return ret;
542 }
543
544 static Eina_Bool
545 _access_highlight_next_get(Evas_Object *obj, Elm_Focus_Direction dir)
546 {
547    int type;
548    Evas_Object *ho, *parent, *target;
549    Eina_Bool ret;
550
551    target = NULL;
552    ret = EINA_FALSE;
553
554    if (!elm_widget_is(obj)) return ret;
555
556    ho = _access_highlight_object_get(obj);
557    if (!ho) return ret;
558
559    parent = ho;
560
561    /* find highlight root */
562    do
563      {
564         ELM_WIDGET_DATA_GET(parent, sd);
565         if (sd->highlight_root)
566           {
567              /* change highlight root */
568              obj = parent;
569              break;
570           }
571         parent = elm_widget_parent_get(parent);
572      }
573    while (parent);
574
575    _elm_access_read_mode_set(EINA_TRUE);
576
577    ret = elm_widget_focus_next_get(obj, dir, &target);
578    if (ret && target)
579      {
580         if (dir == ELM_FOCUS_NEXT)
581           type = ELM_ACCESS_ACTION_HIGHLIGHT_NEXT;
582         else
583           type = ELM_ACCESS_ACTION_HIGHLIGHT_PREV;
584
585         if (!_access_action_callback_call(ho, type, NULL))
586           {
587              /* change highlight object */
588              action_type = type;
589
590              _elm_access_highlight_set(target);
591              ret = EINA_FALSE;
592
593              action_type = ELM_ACCESS_ACTION_FIRST;
594           }
595      }
596
597    _elm_access_read_mode_set(EINA_FALSE);
598
599    return ret;
600 }
601
602 //-------------------------------------------------------------------------//
603 EAPI void
604 _elm_access_highlight_set(Evas_Object* obj)
605 {
606    Evas *evas;
607    Elm_Access_Info *ac;
608    Evas_Object *ho;
609    Evas_Coord_Point ho_point = { 0, 0 };
610
611    if (!obj) return;
612
613    ho = _access_highlight_object_get(obj);
614    if (ho == obj) return;
615
616    ac = evas_object_data_get(obj, "_elm_access");
617    if (!ac) return;
618
619    _access_highlight_read(ac, obj);
620
621    /* move mouse position to inside of highlight object. if an object has a
622       highlight by highlight_cycle();, the mouse still positions at previous
623       position which would be made by MOUSE_IN event. */
624    evas = evas_object_evas_get(obj);
625    if (!evas) return;
626
627    evas_object_geometry_get(obj, &ho_point.x, &ho_point.y, 0, 0);
628    evas_event_feed_mouse_move(evas, ho_point.x, ho_point.y, 0, NULL);
629 }
630
631 EAPI void
632 _elm_access_clear(Elm_Access_Info *ac)
633 {
634    Elm_Access_Item *ai;
635
636    if (!ac) return;
637    if (ac->delay_timer)
638      {
639         ecore_timer_del(ac->delay_timer);
640         ac->delay_timer = NULL;
641      }
642    EINA_LIST_FREE(ac->items, ai)
643      {
644         if (!ai->func)
645           {
646              if (ai->data) eina_stringshare_del(ai->data);
647           }
648         free(ai);
649      }
650 }
651
652 EAPI void
653 _elm_access_text_set(Elm_Access_Info *ac, int type, const char *text)
654 {
655    Elm_Access_Item *ai = _access_add_set(ac, type);
656    if (!ai) return;
657    ai->func = NULL;
658    ai->data = eina_stringshare_add(text);
659 }
660
661 EAPI void
662 _elm_access_callback_set(Elm_Access_Info *ac, int type, Elm_Access_Info_Cb func, const void *data)
663 {
664    Elm_Access_Item *ai = _access_add_set(ac, type);
665    if (!ai) return;
666    ai->func = func;
667    ai->data = data;
668 }
669
670 EAPI void
671 _elm_access_on_highlight_hook_set(Elm_Access_Info           *ac,
672                                   Elm_Access_On_Highlight_Cb func,
673                                   void                      *data)
674 {
675     if (!ac) return;
676     ac->on_highlight = func;
677     ac->on_highlight_data = data;
678 }
679
680 EAPI void
681 _elm_access_activate_callback_set(Elm_Access_Info           *ac,
682                                   Elm_Access_Activate_Cb     func,
683                                   void                      *data)
684 {
685    if (!ac) return;
686    ac->activate = func;
687    ac->activate_data = data;
688 }
689
690 EAPI void
691 _elm_access_highlight_object_activate(Evas_Object *obj, Elm_Activate act)
692 {
693    Evas_Object *ho;
694
695    ho = _access_highlight_object_get(obj);
696    if (!ho) return;
697
698    switch (act)
699      {
700       case ELM_ACTIVATE_DEFAULT:
701       case ELM_ACTIVATE_UP:
702       case ELM_ACTIVATE_DOWN:
703         _elm_access_read_mode_set(EINA_FALSE);
704
705         if (!elm_object_focus_get(ho))
706         elm_object_focus_set(ho, EINA_TRUE);
707
708         elm_widget_activate(ho, act);
709         break;
710
711       case ELM_ACTIVATE_BACK:
712         elm_widget_activate(ho, act);
713         break;
714
715       default:
716         break;
717      }
718
719    return;
720 }
721
722 EAPI void
723 _elm_access_highlight_cycle(Evas_Object *obj, Elm_Focus_Direction dir)
724 {
725    int type;
726    Evas_Object *ho, *parent;
727
728    ho = _access_highlight_object_get(obj);
729    if (!ho) return;
730
731    parent = ho;
732
733    /* find highlight root */
734    do
735      {
736         ELM_WIDGET_DATA_GET(parent, sd);
737         if (sd->highlight_root)
738           {
739              /* change highlight root */
740              obj = parent;
741              break;
742           }
743         parent = elm_widget_parent_get(parent);
744      }
745    while (parent);
746
747    _elm_access_read_mode_set(EINA_TRUE);
748
749    if (dir == ELM_FOCUS_NEXT)
750      type = ELM_ACCESS_ACTION_HIGHLIGHT_NEXT;
751    else
752      type = ELM_ACCESS_ACTION_HIGHLIGHT_PREV;
753
754    action_type = type;
755
756    if (!_access_action_callback_call(ho, type, NULL))
757      elm_widget_focus_cycle(obj, dir);
758
759    action_type = ELM_ACCESS_ACTION_FIRST;
760
761    _elm_access_read_mode_set(EINA_FALSE);
762 }
763
764 EAPI char *
765 _elm_access_text_get(const Elm_Access_Info *ac, int type, const Evas_Object *obj)
766 {
767    Elm_Access_Item *ai;
768    Eina_List *l;
769
770    if (!ac) return NULL;
771    EINA_LIST_FOREACH(ac->items, l, ai)
772      {
773         if (ai->type == type)
774           {
775              if (ai->func) return ai->func((void *)(ai->data), (Evas_Object *)obj);
776              else if (ai->data) return strdup(ai->data);
777              return NULL;
778           }
779      }
780    return NULL;
781 }
782
783 EAPI void
784 _elm_access_read(Elm_Access_Info *ac, int type, const Evas_Object *obj)
785 {
786    char *txt = _elm_access_text_get(ac, type, obj);
787
788    if (txt && strlen(txt) == 0) return; /* Tizen only: TTS engine does not work properly */
789
790    _access_init();
791    if (mapi)
792      {
793         if (mapi->out_done_callback_set)
794            mapi->out_done_callback_set(_access_read_done, NULL);
795         if (type == ELM_ACCESS_DONE)
796           {
797              if (mapi->out_read_done) mapi->out_read_done();
798           }
799         else if (type == ELM_ACCESS_CANCEL)
800           {
801              if (mapi->out_cancel) mapi->out_cancel();
802           }
803         else
804           {
805              if (txt && mapi->out_read) mapi->out_read(txt);
806           }
807      }
808    if (txt) free(txt);
809 }
810
811 EAPI void
812 _elm_access_say(const char *txt)
813 {
814    if (!_elm_config->access_mode) return;
815
816    if (txt && strlen(txt) == 0) return; /* Tizen only: TTS engine does not work properly */
817
818    _access_init();
819    if (mapi)
820      {
821         if (mapi->out_done_callback_set)
822            mapi->out_done_callback_set(_access_read_done, NULL);
823         if (mapi->out_cancel) mapi->out_cancel();
824         if (txt)
825           {
826              if (mapi->out_read) mapi->out_read(txt);
827              //if (mapi->out_read) mapi->out_read(".\n"); /* TIZEN only: Tizen TTS engine performance is not good */
828           }
829         if (mapi->out_read_done) mapi->out_read_done();
830      }
831 }
832
833 EAPI Elm_Access_Info *
834 _elm_access_object_get(const Evas_Object *obj)
835 {
836    return evas_object_data_get(obj, "_elm_access");
837 }
838
839 EAPI void
840 _elm_access_object_hilight(Evas_Object *obj)
841 {
842    Evas_Object *o;
843    Evas_Coord x, y, w, h;
844    Elm_Access_Action_Info *a;
845
846    o = evas_object_name_find(evas_object_evas_get(obj), "_elm_access_disp");
847    if (!o)
848      {
849         o = edje_object_add(evas_object_evas_get(obj));
850         evas_object_name_set(o, "_elm_access_disp");
851         evas_object_layer_set(o, ELM_OBJECT_LAYER_TOOLTIP);
852      }
853    else
854      {
855         Evas_Object *ptarget = evas_object_data_get(o, "_elm_access_target");
856         if (ptarget)
857           {
858              evas_object_data_del(o, "_elm_access_target");
859              elm_widget_parent_highlight_set(ptarget, EINA_FALSE);
860
861              evas_object_event_callback_del_full(ptarget, EVAS_CALLBACK_DEL,
862                                                  _access_obj_hilight_del_cb, NULL);
863              evas_object_event_callback_del_full(ptarget, EVAS_CALLBACK_HIDE,
864                                                  _access_obj_hilight_hide_cb, NULL);
865              evas_object_event_callback_del_full(ptarget, EVAS_CALLBACK_MOVE,
866                                                  _access_obj_hilight_move_cb, NULL);
867              evas_object_event_callback_del_full(ptarget, EVAS_CALLBACK_RESIZE,
868                                                  _access_obj_hilight_resize_cb, NULL);
869              _access_action_callback_call(ptarget, ELM_ACCESS_ACTION_UNHIGHLIGHT, NULL);
870           }
871      }
872    evas_object_data_set(o, "_elm_access_target", obj);
873    elm_widget_parent_highlight_set(obj, EINA_TRUE);
874
875    elm_widget_theme_object_set(obj, o, "access", "base", "default");
876
877    evas_object_event_callback_add(obj, EVAS_CALLBACK_DEL,
878                                   _access_obj_hilight_del_cb, NULL);
879    evas_object_event_callback_add(obj, EVAS_CALLBACK_HIDE,
880                                   _access_obj_hilight_hide_cb, NULL);
881    evas_object_event_callback_add(obj, EVAS_CALLBACK_MOVE,
882                                   _access_obj_hilight_move_cb, NULL);
883    evas_object_event_callback_add(obj, EVAS_CALLBACK_RESIZE,
884                                   _access_obj_hilight_resize_cb, NULL);
885    evas_object_raise(o);
886    evas_object_geometry_get(obj, &x, &y, &w, &h);
887    evas_object_move(o, x, y);
888    evas_object_resize(o, w, h);
889
890    /* use callback, should an access object do below every time when
891         * a window gets a client message ECORE_X_ATOM_E_ILLMUE_ACTION_READ? */
892    a = calloc(1, sizeof(Elm_Access_Action_Info));
893    a->action_type = action_type;
894    if (!_access_action_callback_call(obj, ELM_ACCESS_ACTION_HIGHLIGHT, a))
895      evas_object_show(o);
896    else
897      evas_object_hide(o);
898    free(a);
899 }
900
901 EAPI void
902 _elm_access_object_unhilight(Evas_Object *obj)
903 {
904    Evas_Object *o, *ptarget;
905
906    o = evas_object_name_find(evas_object_evas_get(obj), "_elm_access_disp");
907    if (!o) return;
908    ptarget = evas_object_data_get(o, "_elm_access_target");
909    if (ptarget == obj)
910      {
911         evas_object_event_callback_del_full(ptarget, EVAS_CALLBACK_DEL,
912                                             _access_obj_hilight_del_cb, NULL);
913         evas_object_event_callback_del_full(ptarget, EVAS_CALLBACK_HIDE,
914                                             _access_obj_hilight_hide_cb, NULL);
915         evas_object_event_callback_del_full(ptarget, EVAS_CALLBACK_MOVE,
916                                             _access_obj_hilight_move_cb, NULL);
917         evas_object_event_callback_del_full(ptarget, EVAS_CALLBACK_RESIZE,
918                                             _access_obj_hilight_resize_cb, NULL);
919         evas_object_del(o);
920         elm_widget_parent_highlight_set(ptarget, EINA_FALSE);
921         _access_action_callback_call(ptarget, ELM_ACCESS_ACTION_UNHIGHLIGHT, NULL);
922      }
923 }
924
925 static void
926 _content_resize(void *data, Evas *e __UNUSED__, Evas_Object *obj,
927                 void *event_info __UNUSED__)
928 {
929    Evas_Object *accessobj;
930    Evas_Coord w, h;
931
932    accessobj = data;
933    if (!accessobj) return;
934
935    evas_object_geometry_get(obj, NULL, NULL, &w, &h);
936    evas_object_resize(accessobj, w, h);
937 }
938
939 static void
940 _content_move(void *data, Evas *e __UNUSED__, Evas_Object *obj,
941               void *event_info __UNUSED__)
942 {
943    Evas_Object *accessobj;
944    Evas_Coord x, y;
945
946    accessobj = data;
947    if (!accessobj) return;
948
949    evas_object_geometry_get(obj, &x, &y, NULL, NULL);
950    evas_object_move(accessobj, x, y);
951 }
952
953 static Evas_Object *
954 _access_object_register(Evas_Object *obj, Evas_Object *parent)
955 {
956    Evas_Object *ao;
957    Elm_Access_Info *ac;
958    Evas_Coord x, y, w, h;
959
960    if (!obj) return NULL;
961
962    /* create access object */
963    ao = _elm_access_add(parent);
964    if (!ao) return NULL;
965
966    evas_object_event_callback_add(obj, EVAS_CALLBACK_RESIZE,
967                                   _content_resize, ao);
968    evas_object_event_callback_add(obj, EVAS_CALLBACK_MOVE,
969                                   _content_move, ao);
970
971    evas_object_geometry_get(obj, &x, &y, &w, &h);
972    evas_object_move(ao, x, y);
973    evas_object_resize(ao, w, h);
974    evas_object_show(ao);
975
976    /* register access object */
977    _elm_access_object_register(ao, obj);
978
979    /* set access object */
980    evas_object_data_set(obj, "_part_access_obj", ao);
981
982    /* set owner part object */
983    ac = evas_object_data_get(ao, "_elm_access");
984    ac->part_object = obj;
985
986    return ao;
987 }
988
989 static void
990 _access_object_unregister(Evas_Object *obj)
991 {
992    Elm_Access_Info *ac;
993    Evas_Object *ao;
994
995    if (!obj) return;
996
997    ao = evas_object_data_get(obj, "_part_access_obj");
998
999    if (ao)
1000      {
1001         evas_object_data_del(obj, "_part_access_obj");
1002
1003         /* delete callbacks */
1004         evas_object_event_callback_del_full(obj, EVAS_CALLBACK_RESIZE,
1005                                             _content_resize, ao);
1006         evas_object_event_callback_del_full(obj, EVAS_CALLBACK_MOVE,
1007                                             _content_move, ao);
1008
1009         /* unregister access object */
1010         _elm_access_object_unregister(ao, obj);
1011
1012         evas_object_del(ao);
1013      }
1014    else
1015      {
1016         /* button, check, label etc. */
1017         ac = evas_object_data_get(obj, "_elm_access");
1018         if (ac && ac->hoverobj)
1019           _elm_access_object_unregister(obj, ac->hoverobj);
1020      }
1021 }
1022
1023 EAPI Evas_Object *
1024 _elm_access_edje_object_part_object_register(Evas_Object* obj,
1025                                              const Evas_Object *eobj,
1026                                              const char* part)
1027 {
1028    Evas_Object *ao, *po;
1029
1030    po = (Evas_Object *)edje_object_part_object_get(eobj, part);
1031    if (!obj || !po) return NULL;
1032
1033    /* check previous access object */
1034    ao = evas_object_data_get(po, "_part_access_obj");
1035    if (ao)
1036      _elm_access_edje_object_part_object_unregister(obj, eobj, part);
1037
1038    ao = _access_object_register(po, obj);
1039
1040    return ao;
1041 }
1042
1043 //FIXME: unused obj should be removed from here and each widget.
1044 EAPI void
1045 _elm_access_edje_object_part_object_unregister(Evas_Object* obj __UNUSED__,
1046                                                const Evas_Object *eobj,
1047                                                const char* part)
1048 {
1049    Evas_Object *po;
1050
1051    po = (Evas_Object *)edje_object_part_object_get(eobj, part);
1052    if (!po) return;
1053
1054    _access_object_unregister(po);
1055 }
1056
1057 EAPI void
1058 _elm_access_object_hilight_disable(Evas *e)
1059 {
1060    Evas_Object *o, *ptarget;
1061
1062    o = evas_object_name_find(e, "_elm_access_disp");
1063    if (!o) return;
1064    ptarget = evas_object_data_get(o, "_elm_access_target");
1065    if (ptarget)
1066      {
1067         evas_object_event_callback_del_full(ptarget, EVAS_CALLBACK_DEL,
1068                                             _access_obj_hilight_del_cb, NULL);
1069         evas_object_event_callback_del_full(ptarget, EVAS_CALLBACK_HIDE,
1070                                             _access_obj_hilight_hide_cb, NULL);
1071         evas_object_event_callback_del_full(ptarget, EVAS_CALLBACK_MOVE,
1072                                             _access_obj_hilight_move_cb, NULL);
1073         evas_object_event_callback_del_full(ptarget, EVAS_CALLBACK_RESIZE,
1074                                             _access_obj_hilight_resize_cb, NULL);
1075      }
1076    evas_object_del(o);
1077    elm_widget_parent_highlight_set(ptarget, EINA_FALSE);
1078 }
1079
1080 EAPI void
1081 _elm_access_object_register(Evas_Object *obj, Evas_Object *hoverobj)
1082 {
1083    Elm_Access_Info *ac;
1084
1085    evas_object_event_callback_add(hoverobj, EVAS_CALLBACK_MOUSE_IN,
1086                                   _access_obj_mouse_in_cb, obj);
1087    evas_object_event_callback_add(hoverobj, EVAS_CALLBACK_MOUSE_OUT,
1088                                   _access_obj_mouse_out_cb, obj);
1089    evas_object_event_callback_add(hoverobj, EVAS_CALLBACK_DEL,
1090                                   _access_obj_del_cb, obj);
1091    ac = calloc(1, sizeof(Elm_Access_Info));
1092    evas_object_data_set(obj, "_elm_access", ac);
1093
1094    ac->hoverobj = hoverobj;
1095 }
1096
1097 EAPI void
1098 _elm_access_object_unregister(Evas_Object *obj, Evas_Object *hoverobj)
1099 {
1100    Elm_Access_Info *ac;
1101
1102    evas_object_event_callback_del_full(hoverobj, EVAS_CALLBACK_MOUSE_IN,
1103                                        _access_obj_mouse_in_cb, obj);
1104    evas_object_event_callback_del_full(hoverobj, EVAS_CALLBACK_MOUSE_OUT,
1105                                        _access_obj_mouse_out_cb, obj);
1106    evas_object_event_callback_del_full(hoverobj, EVAS_CALLBACK_DEL,
1107                                        _access_obj_del_cb, obj);
1108
1109    ac = evas_object_data_get(obj, "_elm_access");
1110    evas_object_data_del(obj, "_elm_access");
1111    if (ac)
1112      {
1113         _elm_access_clear(ac);
1114         free(ac);
1115      }
1116
1117    Action_Info *a;
1118    a = evas_object_data_get(obj, "_elm_access_action_info");
1119    if (a) free(a);
1120 }
1121
1122 EAPI void
1123 _elm_access_widget_item_register(Elm_Widget_Item *item)
1124 {
1125    Evas_Object *ao, *ho;
1126    Evas_Coord x, y, w, h;
1127    Elm_Access_Info *ac;
1128
1129    ELM_WIDGET_ITEM_CHECK_OR_RETURN(item);
1130
1131    /* check previous access object */
1132    if (item->access_obj)
1133      _elm_access_widget_item_unregister(item);
1134
1135    // create access object
1136    ho = item->view;
1137    ao = _elm_access_add(item->widget);
1138    if (!ao) return;
1139
1140    evas_object_event_callback_add(ho, EVAS_CALLBACK_RESIZE,
1141                                   _content_resize, ao);
1142    evas_object_event_callback_add(ho, EVAS_CALLBACK_MOVE,
1143                                   _content_move, ao);
1144
1145    evas_object_geometry_get(ho, &x, &y, &w, &h);
1146    evas_object_move(ao, x, y);
1147    evas_object_resize(ao, w, h);
1148    evas_object_show(ao);
1149
1150    // register access object
1151    _elm_access_object_register(ao, ho);
1152
1153    item->access_obj = ao;
1154
1155    /* set owner widget item */
1156    ac = evas_object_data_get(ao, "_elm_access");
1157    ac->widget_item = item;
1158 }
1159
1160 EAPI void
1161 _elm_access_widget_item_unregister(Elm_Widget_Item *item)
1162 {
1163    Evas_Object *ho;
1164
1165    ELM_WIDGET_ITEM_CHECK_OR_RETURN(item);
1166
1167    if (!item->access_obj) return;
1168
1169    ho = item->view;
1170    evas_object_event_callback_del_full(ho, EVAS_CALLBACK_RESIZE,
1171                                   _content_resize, item->access_obj);
1172    evas_object_event_callback_del_full(ho, EVAS_CALLBACK_MOVE,
1173                                   _content_move, item->access_obj);
1174
1175    _elm_access_object_unregister(item->access_obj, ho);
1176
1177    evas_object_del(item->access_obj);
1178    item->access_obj = NULL;
1179 }
1180
1181 EAPI Eina_Bool
1182 _elm_access_2nd_click_timeout(Evas_Object *obj)
1183 {
1184    Ecore_Timer *t;
1185
1186    t = evas_object_data_get(obj, "_elm_2nd_timeout");
1187    if (t)
1188      {
1189         ecore_timer_del(t);
1190         evas_object_data_del(obj, "_elm_2nd_timeout");
1191         evas_object_event_callback_del_full(obj, EVAS_CALLBACK_DEL,
1192                                             _access_2nd_click_del_cb, NULL);
1193         return EINA_TRUE;
1194      }
1195    t = ecore_timer_add(0.3, _access_2nd_click_timeout_cb, obj);
1196    evas_object_data_set(obj, "_elm_2nd_timeout", t);
1197    evas_object_event_callback_add(obj, EVAS_CALLBACK_DEL,
1198                                   _access_2nd_click_del_cb, NULL);
1199    return EINA_FALSE;
1200 }
1201
1202 static Evas_Object *
1203 _elm_access_add(Evas_Object *parent)
1204 {
1205    Evas_Object *obj;
1206
1207    EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL);
1208
1209    obj = elm_widget_add(_elm_access_smart_class_new(), parent);
1210    if (!obj) return NULL;
1211
1212    if (!elm_widget_sub_object_add(parent, obj))
1213      ERR("could not add %p as sub object of %p", obj, parent);
1214
1215    return obj;
1216 }
1217
1218 EAPI Evas_Object *
1219 elm_access_object_register(Evas_Object *obj, Evas_Object *parent)
1220 {
1221    return _access_object_register(obj, parent);
1222 }
1223
1224 EAPI void
1225 elm_access_object_unregister(Evas_Object *obj)
1226 {
1227    _access_object_unregister(obj);
1228 }
1229
1230 EAPI Evas_Object *
1231 elm_access_object_get(const Evas_Object *obj)
1232 {
1233    return evas_object_data_get(obj, "_part_access_obj");
1234 }
1235
1236 EAPI void
1237 elm_access_info_set(Evas_Object *obj, int type, const char *text)
1238 {
1239    _elm_access_text_set(_elm_access_object_get(obj), type, text);
1240 }
1241
1242 EAPI char *
1243 elm_access_info_get(const Evas_Object *obj, int type)
1244 {
1245    return _elm_access_text_get(_elm_access_object_get(obj), type, obj);
1246 }
1247
1248 EAPI void
1249 elm_access_info_cb_set(Evas_Object *obj, int type,
1250                           Elm_Access_Info_Cb func, const void *data)
1251 {
1252    _elm_access_callback_set(_elm_access_object_get(obj), type, func, data);
1253 }
1254
1255 EAPI void
1256 elm_access_activate_cb_set(Evas_Object *obj,
1257                            Elm_Access_Activate_Cb  func, void *data)
1258 {
1259    Elm_Access_Info *ac;
1260
1261    ac = _elm_access_object_get(obj);
1262    if (!ac) return;
1263
1264    ac->activate = func;
1265    ac->activate_data = data;
1266 }
1267
1268 EAPI void
1269 elm_access_say(const char *text)
1270 {
1271    if (!text) return;
1272
1273    _elm_access_say(text);
1274 }
1275
1276 EAPI void
1277 elm_access_highlight_set(Evas_Object* obj)
1278 {
1279    _elm_access_highlight_set(obj);
1280 }
1281
1282 EAPI Eina_Bool
1283 elm_access_action(Evas_Object *obj, const Elm_Access_Action_Type type, void *action_info)
1284 {
1285    Evas *evas;
1286    Evas_Object *ho;
1287    Elm_Access_Action_Info *a;
1288
1289    a = (Elm_Access_Action_Info *) action_info;
1290
1291    switch (type)
1292      {
1293       case ELM_ACCESS_ACTION_READ:
1294       case ELM_ACCESS_ACTION_HIGHLIGHT:
1295         evas = evas_object_evas_get(obj);
1296         if (!evas) return EINA_FALSE;
1297
1298         _elm_access_mouse_event_enabled_set(EINA_TRUE);
1299
1300         evas_event_feed_mouse_in(evas, 0, NULL);
1301         evas_event_feed_mouse_move(evas, a->x, a->y, 0, NULL);
1302         _elm_access_mouse_event_enabled_set(EINA_FALSE);
1303
1304         ho = _access_highlight_object_get(obj);
1305         if (ho)
1306           _access_action_callback_call(ho, ELM_ACCESS_ACTION_READ, a);
1307         break;
1308
1309       case ELM_ACCESS_ACTION_UNHIGHLIGHT:
1310         break;
1311
1312       case ELM_ACCESS_ACTION_HIGHLIGHT_NEXT:
1313         if (a->highlight_cycle)
1314           _elm_access_highlight_cycle(obj, ELM_FOCUS_NEXT);
1315         else
1316           return _access_highlight_next_get(obj, ELM_FOCUS_NEXT);
1317         break;
1318
1319       case ELM_ACCESS_ACTION_HIGHLIGHT_PREV:
1320         if (a->highlight_cycle)
1321           _elm_access_highlight_cycle(obj, ELM_FOCUS_PREVIOUS);
1322         else
1323           return _access_highlight_next_get(obj, ELM_FOCUS_PREVIOUS);
1324         break;
1325
1326       case ELM_ACCESS_ACTION_ACTIVATE:
1327         _elm_access_highlight_object_activate(obj, ELM_ACTIVATE_DEFAULT);
1328         break;
1329
1330       case ELM_ACCESS_ACTION_VALUE_CHANGE:
1331         _elm_access_highlight_object_activate(obj, ELM_ACTIVATE_UP);
1332         break;
1333
1334       case ELM_ACCESS_ACTION_SCROLL:
1335         _elm_access_highlight_object_scroll(obj, a->mouse_type, a->x, a->y);
1336         break;
1337
1338       case ELM_ACCESS_ACTION_BACK:
1339         break;
1340
1341       default:
1342         break;
1343      }
1344
1345    return EINA_TRUE;
1346 }
1347
1348 EAPI void
1349 elm_access_action_cb_set(Evas_Object *obj, const Elm_Access_Action_Type type, const Elm_Access_Action_Cb cb, const void *data)
1350 {
1351    Action_Info *a;
1352    a =  evas_object_data_get(obj, "_elm_access_action_info");
1353
1354    if (!a)
1355      {
1356         a = calloc(1, sizeof(Action_Info));
1357         evas_object_data_set(obj, "_elm_access_action_info", a);
1358      }
1359
1360    a->obj = obj;
1361    a->fn[type].cb = cb;
1362    a->fn[type].user_data = (void *)data;
1363 }
1364
1365 EAPI void
1366 elm_access_external_info_set(Evas_Object *obj, const char *text)
1367 {
1368    _elm_access_text_set
1369      (_elm_access_object_get(obj), ELM_ACCESS_CONTEXT_INFO, text);
1370 }
1371
1372 EAPI char *
1373 elm_access_external_info_get(const Evas_Object *obj)
1374 {
1375    Elm_Access_Info *ac;
1376
1377    ac = _elm_access_object_get(obj);
1378    return _elm_access_text_get(ac, ELM_ACCESS_CONTEXT_INFO, obj);
1379 }