2 * Copyright (c) 2013 Samsung Electronics Co., Ltd.
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.
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.
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.
22 * @brief EailItem implementation
24 * In general - this object represents all kinds of objects that have form of
25 * a Elm_Obj_Item* and need ATK accessible representation. It stores
26 * Elm_Obj_Item* inside - unlike Evas_Object like those components that are
27 * extending EailWidget.
30 #include "eail_item.h"
31 #include "eail_item_parent.h"
32 #include "eail_factory.h"
33 #include "eail_utils.h"
34 #include "eail_priv.h"
36 static void atk_component_interface_init(AtkComponentIface *iface);
37 static void atk_action_interface_init(AtkActionIface *iface);
38 static void atk_text_interface_init(AtkTextIface *iface);
40 #define EAIL_ITEM_CLICK_NAME "click" /**< @brief 'click' action name*/
41 #define EAIL_ITEM_PRESS_NAME "press" /**< @brief 'press' action name*/
42 #define EAIL_ITEM_RELEASE_NAME "release" /**< @brief 'release' action name*/
43 #define EAIL_ITEM_EXPAND_NAME "expand" /**< @brief 'expand' action name*/
44 #define EAIL_ITEM_SHRINK_NAME "shrink" /**< @brief 'shrink' action name*/
45 #define EAIL_ITEM_PART_FIRST "start" /**< @brief 'start' action name*/
46 #define EAIL_ITEM_PART_SECOND "end" /**< @brief 'end' action name*/
47 #define EAIL_ITEM_PART_ICON "elm.swallow.icon" /**< @brief icon item part*/
48 #define EAIL_ITEM_PART_END "elm.swallow.end" /**< @brief end item part*/
49 #define EAIL_TXT_SEPARATOR " " /**< @brief separator for text content*/
52 * @brief EailItem GObject definition
54 * It extends ATK_TYPE_OBJECT class and implements ATK_TYPE_COMPONENT,
55 * ATK_TYPE_TEXT and ATK_TYPE_ACTION interfaces
57 G_DEFINE_TYPE_WITH_CODE(EailItem,
60 G_IMPLEMENT_INTERFACE(ATK_TYPE_COMPONENT,
61 atk_component_interface_init)
62 G_IMPLEMENT_INTERFACE(ATK_TYPE_ACTION,
63 atk_action_interface_init)
64 G_IMPLEMENT_INTERFACE(ATK_TYPE_TEXT,
65 atk_text_interface_init));
68 * @brief Initializer for AtkObjectClass
70 * @param obj AtkObject instance
71 * @param data additional initialization data
74 eail_item_initialize(AtkObject *obj, gpointer data)
76 EailItem *item = EAIL_ITEM(obj);
78 ATK_OBJECT_CLASS(eail_item_parent_class)->initialize(obj, data);
80 item->item = (Elm_Object_Item *)data;
82 /* NOTE: initializing role is being done only in eail_item_new(..) */
87 * @param item EailItem to take nested Elm_Object_Item from
89 * @returns Elm_Object_Item * representing nested item or NULL if an error occured
92 eail_item_get_item(EailItem *item)
94 g_return_val_if_fail(EAIL_IS_ITEM(item), NULL);
100 * Implementation of the *AtkObject* interface
104 * @brief Gets the accessible name of the accessible.
106 * Implementation of AtkObject->get_name callback.
108 * @param obj AtkObject for EailItem
109 * @returns string representing the accessible description of
113 eail_item_get_name(AtkObject *obj)
115 const gchar *atk_name_default = NULL;
116 AtkObject *parent = atk_object_get_parent(obj);
118 if (!parent) return NULL;
120 /* returning name from default atk implementation when it available (it
121 * means that it was set by user)*/
122 atk_name_default = ATK_OBJECT_CLASS(eail_item_parent_class)->get_name(obj);
123 if (atk_name_default) return atk_name_default;
125 return eail_item_parent_get_item_name(EAIL_ITEM_PARENT(parent),
130 * @brief Gets the role of the accessible
132 * Implementation of AtkObject->get_role callback.
134 * @param obj AtkObject for EailItem
135 * @returns AtkRole representing the parameter's role
138 eail_item_get_role(AtkObject *obj)
140 AtkObject *parent = atk_object_get_parent(obj);
142 if (!parent) return ATK_ROLE_INVALID;
144 return eail_item_parent_get_item_role(EAIL_ITEM_PARENT(parent),
149 * @brief Gets the 0-based index of this accessible in its parent.
151 * Implementation of AtkObject->get_index_in_parent callback.
153 * @param obj AtkObject for EailItem
154 * @returns integer representing the index of the accessible in its parent or -1 if the accessible does not have an accessible parent
157 eail_item_get_index_in_parent(AtkObject *obj)
159 AtkObject *parent = atk_object_get_parent(obj);
161 if (!parent) return -1;
163 return eail_item_parent_get_item_index_in_parent(EAIL_ITEM_PARENT(parent),
168 * @brief Gets a reference to the state set of the accessible.
170 * The caller must unreference it when it is no longer needed.
172 * Implementation of AtkObject->ref_state_set callback.
174 * @param obj AtkObject for EailItem
175 * @returns AtkStateSet containing a reference to the state set of the
179 eail_item_ref_state_set(AtkObject *obj)
181 AtkStateSet *state_set;
182 AtkObject *parent = atk_object_get_parent(obj);
183 EailItem *item = EAIL_ITEM(obj);
184 Elm_Object_Item *it = eail_item_get_item(item);
186 state_set = ATK_OBJECT_CLASS(eail_item_parent_class)->ref_state_set(obj);
190 atk_state_set_add_state(state_set, ATK_STATE_DEFUNCT);
194 if (!elm_object_item_disabled_get(it))
196 atk_state_set_add_state(state_set, ATK_STATE_SENSITIVE);
197 atk_state_set_add_state(state_set, ATK_STATE_ENABLED);
200 return eail_item_parent_ref_item_state_set(EAIL_ITEM_PARENT(parent),
206 * @brief Initializer for GObject class
207 * @param item EailItem instance
210 eail_item_init(EailItem *item)
215 * @brief Finalizes implementation for GObject class
216 * @param obj AtkObject for EailItem instance that needs to be finalized
219 eail_item_class_finalize(GObject *obj)
221 EailItem *eail_item = EAIL_ITEM(obj);
222 Elm_Object_Item *obj_item = eail_item_get_item(eail_item);
225 eail_factory_unregister_item_from_cache(obj_item);
227 G_OBJECT_CLASS(eail_item_parent_class)->finalize(obj);
231 * @brief Helper function used for adding Evas_Object* content to the item's list
232 * if the content is available.
234 * @param eail_obj_item item used for browsing for given content part
235 * @param items list of items that will be extended by the content part
236 * @param part_str name of content part to be found
238 * @returns Eina_List filled with Evas_Object* objects
241 _eail_item_append_part_if_exist(Elm_Object_Item *eail_obj_item,
245 Evas_Object *content = NULL;
247 content = elm_object_item_part_content_get
248 (eail_obj_item, part_str);
250 items = eina_list_append(items, content);
256 * @brief Checks if content_get is supported by the given EailItem object
258 * @param atk_object AtkObject instance
260 * @returns TRUE if content_get is supported, FALSE otherwise
263 _eail_item_is_content_get_supported(AtkObject *atk_object)
265 AtkObject *parent = NULL;
268 parent = atk_object_get_parent(atk_object);
269 g_return_val_if_fail(parent, FALSE);
271 return eail_item_parent_is_is_content_get_supported
272 (EAIL_ITEM_PARENT(parent),EAIL_ITEM(atk_object));
276 * @brief Gets list of item's content parts with well documented content strings
278 * @param eail_item item to get content from
280 * @returns Eina_List filled with the content parts that have been found
283 _eail_item_get_basic_parts(EailItem *eail_item)
285 Eina_List *items = NULL;
286 Elm_Object_Item *obj_item = NULL;
288 /* if content_get is not supported then content from basic parts will be
289 * always empty. This checking is being done, because elementary library
290 * don't want to ask for some widget usign content_get API */
291 if (!_eail_item_is_content_get_supported(ATK_OBJECT(eail_item)))
294 obj_item = eail_item_get_item(EAIL_ITEM(eail_item));
295 items = _eail_item_append_part_if_exist
296 (obj_item, items, EAIL_ITEM_PART_FIRST);
297 items = _eail_item_append_part_if_exist
298 (obj_item, items, EAIL_ITEM_PART_SECOND);
299 items = _eail_item_append_part_if_exist
300 (obj_item, items, EAIL_ITEM_PART_ICON);
301 items = _eail_item_append_part_if_exist
302 (obj_item, items, EAIL_ITEM_PART_END);
308 * @brief Helper function for adding unique entries from one list to another
310 * @param item_list list which unique items will be appended to
311 * @param additional_items source list with items to be added to item_list
313 * @returns Eina_List filled with unique entries from both lists
316 _eail_add_unique_listsparts(Eina_List *item_list,
317 Eina_List *additional_items)
320 Elm_Object_Item *item_edj;
322 EINA_LIST_FOREACH(additional_items, l, item_edj)
324 if (!eina_list_data_find(item_list, item_edj))
325 item_list = eina_list_append(item_list, item_edj);
332 * @brief Gets content part items from the given EailItem object
334 * List should be freed when results will be processed.
336 * @param eail_item EailItem object used to get content from
338 * @returns Eina_List containing content parts for the given item.
341 _eail_item_get_part_items(EailItem *eail_item)
343 Eina_List *items = NULL, *edje_items = NULL;
344 Elm_Object_Item *obj_item = NULL;
346 obj_item = eail_item_get_item(eail_item);
347 g_return_val_if_fail(obj_item, NULL);
349 /* parts from well documented default content parts - there are being used
350 * mostly by desktop applications. Some of these parts are not listed in
351 * edje objects (used below), so results from both list need to be merged
352 * to list full item content */
353 items = _eail_item_get_basic_parts(eail_item);
355 /* content parts from taken from edje object*/
356 edje_items = eail_get_edje_parts_for_item(obj_item);
358 /* adding unique edje items to content list*/
359 items = _eail_add_unique_listsparts(items, edje_items);
361 eina_list_free(edje_items);
367 * @brief Gets the number of accessible children of the accessible.
369 * Implementation AtkObject->get_n_children callback.
371 * @param obj AtkObject (EailItem) instance
373 * @returns integer representing the number of accessible children of
377 eail_item_get_n_children(AtkObject *obj)
379 gint n_items, parent_n_items;
381 AtkObject *parent = atk_object_get_parent(obj);
383 if (!parent) return 0;
384 parent_n_items = eail_item_parent_get_n_children(EAIL_ITEM_PARENT(parent),
386 /* if there is item parent impl, then returning item count
388 if (parent_n_items != -1) return parent_n_items;
390 items = _eail_item_get_part_items(EAIL_ITEM(obj));
391 n_items = eina_list_count(items);
393 eina_list_free(items);
399 * @brief Gets a reference to the specified accessible child of the object.
401 * The accessible children are 0-based so the first accessible child is at index 0,
402 * the second at index 1 and so on.
404 * Implementation of AtkObject->ref_child callback.
406 * @param obj AtkObject for EailItem instance
407 * @param i index of item to reference
409 * @returns AtkObject representing the specified accessible child of the
413 eail_item_ref_child(AtkObject *obj, gint i)
416 AtkObject *child = NULL;
417 AtkObject *parent = atk_object_get_parent(obj);
418 AtkObject *ref_child_from_parent = NULL;
420 if (!parent) return NULL;
422 ref_child_from_parent = eail_item_parent_ref_n_child
423 (EAIL_ITEM_PARENT(parent), EAIL_ITEM(obj), i);
425 /* if there is item parent implementation for ref child, then returning
426 * object using that parent implementation*/
427 if (ref_child_from_parent)
429 g_object_ref(ref_child_from_parent);
430 return ref_child_from_parent;
433 items = _eail_item_get_part_items(EAIL_ITEM(obj));
434 if (eina_list_count(items) > i)
435 child = eail_factory_get_accessible(eina_list_nth(items, i));
437 ERR("Tried to ref child with index %d out of bounds!", i);
439 eina_list_free(items);
444 DBG("Child could not created in factory");
450 * @brief Initializer for GObject class
452 * Defines callbacks for base AtkObject.
454 * @param klass EailItemClass instance
457 eail_item_class_init(EailItemClass *klass)
459 AtkObjectClass *class = ATK_OBJECT_CLASS(klass);
460 GObjectClass *g_object_class = G_OBJECT_CLASS(klass);
462 class->initialize = eail_item_initialize;
463 class->get_name = eail_item_get_name;
464 class->get_role = eail_item_get_role;
465 class->get_index_in_parent = eail_item_get_index_in_parent;
466 class->ref_state_set = eail_item_ref_state_set;
467 class->get_n_children = eail_item_get_n_children;
468 class->ref_child = eail_item_ref_child;
470 g_object_class->finalize = eail_item_class_finalize;
474 * Implementation of the *AtkComponent* interface
478 * @brief Grabs focus for this component.
480 * Implementation of AtkComponent->grab_focus callback.
482 * @param component AtkComponent (EailItem) instance
483 * @returns TRUE on success, FALSE otherwise.
486 eail_item_grab_focus(AtkComponent *component)
488 AtkObject *obj = ATK_OBJECT(component);
489 AtkObject *parent = atk_object_get_parent(obj);
491 if (!parent) return FALSE;
493 return eail_item_parent_grab_item_focus(EAIL_ITEM_PARENT(parent),
498 * @brief Gets the rectangle which gives the extent of the component.
500 * Implementation of AtkComponent->get_extents callback.
502 * @param component AtkComponent instance
503 * @param [out] x rectangle upper left x coordinate
504 * @param [out] y rectangle upper left y coordinate
505 * @param [out] width width of the rectangle
506 * @param [out] height height of the rectangle
507 * @param coord_type specifies whether the coordinates are relative to the
508 * screen or to the components top level window
511 eail_item_get_extents(AtkComponent *component,
516 AtkCoordType coord_type)
518 AtkObject *obj = ATK_OBJECT(component);
519 AtkObject *parent = atk_object_get_parent(obj);
522 *width = *height = -1;
526 eail_item_parent_get_item_extents(EAIL_ITEM_PARENT(parent),
528 x, y, width, height, coord_type);
532 * @brief AtkComponent interface initialization
534 * @param iface EailNaviframPage instance
537 atk_component_interface_init(AtkComponentIface *iface)
539 iface->grab_focus = eail_item_grab_focus;
540 iface->get_extents = eail_item_get_extents;
544 * AtkAction interface init
548 * @brief Gets actions supported by given EailItem object
550 * @param action AtkAction instance
552 * @returns EailActionSupported enum entry that shows what kind of actions are
556 _eail_item_get_actions_supported(AtkAction *action)
558 AtkObject *parent = NULL;
560 parent = atk_object_get_parent(ATK_OBJECT(action));
561 g_return_val_if_fail(parent, FALSE);
563 return eail_item_parent_get_actions_supported
564 (EAIL_ITEM_PARENT(parent),EAIL_ITEM(action));
569 * @brief Returns the number of implemented ATK actions.
571 * Implementation of AtkActionIface get_n_actions callback.
573 * @param action object that implements AtkAction interface
574 * @returns integer representing number of implemented actions
577 eail_item_n_actions_get(AtkAction *action)
580 /* if parent item does not support click action, then return no action */
581 if (_eail_item_get_actions_supported(action) == EAIL_ACTION_SUPPORTED_NONE)
584 if (_eail_item_get_actions_supported(action) & EAIL_ACTION_SUPPORTED_CLICK)
587 if (_eail_item_get_actions_supported(action) & EAIL_ACTION_SUPPORTED_PRESS)
590 if (_eail_item_get_actions_supported(action) & EAIL_ACTION_SUPPORTED_RELEASE)
593 if (_eail_item_get_actions_supported(action) & EAIL_ACTION_SUPPORTED_EXPAND)
596 if (_eail_item_get_actions_supported(action) & EAIL_ACTION_SUPPORTED_SHRINK)
603 * @brief Returns the accessible name of the specified action
605 * @param action object that implements AtkAction interface
606 * @param i index (number) of action
607 * @returns string containing the accessible name of the specified action
610 eail_item_action_name_get(AtkAction *action, int i)
612 const char* action_name;
614 /* if parent item does not support click action, then return no action */
615 if (_eail_item_get_actions_supported(action) == EAIL_ACTION_SUPPORTED_NONE)
621 /*"click": the user clicked the item*/
622 action_name = EAIL_ITEM_CLICK_NAME;
625 /*"press": the user pressed the item*/
626 action_name = EAIL_ITEM_PRESS_NAME;
629 /*"release": the user pressed the item*/
630 action_name = EAIL_ITEM_RELEASE_NAME;
633 /*"expand": the user expand the item*/
634 action_name = EAIL_ITEM_EXPAND_NAME;
637 /*"shrink": the user shrink the item*/
638 action_name = EAIL_ITEM_SHRINK_NAME;
649 * @brief Gets the clickable Evas_Object for the given EailItem
651 * @param atk_item EailItem instance
653 * @returns clickable Evas_Object or NULL of no clickable content was found
656 _eail_item_get_clickable_evas_obj(AtkObject *atk_item)
658 AtkObject *parent = NULL;
659 Evas_Object *widget = NULL;
660 Eina_List *parts = NULL;
662 parent = atk_object_get_parent(atk_item);
663 g_return_val_if_fail(parent, FALSE);
665 widget = eail_item_parent_get_evas_obj
666 (EAIL_ITEM_PARENT(parent), EAIL_ITEM(atk_item));
667 if (widget) return widget;
669 /* if not supported from parent, then trying to get content widget nested
671 parts = _eail_item_get_part_items(EAIL_ITEM(atk_item));
672 if (parts && eina_list_count(parts) > 0)
674 /* getting first widget from content */
675 widget = eina_list_nth(parts, 0);
677 eina_list_free(parts);
678 if (widget) return widget;
680 /* if no nested widget, then getting evas clickable area */
681 return eail_get_edje_obj_from_item
682 (eail_item_get_item(EAIL_ITEM(atk_item)));
686 * @brief Expand or shrink the item
688 * @param atk_item item object to expand
689 * @param expand info if item should be expanded or shrinked
691 * @returns TRUE if operation was successful, FALSE otherwise
693 static gboolean _eail_item_expand(AtkObject *atk_item, Eina_Bool expand)
695 Elm_Object_Item *item = eail_item_get_item(EAIL_ITEM(atk_item));
697 if(elm_genlist_item_type_get(item) != ELM_GENLIST_ITEM_TREE ||
698 elm_genlist_item_expanded_get(item) == expand)
702 elm_genlist_item_expanded_set(item, expand);
707 * @brief Performs an action with the given name on given item
709 * @param atk_item item object to perform the action on
710 * @param action_name name of the action (eg. 'click', 'press'...)
712 * @returns TRUE if operation was successful, FALSE otherwise
715 _eail_item_perform_action(AtkObject *atk_item, const gchar *action_name)
717 AtkObject *parent = NULL;
718 Evas_Object *widget = NULL;
721 parent = atk_object_get_parent(atk_item);
722 g_return_val_if_fail(parent, FALSE);
724 widget =_eail_item_get_clickable_evas_obj(atk_item);
728 ERR("No widget for click found");
732 if (0 == g_strcmp0(action_name, EAIL_ITEM_CLICK_NAME))
734 DBG("Calling 'click' on item");
735 eail_get_coords_widget_center(widget, &x, &y);
736 eail_mouse_click_on_coords(widget, x, y);
738 else if (0 == g_strcmp0(action_name, EAIL_ITEM_PRESS_NAME))
740 DBG("Calling 'press' on item");
741 eail_get_coords_widget_center(widget, &x, &y);
742 eail_mouse_press_on_coords(widget, x, y);
744 else if (0 == g_strcmp0(action_name, EAIL_ITEM_RELEASE_NAME))
746 DBG("Calling 'release' on item");
747 eail_get_coords_widget_center(widget, &x, &y);
748 eail_mouse_release_on_coords(widget, x, y);
750 else if (0 == g_strcmp0(action_name, EAIL_ITEM_EXPAND_NAME))
752 DBG("Calling 'expand' on item");
753 return _eail_item_expand(atk_item, EINA_TRUE);
755 else if (0 == g_strcmp0(action_name, EAIL_ITEM_SHRINK_NAME))
757 DBG("Calling 'shrink' on item");
758 return _eail_item_expand(atk_item, EINA_FALSE);
762 DBG("Action name not found: %s", action_name);
770 * \brief Launches action with given index
772 * @param action AtkAction instance
773 * @param i index (number) of the action
775 * @returns TRUE if action was successfully launched, FALSE otherwise
778 eail_item_do_action(AtkAction *action, int i)
780 const char *action_name = atk_action_get_name(action, i);
782 /* if parent item does not support click action, then return immediately */
783 if (_eail_item_get_actions_supported(action) == EAIL_ACTION_SUPPORTED_NONE)
786 if (action_name == NULL) return FALSE;
788 return _eail_item_perform_action(ATK_OBJECT(action), action_name);;
792 * @brief Initializer for AtkActionIface
793 * @param iface AtkActionIface instance to fill
796 atk_action_interface_init(AtkActionIface *iface)
798 g_return_if_fail(iface != NULL);
800 iface->do_action = eail_item_do_action;
801 iface->get_n_actions = eail_item_n_actions_get;
802 iface->get_name = eail_item_action_name_get;
806 * @brief Gets text content from item
808 * Implementation of AtkTextIface->get_text callback.
810 * Use g_free() to free the returned.
812 * @param text AtkText instance
813 * @param start_offset start position
814 * @param end_offset end position, or -1 for the end of the string.
816 * @returns a newly allocated string containing the text from start_offset
817 * up to, but not including end_offset.
821 eail_item_get_text(AtkText *text,
825 Eina_Strbuf *buf = NULL;
826 Elm_Object_Item *obj_item = NULL;
827 Eina_List *string_parts = NULL, *l = NULL;
828 gchar *string_part = NULL;
829 gchar *ret_str = NULL;
830 gboolean first_part = TRUE;
832 obj_item = eail_item_get_item(EAIL_ITEM(text));
833 g_return_val_if_fail(obj_item, NULL);
835 string_parts = eail_item_get_content_strings(obj_item);
836 if (!string_parts) return NULL;
838 buf = eina_strbuf_new();
839 EINA_LIST_FOREACH(string_parts, l, string_part)
842 eina_strbuf_append(buf, EAIL_TXT_SEPARATOR);
844 eina_strbuf_append(buf, string_part);
848 /* ret_str is newly allocated */
849 ret_str = eail_get_substring
850 (eina_strbuf_string_get(buf), start_offset, end_offset);
852 eina_list_free(string_parts);
853 eina_strbuf_free(buf);
859 * @brief Gets character from an item at the given offset
861 * Implementation of AtkTextIface->get_character_at_offset callback.
863 * @param text AtkText instance
864 * @param offset offset to get the character from
866 * @returns char located at offset
869 eail_item_get_character_at_offset(AtkText *text, gint offset)
871 gunichar character = '\0';
872 gchar* time_str = NULL;
874 time_str = eail_item_get_text(text, 0, -1);
878 character = g_utf8_get_char
879 (g_utf8_offset_to_pointer(time_str, offset));
887 * @brief Gets the character count from text content in item
889 * Implementation of AtkTextIface->get_character_count callback.
891 * @param text AtkText instance
893 * @returns integer representing the character count
896 eail_item_get_character_count(AtkText *text)
901 str = eail_item_get_text(text, 0, -1);
905 count = g_utf8_strlen(str, -1);
913 * @brief Initializer for AtkTextinterface
914 * @param iface AtkTextIface instance to fill
917 atk_text_interface_init(AtkTextIface *iface)
919 iface->get_text = eail_item_get_text;
920 iface->get_character_at_offset = eail_item_get_character_at_offset;
921 iface->get_character_count = eail_item_get_character_count;