[access] use given object, if there is no highlight object in _access_highlight_next_...
[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_del_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
287 {
288    _elm_access_object_unregister(data, obj);
289
290    // _elm_access_edje_object_part_object_register(); set below object data
291    evas_object_data_del(obj, "_part_access_obj");
292 }
293
294 static void
295 _access_read_done(void *data __UNUSED__)
296 {
297    printf("read done\n");
298    // FIXME: produce event here
299 }
300
301 static void
302 _access_2nd_click_del_cb(void *data __UNUSED__, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
303 {
304    Ecore_Timer *t;
305
306    evas_object_event_callback_del_full(obj, EVAS_CALLBACK_DEL,
307                                        _access_2nd_click_del_cb, NULL);
308    t = evas_object_data_get(obj, "_elm_2nd_timeout");
309    if (t)
310      {
311         ecore_timer_del(t);
312         evas_object_data_del(obj, "_elm_2nd_timeout");
313      }
314 }
315
316 static Eina_Bool
317 _access_2nd_click_timeout_cb(void *data)
318 {
319    evas_object_event_callback_del_full(data, EVAS_CALLBACK_DEL,
320                                        _access_2nd_click_del_cb, NULL);
321    evas_object_data_del(data, "_elm_2nd_timeout");
322    return EINA_FALSE;
323 }
324
325 static void
326 _access_obj_hilight_del_cb(void *data __UNUSED__, Evas *e, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
327 {
328    _elm_access_object_hilight_disable(e);
329 }
330
331 static void
332 _access_obj_hilight_hide_cb(void *data __UNUSED__, Evas *e, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
333 {
334    _elm_access_object_hilight_disable(e);
335 }
336
337 static void
338 _access_obj_hilight_move_cb(void *data __UNUSED__, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
339 {
340    Evas_Coord x, y;
341    Evas_Object *o;
342
343    o = evas_object_name_find(evas_object_evas_get(obj), "_elm_access_disp");
344    if (!o) return;
345    evas_object_geometry_get(obj, &x, &y, NULL, NULL);
346    evas_object_move(o, x, y);
347 }
348
349 static void
350 _access_obj_hilight_resize_cb(void *data __UNUSED__, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
351 {
352    Evas_Coord w, h;
353    Evas_Object *o;
354
355    o = evas_object_name_find(evas_object_evas_get(obj), "_elm_access_disp");
356    if (!o) return;
357    evas_object_geometry_get(obj, NULL, NULL, &w, &h);
358    evas_object_resize(o, w, h);
359 }
360
361 void
362 _elm_access_mouse_event_enabled_set(Eina_Bool enabled)
363 {
364    enabled = !!enabled;
365    if (mouse_event_enable == enabled) return;
366    mouse_event_enable = enabled;
367 }
368
369 void
370 _elm_access_read_mode_set(Eina_Bool enabled)
371 {
372    enabled = !!enabled;
373    if (read_mode == enabled) return;
374    read_mode = enabled;
375 }
376
377 Eina_Bool
378 _elm_access_read_mode_get()
379 {
380    return read_mode;
381 }
382
383 void
384 _elm_access_shutdown()
385 {
386    _access_shutdown();
387 }
388
389 static void
390 _access_order_del_cb(void *data,
391                      Evas *e __UNUSED__,
392                      Evas_Object *obj,
393                      void *event_info __UNUSED__)
394 {
395    Elm_Widget_Item *item = data;
396
397    item->access_order = eina_list_remove(item->access_order, obj);
398 }
399
400 void
401 _elm_access_widget_item_access_order_set(Elm_Widget_Item *item,
402                                               Eina_List *objs)
403 {
404    Eina_List *l;
405    Evas_Object *o;
406
407    if (!item) return;
408
409    _elm_access_widget_item_access_order_unset(item);
410
411    EINA_LIST_FOREACH(objs, l, o)
412      {
413         evas_object_event_callback_add(o, EVAS_CALLBACK_DEL,
414                                        _access_order_del_cb, item);
415      }
416
417    item->access_order = objs;
418 }
419
420 const Eina_List *
421 _elm_access_widget_item_access_order_get(const Elm_Widget_Item *item)
422 {
423    if (!item) return NULL;
424    return item->access_order;
425 }
426
427 void
428 _elm_access_widget_item_access_order_unset(Elm_Widget_Item *item)
429 {
430    Eina_List *l, *l_next;
431    Evas_Object *o;
432
433    if (!item) return;
434
435    EINA_LIST_FOREACH_SAFE(item->access_order, l, l_next, o)
436      {
437         evas_object_event_callback_del_full
438           (o, EVAS_CALLBACK_DEL, _access_order_del_cb, item);
439         item->access_order = eina_list_remove_list(item->access_order, l);
440      }
441 }
442
443 void
444 _elm_access_highlight_object_scroll(Evas_Object *obj, int type, int x, int y)
445 {
446    Evas *evas;
447    Evas_Object *ho;
448    Evas_Coord_Rectangle ho_area;
449
450    if (!obj) return;
451
452    evas = evas_object_evas_get(obj);
453    if (!evas) return;
454
455    switch (type)
456      {
457       case 0:
458         ho = _access_highlight_object_get(obj);
459         if (!ho)
460           {
461              s_parent = NULL;
462              return;
463           }
464         else
465           {
466              /* find scrollable parent */
467              s_parent = elm_widget_parent_get(ho);
468              while (s_parent)
469                {
470                   if(!!evas_object_smart_interface_get(s_parent, ELM_SCROLLABLE_IFACE_NAME))
471                     break;
472                   s_parent = elm_widget_parent_get(s_parent);
473                }
474
475               if (!s_parent) return;
476
477               ELM_SCROLLABLE_IFACE_GET(s_parent, s_iface);
478               s_iface->repeat_events_set(s_parent, EINA_FALSE);
479
480               evas_object_geometry_get
481                 (ho, &ho_area.x, &ho_area.y, &ho_area.w, &ho_area.h);
482
483               offset.x = x - (ho_area.x + (ho_area.w / 2));
484               offset.y = y - (ho_area.y + (ho_area.h / 2));
485           }
486
487         evas_event_feed_mouse_in(evas, 0, NULL);
488         evas_event_feed_mouse_move(evas, x - offset.x, y - offset.y, 0, NULL);
489         evas_event_feed_mouse_down(evas, 1, EVAS_BUTTON_NONE, 0, NULL);
490         break;
491
492       case 1:
493         if (!s_parent) return;
494         evas_event_feed_mouse_move(evas, x - offset.x, y - offset.y, 0, NULL);
495         break;
496
497       case 2:
498         if (!s_parent) return;
499         evas_event_feed_mouse_up(evas, 1, EVAS_BUTTON_NONE, 0, NULL);
500
501         ELM_SCROLLABLE_IFACE_GET(s_parent, s_iface);
502         s_iface->repeat_events_set(s_parent, EINA_TRUE);
503         break;
504
505       default:
506         break;
507      }
508 }
509
510 static Eina_Bool
511 _access_action_callback_call(Evas_Object *obj,
512                              Elm_Access_Action_Type type,
513                              void *data)
514 {
515    Action_Info *a;
516    Eina_Bool ret;
517
518    ret = EINA_FALSE;
519    a = evas_object_data_get(obj, "_elm_access_action_info");
520
521    if (a && (a->fn[type].cb))
522      ret = a->fn[type].cb(a->fn[type].user_data, obj, data);
523
524    return ret;
525 }
526
527 static Eina_Bool
528 _access_highlight_next_get(Evas_Object *obj, Elm_Focus_Direction dir)
529 {
530    int type;
531    Evas_Object *ho, *parent, *target;
532    Eina_Bool ret;
533
534    target = NULL;
535    ret = EINA_FALSE;
536
537    if (!elm_widget_is(obj)) return ret;
538
539    ho = _access_highlight_object_get(obj);
540    if (!ho) ho = obj;
541
542    parent = ho;
543
544    /* find highlight root */
545    do
546      {
547         ELM_WIDGET_DATA_GET(parent, sd);
548         if (sd->highlight_root)
549           {
550              /* change highlight root */
551              obj = parent;
552              break;
553           }
554         parent = elm_widget_parent_get(parent);
555      }
556    while (parent);
557
558    _elm_access_read_mode_set(EINA_TRUE);
559
560    ret = elm_widget_focus_next_get(obj, dir, &target);
561    if (ret && target)
562      {
563         if (dir == ELM_FOCUS_NEXT)
564           type = ELM_ACCESS_ACTION_HIGHLIGHT_NEXT;
565         else
566           type = ELM_ACCESS_ACTION_HIGHLIGHT_PREV;
567
568         if (!_access_action_callback_call(ho, type, NULL))
569           {
570              /* change highlight object */
571              action_type = type;
572
573              _elm_access_highlight_set(target);
574              ret = EINA_FALSE;
575
576              action_type = ELM_ACCESS_ACTION_FIRST;
577           }
578      }
579
580    _elm_access_read_mode_set(EINA_FALSE);
581
582    return ret;
583 }
584
585 //-------------------------------------------------------------------------//
586 EAPI void
587 _elm_access_highlight_set(Evas_Object* obj)
588 {
589    Evas *evas;
590    Elm_Access_Info *ac;
591    Evas_Object *ho;
592    Evas_Coord_Point ho_point = { 0, 0 };
593
594    if (!obj) return;
595
596    ho = _access_highlight_object_get(obj);
597    if (ho == obj) return;
598
599    ac = evas_object_data_get(obj, "_elm_access");
600    if (!ac) return;
601
602    _access_highlight_read(ac, obj);
603
604    /* move mouse position to inside of highlight object. if an object has a
605       highlight by highlight_cycle();, the mouse still positions at previous
606       position which would be made by MOUSE_IN event. */
607    evas = evas_object_evas_get(obj);
608    if (!evas) return;
609
610    evas_object_geometry_get(obj, &ho_point.x, &ho_point.y, 0, 0);
611    evas_event_feed_mouse_move(evas, ho_point.x, ho_point.y, 0, NULL);
612 }
613
614 EAPI void
615 _elm_access_clear(Elm_Access_Info *ac)
616 {
617    Elm_Access_Item *ai;
618
619    if (!ac) return;
620    if (ac->delay_timer)
621      {
622         ecore_timer_del(ac->delay_timer);
623         ac->delay_timer = NULL;
624      }
625    EINA_LIST_FREE(ac->items, ai)
626      {
627         if (!ai->func)
628           {
629              if (ai->data) eina_stringshare_del(ai->data);
630           }
631         free(ai);
632      }
633 }
634
635 EAPI void
636 _elm_access_text_set(Elm_Access_Info *ac, int type, const char *text)
637 {
638    Elm_Access_Item *ai = _access_add_set(ac, type);
639    if (!ai) return;
640    ai->func = NULL;
641    ai->data = eina_stringshare_add(text);
642 }
643
644 EAPI void
645 _elm_access_callback_set(Elm_Access_Info *ac, int type, Elm_Access_Info_Cb func, const void *data)
646 {
647    Elm_Access_Item *ai = _access_add_set(ac, type);
648    if (!ai) return;
649    ai->func = func;
650    ai->data = data;
651 }
652
653 EAPI void
654 _elm_access_on_highlight_hook_set(Elm_Access_Info           *ac,
655                                   Elm_Access_On_Highlight_Cb func,
656                                   void                      *data)
657 {
658     if (!ac) return;
659     ac->on_highlight = func;
660     ac->on_highlight_data = data;
661 }
662
663 EAPI void
664 _elm_access_activate_callback_set(Elm_Access_Info           *ac,
665                                   Elm_Access_Activate_Cb     func,
666                                   void                      *data)
667 {
668    if (!ac) return;
669    ac->activate = func;
670    ac->activate_data = data;
671 }
672
673 EAPI void
674 _elm_access_highlight_object_activate(Evas_Object *obj, Elm_Activate act)
675 {
676    Evas_Object *ho;
677
678    ho = _access_highlight_object_get(obj);
679    if (!ho) return;
680
681    switch (act)
682      {
683       case ELM_ACTIVATE_DEFAULT:
684       case ELM_ACTIVATE_UP:
685       case ELM_ACTIVATE_DOWN:
686         _elm_access_read_mode_set(EINA_FALSE);
687
688         if (!elm_object_focus_get(ho))
689         elm_object_focus_set(ho, EINA_TRUE);
690
691         elm_widget_activate(ho, act);
692         break;
693
694       case ELM_ACTIVATE_BACK:
695         elm_widget_activate(ho, act);
696         break;
697
698       default:
699         break;
700      }
701
702    return;
703 }
704
705 EAPI void
706 _elm_access_highlight_cycle(Evas_Object *obj, Elm_Focus_Direction dir)
707 {
708    int type;
709    Evas_Object *ho, *parent;
710
711    ho = _access_highlight_object_get(obj);
712    if (!ho) return;
713
714    parent = ho;
715
716    /* find highlight root */
717    do
718      {
719         ELM_WIDGET_DATA_GET(parent, sd);
720         if (sd->highlight_root)
721           {
722              /* change highlight root */
723              obj = parent;
724              break;
725           }
726         parent = elm_widget_parent_get(parent);
727      }
728    while (parent);
729
730    _elm_access_read_mode_set(EINA_TRUE);
731
732    if (dir == ELM_FOCUS_NEXT)
733      type = ELM_ACCESS_ACTION_HIGHLIGHT_NEXT;
734    else
735      type = ELM_ACCESS_ACTION_HIGHLIGHT_PREV;
736
737    action_type = type;
738
739    if (!_access_action_callback_call(ho, type, NULL))
740      elm_widget_focus_cycle(obj, dir);
741
742    action_type = ELM_ACCESS_ACTION_FIRST;
743
744    _elm_access_read_mode_set(EINA_FALSE);
745 }
746
747 EAPI char *
748 _elm_access_text_get(const Elm_Access_Info *ac, int type, const Evas_Object *obj)
749 {
750    Elm_Access_Item *ai;
751    Eina_List *l;
752
753    if (!ac) return NULL;
754    EINA_LIST_FOREACH(ac->items, l, ai)
755      {
756         if (ai->type == type)
757           {
758              if (ai->func) return ai->func((void *)(ai->data), (Evas_Object *)obj);
759              else if (ai->data) return strdup(ai->data);
760              return NULL;
761           }
762      }
763    return NULL;
764 }
765
766 EAPI void
767 _elm_access_read(Elm_Access_Info *ac, int type, const Evas_Object *obj)
768 {
769    char *txt = _elm_access_text_get(ac, type, obj);
770
771    if (txt && strlen(txt) == 0) return; /* Tizen only: TTS engine does not work properly */
772
773    _access_init();
774    if (mapi)
775      {
776         if (mapi->out_done_callback_set)
777            mapi->out_done_callback_set(_access_read_done, NULL);
778         if (type == ELM_ACCESS_DONE)
779           {
780              if (mapi->out_read_done) mapi->out_read_done();
781           }
782         else if (type == ELM_ACCESS_CANCEL)
783           {
784              if (mapi->out_cancel) mapi->out_cancel();
785           }
786         else
787           {
788              if (txt && mapi->out_read) mapi->out_read(txt);
789           }
790      }
791    if (txt) free(txt);
792 }
793
794 EAPI void
795 _elm_access_say(const char *txt)
796 {
797    if (!_elm_config->access_mode) return;
798
799    if (txt && strlen(txt) == 0) return; /* Tizen only: TTS engine does not work properly */
800
801    _access_init();
802    if (mapi)
803      {
804         if (mapi->out_done_callback_set)
805            mapi->out_done_callback_set(_access_read_done, NULL);
806         if (mapi->out_cancel) mapi->out_cancel();
807         if (txt)
808           {
809              if (mapi->out_read) mapi->out_read(txt);
810              //if (mapi->out_read) mapi->out_read(".\n"); /* TIZEN only: Tizen TTS engine performance is not good */
811           }
812         if (mapi->out_read_done) mapi->out_read_done();
813      }
814 }
815
816 EAPI Elm_Access_Info *
817 _elm_access_object_get(const Evas_Object *obj)
818 {
819    return evas_object_data_get(obj, "_elm_access");
820 }
821
822 EAPI void
823 _elm_access_object_hilight(Evas_Object *obj)
824 {
825    Evas_Object *o;
826    Evas_Coord x, y, w, h;
827    Elm_Access_Action_Info *a;
828
829    o = evas_object_name_find(evas_object_evas_get(obj), "_elm_access_disp");
830    if (!o)
831      {
832         o = edje_object_add(evas_object_evas_get(obj));
833         evas_object_name_set(o, "_elm_access_disp");
834         evas_object_layer_set(o, ELM_OBJECT_LAYER_TOOLTIP);
835      }
836    else
837      {
838         Evas_Object *ptarget = evas_object_data_get(o, "_elm_access_target");
839         if (ptarget)
840           {
841              evas_object_data_del(o, "_elm_access_target");
842              elm_widget_parent_highlight_set(ptarget, EINA_FALSE);
843
844              evas_object_event_callback_del_full(ptarget, EVAS_CALLBACK_DEL,
845                                                  _access_obj_hilight_del_cb, NULL);
846              evas_object_event_callback_del_full(ptarget, EVAS_CALLBACK_HIDE,
847                                                  _access_obj_hilight_hide_cb, NULL);
848              evas_object_event_callback_del_full(ptarget, EVAS_CALLBACK_MOVE,
849                                                  _access_obj_hilight_move_cb, NULL);
850              evas_object_event_callback_del_full(ptarget, EVAS_CALLBACK_RESIZE,
851                                                  _access_obj_hilight_resize_cb, NULL);
852              _access_action_callback_call(ptarget, ELM_ACCESS_ACTION_UNHIGHLIGHT, NULL);
853           }
854      }
855    evas_object_data_set(o, "_elm_access_target", obj);
856    elm_widget_parent_highlight_set(obj, EINA_TRUE);
857
858    elm_widget_theme_object_set(obj, o, "access", "base", "default");
859
860    evas_object_event_callback_add(obj, EVAS_CALLBACK_DEL,
861                                   _access_obj_hilight_del_cb, NULL);
862    evas_object_event_callback_add(obj, EVAS_CALLBACK_HIDE,
863                                   _access_obj_hilight_hide_cb, NULL);
864    evas_object_event_callback_add(obj, EVAS_CALLBACK_MOVE,
865                                   _access_obj_hilight_move_cb, NULL);
866    evas_object_event_callback_add(obj, EVAS_CALLBACK_RESIZE,
867                                   _access_obj_hilight_resize_cb, NULL);
868    evas_object_raise(o);
869    evas_object_geometry_get(obj, &x, &y, &w, &h);
870    evas_object_move(o, x, y);
871    evas_object_resize(o, w, h);
872
873    /* use callback, should an access object do below every time when
874         * a window gets a client message ECORE_X_ATOM_E_ILLMUE_ACTION_READ? */
875    a = calloc(1, sizeof(Elm_Access_Action_Info));
876    a->action_type = action_type;
877    if (!_access_action_callback_call(obj, ELM_ACCESS_ACTION_HIGHLIGHT, a))
878      evas_object_show(o);
879    else
880      evas_object_hide(o);
881    free(a);
882 }
883
884 EAPI void
885 _elm_access_object_unhilight(Evas_Object *obj)
886 {
887    Evas_Object *o, *ptarget;
888
889    o = evas_object_name_find(evas_object_evas_get(obj), "_elm_access_disp");
890    if (!o) return;
891    ptarget = evas_object_data_get(o, "_elm_access_target");
892    if (ptarget == obj)
893      {
894         evas_object_event_callback_del_full(ptarget, EVAS_CALLBACK_DEL,
895                                             _access_obj_hilight_del_cb, NULL);
896         evas_object_event_callback_del_full(ptarget, EVAS_CALLBACK_HIDE,
897                                             _access_obj_hilight_hide_cb, NULL);
898         evas_object_event_callback_del_full(ptarget, EVAS_CALLBACK_MOVE,
899                                             _access_obj_hilight_move_cb, NULL);
900         evas_object_event_callback_del_full(ptarget, EVAS_CALLBACK_RESIZE,
901                                             _access_obj_hilight_resize_cb, NULL);
902         evas_object_del(o);
903         elm_widget_parent_highlight_set(ptarget, EINA_FALSE);
904         _access_action_callback_call(ptarget, ELM_ACCESS_ACTION_UNHIGHLIGHT, NULL);
905      }
906 }
907
908 static void
909 _content_resize(void *data, Evas *e __UNUSED__, Evas_Object *obj,
910                 void *event_info __UNUSED__)
911 {
912    Evas_Object *accessobj;
913    Evas_Coord w, h;
914
915    accessobj = data;
916    if (!accessobj) return;
917
918    evas_object_geometry_get(obj, NULL, NULL, &w, &h);
919    evas_object_resize(accessobj, w, h);
920 }
921
922 static void
923 _content_move(void *data, Evas *e __UNUSED__, Evas_Object *obj,
924               void *event_info __UNUSED__)
925 {
926    Evas_Object *accessobj;
927    Evas_Coord x, y;
928
929    accessobj = data;
930    if (!accessobj) return;
931
932    evas_object_geometry_get(obj, &x, &y, NULL, NULL);
933    evas_object_move(accessobj, x, y);
934 }
935
936 static Evas_Object *
937 _access_object_register(Evas_Object *obj, Evas_Object *parent)
938 {
939    Evas_Object *ao;
940    Elm_Access_Info *ac;
941    Evas_Coord x, y, w, h;
942
943    if (!obj) return NULL;
944
945    /* create access object */
946    ao = _elm_access_add(parent);
947    if (!ao) return NULL;
948
949    evas_object_event_callback_add(obj, EVAS_CALLBACK_RESIZE,
950                                   _content_resize, ao);
951    evas_object_event_callback_add(obj, EVAS_CALLBACK_MOVE,
952                                   _content_move, ao);
953
954    evas_object_geometry_get(obj, &x, &y, &w, &h);
955    evas_object_move(ao, x, y);
956    evas_object_resize(ao, w, h);
957    evas_object_show(ao);
958
959    /* register access object */
960    _elm_access_object_register(ao, obj);
961
962    /* set access object */
963    evas_object_data_set(obj, "_part_access_obj", ao);
964
965    /* set owner part object */
966    ac = evas_object_data_get(ao, "_elm_access");
967    ac->part_object = obj;
968
969    return ao;
970 }
971
972 static void
973 _access_object_unregister(Evas_Object *obj)
974 {
975    Elm_Access_Info *ac;
976    Evas_Object *ao;
977
978    if (!obj) return;
979
980    ao = evas_object_data_get(obj, "_part_access_obj");
981
982    if (ao)
983      {
984         evas_object_data_del(obj, "_part_access_obj");
985
986         /* delete callbacks */
987         evas_object_event_callback_del_full(obj, EVAS_CALLBACK_RESIZE,
988                                             _content_resize, ao);
989         evas_object_event_callback_del_full(obj, EVAS_CALLBACK_MOVE,
990                                             _content_move, ao);
991
992         /* unregister access object */
993         _elm_access_object_unregister(ao, obj);
994
995         evas_object_del(ao);
996      }
997    else
998      {
999         /* button, check, label etc. */
1000         ac = evas_object_data_get(obj, "_elm_access");
1001         if (ac && ac->hoverobj)
1002           _elm_access_object_unregister(obj, ac->hoverobj);
1003      }
1004 }
1005
1006 EAPI Evas_Object *
1007 _elm_access_edje_object_part_object_register(Evas_Object* obj,
1008                                              const Evas_Object *eobj,
1009                                              const char* part)
1010 {
1011    Evas_Object *ao, *po;
1012
1013    po = (Evas_Object *)edje_object_part_object_get(eobj, part);
1014    if (!obj || !po) return NULL;
1015
1016    /* check previous access object */
1017    ao = evas_object_data_get(po, "_part_access_obj");
1018    if (ao)
1019      _elm_access_edje_object_part_object_unregister(obj, eobj, part);
1020
1021    ao = _access_object_register(po, obj);
1022
1023    return ao;
1024 }
1025
1026 //FIXME: unused obj should be removed from here and each widget.
1027 EAPI void
1028 _elm_access_edje_object_part_object_unregister(Evas_Object* obj __UNUSED__,
1029                                                const Evas_Object *eobj,
1030                                                const char* part)
1031 {
1032    Evas_Object *po;
1033
1034    po = (Evas_Object *)edje_object_part_object_get(eobj, part);
1035    if (!po) return;
1036
1037    _access_object_unregister(po);
1038 }
1039
1040 EAPI void
1041 _elm_access_object_hilight_disable(Evas *e)
1042 {
1043    Evas_Object *o, *ptarget;
1044
1045    o = evas_object_name_find(e, "_elm_access_disp");
1046    if (!o) return;
1047    ptarget = evas_object_data_get(o, "_elm_access_target");
1048    if (ptarget)
1049      {
1050         evas_object_event_callback_del_full(ptarget, EVAS_CALLBACK_DEL,
1051                                             _access_obj_hilight_del_cb, NULL);
1052         evas_object_event_callback_del_full(ptarget, EVAS_CALLBACK_HIDE,
1053                                             _access_obj_hilight_hide_cb, NULL);
1054         evas_object_event_callback_del_full(ptarget, EVAS_CALLBACK_MOVE,
1055                                             _access_obj_hilight_move_cb, NULL);
1056         evas_object_event_callback_del_full(ptarget, EVAS_CALLBACK_RESIZE,
1057                                             _access_obj_hilight_resize_cb, NULL);
1058         elm_widget_parent_highlight_set(ptarget, EINA_FALSE);
1059         _access_action_callback_call(ptarget, ELM_ACCESS_ACTION_UNHIGHLIGHT, NULL);
1060      }
1061    evas_object_del(o);
1062 }
1063
1064 EAPI void
1065 _elm_access_object_register(Evas_Object *obj, Evas_Object *hoverobj)
1066 {
1067    Elm_Access_Info *ac;
1068
1069    evas_object_event_callback_add(hoverobj, EVAS_CALLBACK_MOUSE_IN,
1070                                   _access_obj_mouse_in_cb, obj);
1071    evas_object_event_callback_add(hoverobj, EVAS_CALLBACK_DEL,
1072                                   _access_obj_del_cb, obj);
1073    ac = calloc(1, sizeof(Elm_Access_Info));
1074    evas_object_data_set(obj, "_elm_access", ac);
1075
1076    ac->hoverobj = hoverobj;
1077 }
1078
1079 EAPI void
1080 _elm_access_object_unregister(Evas_Object *obj, Evas_Object *hoverobj)
1081 {
1082    Elm_Access_Info *ac;
1083
1084    evas_object_event_callback_del_full(hoverobj, EVAS_CALLBACK_MOUSE_IN,
1085                                        _access_obj_mouse_in_cb, obj);
1086    evas_object_event_callback_del_full(hoverobj, EVAS_CALLBACK_DEL,
1087                                        _access_obj_del_cb, obj);
1088
1089    ac = evas_object_data_get(obj, "_elm_access");
1090    evas_object_data_del(obj, "_elm_access");
1091    if (ac)
1092      {
1093         _elm_access_clear(ac);
1094         free(ac);
1095      }
1096
1097    Action_Info *a;
1098    a = evas_object_data_get(obj, "_elm_access_action_info");
1099    if (a) free(a);
1100 }
1101
1102 EAPI void
1103 _elm_access_widget_item_register(Elm_Widget_Item *item)
1104 {
1105    Evas_Object *ao, *ho;
1106    Evas_Coord x, y, w, h;
1107    Elm_Access_Info *ac;
1108
1109    ELM_WIDGET_ITEM_CHECK_OR_RETURN(item);
1110
1111    /* check previous access object */
1112    if (item->access_obj)
1113      _elm_access_widget_item_unregister(item);
1114
1115    // create access object
1116    ho = item->view;
1117    ao = _elm_access_add(item->widget);
1118    if (!ao) return;
1119
1120    evas_object_event_callback_add(ho, EVAS_CALLBACK_RESIZE,
1121                                   _content_resize, ao);
1122    evas_object_event_callback_add(ho, EVAS_CALLBACK_MOVE,
1123                                   _content_move, ao);
1124
1125    evas_object_geometry_get(ho, &x, &y, &w, &h);
1126    evas_object_move(ao, x, y);
1127    evas_object_resize(ao, w, h);
1128    evas_object_show(ao);
1129
1130    // register access object
1131    _elm_access_object_register(ao, ho);
1132
1133    item->access_obj = ao;
1134
1135    /* set owner widget item */
1136    ac = evas_object_data_get(ao, "_elm_access");
1137    ac->widget_item = item;
1138 }
1139
1140 EAPI void
1141 _elm_access_widget_item_unregister(Elm_Widget_Item *item)
1142 {
1143    Evas_Object *ho;
1144
1145    ELM_WIDGET_ITEM_CHECK_OR_RETURN(item);
1146
1147    if (!item->access_obj) return;
1148
1149    ho = item->view;
1150    evas_object_event_callback_del_full(ho, EVAS_CALLBACK_RESIZE,
1151                                   _content_resize, item->access_obj);
1152    evas_object_event_callback_del_full(ho, EVAS_CALLBACK_MOVE,
1153                                   _content_move, item->access_obj);
1154
1155    _elm_access_object_unregister(item->access_obj, ho);
1156
1157    evas_object_del(item->access_obj);
1158    item->access_obj = NULL;
1159 }
1160
1161 EAPI Eina_Bool
1162 _elm_access_2nd_click_timeout(Evas_Object *obj)
1163 {
1164    Ecore_Timer *t;
1165
1166    t = evas_object_data_get(obj, "_elm_2nd_timeout");
1167    if (t)
1168      {
1169         ecore_timer_del(t);
1170         evas_object_data_del(obj, "_elm_2nd_timeout");
1171         evas_object_event_callback_del_full(obj, EVAS_CALLBACK_DEL,
1172                                             _access_2nd_click_del_cb, NULL);
1173         return EINA_TRUE;
1174      }
1175    t = ecore_timer_add(0.3, _access_2nd_click_timeout_cb, obj);
1176    evas_object_data_set(obj, "_elm_2nd_timeout", t);
1177    evas_object_event_callback_add(obj, EVAS_CALLBACK_DEL,
1178                                   _access_2nd_click_del_cb, NULL);
1179    return EINA_FALSE;
1180 }
1181
1182 static Evas_Object *
1183 _elm_access_add(Evas_Object *parent)
1184 {
1185    Evas_Object *obj;
1186
1187    EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL);
1188
1189    obj = elm_widget_add(_elm_access_smart_class_new(), parent);
1190    if (!obj) return NULL;
1191
1192    if (!elm_widget_sub_object_add(parent, obj))
1193      ERR("could not add %p as sub object of %p", obj, parent);
1194
1195    return obj;
1196 }
1197
1198 EAPI Evas_Object *
1199 elm_access_object_register(Evas_Object *obj, Evas_Object *parent)
1200 {
1201    return _access_object_register(obj, parent);
1202 }
1203
1204 EAPI void
1205 elm_access_object_unregister(Evas_Object *obj)
1206 {
1207    _access_object_unregister(obj);
1208 }
1209
1210 EAPI Evas_Object *
1211 elm_access_object_get(const Evas_Object *obj)
1212 {
1213    return evas_object_data_get(obj, "_part_access_obj");
1214 }
1215
1216 EAPI void
1217 elm_access_info_set(Evas_Object *obj, int type, const char *text)
1218 {
1219    _elm_access_text_set(_elm_access_object_get(obj), type, text);
1220 }
1221
1222 EAPI char *
1223 elm_access_info_get(const Evas_Object *obj, int type)
1224 {
1225    return _elm_access_text_get(_elm_access_object_get(obj), type, obj);
1226 }
1227
1228 EAPI void
1229 elm_access_info_cb_set(Evas_Object *obj, int type,
1230                           Elm_Access_Info_Cb func, const void *data)
1231 {
1232    _elm_access_callback_set(_elm_access_object_get(obj), type, func, data);
1233 }
1234
1235 EAPI void
1236 elm_access_activate_cb_set(Evas_Object *obj,
1237                            Elm_Access_Activate_Cb  func, void *data)
1238 {
1239    Elm_Access_Info *ac;
1240
1241    ac = _elm_access_object_get(obj);
1242    if (!ac) return;
1243
1244    ac->activate = func;
1245    ac->activate_data = data;
1246 }
1247
1248 EAPI void
1249 elm_access_say(const char *text)
1250 {
1251    if (!text) return;
1252
1253    _elm_access_say(text);
1254 }
1255
1256 EAPI void
1257 elm_access_highlight_set(Evas_Object* obj)
1258 {
1259    _elm_access_highlight_set(obj);
1260 }
1261
1262 EAPI Eina_Bool
1263 elm_access_action(Evas_Object *obj, const Elm_Access_Action_Type type, void *action_info)
1264 {
1265    Evas *evas;
1266    Evas_Object *ho;
1267    Elm_Access_Action_Info *a;
1268
1269    a = (Elm_Access_Action_Info *) action_info;
1270
1271    switch (type)
1272      {
1273       case ELM_ACCESS_ACTION_READ:
1274       case ELM_ACCESS_ACTION_HIGHLIGHT:
1275         evas = evas_object_evas_get(obj);
1276         if (!evas) return EINA_FALSE;
1277
1278         _elm_access_mouse_event_enabled_set(EINA_TRUE);
1279
1280         evas_event_feed_mouse_in(evas, 0, NULL);
1281         evas_event_feed_mouse_move(evas, a->x, a->y, 0, NULL);
1282         _elm_access_mouse_event_enabled_set(EINA_FALSE);
1283
1284         ho = _access_highlight_object_get(obj);
1285         if (ho)
1286           _access_action_callback_call(ho, ELM_ACCESS_ACTION_READ, a);
1287         break;
1288
1289       case ELM_ACCESS_ACTION_UNHIGHLIGHT:
1290         evas = evas_object_evas_get(obj);
1291         if (!evas) return EINA_FALSE;
1292         _elm_access_object_hilight_disable(evas);
1293         break;
1294
1295       case ELM_ACCESS_ACTION_HIGHLIGHT_NEXT:
1296         if (a->highlight_cycle)
1297           _elm_access_highlight_cycle(obj, ELM_FOCUS_NEXT);
1298         else
1299           return _access_highlight_next_get(obj, ELM_FOCUS_NEXT);
1300         break;
1301
1302       case ELM_ACCESS_ACTION_HIGHLIGHT_PREV:
1303         if (a->highlight_cycle)
1304           _elm_access_highlight_cycle(obj, ELM_FOCUS_PREVIOUS);
1305         else
1306           return _access_highlight_next_get(obj, ELM_FOCUS_PREVIOUS);
1307         break;
1308
1309       case ELM_ACCESS_ACTION_ACTIVATE:
1310         _elm_access_highlight_object_activate(obj, ELM_ACTIVATE_DEFAULT);
1311         break;
1312
1313       case ELM_ACCESS_ACTION_VALUE_CHANGE:
1314         _elm_access_highlight_object_activate(obj, ELM_ACTIVATE_UP);
1315         break;
1316
1317       case ELM_ACCESS_ACTION_SCROLL:
1318         _elm_access_highlight_object_scroll(obj, a->mouse_type, a->x, a->y);
1319         break;
1320
1321       case ELM_ACCESS_ACTION_BACK:
1322         break;
1323
1324       default:
1325         break;
1326      }
1327
1328    return EINA_TRUE;
1329 }
1330
1331 EAPI void
1332 elm_access_action_cb_set(Evas_Object *obj, const Elm_Access_Action_Type type, const Elm_Access_Action_Cb cb, const void *data)
1333 {
1334    Action_Info *a;
1335    a =  evas_object_data_get(obj, "_elm_access_action_info");
1336
1337    if (!a)
1338      {
1339         a = calloc(1, sizeof(Action_Info));
1340         evas_object_data_set(obj, "_elm_access_action_info", a);
1341      }
1342
1343    a->obj = obj;
1344    a->fn[type].cb = cb;
1345    a->fn[type].user_data = (void *)data;
1346 }
1347
1348 EAPI void
1349 elm_access_external_info_set(Evas_Object *obj, const char *text)
1350 {
1351    _elm_access_text_set
1352      (_elm_access_object_get(obj), ELM_ACCESS_CONTEXT_INFO, text);
1353 }
1354
1355 EAPI char *
1356 elm_access_external_info_get(const Evas_Object *obj)
1357 {
1358    Elm_Access_Info *ac;
1359
1360    ac = _elm_access_object_get(obj);
1361    return _elm_access_text_get(ac, ELM_ACCESS_CONTEXT_INFO, obj);
1362 }