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