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_PART_FIRST "start" /**< @brief 'start' action name*/
44 #define EAIL_ITEM_PART_SECOND "end" /**< @brief 'end' action name*/
45 #define EAIL_ITEM_PART_ICON "elm.swallow.icon" /**< @brief icon item part*/
46 #define EAIL_ITEM_PART_END "elm.swallow.end" /**< @brief end item part*/
47 #define EAIL_TXT_SEPARATOR " " /**< @brief separator for text content*/
50 * @brief EailItem GObject definition
52 * It extends ATK_TYPE_OBJECT class and implements ATK_TYPE_COMPONENT,
53 * ATK_TYPE_TEXT and ATK_TYPE_ACTION interfaces
55 G_DEFINE_TYPE_WITH_CODE(EailItem,
58 G_IMPLEMENT_INTERFACE(ATK_TYPE_COMPONENT,
59 atk_component_interface_init)
60 G_IMPLEMENT_INTERFACE(ATK_TYPE_ACTION,
61 atk_action_interface_init)
62 G_IMPLEMENT_INTERFACE(ATK_TYPE_TEXT,
63 atk_text_interface_init));
66 * @brief Initializer for AtkObjectClass
68 * @param obj AtkObject instance
69 * @param data additional initialization data
72 eail_item_initialize(AtkObject *obj, gpointer data)
74 EailItem *item = EAIL_ITEM(obj);
76 ATK_OBJECT_CLASS(eail_item_parent_class)->initialize(obj, data);
78 item->item = (Elm_Object_Item *)data;
80 /* NOTE: initializing role is being done only in eail_item_new(..) */
85 * @param item EailItem to take nested Elm_Object_Item from
87 * @returns Elm_Object_Item * representing nested item or NULL if an error occured
90 eail_item_get_item(EailItem *item)
92 g_return_val_if_fail(EAIL_IS_ITEM(item), NULL);
98 * Implementation of the *AtkObject* interface
102 * @brief Gets the accessible name of the accessible.
104 * Implementation of AtkObject->get_name callback.
106 * @param obj AtkObject for EailItem
107 * @returns string representing the accessible description of
111 eail_item_get_name(AtkObject *obj)
113 const gchar *atk_name_default = NULL;
114 AtkObject *parent = atk_object_get_parent(obj);
116 if (!parent) return NULL;
118 /* returning name from default atk implementation when it available (it
119 * means that it was set by user)*/
120 atk_name_default = ATK_OBJECT_CLASS(eail_item_parent_class)->get_name(obj);
121 if (atk_name_default) return atk_name_default;
123 return eail_item_parent_get_item_name(EAIL_ITEM_PARENT(parent),
128 * @brief Gets the role of the accessible
130 * Implementation of AtkObject->get_role callback.
132 * @param obj AtkObject for EailItem
133 * @returns AtkRole representing the parameter's role
136 eail_item_get_role(AtkObject *obj)
138 AtkObject *parent = atk_object_get_parent(obj);
140 if (!parent) return ATK_ROLE_INVALID;
142 return eail_item_parent_get_item_role(EAIL_ITEM_PARENT(parent),
147 * @brief Gets the 0-based index of this accessible in its parent.
149 * Implementation of AtkObject->get_index_in_parent callback.
151 * @param obj AtkObject for EailItem
152 * @returns integer representing the index of the accessible in its parent or -1 if the accessible does not have an accessible parent
155 eail_item_get_index_in_parent(AtkObject *obj)
157 AtkObject *parent = atk_object_get_parent(obj);
159 if (!parent) return -1;
161 return eail_item_parent_get_item_index_in_parent(EAIL_ITEM_PARENT(parent),
166 * @brief Gets a reference to the state set of the accessible.
168 * The caller must unreference it when it is no longer needed.
170 * Implementation of AtkObject->ref_state_set callback.
172 * @param obj AtkObject for EailItem
173 * @returns AtkStateSet containing a reference to the state set of the
177 eail_item_ref_state_set(AtkObject *obj)
179 AtkStateSet *state_set;
180 AtkObject *parent = atk_object_get_parent(obj);
181 EailItem *item = EAIL_ITEM(obj);
182 Elm_Object_Item *it = eail_item_get_item(item);
184 state_set = ATK_OBJECT_CLASS(eail_item_parent_class)->ref_state_set(obj);
188 atk_state_set_add_state(state_set, ATK_STATE_DEFUNCT);
192 if (!elm_object_item_disabled_get(it))
194 atk_state_set_add_state(state_set, ATK_STATE_SENSITIVE);
195 atk_state_set_add_state(state_set, ATK_STATE_ENABLED);
198 return eail_item_parent_ref_item_state_set(EAIL_ITEM_PARENT(parent),
204 * @brief Initializer for GObject class
205 * @param item EailItem instance
208 eail_item_init(EailItem *item)
213 * @brief Finalizes implementation for GObject class
214 * @param obj AtkObject for EailItem instance that needs to be finalized
217 eail_item_class_finalize(GObject *obj)
219 EailItem *eail_item = EAIL_ITEM(obj);
220 Elm_Object_Item *obj_item = eail_item_get_item(eail_item);
223 eail_factory_unregister_item_from_cache(obj_item);
225 G_OBJECT_CLASS(eail_item_parent_class)->finalize(obj);
229 * @brief Helper function used for adding Evas_Object* content to the item's list
230 * if the content is available.
232 * @param eail_obj_item item used for browsing for given content part
233 * @param items list of items that will be extended by the content part
234 * @param part_str name of content part to be found
236 * @returns Eina_List filled with Evas_Object* objects
239 _eail_item_append_part_if_exist(Elm_Object_Item *eail_obj_item,
243 Evas_Object *content = NULL;
245 content = elm_object_item_part_content_get
246 (eail_obj_item, part_str);
248 items = eina_list_append(items, content);
254 * @brief Checks if content_get is supported by the given EailItem object
256 * @param atk_object AtkObject instance
258 * @returns TRUE if content_get is supported, FALSE otherwise
261 _eail_item_is_content_get_supported(AtkObject *atk_object)
263 AtkObject *parent = NULL;
266 parent = atk_object_get_parent(atk_object);
267 g_return_val_if_fail(parent, FALSE);
269 return eail_item_parent_is_is_content_get_supported
270 (EAIL_ITEM_PARENT(parent),EAIL_ITEM(atk_object));
274 * @brief Gets list of item's content parts with well documented content strings
276 * @param eail_item item to get content from
278 * @returns Eina_List filled with the content parts that have been found
281 _eail_item_get_basic_parts(EailItem *eail_item)
283 Eina_List *items = NULL;
284 Elm_Object_Item *obj_item = NULL;
286 /* if content_get is not supported then content from basic parts will be
287 * always empty. This checking is being done, because elementary library
288 * don't want to ask for some widget usign content_get API */
289 if (!_eail_item_is_content_get_supported(ATK_OBJECT(eail_item)))
292 obj_item = eail_item_get_item(EAIL_ITEM(eail_item));
293 items = _eail_item_append_part_if_exist
294 (obj_item, items, EAIL_ITEM_PART_FIRST);
295 items = _eail_item_append_part_if_exist
296 (obj_item, items, EAIL_ITEM_PART_SECOND);
297 items = _eail_item_append_part_if_exist
298 (obj_item, items, EAIL_ITEM_PART_ICON);
299 items = _eail_item_append_part_if_exist
300 (obj_item, items, EAIL_ITEM_PART_END);
306 * @brief Helper function for adding unique entries from one list to another
308 * @param item_list list which unique items will be appended to
309 * @param additional_items source list with items to be added to item_list
311 * @returns Eina_List filled with unique entries from both lists
314 _eail_add_unique_listsparts(Eina_List *item_list,
315 Eina_List *additional_items)
318 Elm_Object_Item *item_edj;
320 EINA_LIST_FOREACH(additional_items, l, item_edj)
322 if (!eina_list_data_find(item_list, item_edj))
323 item_list = eina_list_append(item_list, item_edj);
330 * @brief Gets content part items from the given EailItem object
332 * List should be freed when results will be processed.
334 * @param eail_item EailItem object used to get content from
336 * @returns Eina_List containing content parts for the given item.
339 _eail_item_get_part_items(EailItem *eail_item)
341 Eina_List *items = NULL, *edje_items = NULL;
342 Elm_Object_Item *obj_item = NULL;
344 obj_item = eail_item_get_item(eail_item);
345 g_return_val_if_fail(obj_item, NULL);
347 /* parts from well documented default content parts - there are being used
348 * mostly by desktop applications. Some of these parts are not listed in
349 * edje objects (used below), so results from both list need to be merged
350 * to list full item content */
351 items = _eail_item_get_basic_parts(eail_item);
353 /* content parts from taken from edje object*/
354 edje_items = eail_get_edje_parts_for_item(obj_item);
356 /* adding unique edje items to content list*/
357 items = _eail_add_unique_listsparts(items, edje_items);
359 eina_list_free(edje_items);
365 * @brief Gets the number of accessible children of the accessible.
367 * Implementation AtkObject->get_n_children callback.
369 * @param obj AtkObject (EailItem) instance
371 * @returns integer representing the number of accessible children of
375 eail_item_get_n_children(AtkObject *obj)
377 gint n_items, parent_n_items;
379 AtkObject *parent = atk_object_get_parent(obj);
381 if (!parent) return 0;
382 parent_n_items = eail_item_parent_get_n_children(EAIL_ITEM_PARENT(parent),
384 /* if there is item parent impl, then returning item count
386 if (parent_n_items != -1) return parent_n_items;
388 items = _eail_item_get_part_items(EAIL_ITEM(obj));
389 n_items = eina_list_count(items);
391 eina_list_free(items);
397 * @brief Gets a reference to the specified accessible child of the object.
399 * The accessible children are 0-based so the first accessible child is at index 0,
400 * the second at index 1 and so on.
402 * Implementation of AtkObject->ref_child callback.
404 * @param obj AtkObject for EailItem instance
405 * @param i index of item to reference
407 * @returns AtkObject representing the specified accessible child of the
411 eail_item_ref_child(AtkObject *obj, gint i)
414 AtkObject *child = NULL;
415 AtkObject *parent = atk_object_get_parent(obj);
416 AtkObject *ref_child_from_parent = NULL;
418 if (!parent) return NULL;
420 ref_child_from_parent = eail_item_parent_ref_n_child
421 (EAIL_ITEM_PARENT(parent), EAIL_ITEM(obj), i);
423 /* if there is item parent implementation for ref child, then returning
424 * object using that parent implementation*/
425 if (ref_child_from_parent)
427 g_object_ref(ref_child_from_parent);
428 return ref_child_from_parent;
431 items = _eail_item_get_part_items(EAIL_ITEM(obj));
432 if (eina_list_count(items) > i)
433 child = eail_factory_get_accessible(eina_list_nth(items, i));
435 ERR("Tried to ref child with index %d out of bounds!", i);
437 eina_list_free(items);
442 DBG("Child could not created in factory");
448 * @brief Initializer for GObject class
450 * Defines callbacks for base AtkObject.
452 * @param klass EailItemClass instance
455 eail_item_class_init(EailItemClass *klass)
457 AtkObjectClass *class = ATK_OBJECT_CLASS(klass);
458 GObjectClass *g_object_class = G_OBJECT_CLASS(klass);
460 class->initialize = eail_item_initialize;
461 class->get_name = eail_item_get_name;
462 class->get_role = eail_item_get_role;
463 class->get_index_in_parent = eail_item_get_index_in_parent;
464 class->ref_state_set = eail_item_ref_state_set;
465 class->get_n_children = eail_item_get_n_children;
466 class->ref_child = eail_item_ref_child;
468 g_object_class->finalize = eail_item_class_finalize;
472 * Implementation of the *AtkComponent* interface
476 * @brief Grabs focus for this component.
478 * Implementation of AtkComponent->grab_focus callback.
480 * @param component AtkComponent (EailItem) instance
481 * @returns TRUE on success, FALSE otherwise.
484 eail_item_grab_focus(AtkComponent *component)
486 AtkObject *obj = ATK_OBJECT(component);
487 AtkObject *parent = atk_object_get_parent(obj);
489 if (!parent) return FALSE;
491 return eail_item_parent_grab_item_focus(EAIL_ITEM_PARENT(parent),
496 * @brief Gets the rectangle which gives the extent of the component.
498 * Implementation of AtkComponent->get_extents callback.
500 * @param component AtkComponent instance
501 * @param [out] x rectangle upper left x coordinate
502 * @param [out] y rectangle upper left y coordinate
503 * @param [out] width width of the rectangle
504 * @param [out] height height of the rectangle
505 * @param coord_type specifies whether the coordinates are relative to the
506 * screen or to the components top level window
509 eail_item_get_extents(AtkComponent *component,
514 AtkCoordType coord_type)
516 AtkObject *obj = ATK_OBJECT(component);
517 AtkObject *parent = atk_object_get_parent(obj);
520 *width = *height = -1;
524 eail_item_parent_get_item_extents(EAIL_ITEM_PARENT(parent),
526 x, y, width, height, coord_type);
530 * @brief AtkComponent interface initialization
532 * @param iface EailNaviframPage instance
535 atk_component_interface_init(AtkComponentIface *iface)
537 iface->grab_focus = eail_item_grab_focus;
538 iface->get_extents = eail_item_get_extents;
542 * AtkAction interface init
546 * @brief Gets actions supported by given EailItem object
548 * @param action AtkAction instance
550 * @returns EailActionSupported enum entry that shows what kind of actions are
554 _eail_item_get_actions_supported(AtkAction *action)
556 AtkObject *parent = NULL;
558 parent = atk_object_get_parent(ATK_OBJECT(action));
559 g_return_val_if_fail(parent, FALSE);
561 return eail_item_parent_get_actions_supported
562 (EAIL_ITEM_PARENT(parent),EAIL_ITEM(action));
567 * @brief Returns the number of implemented ATK actions.
569 * Implementation of AtkActionIface get_n_actions callback.
571 * @param action object that implements AtkAction interface
572 * @returns integer representing number of implemented actions
575 eail_item_n_actions_get(AtkAction *action)
578 /* if parent item does not support click action, then return no action */
579 if (_eail_item_get_actions_supported(action) == EAIL_ACTION_SUPPORTED_NONE)
582 if (_eail_item_get_actions_supported(action) & EAIL_ACTION_SUPPORTED_CLICK)
585 if (_eail_item_get_actions_supported(action) & EAIL_ACTION_SUPPORTED_PRESS)
588 if (_eail_item_get_actions_supported(action) & EAIL_ACTION_SUPPORTED_RELEASE)
595 * @brief Returns the accessible name of the specified action
597 * @param action object that implements AtkAction interface
598 * @param i index (number) of action
599 * @returns string containing the accessible name of the specified action
602 eail_item_action_name_get(AtkAction *action, int i)
604 const char* action_name;
606 /* if parent item does not support click action, then return no action */
607 if (_eail_item_get_actions_supported(action) == EAIL_ACTION_SUPPORTED_NONE)
613 /*"click": the user clicked the item*/
614 action_name = EAIL_ITEM_CLICK_NAME;
617 /*"press": the user pressed the item*/
618 action_name = EAIL_ITEM_PRESS_NAME;
621 /*"release": the user pressed the item*/
622 action_name = EAIL_ITEM_RELEASE_NAME;
633 * @brief Gets the clickable Evas_Object for the given EailItem
635 * @param atk_item EailItem instance
637 * @returns clickable Evas_Object or NULL of no clickable content was found
640 _eail_item_get_clickable_evas_obj(AtkObject *atk_item)
642 AtkObject *parent = NULL;
643 Evas_Object *widget = NULL;
644 Eina_List *parts = NULL;
646 parent = atk_object_get_parent(atk_item);
647 g_return_val_if_fail(parent, FALSE);
649 widget = eail_item_parent_get_evas_obj
650 (EAIL_ITEM_PARENT(parent), EAIL_ITEM(atk_item));
651 if (widget) return widget;
653 /* if not supported from parent, then trying to get content widget nested
655 parts = _eail_item_get_part_items(EAIL_ITEM(atk_item));
656 if (parts && eina_list_count(parts) > 0)
658 /* getting first widget from content */
659 widget = eina_list_nth(parts, 0);
661 eina_list_free(parts);
662 if (widget) return widget;
664 /* if no nested widget, then getting evas clickable area */
665 parts = eail_get_raw_evas_obj_list_from_item
666 (eail_item_get_item(EAIL_ITEM(atk_item)));
668 if (parts && eina_list_count(parts) > 0)
670 /* getting first widget from content */
671 widget = eina_list_nth(parts, 0);
679 * @brief Performs an action with the given name on given item
681 * @param atk_item item object to perform the action on
682 * @param action_name name of the action (eg. 'click', 'press'...)
684 * @returns TRUE if operation was successful, FALSE otherwise
687 _eail_item_perform_action(AtkObject *atk_item, const gchar *action_name)
689 AtkObject *parent = NULL;
690 Evas_Object *widget = NULL;
693 parent = atk_object_get_parent(atk_item);
694 g_return_val_if_fail(parent, FALSE);
696 widget =_eail_item_get_clickable_evas_obj(atk_item);
700 ERR("No widget for click found");
704 /* getting coordinates of center of the widget to make sure, that
705 * click will be performed on active widget area */
706 eail_get_coords_widget_center(widget, &x, &y);
708 if (0 == g_strcmp0(action_name, EAIL_ITEM_CLICK_NAME))
710 DBG("Calling 'click' on item");
711 eail_mouse_click_on_coords(widget, x, y);
713 else if (0 == g_strcmp0(action_name, EAIL_ITEM_PRESS_NAME))
715 DBG("Calling 'press' on item");
716 eail_mouse_press_on_coords(widget, x, y);
718 else if (0 == g_strcmp0(action_name, EAIL_ITEM_RELEASE_NAME))
720 DBG("Calling 'release' on item");
721 eail_mouse_release_on_coords(widget, x, y);
725 DBG("Action name not found: %s", action_name);
733 * \brief Launches action with given index
735 * @param action AtkAction instance
736 * @param i index (number) of the action
738 * @returns TRUE if action was successfully launched, FALSE otherwise
741 eail_item_do_action(AtkAction *action, int i)
743 const char *action_name = atk_action_get_name(action, i);
745 /* if parent item does not support click action, then return immediately */
746 if (_eail_item_get_actions_supported(action) == EAIL_ACTION_SUPPORTED_NONE)
749 if (action_name == NULL) return FALSE;
751 return _eail_item_perform_action(ATK_OBJECT(action), action_name);;
755 * @brief Initializer for AtkActionIface
756 * @param iface AtkActionIface instance to fill
759 atk_action_interface_init(AtkActionIface *iface)
761 g_return_if_fail(iface != NULL);
763 iface->do_action = eail_item_do_action;
764 iface->get_n_actions = eail_item_n_actions_get;
765 iface->get_name = eail_item_action_name_get;
769 * @brief Gets text content from item
771 * Implementation of AtkTextIface->get_text callback.
773 * Use g_free() to free the returned.
775 * @param text AtkText instance
776 * @param start_offset start position
777 * @param end_offset end position, or -1 for the end of the string.
779 * @returns a newly allocated string containing the text from start_offset
780 * up to, but not including end_offset.
784 eail_item_get_text(AtkText *text,
788 Eina_Strbuf *buf = NULL;
789 Elm_Object_Item *obj_item = NULL;
790 Eina_List *string_parts = NULL, *l = NULL;
791 gchar *string_part = NULL;
792 gchar *ret_str = NULL;
793 gboolean first_part = TRUE;
795 obj_item = eail_item_get_item(EAIL_ITEM(text));
796 g_return_val_if_fail(obj_item, NULL);
798 string_parts = eail_item_get_content_strings(obj_item);
799 if (!string_parts) return NULL;
801 buf = eina_strbuf_new();
802 EINA_LIST_FOREACH(string_parts, l, string_part)
805 eina_strbuf_append(buf, EAIL_TXT_SEPARATOR);
807 eina_strbuf_append(buf, string_part);
811 /* ret_str is newly allocated */
812 ret_str = eail_get_substring
813 (eina_strbuf_string_get(buf), start_offset, end_offset);
815 eina_list_free(string_parts);
816 eina_strbuf_free(buf);
822 * @brief Gets character from an item at the given offset
824 * Implementation of AtkTextIface->get_character_at_offset callback.
826 * @param text AtkText instance
827 * @param offset offset to get the character from
829 * @returns char located at offset
832 eail_item_get_character_at_offset(AtkText *text, gint offset)
834 gunichar character = '\0';
835 gchar* time_str = NULL;
837 time_str = eail_item_get_text(text, 0, -1);
841 character = g_utf8_get_char
842 (g_utf8_offset_to_pointer(time_str, offset));
850 * @brief Gets the character count from text content in item
852 * Implementation of AtkTextIface->get_character_count callback.
854 * @param text AtkText instance
856 * @returns integer representing the character count
859 eail_item_get_character_count(AtkText *text)
864 str = eail_item_get_text(text, 0, -1);
868 count = g_utf8_strlen(str, -1);
876 * @brief Initializer for AtkTextinterface
877 * @param iface AtkTextIface instance to fill
880 atk_text_interface_init(AtkTextIface *iface)
882 iface->get_text = eail_item_get_text;
883 iface->get_character_at_offset = eail_item_get_character_at_offset;
884 iface->get_character_count = eail_item_get_character_count;