Merge "custom eail widget implementation" into tizen
[platform/core/uifw/eail.git] / eail / eail_list.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_list.c
22  * @brief EailList implementation
23  */
24
25 #include <Elementary.h>
26
27 #include "eail_list.h"
28 #include "eail_item_parent.h"
29 #include "eail_factory.h"
30 #include "eail_utils.h"
31 #include "eail_priv.h"
32
33 static void eail_item_parent_interface_init(EailItemParentIface *iface);
34 static void atk_selection_interface_init(AtkSelectionIface *iface);
35
36 /**
37  * @brief Definition of EailList as GObject
38  *
39  * EailList is extended ScrollableWidget with EAIL_TYPE_ITEM_PARENT and
40  * ATK_TYPE_SELECTION implemented
41  */
42 G_DEFINE_TYPE_WITH_CODE(EailList,
43                         eail_list,
44                         EAIL_TYPE_SCROLLABLE_WIDGET,
45                         G_IMPLEMENT_INTERFACE(EAIL_TYPE_ITEM_PARENT,
46                                               eail_item_parent_interface_init)
47                         G_IMPLEMENT_INTERFACE(ATK_TYPE_SELECTION,
48                                               atk_selection_interface_init));
49
50 /**
51  * @brief Initializer for AtkObjectClass
52  *
53  * @param obj AtkObject instance
54  * @param data initialization data
55  */
56 static void
57 eail_list_initialize(AtkObject *obj, gpointer data)
58 {
59    Evas_Object *nested_widget = NULL;
60    ATK_OBJECT_CLASS(eail_list_parent_class)->initialize(obj, data);
61
62    obj->role = ATK_ROLE_LIST;
63
64    g_return_if_fail(EAIL_IS_WIDGET(obj));
65    nested_widget = eail_widget_get_widget(EAIL_WIDGET(obj));
66    if (!nested_widget)
67      {
68         ERR("No evas object inside EailWidget was found");
69         return;
70      }
71
72    evas_object_smart_callback_add(nested_widget, "selected",
73                                   eail_list_item_handle_selected_event, obj);
74    evas_object_smart_callback_add(nested_widget, "unselected",
75                                   eail_list_item_handle_unselected_event, obj);
76 }
77
78 /**
79  * @brief Prepares Eina_List filled with Elm_Object_Item* objects
80  * representing items in the list
81  *
82  * Call eina_list_free on the returned when results processing has been finished.
83  *
84  * @param list EailList instance
85  *
86  * @return Eina_List representing a list of the list's item
87  */
88 static Eina_List *
89 eail_list_get_items(EailList *list)
90 {
91    Eina_List *items = NULL;
92    Elm_Object_Item *item;
93    Evas_Object *widget = eail_widget_get_widget(EAIL_WIDGET(list));
94
95    item = elm_list_first_item_get(widget);
96    while (item)
97      {
98         items = eina_list_append(items, item);
99         item = elm_list_item_next(item);
100      }
101
102    return items;
103 }
104
105 /**
106  * @brief Gets the number of accessible children of the accessible
107  *
108  * Implementation of AtkObject->get_n_children callback.
109  *
110  * @param obj AtkObject instance
111  *
112  * @returns integer representing the number of accessible children of
113  * the accessible
114  */
115 static gint
116 eail_list_get_n_children(AtkObject *obj)
117 {
118    gint n_items;
119    Eina_List *items;
120
121    items = eail_list_get_items(EAIL_LIST(obj));
122    n_items = eina_list_count(items);
123
124    eina_list_free(items);
125
126    return n_items;
127 }
128
129 /**
130  * @brief Gets a reference to the specified accessible child of the object.
131  *
132  * The accessible children are 0-based so the first accessible child is at index 0,
133  * the second at index 1 and so on.
134  *
135  * Implementation of AtkObject->ref_child callback.
136  *
137  * @param obj AtkObject instance
138  * @param i child index
139  *
140  * @returns AtkObject representing the specified accessible child of the
141  * accessible
142  */
143 static AtkObject *
144 eail_list_ref_child(AtkObject *obj, gint i)
145 {
146    Eina_List *items;
147    AtkObject *child = NULL;
148
149    items = eail_list_get_items(EAIL_LIST(obj));
150    if (eina_list_count(items) > i)
151      {
152         if (elm_list_item_separator_get(eina_list_nth(items, i)))
153            child = eail_factory_get_item_atk_obj
154                              (eina_list_nth(items, i), ATK_ROLE_SEPARATOR, obj);
155         else
156            child = eail_factory_get_item_atk_obj
157                              (eina_list_nth(items, i), ATK_ROLE_LIST_ITEM, obj);
158
159         g_object_ref(child);
160      }
161
162    eina_list_free(items);
163
164    return child;
165 }
166
167 /**
168  * @brief Gets a reference to the state set of the accessible
169  *
170  * The caller must unreference it when it is no longer needed.
171  *
172  * Implementation of AtkObject->ref_state_set callback.
173  *
174  * @param obj AtkObject instance
175  *
176  * @returns AtkStateSet representing the state set of the
177  * accessible
178  */
179 static AtkStateSet *
180 eail_list_ref_state_set(AtkObject *obj)
181 {
182    AtkStateSet *state_set;
183    Evas_Object *widget = eail_widget_get_widget(EAIL_WIDGET(obj));
184
185    state_set = ATK_OBJECT_CLASS(eail_list_parent_class)->ref_state_set(obj);
186
187    if (!widget) return state_set;
188
189    if (elm_list_multi_select_get(widget))
190      atk_state_set_add_state(state_set, ATK_STATE_MULTISELECTABLE);
191
192    return state_set;
193 }
194
195 /**
196  * @brief Initializer for GObject EailList instance
197  *
198  * @param list EailList instance
199  */
200 static void
201 eail_list_init(EailList *list)
202 {
203 }
204
205 /**
206  * @brief Initializer for GObject class
207  *
208  * Defines callbacks for base AtkObject.
209  *
210  * @param klass EailListClass instance
211  */
212 static void
213 eail_list_class_init(EailListClass *klass)
214 {
215    AtkObjectClass *atk_class = ATK_OBJECT_CLASS(klass);
216
217    atk_class->initialize = eail_list_initialize;
218    atk_class->get_n_children = eail_list_get_n_children;
219    atk_class->ref_child = eail_list_ref_child;
220    atk_class->ref_state_set = eail_list_ref_state_set;
221 }
222
223 /*
224  * Functions for *EailItemParent* interface
225  */
226
227 /**
228  * @brief Gets the role of item
229  *
230  * Implementation of EailItemParent->get_item_role callback.
231  *
232  * @param parent EailItemParent instance
233  * @param item EailItem instance
234  *
235  * @returns AtkRole representing the role of the specified item in the list
236  */
237 static AtkRole
238 eail_list_get_item_role(EailItemParent  *parent,
239                         EailItem        *item)
240 {
241    Evas_Object *widget = eail_widget_get_widget(EAIL_WIDGET(parent));
242    Elm_Object_Item *it = eail_item_get_item(item);
243
244    if (!widget || !it) return ATK_ROLE_INVALID;
245
246    return ATK_OBJECT(item)->role;
247 }
248
249 /**
250  * @brief Gets a reference to the state set of item
251  *
252  * Implementation of EailItemParent->ref_item_state_set callback.
253  *
254  * @param parent EailItemParent instance
255  * @param item EailItem instance
256  * @param state_set AtkStateSet instance
257  * @returns AtkStateSet representing the state set of item
258  */
259 static AtkStateSet *
260 eail_list_ref_item_state_set(EailItemParent *parent,
261                              EailItem       *item,
262                              AtkStateSet    *state_set)
263 {
264    Evas_Object *widget = eail_widget_get_widget(EAIL_WIDGET(parent));
265    Elm_Object_Item *it = eail_item_get_item(item);
266
267    if (!widget || !it) return state_set;
268
269    if (elm_list_select_mode_get(widget) != ELM_OBJECT_SELECT_MODE_NONE)
270      {
271         atk_state_set_add_state(state_set, ATK_STATE_SELECTABLE);
272
273         if (elm_list_item_selected_get(it))
274           atk_state_set_add_state(state_set, ATK_STATE_SELECTED);
275      }
276
277    atk_state_set_add_state(state_set, ATK_STATE_VISIBLE);
278    atk_state_set_add_state(state_set, ATK_STATE_SHOWING);
279
280    return state_set;
281 }
282
283 /**
284  * @brief Grabs focus of list
285  *
286  * @param parent EailItemParent instance
287  * @param item EailItem instance
288  * @returns TRUE on success, FALSE otherwise
289  */
290 static gboolean
291 eail_list_grab_item_focus(EailItemParent    *parent,
292                           EailItem          *item)
293 {
294    /* it is not possible to focus entire list object */
295    return FALSE;
296 }
297
298 /**
299  * @brief Gets nested Evas_Object of given EailItem
300  *
301  * Implementation of EailItemParent->get_evas_obj callback.
302  *
303  * @param parent EailItemParent instance
304  * @param item EailItem instance
305  * @returns Evas_Object representing item
306  */
307 static Evas_Object *
308 eail_list_get_evas_obj(EailItemParent   *parent,
309                        EailItem         *item)
310 {
311    Elm_Object_Item *it = eail_item_get_item(item);
312
313    if (!it)
314      {
315         ERR("Error. Could not get Elm_Object_item from EailItem");
316         return NULL;
317      }
318
319    return elm_list_item_object_get(it);
320 }
321
322 /**
323  * @brief Gets supported actions
324  *
325  * Implementation of EailItemParent->get_actions_supported callback.
326  *
327  * @param parent EailItemParent instance
328  * @param item EailItem instance
329  * @returns integer representing supported actions
330  */
331 static gint
332 eail_list_get_actions_supported(EailItemParent   *parent,
333                                 EailItem         *item)
334 {
335    return EAIL_ACTION_SUPPORTED_CLICK;
336 }
337
338 /**
339  * @brief Gets index in parent
340  *
341  * @param parent EailItemParent instance
342  * @param item EailItem child instance
343  *
344  * @returns int representing the index in parent
345  */
346 static gint
347 eail_list_get_item_index_in_parent(EailItemParent *parent, EailItem *item)
348 {
349    Elm_Object_Item *it = eail_item_get_item(item);
350    Eina_List *items;
351    gint index = -1;
352    int i = 0;
353
354    if (!it) return -1;
355
356    items = eail_list_get_items(EAIL_LIST(ATK_OBJECT(parent)));
357    for (i=0; i<eina_list_count(items); ++i)
358    {
359       if (eina_list_nth(items, i) == it)
360       {
361          index = i;
362          break;
363       }
364    }
365
366    eina_list_free(items);
367    return index;
368 }
369
370 /**
371  * @brief Gets the name of a list's child
372  *
373  * @param parent EailItemParent instance
374  * @param item EailItem child instance
375  *
376  * @returns string representing the name of the child
377  */
378 static const gchar *
379 eail_list_item_name_get(EailItemParent *parent, EailItem *item)
380 {
381    Elm_Object_Item *it = eail_item_get_item(item);
382    /*that will work only for default theme*/
383    if (!it) return NULL;
384
385    return elm_object_item_part_text_get(it, NULL);
386 }
387
388 /**
389  * @brief Initialization of EailItemParentIface callbacks
390  *
391  * @param iface EailItemParent instance
392  */
393 static void
394 eail_item_parent_interface_init(EailItemParentIface *iface)
395 {
396    iface->get_item_name            = eail_list_item_name_get;
397    iface->get_item_role            = eail_list_get_item_role;
398    iface->ref_item_state_set       = eail_list_ref_item_state_set;
399    iface->grab_item_focus          = eail_list_grab_item_focus;
400    iface->get_evas_obj             = eail_list_get_evas_obj;
401    iface->get_actions_supported    = eail_list_get_actions_supported;
402    iface->get_item_index_in_parent = eail_list_get_item_index_in_parent;
403 }
404
405 /*
406  * Functions for the *AtkSelection* interface
407  */
408
409 /**
410  * @brief Adds the specified accessible child of the object to the object's selection
411  *
412  * Implementation of AtkSelection->add_selection callback.
413  *
414  * @param selection AtkSelection instance
415  * @param i index of object
416  *
417  * @returns TRUE on success, FALSE otherwise
418  */
419 static gboolean
420 eail_list_add_selection(AtkSelection *selection,
421                         gint i)
422 {
423    g_return_val_if_fail(EAIL_IS_WIDGET(selection), FALSE);
424    Evas_Object *object = eail_widget_get_widget(EAIL_WIDGET(selection));
425    const Eina_List *items = elm_list_items_get(object);
426    Elm_Object_Item *item = eina_list_nth(items, i);
427
428    if (!item) return FALSE;
429
430    elm_list_item_selected_set(item, EINA_TRUE);
431    return TRUE;
432 }
433
434 /**
435  * @brief Gets a reference to the accessible object representing the specified selected
436  * child of the object.
437  *
438  * Note: callers should not rely on NULL or on a zero value
439  * for indication of whether AtkSelectionIface is implemented, they should use
440  * type checking/interface checking macros or the atk_get_accessible_value()
441  * convenience method.
442  *
443  * Implementation of AtkSelection->ref_selection callback.
444  *
445  * @param selection AtkSelection instance
446  * @param i index of object
447  *
448  * @returns AtkObject representing the selected accessible
449  */
450 static AtkObject *
451 eail_list_ref_selection(AtkSelection *selection,
452                         gint i)
453 {
454    g_return_val_if_fail(EAIL_IS_WIDGET(selection), NULL);
455
456    return eail_list_ref_child(ATK_OBJECT(selection), i);
457 }
458
459 /**
460  * @brief Gets the number of accessible children currently selected.
461  *
462  * Note: callers should not rely on NULL or on a zero value for indication of whether
463  * AtkSelectionIface is implemented, they should use type checking/interface
464  * checking macros or the atk_get_accessible_value() convenience method.
465  *
466  * Implementation of AtkSelection->get_selection_count callback.
467  *
468  * @param selection AtkSelection instance
469  *
470  * @returns integer representing the number of selected elements
471  */
472 static gint
473 eail_list_get_selection_count(AtkSelection *selection)
474 {
475    g_return_val_if_fail(EAIL_IS_WIDGET(selection), FALSE);
476    Evas_Object *object = eail_widget_get_widget(EAIL_WIDGET(selection));
477    const Eina_List *sel_objs = elm_list_selected_items_get(object);
478
479    if (!sel_objs) return 0;
480
481    return eina_list_count(sel_objs);
482 }
483
484 /**
485  * @brief Determines if the current child of this object is selected.
486  *
487  * Note: callers should not rely on NULL or on a zero value for indication of whether
488  * AtkSelectionIface is implemented, they should use type checking/interface
489  * checking macros or the atk_get_accessible_value() convenience method.
490  *
491  * Implementation of AtkSelection->is_child_selected callback.
492  *
493  * @param selection AtkSelection instance
494  * @param i index of object
495  *
496  * @returns gboolean representing whether the specified child is selected
497  */
498 static gboolean
499 eail_list_is_child_selected(AtkSelection *selection,
500                             gint i)
501 {
502    g_return_val_if_fail(EAIL_IS_WIDGET(selection), FALSE);
503    Evas_Object *object = eail_widget_get_widget(EAIL_WIDGET(selection));
504    Elm_Object_Item *selected_item = elm_list_selected_item_get(object);
505    Elm_Object_Item *last_item = elm_list_last_item_get(object);
506    Elm_Object_Item *item = elm_list_first_item_get(object);
507    gint n = 0;
508
509    /* if there is no selected object, that naturally means that none
510     * of children is selected */
511    if (!selected_item) return FALSE;
512
513    do
514      {
515         if (selected_item == item) break;
516
517         item = elm_list_item_next(item);
518         n++;
519      } while (item != last_item);
520
521    return i == n;
522 }
523
524 /**
525  * @brief Removes the specified child of the object from the object's selection
526  *
527  * Implementation of AtkSelection->remove_selection callback.
528  *
529  * @param selection AtkSelection instance
530  * @param i selection index
531  *
532  * @returns TRUE on success, FALSE otherwise
533  */
534 static gboolean
535 eail_list_remove_selection(AtkSelection *selection,
536                            gint i)
537 {
538    const Eina_List *items = NULL;
539    Evas_Object *object = NULL;
540    Elm_Object_Item *item = NULL;
541
542    g_return_val_if_fail(EAIL_IS_WIDGET(selection), FALSE);
543    object = eail_widget_get_widget(EAIL_WIDGET(selection));
544    g_return_val_if_fail(object, FALSE);
545
546    items = elm_list_items_get(object);
547    g_return_val_if_fail((eina_list_count(items) > i), FALSE);
548
549    item = eina_list_nth(items, i);
550    elm_list_item_selected_set(item, EINA_FALSE);
551
552    /* if change was successful, then selected item now should be different */
553    return (elm_list_selected_item_get(object) != item);
554 }
555
556 /**
557  * @brief Helper function that selects/unselects all items in the list depending
558  * on "selected" param value
559  *
560  * @param object Evas_Object instance
561  * @param selected selected state (TRUE means selected)
562  *
563  * @returns TRUE on success, FALSE otherwise
564  */
565 static gboolean
566 eail_list_mark_all_selected_state(const Evas_Object *object, Eina_Bool selected)
567 {
568    const Eina_List *items = NULL;
569    int i = 0;
570
571    items = elm_list_items_get(object);
572    for (i = 0; i < eina_list_count(items); ++i)
573      {
574         Elm_Object_Item *item = eina_list_nth(items, i);
575         if (!item) return FALSE;
576
577         elm_list_item_selected_set(item, selected);
578      }
579
580    return TRUE;
581 }
582
583 /**
584  * @brief Causes every child of the object to be selected if the object supports
585  * multiple selections
586  *
587  * Implementation of AtkSelection->select_all_selection callback.
588  *
589  * @param selection AtkSelection instance
590  *
591  * @returns TRUE on success, FALSE otherwise
592  */
593 static gboolean
594 eail_list_select_all_selection(AtkSelection *selection)
595 {
596    g_return_val_if_fail(EAIL_IS_WIDGET(selection), FALSE);
597    Evas_Object *object = eail_widget_get_widget(EAIL_WIDGET(selection));
598
599    /* can select all only when multiselect is enabled */
600    if (!elm_list_multi_select_get(object)) return FALSE;
601
602    return eail_list_mark_all_selected_state(object, EINA_TRUE);
603 }
604
605 /**
606  * @brief Clears the selection in the object so that no child in the object is
607  * selected
608  *
609  * Implementation of AtkSelection->clear_selection callback.
610  *
611  * @param selection AtkSelection instance
612  *
613  * @returns TRUE on success, FALSE otherwise
614  */
615 static gboolean
616 eail_list_clear_selection(AtkSelection *selection)
617 {
618    g_return_val_if_fail(EAIL_IS_WIDGET(selection), FALSE);
619    Evas_Object *object = eail_widget_get_widget(EAIL_WIDGET(selection));
620
621    return eail_list_mark_all_selected_state(object, EINA_FALSE);
622 }
623
624 /**
625  * @brief Initializer of AtkSelectionIface interface
626  *
627  * @param iface AtkSelectionIface instance
628  */
629 static void atk_selection_interface_init(AtkSelectionIface *iface)
630 {
631    iface->add_selection        = eail_list_add_selection;
632    iface->clear_selection      = eail_list_clear_selection;
633    iface->ref_selection        = eail_list_ref_selection;
634    iface->get_selection_count  = eail_list_get_selection_count;
635    iface->is_child_selected    = eail_list_is_child_selected;
636    iface->remove_selection     = eail_list_remove_selection;
637    iface->select_all_selection = eail_list_select_all_selection;
638 }