2a5a59e1eb75482035a2667408576af14687cb6f
[platform/core/uifw/eail.git] / eail / eail / eail_utils.c
1 /*
2  * Copyright (c) 2013 Samsung Electronics Co., Ltd.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with this library; see the file COPYING.LIB.  If not, write to
16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 /**
21  * @file eail_utils.c
22  * @brief Implementation of utility functions used in various places in EAIL
23  * code
24  */
25
26 /* enabling beta API support for Eo parts*/
27 #define EFL_BETA_API_SUPPORT
28
29 #include <atk/atk.h>
30 #include <Elementary.h>
31 #include <Eina.h>
32 #include <Evas.h>
33
34 /* internal elm_widget api for listening EDJE parts */
35
36 #define ELM_INTERNAL_API_ARGESFSDFEFC
37 #include <elm_widget.h>
38
39 #include "eail_utils.h"
40 #include "eail_factory.h"
41 #include "eail_dynamic_content.h"
42 #include "eail_priv.h"
43
44 /**
45  * @param string base string to get substring from
46  * @param start_offset beginning offset
47  * @param end_offset end offset
48  *
49  * @returns newly allocated substring
50  */
51 gchar *
52 eail_get_substring(const gchar *string,
53                    gint         start_offset,
54                    gint         end_offset)
55 {
56    gchar *substring = NULL;
57    gint len = 0;
58    gint sub_len = 0;
59
60    if (!string) return NULL;
61
62    len = g_utf8_strlen(string, -1);
63
64    if ((start_offset <  0) ||
65       (start_offset > len - 1) ||
66       (end_offset   <  -1))
67     return NULL;
68    else if (end_offset == -1 ||
69            end_offset >= len - 1)
70      sub_len = len - start_offset + 1;
71    else
72      sub_len = end_offset - start_offset;
73
74    substring = g_malloc0(sub_len + 1);
75
76    return g_utf8_strncpy(substring, &string[start_offset], sub_len);
77 }
78
79 /**
80  * @param widget Evas_Object for getting state_set
81  * @param state_set current state_set taken from objects parent
82  *
83  * @returns filled AtkStateSet for given EvasObject
84  */
85 AtkStateSet *
86 eail_evas_obj_ref_state_set(Evas_Object *widget, AtkStateSet *state_set)
87 {
88    if (!widget) {
89        atk_state_set_add_state(state_set, ATK_STATE_DEFUNCT);
90        return state_set;
91    }
92
93    if (!elm_object_disabled_get(widget)) {
94        atk_state_set_add_state(state_set, ATK_STATE_SENSITIVE);
95        atk_state_set_add_state(state_set, ATK_STATE_ENABLED);
96    }
97
98    if (evas_object_visible_get(widget)) {
99        int x, y, width, height;
100        int vp_x, vp_y, vp_width, vp_height;
101
102        atk_state_set_add_state(state_set, ATK_STATE_VISIBLE);
103
104        evas_object_geometry_get(widget, &x, &y, &width, &height);
105        evas_output_viewport_get(evas_object_evas_get(widget),
106                                 &vp_x, &vp_y, &vp_width, &vp_height);
107
108        if ((x + width) >= vp_x && (y + height) >= vp_y &&
109            (vp_x + vp_width) >= x && (vp_y + vp_height) >= y) {
110            atk_state_set_add_state(state_set, ATK_STATE_SHOWING);
111        }
112    }
113
114    if (elm_object_focus_allow_get(widget)) {
115        atk_state_set_add_state(state_set, ATK_STATE_FOCUSABLE);
116
117        if (elm_object_focus_get(widget)) {
118            atk_state_set_add_state(state_set, ATK_STATE_FOCUSED);
119        }
120    }
121
122    return state_set;
123 }
124
125 /**
126  * @param widget an Evas_Object
127  * @return TRUE if grabbing focus was successfull, FALSE otherwise
128  */
129 gboolean
130 eail_evas_obj_grab_focus(Evas_Object *widget)
131 {
132    Ecore_Evas *ee = ecore_evas_ecore_evas_get(evas_object_evas_get(widget));
133
134    if (!widget || !elm_object_focus_allow_get(widget)) {
135          return FALSE;
136    }
137
138    ecore_evas_activate(ee);
139    elm_object_focus_set(widget, EINA_TRUE);
140
141    return TRUE;
142 }
143
144 /**
145  * @param widget an Evas_Object to press on
146  * @param x X coord
147  * @param y y coord
148  */
149 void
150 eail_mouse_press_on_coords(Evas_Object *widget, int x, int y)
151 {
152    Evas* evas = NULL;
153    evas =  evas_object_evas_get(widget);
154
155    evas_event_feed_mouse_move(evas, x, y, 0, NULL);
156    evas_event_feed_mouse_down(evas, 1, EVAS_BUTTON_NONE, 0, NULL);
157 }
158
159 /**
160  * @param widget an Evas_Object to press on
161  * @param x X coord
162  * @param y y coord
163  */
164 void
165 eail_mouse_release_on_coords(Evas_Object *widget, int x, int y)
166 {
167    Evas* evas = NULL;
168    evas =  evas_object_evas_get(widget);
169
170    evas_event_feed_mouse_move(evas, x, y, 0, NULL);
171    evas_event_feed_mouse_up(evas, 1, EVAS_BUTTON_NONE, 0, NULL);
172 }
173
174 /**
175  * @param widget an Evas_Object to click on
176  * @param x X coord
177  * @param y y coord
178  */
179 void
180 eail_mouse_click_on_coords(Evas_Object *widget, int x, int y)
181 {
182    Evas* evas = NULL;
183    evas =  evas_object_evas_get(widget);
184
185    evas_event_feed_mouse_move(evas, x, y, 0, NULL);
186    evas_event_feed_mouse_down(evas, 1, EVAS_BUTTON_NONE, 0, NULL);
187    evas_event_feed_mouse_up(evas, 1, EVAS_BUTTON_NONE, 0, NULL);
188 }
189
190 /**
191  * @param widget an Evas_Object to get values for
192  * @param x address of int to put X coord in
193  * @param y address of int to put Y coord in
194  */
195 void
196 eail_get_coords_widget_center(Evas_Object *widget, int *x, int *y)
197 {
198    int w, h;
199    evas_object_geometry_get(widget, x, y, &w, &h);
200
201    *x = *x + (w / 2);
202    *y = *y + (h / 2);
203 }
204
205 /**
206  * @param item an Elm_Object_Item*
207  *
208  * @returns Eina_List * filled with raw Evas_Object* (not every returned
209  * Evas_Object is widget - widget-only content is returned by
210  * eail_get_edje_parts_for_item function)
211  */
212 Eina_List *
213 eail_get_raw_evas_obj_list_from_item(Elm_Object_Item *item)
214 {
215    Evas_Object *edje = NULL;
216
217    edje = VIEW(item);
218    if (!edje)
219      {
220         DBG("Edje object for item not found. Returning empty list");\
221         return NULL;
222      }
223
224     return evas_object_smart_members_get(edje);
225 }
226
227 /**
228  * @param item Elm_Object_Item * to get strings from
229  * @returns EinaList filled with const gchar* that are representing text
230  * content of item. List have to be freed when no longer needed but DO NOT
231  * FREE CONTENT STRINGS.
232  */
233 Eina_List *
234 eail_item_get_content_strings(Elm_Object_Item *item)
235 {
236    Eina_List *strings_list = NULL;
237    Eina_List *edje_parts = NULL;
238    Evas_Object *part = NULL;
239    Eina_List *l;
240
241
242    edje_parts = eail_get_raw_evas_obj_list_from_item(item);
243    EINA_LIST_FOREACH(edje_parts, l, part)
244      {
245       const gchar *type_name = evas_object_type_get(part);
246
247       if (0 == strcmp(type_name, "text"))
248         {
249            const gchar *text = evas_object_text_text_get(part);
250
251            if (text)
252              strings_list = eina_list_append(strings_list, text);
253         }
254      }
255
256    return strings_list;
257 }
258
259 /**
260  * It does filtering inside and returs only parts that can be used later in
261  * eail factory (only widgets will be returned)
262  *
263  * @param item Elm_Object_Item * to get objects from
264  *
265  * @returns Eina_List* filled with Evas_Object* objects representing content
266  * found inside item
267  */
268 Eina_List *
269 eail_get_edje_parts_for_item(Elm_Object_Item *item)
270 {
271    Eina_List *edje_parts = NULL;
272    Eina_List *usable_parts = NULL;
273    int i = 0;
274
275    edje_parts = eail_get_raw_evas_obj_list_from_item(item);
276    for (i = 0; i < eina_list_count(edje_parts); ++i)
277      {
278         Evas_Object *obj = eina_list_nth(edje_parts, i);
279
280         /* adding only parts that can be used by eail_factory later */
281         if (elm_object_widget_check(obj))
282           usable_parts = eina_list_append(usable_parts, obj);
283      }
284
285    eina_list_free(edje_parts);
286
287    return usable_parts;
288 }
289
290 /**
291  *
292  * @param widget am Evas_Object
293  * @param type type of scroll action
294  *
295  * @returns TRUE if scroll action was successful, FALSE otherwise
296  */
297 gboolean
298 eail_handle_scroll(Evas_Object *widget,
299                    enum EAIL_SCROLL_TYPE type)
300 {
301    int x, y, w, h;
302
303    elm_scroller_region_get(widget, &x, &y, &w, &h);
304
305    if (EAIL_SCROLL_TYPE_UP == type)
306       y = y - h;
307    else if (EAIL_SCROLL_TYPE_DOWN == type)
308       y = y + h;
309    else if (EAIL_SCROLL_TYPE_RIGHT == type)
310       x = x + w;
311    else if (EAIL_SCROLL_TYPE_LEFT == type)
312       x = x - w;
313
314    elm_scroller_region_bring_in(widget, x, y, w, h);
315
316    return TRUE;
317 }
318
319 /**
320  *
321  * @param widget an Evas_Object*
322  * @param data additional action data
323  * @returns TRUE if scroll action was successful, FALSE otherwise
324  */
325 gboolean
326 eail_action_scroll_up(Evas_Object *widget,
327                       void *data)
328 {
329    return eail_handle_scroll(widget, EAIL_SCROLL_TYPE_UP);
330 }
331
332 /**
333  *
334  * @param widget an Evas_Object*
335  * @param data additional action data
336  *
337  * @returns TRUE if scroll action was successful, FALSE otherwise
338  */
339 gboolean
340 eail_action_scroll_down(Evas_Object *widget,
341                         void *data)
342 {
343    return eail_handle_scroll(widget, EAIL_SCROLL_TYPE_DOWN);
344 }
345
346 /**
347  *
348  * @param widget an Evas_Object*
349  * @param data additional action data
350  *
351  * @returns TRUE if scroll action was successful, FALSE otherwise
352  */
353 gboolean
354 eail_action_scroll_left(Evas_Object *widget,
355                         void *data)
356 {
357    return eail_handle_scroll(widget, EAIL_SCROLL_TYPE_LEFT);
358 }
359
360 /**
361  *
362  * @param widget an Evas_Object*
363  * @param data additional action data
364  *
365  * @returns TRUE if scroll action was successful, FALSE otherwise
366  */
367 gboolean
368 eail_action_scroll_right(Evas_Object *widget,
369                          void *data)
370 {
371    return eail_handle_scroll(widget, EAIL_SCROLL_TYPE_RIGHT);
372 }
373
374 /**
375  * @param atk_obj an AtkObject that emits the signal
376  * @param signal_name name of signal
377  * @param object_type an GType of object
378  */
379 void
380 eail_emit_atk_signal(AtkObject *atk_obj, const gchar *signal_name,
381                      GType object_type)
382 {
383    guint signal = g_signal_lookup (signal_name,object_type);
384    if (!signal)
385      {
386         ERR("No signal with name %s was found", signal_name);
387         return;
388      }
389
390    DBG("Raising %s signal", signal_name);
391    g_signal_emit (atk_obj, signal, 0);
392 }
393
394 /**
395  * @param added boolean used for marking if child is added. TRUE if child was
396  * added, FALSE when child was removed
397  * @param atk_obj an AtkObject that emits the signal
398  * @param child_number index number of changed child
399  */
400 void
401 eail_emit_children_changed(gboolean added, AtkObject *atk_obj, gint child_number)
402 {
403
404    DBG("Emitting child-changed for index %d. ADDED: %d", child_number, added);
405
406    if (added)
407      g_signal_emit_by_name
408                  (atk_obj, "children_changed::add", child_number, NULL, NULL);
409    else
410      g_signal_emit_by_name
411                (atk_obj, "children_changed::remove", child_number, NULL, NULL);
412
413 }
414
415 /**
416  * @param added boolean used for marking if child is added. TRUE if child was
417  * added, FALSE when child was removed
418  * @param atk_obj an AtkObject that emits the signal
419  * @param changed_obj pointer to object that has been added/removed
420  */
421 void
422 eail_emit_children_changed_obj(gboolean added,
423                                AtkObject *atk_obj,
424                                AtkObject *changed_obj)
425 {
426
427    DBG("Emitting child-changed for obj. ADDED: %d", added);
428
429    if (added)
430      g_signal_emit_by_name
431                  (atk_obj, "children_changed::add", NULL, changed_obj, NULL);
432    else
433      g_signal_emit_by_name
434                (atk_obj, "children_changed::remove", NULL, changed_obj, NULL);
435
436 }
437 /**
438  * @brief handles 'selected' state changes for item
439  *
440  * @param item an Elm_Object* item
441  * @param selected value of 'selected' state
442  * @param parent an AtkObject* that holds item inside
443  * @param role an AtkRole for item
444  */
445 static void
446 _eail_handle_selected_for_item(Elm_Object_Item *item,
447                                gboolean selected,
448                                AtkObject *parent,
449                                AtkRole role)
450 {
451    AtkObject *atk_item_obj = NULL;
452    if (!item && !parent)
453      {
454         ERR("Wrong parameters passed. Ignoring event");
455         return;
456      }
457
458    atk_item_obj = eail_factory_get_item_atk_obj(item, role, parent);
459    if (!atk_item_obj)
460      {
461         DBG("Couldn't find factory obj for item. Ignoring 'selected' event");
462         return;
463      }
464
465    atk_object_notify_state_change(atk_item_obj, ATK_STATE_SELECTED, selected);
466 }
467
468 /**
469  * @brief Function that calls apropriate handling for selected event
470  *
471  * @param data passed to callback
472  * @param obj object that raised event
473  * @param event_info additional event info
474  * @param selected selected state
475  */
476 static void
477 _eail_list_item_do_handle_selected_event(void *data,
478                                          Evas_Object *obj,
479                                          void *event_info,
480                                          gboolean selected)
481 {
482    Elm_Object_Item *obj_item = (Elm_Object_Item*)event_info;
483    if (!obj_item)
484      {
485         DBG("obj_item not found in event_info. Ignoring event..");
486         return;
487      }
488
489    eail_notify_child_focus_changes();
490    _eail_handle_selected_for_item
491                      (obj_item, selected, ATK_OBJECT(data), ATK_ROLE_LIST_ITEM);
492 }
493
494 /**
495  *
496  * @param data passed to callback
497  * @param obj object that raised event
498  * @param event_info additional event info
499  */
500 void
501 eail_list_item_handle_selected_event(void *data,
502                                       Evas_Object *obj,
503                                       void *event_info)
504 {
505    _eail_list_item_do_handle_selected_event(data, obj, event_info, TRUE);
506 }
507
508 /**
509  *
510  * @param data passed to callback
511  * @param obj object that raised event
512  * @param event_info additional event info
513  */
514 void
515 eail_list_item_handle_unselected_event(void *data,
516                                        Evas_Object *obj,
517                                        void *event_info)
518 {
519    _eail_list_item_do_handle_selected_event(data, obj, event_info, FALSE);
520 }
521
522 /**
523  * @brief Notifies objects with given role about possible hierarchy changes
524  *
525  * @param data an AtkRole
526  *
527  * @returns always FALSE
528  */
529 static gboolean
530 _notifiy_content_holders_by_type(gpointer data)
531 {
532    Eina_List *objs = NULL, *l = NULL;
533    AtkObject *atk_obj = NULL;
534    AtkRole role = ATK_ROLE_INVALID;
535
536    role = (AtkRole)data;
537
538    if (ATK_ROLE_APPLICATION == role)
539      {
540          atk_obj = atk_get_root();
541          if (atk_obj && EAIL_IS_DYNAMIC_CONTENT(atk_obj))
542             eail_dynamic_content_update_hierarchy(EAIL_DYNAMIC_CONTENT(atk_obj));
543
544          return FALSE;
545      }
546    else
547      {
548         objs = eail_factory_find_objects_with_role(role);
549         EINA_LIST_FOREACH(objs, l, atk_obj)
550           {
551             if (EAIL_IS_DYNAMIC_CONTENT(atk_obj))
552               {
553                    eail_dynamic_content_update_hierarchy
554                                                (EAIL_DYNAMIC_CONTENT(atk_obj));
555               }
556           }
557         eina_list_free(objs);
558      }
559
560    return FALSE;
561 }
562
563 /**
564  * @brief Sends notification about possible hierarchy changes to objects
565  * with dynamic content interface
566  *
567  * @param delay value in miliseconds, objects will be notified after this
568  * timeout
569  */
570 static void
571 _eail_notify_focus_listeners_delayed(guint delay)
572 {
573    g_timeout_add
574       (delay, _notifiy_content_holders_by_type, (gpointer)ATK_ROLE_APPLICATION);
575    g_timeout_add
576       (delay, _notifiy_content_holders_by_type, (gpointer)ATK_ROLE_WINDOW);
577    g_timeout_add
578       (delay, _notifiy_content_holders_by_type, (gpointer)ATK_ROLE_FILLER);
579 }
580
581 void
582 eail_notify_child_focus_changes(void)
583 {
584    _eail_notify_focus_listeners_delayed(0);
585    /* sending delayed notifications to objects after hierarchy has
586     * been fully established*/
587    _eail_notify_focus_listeners_delayed(1600);
588 }