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 Implementation of widget that is being used to represent
23 * list/toolbar/menu items/genlist items/genlist items...
25 * In general - this object represents all kinds of objects that have form of
26 * a Elm_Obj_Item* and need ATK accessible representation. It stores
27 * Elm_Obj_Item* inside - unlike Evas_Object like those components that are
28 * extending EailWidget.
31 #include "eail_item.h"
32 #include "eail_item_parent.h"
33 #include "eail_factory.h"
34 #include "eail_utils.h"
35 #include "eail_priv.h"
37 static void atk_component_interface_init(AtkComponentIface *iface);
38 static void atk_action_interface_init(AtkActionIface *iface);
39 static void atk_text_interface_init(AtkTextIface *iface);
41 #define EAIL_ITEM_CLICK_NAME "click" /**< @brief 'click' action name*/
42 #define EAIL_ITEM_PRESS_NAME "press" /**< @brief 'press' action name*/
43 #define EAIL_ITEM_RELEASE_NAME "release" /**< @brief 'release' action name*/
44 #define EAIL_ITEM_PART_FIRST "start" /**< @brief 'start' action name*/
45 #define EAIL_ITEM_PART_SECOND "end" /**< @brief 'end' action name*/
46 #define EAIL_ITEM_PART_ICON "elm.swallow.icon" /**< @brief icon item part*/
47 #define EAIL_ITEM_PART_END "elm.swallow.end" /**< @brief end item part*/
48 #define EAIL_TXT_SEPARATOR " " /**< @brief separator for text content*/
51 * @brief EailItem GObject definition
53 * It extends ATK_TYPE_OBJECT class and implements ATK_TYPE_COMPONENT,
54 * ATK_TYPE_TEXT and ATK_TYPE_ACTION interfaces
56 G_DEFINE_TYPE_WITH_CODE(EailItem,
59 G_IMPLEMENT_INTERFACE(ATK_TYPE_COMPONENT,
60 atk_component_interface_init)
61 G_IMPLEMENT_INTERFACE(ATK_TYPE_ACTION,
62 atk_action_interface_init)
63 G_IMPLEMENT_INTERFACE(ATK_TYPE_TEXT,
64 atk_text_interface_init));
67 * @brief Initializer for AtkObjectClass
69 * @param obj an AtkObject for EailItem
70 * @param data additional initialization data (Elm_Object_Item *)
73 eail_item_initialize(AtkObject *obj, gpointer data)
75 EailItem *item = EAIL_ITEM(obj);
77 ATK_OBJECT_CLASS(eail_item_parent_class)->initialize(obj, data);
79 item->item = (Elm_Object_Item *)data;
81 /* NOTE: initializing role is being done only in eail_item_new(..) */
86 * @param item EailItem to take nested Elm_Object_Item from
88 * @returns nested Elm_Object_Item * object or NULL in case of error
91 eail_item_get_item(EailItem *item)
93 g_return_val_if_fail(EAIL_IS_ITEM(item), NULL);
99 * Implementation of the *AtkObject* interface
103 * @brief Gets name of EailItem
105 * Implementation of AtkObject->get_name callback
108 * Gets the accessible name of the accessible.
110 * @param obj an AtkObject for EailItem
111 * @returns a character string representing the accessible description of
115 eail_item_get_name(AtkObject *obj)
117 const gchar *atk_name_default = NULL;
118 AtkObject *parent = atk_object_get_parent(obj);
120 if (!parent) return NULL;
122 /* returning name from default atk implementation when it available (it
123 * means that it was set by user)*/
124 atk_name_default = ATK_OBJECT_CLASS(eail_item_parent_class)->get_name(obj);
125 if (atk_name_default) return atk_name_default;
127 return eail_item_parent_get_item_name(EAIL_ITEM_PARENT(parent),
132 * @brief Implementation of AtkObject->get_role callback
134 * @param obj an AtkObject for EailItem
135 * @returns ATK role of target item in the list
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 Implementation of AtkObject->get_index_in_parent callback
152 * Gets the 0-based index of this accessible in its parent; returns -1 if the
153 * accessible does not have an accessible parent.
154 * @param obj an AtkObject for EailItem
155 * @returns an integer which is the index of the accessible in its parent
158 eail_item_get_index_in_parent(AtkObject *obj)
160 AtkObject *parent = atk_object_get_parent(obj);
162 if (!parent) return -1;
164 return eail_item_parent_get_item_index_in_parent(EAIL_ITEM_PARENT(parent),
169 * @brief Implementation of AtkObject->ref_state_set callback
172 * Gets a reference to the state set of the accessible; the caller must
173 * unreference it when it is no longer needed.
175 * @param obj an AtkObject for EailItem
176 * @returns a reference to an AtkStateSet which is the state set of the
180 eail_item_ref_state_set(AtkObject *obj)
182 AtkStateSet *state_set;
183 AtkObject *parent = atk_object_get_parent(obj);
184 EailItem *item = EAIL_ITEM(obj);
185 Elm_Object_Item *it = eail_item_get_item(item);
187 state_set = ATK_OBJECT_CLASS(eail_item_parent_class)->ref_state_set(obj);
191 atk_state_set_add_state(state_set, ATK_STATE_DEFUNCT);
195 if (!elm_object_item_disabled_get(it))
197 atk_state_set_add_state(state_set, ATK_STATE_SENSITIVE);
198 atk_state_set_add_state(state_set, ATK_STATE_ENABLED);
201 return eail_item_parent_ref_item_state_set(EAIL_ITEM_PARENT(parent),
207 * @brief Initializer for GObject class
208 * @param item an EailItem object
211 eail_item_init(EailItem *item)
216 * @brief Finalize implementation for GObject class
217 * @param obj an AtkObject for EailItem that needs to be finalized
220 eail_item_class_finalize(GObject *obj)
222 EailItem *eail_item = EAIL_ITEM(obj);
223 Elm_Object_Item *obj_item = eail_item_get_item(eail_item);
226 eail_factory_unregister_item_from_cache(obj_item);
228 G_OBJECT_CLASS(eail_item_parent_class)->finalize(obj);
232 * @brief Helper function used for adding content Evas_Object* part if part
233 * with given name is available
235 * @param eail_obj_item item used for lookup for given content part
236 * @param items list of items that will be extended by found part
237 * @param part_str name of part to be found
239 * @returns filled list with Evas_Object* objects
242 _eail_item_append_part_if_exist(Elm_Object_Item *eail_obj_item,
246 Evas_Object *content = NULL;
248 content = elm_object_item_part_content_get
249 (eail_obj_item, part_str);
251 items = eina_list_append(items, content);
257 * @brief Checks if content_get is supported by given EailItem object
259 * @param atk_object an AtkObject
261 * @returns TRUE if content_get is supported, FALSE otherwise
264 _eail_item_is_content_get_supported(AtkObject *atk_object)
266 AtkObject *parent = NULL;
269 parent = atk_object_get_parent(atk_object);
270 g_return_val_if_fail(parent, FALSE);
272 return eail_item_parent_is_is_content_get_supported
273 (EAIL_ITEM_PARENT(parent),EAIL_ITEM(atk_object));
277 * @brief Gets list of item content parts with well documented content strings
279 * @param eail_item item to get content from
281 * @returns list filled with Evas_Object* that has been found
284 _eail_item_get_basic_parts(EailItem *eail_item)
286 Eina_List *items = NULL;
287 Elm_Object_Item *obj_item = NULL;
289 /* if content_get is not supported then content from basic parts will be
290 * always empty. This checking is being done, because elementary library
291 * don't want to ask for some widget usign content_get API */
292 if (!_eail_item_is_content_get_supported(ATK_OBJECT(eail_item)))
295 obj_item = eail_item_get_item(EAIL_ITEM(eail_item));
296 items = _eail_item_append_part_if_exist
297 (obj_item, items, EAIL_ITEM_PART_FIRST);
298 items = _eail_item_append_part_if_exist
299 (obj_item, items, EAIL_ITEM_PART_SECOND);
300 items = _eail_item_append_part_if_exist
301 (obj_item, items, EAIL_ITEM_PART_ICON);
302 items = _eail_item_append_part_if_exist
303 (obj_item, items, EAIL_ITEM_PART_END);
309 * @brief Helper function that adds unique entries from one list to another
311 * @param item_list source list, unique items will be appended here
312 * @param additional_items list with potential items to add
314 * @returns result list filled with unique entries from both lists
317 _eail_add_unique_listsparts(Eina_List *item_list,
318 Eina_List *additional_items)
321 Elm_Object_Item *item_edj;
323 EINA_LIST_FOREACH(additional_items, l, item_edj)
325 if (!eina_list_data_find(item_list, item_edj))
326 item_list = eina_list_append(item_list, item_edj);
333 * @brief Gets content part items from given EailItem object
335 * @param eail_item EailItem object used to get content from
337 * @returns content parts (Evas_Object *) for given item. List should be freed
338 * when results will be processed.
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 Implementation AtkObject->get_n_children callback
370 * Gets the number of accessible children of the accessible.
372 * @param obj an AtkObject (EailItem)
374 * @returns an integer representing the number of accessible children of
378 eail_item_get_n_children(AtkObject *obj)
380 gint n_items, parent_n_items;
382 AtkObject *parent = atk_object_get_parent(obj);
384 if (!parent) return 0;
385 parent_n_items = eail_item_parent_get_n_children(EAIL_ITEM_PARENT(parent),
387 /* if there is item parent impl, then returning item count
389 if (parent_n_items != -1) return parent_n_items;
391 items = _eail_item_get_part_items(EAIL_ITEM(obj));
392 n_items = eina_list_count(items);
394 eina_list_free(items);
400 * @brief Implementation AtkObject->ref_child callback
403 * Gets a reference to the specified accessible child of the object. The
404 * accessible children are 0-based so the first accessible child is at index 0,
405 * the second at index 1 and so on.
407 * @param obj an AtkObject for EailItem
408 * @param i index of item to ref
410 * @returns an AtkObject representing the specified accessible child of the
414 eail_item_ref_child(AtkObject *obj, gint i)
417 AtkObject *child = NULL;
418 AtkObject *parent = atk_object_get_parent(obj);
419 AtkObject *ref_child_from_parent = NULL;
421 if (!parent) return NULL;
423 ref_child_from_parent = eail_item_parent_ref_n_child
424 (EAIL_ITEM_PARENT(parent), EAIL_ITEM(obj), i);
426 /* if there is item parent implementation for ref child, then returning
427 * object using that parent implementation*/
428 if (ref_child_from_parent)
430 g_object_ref(ref_child_from_parent);
431 return ref_child_from_parent;
434 items = _eail_item_get_part_items(EAIL_ITEM(obj));
435 if (eina_list_count(items) > i)
436 child = eail_factory_get_accessible(eina_list_nth(items, i));
438 ERR("Tried to ref child with index %d out of bounds!", i);
440 eina_list_free(items);
445 DBG("Child could not created in factory");
451 * @brief Initializer for GObject class (defines callbacks for base AtkObject)
452 * @param klass an EailItemClass
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 Implementation of AtkComponent->grab_focus callback
479 * Grabs focus for this component.
481 * @param component an AtkComponent (EailItem)
482 * @returns TRUE if successful, FALSE otherwise.
485 eail_item_grab_focus(AtkComponent *component)
487 AtkObject *obj = ATK_OBJECT(component);
488 AtkObject *parent = atk_object_get_parent(obj);
490 if (!parent) return FALSE;
492 return eail_item_parent_grab_item_focus(EAIL_ITEM_PARENT(parent),
497 * @brief Implementation of AtkComponent->get_extents callback
500 * Gets the rectangle which gives the extent of the component.
502 * @param component an AtkComponent
503 * @param x address of gint to put x coordinate
504 * @param y address of gint to put y coordinate
505 * @param width ddress of gint to put width
506 * @param height address of gint to put height
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 AtkComponetn interface initialization
534 * @param iface EailNaviframPage object
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 an AtkAction
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 number of implemented ATK Actions.
570 * Implementation of AtkActionIface get_n_actions callback
572 * @param action object that implements AtkAction interface
573 * @returns number of implemented actions
576 eail_item_n_actions_get(AtkAction *action)
579 /* if parent item does not support click action, then return no action */
580 if (_eail_item_get_actions_supported(action) == EAIL_ACTION_SUPPORTED_NONE)
583 if (_eail_item_get_actions_supported(action) & EAIL_ACTION_SUPPORTED_CLICK)
586 if (_eail_item_get_actions_supported(action) & EAIL_ACTION_SUPPORTED_PRESS)
589 if (_eail_item_get_actions_supported(action) & EAIL_ACTION_SUPPORTED_RELEASE)
596 * @brief Returns EailActionObj of action with given index
598 * @param action object that implements AtkAction interface
599 * @param i index (number) of action
600 * @returns i-th action name
603 eail_item_action_name_get(AtkAction *action, int i)
605 const char* action_name;
607 /* if parent item does not support click action, then return no action */
608 if (_eail_item_get_actions_supported(action) == EAIL_ACTION_SUPPORTED_NONE)
614 /*"click": the user clicked the item*/
615 action_name = EAIL_ITEM_CLICK_NAME;
618 /*"press": the user pressed the item*/
619 action_name = EAIL_ITEM_PRESS_NAME;
622 /*"release": the user pressed the item*/
623 action_name = EAIL_ITEM_RELEASE_NAME;
634 * @brief Gets clickable Evas_Object for given EailItem
636 * @param atk_item an EailItem object
638 * @returns Evas_Object * to click on or NULL of no clickable content was found
641 _eail_item_get_clickable_evas_obj(AtkObject *atk_item)
643 AtkObject *parent = NULL;
644 Evas_Object *widget = NULL;
645 Eina_List *parts = NULL;
647 parent = atk_object_get_parent(atk_item);
648 g_return_val_if_fail(parent, FALSE);
650 widget = eail_item_parent_get_evas_obj
651 (EAIL_ITEM_PARENT(parent), EAIL_ITEM(atk_item));
652 if (widget) return widget;
654 /* if not supported from parent, then trying to get content widget nested
656 parts = _eail_item_get_part_items(EAIL_ITEM(atk_item));
657 if (parts && eina_list_count(parts) > 0)
659 /* getting first widget from content */
660 widget = eina_list_nth(parts, 0);
662 eina_list_free(parts);
663 if (widget) return widget;
665 /* if no nested widget, then getting evas clickable area */
666 parts = eail_get_raw_evas_obj_list_from_item
667 (eail_item_get_item(EAIL_ITEM(atk_item)));
669 if (parts && eina_list_count(parts) > 0)
671 /* getting first widget from content */
672 widget = eina_list_nth(parts, 0);
680 * @brief Performs action with name on given item
682 * @param atk_item item object to do action on
683 * @param action_name name of action (eg. 'click', 'press'...)
685 * @returns TRUE if operation was successful, FALSE otherwise
688 _eail_item_perform_action(AtkObject *atk_item, const gchar *action_name)
690 AtkObject *parent = NULL;
691 Evas_Object *widget = NULL;
694 parent = atk_object_get_parent(atk_item);
695 g_return_val_if_fail(parent, FALSE);
697 widget =_eail_item_get_clickable_evas_obj(atk_item);
701 ERR("No widget for click found");
705 /* getting coordinates of center of the widget to make sure, that
706 * click will be performed on active widget area */
707 eail_get_coords_widget_center(widget, &x, &y);
709 if (0 == g_strcmp0(action_name, EAIL_ITEM_CLICK_NAME))
711 DBG("Calling 'click' on item");
712 eail_mouse_click_on_coords(widget, x, y);
714 else if (0 == g_strcmp0(action_name, EAIL_ITEM_PRESS_NAME))
716 DBG("Calling 'press' on item");
717 eail_mouse_press_on_coords(widget, x, y);
719 else if (0 == g_strcmp0(action_name, EAIL_ITEM_RELEASE_NAME))
721 DBG("Calling 'release' on item");
722 eail_mouse_release_on_coords(widget, x, y);
726 DBG("Action name not found: %s", action_name);
734 * \brief Launches action with given index
736 * @param action object that implements AtkAction interface
737 * @param i index (number) of action
739 * @returns TRUE if action was successfully launched, FALSE otherwise
742 eail_item_do_action(AtkAction *action, int i)
744 const char *action_name = atk_action_get_name(action, i);
746 /* if parent item does not support click action, then return immediately */
747 if (_eail_item_get_actions_supported(action) == EAIL_ACTION_SUPPORTED_NONE)
750 if (action_name == NULL) return FALSE;
752 return _eail_item_perform_action(ATK_OBJECT(action), action_name);;
756 * @brief Initializer for AtkActionIface
757 * @param iface an AtkActionIface to fill
760 atk_action_interface_init(AtkActionIface *iface)
762 g_return_if_fail(iface != NULL);
764 iface->do_action = eail_item_do_action;
765 iface->get_n_actions = eail_item_n_actions_get;
766 iface->get_name = eail_item_action_name_get;
770 * @brief Gets text content from item
772 * Implementation of AtkTextIface->get_text callback\n
774 * @param text an AtkText
775 * @param start_offset start position
776 * @param end_offset end position, or -1 for the end of the string.
778 * @returns a newly allocated string containing the text from start_offset
779 * up to, but not including end_offset. Use g_free() to free the returned
783 eail_item_get_text(AtkText *text,
787 Eina_Strbuf *buf = NULL;
788 Elm_Object_Item *obj_item = NULL;
789 Eina_List *string_parts = NULL, *l = NULL;
790 gchar *string_part = NULL;
791 gchar *ret_str = NULL;
792 gboolean first_part = TRUE;
794 obj_item = eail_item_get_item(EAIL_ITEM(text));
795 g_return_val_if_fail(obj_item, NULL);
797 string_parts = eail_item_get_content_strings(obj_item);
798 if (!string_parts) return NULL;
800 buf = eina_strbuf_new();
801 EINA_LIST_FOREACH(string_parts, l, string_part)
804 eina_strbuf_append(buf, EAIL_TXT_SEPARATOR);
806 eina_strbuf_append(buf, string_part);
810 /* ret_str is newly allocated */
811 ret_str = eail_get_substring
812 (eina_strbuf_string_get(buf), start_offset, end_offset);
814 eina_list_free(string_parts);
815 eina_strbuf_free(buf);
821 * @brief Gets character from item at given offset
823 * Implementation of AtkTextIface->get_character_at_offset callback
825 * @param text an AtkText
826 * @param offset an offset
828 * @returns the character at offset.
831 eail_item_get_character_at_offset(AtkText *text, gint offset)
833 gunichar character = '\0';
834 gchar* time_str = NULL;
836 time_str = eail_item_get_text(text, 0, -1);
840 character = g_utf8_get_char
841 (g_utf8_offset_to_pointer(time_str, offset));
849 * @brief Gets character count from text content in item
850 * Implementation of AtkTextIface->get_character_count callback
852 * @param text an AtkText
854 * @returns the character count
857 eail_item_get_character_count(AtkText *text)
862 str = eail_item_get_text(text, 0, -1);
866 count = g_utf8_strlen(str, -1);
874 * @brief Initializer for AtkTextinterface
875 * @param iface an AtkTextIface to fill
878 atk_text_interface_init(AtkTextIface *iface)
880 iface->get_text = eail_item_get_text;
881 iface->get_character_at_offset = eail_item_get_character_at_offset;
882 iface->get_character_count = eail_item_get_character_count;