Merge "custom eail widget implementation" into tizen
[platform/core/uifw/eail.git] / eail / eail_genlist.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_genlist.c
22  * @brief EailGenlist implementation
23  */
24
25 #include <Eina.h>
26 #include <Evas.h>
27 #include <Elementary.h>
28
29 #include "eail_genlist.h"
30 #include "eail_item_parent.h"
31 #include "eail_factory.h"
32 #include "eail_utils.h"
33 #include "eail_priv.h"
34
35 static void eail_item_parent_interface_init   (EailItemParentIface *iface);
36 static void atk_selection_interface_init(AtkSelectionIface *iface);
37
38 /**
39  * @brief Definition of EailGenlist as GObject
40  *
41  * EailList is extended ScrollableWidget with EAIL_TYPE_ITEM_PARENT and
42  * ATK_TYPE_SELECTION implemented
43  */
44 G_DEFINE_TYPE_WITH_CODE(EailGenlist, eail_genlist, 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 Genlist text part name
52  */
53 #define EAIL_GENLIST_TEXT "elm.text"
54
55 /*
56  * Implementation of the *AtkObject* interface
57  */
58
59 /**
60  * @brief Handler for realized event
61  *
62  * Notifies about genlist's content changes.
63  *
64  * @param data passed to callback
65  * @param obj Evas_Object that raised event
66  * @param event_info additional event info (item is passed here)
67  */
68 void
69 eail_list_item_handle_realized_event(void *data,
70                                      Evas_Object *obj,
71                                      void *event_info)
72 {
73    Elm_Object_Item *item = (Elm_Object_Item *) event_info;
74    AtkObject *atk_item = NULL, *atk_parent = NULL;
75
76    atk_parent = ATK_OBJECT(data);
77    if (!atk_parent) return;
78
79    atk_item = eail_factory_get_item_atk_obj
80                                          (item, ATK_ROLE_LIST_ITEM, atk_parent);
81
82    if (!atk_item) return;
83
84    eail_emit_children_changed_obj(TRUE, atk_parent, atk_item);
85 }
86
87 /**
88  * @brief Handler for unrealized event, used to notify about genlist content
89  * changes
90  *
91  * @param data passed to callback
92  * @param obj object that raised event
93  * @param event_info additional event info (item is passed here)
94  */
95 void
96 eail_list_item_handle_unrealized_event(void *data,
97                                      Evas_Object *obj,
98                                      void *event_info)
99 {
100    Elm_Object_Item *item = (Elm_Object_Item *) event_info;
101    AtkObject *atk_item = NULL, *atk_parent = NULL;
102
103    atk_parent = ATK_OBJECT(data);
104    if (!atk_parent) return;
105
106    atk_item = eail_factory_get_item_atk_obj
107                                          (item, ATK_ROLE_LIST_ITEM, atk_parent);
108
109    if (!atk_item) return;
110
111    eail_emit_children_changed_obj(FALSE, atk_parent, atk_item);
112    atk_object_notify_state_change(atk_item, ATK_STATE_DEFUNCT, TRUE);
113
114    DBG("Unregistering item from cache...");
115    eail_factory_unregister_item_from_cache(item);
116 }
117
118 /**
119  * @brief Initializer for AtkObjectClass
120  * @param obj AtkObject instance
121  * @param data initialization data
122  */
123 static void
124 eail_genlist_initialize(AtkObject *obj, gpointer data)
125 {
126    Evas_Object *nested_widget = NULL;
127    ATK_OBJECT_CLASS(eail_genlist_parent_class)->initialize(obj, data);
128
129    obj->role = ATK_ROLE_LIST;
130    g_return_if_fail(EAIL_IS_WIDGET(obj));
131
132    nested_widget = eail_widget_get_widget(EAIL_WIDGET(obj));
133    if (!nested_widget)
134      {
135         ERR("No evas object inside EailWidget was found");
136         return;
137      }
138
139    evas_object_smart_callback_add(nested_widget, "selected",
140                                   eail_list_item_handle_selected_event, obj);
141    evas_object_smart_callback_add(nested_widget, "unselected",
142                                   eail_list_item_handle_unselected_event, obj);
143    evas_object_smart_callback_add(nested_widget, "realized",
144                                   eail_list_item_handle_realized_event, obj);
145    evas_object_smart_callback_add(nested_widget, "unrealized",
146                                   eail_list_item_handle_unrealized_event, obj);
147 }
148
149 /**
150  * @brief Prepares Eina_List filled with Elm_Object_Item* objects
151  * representing items in the list
152  *
153  * Call eina_list_free on the returned list when results processing has been finished.
154  *
155  * @param genlist EailGenlist instance
156  *
157  * @return Eina_List representing the list of list's items
158  */
159 static Eina_List *
160 eail_genlist_get_items(EailGenlist *genlist)
161 {
162     Eina_List *items = NULL;
163     Elm_Object_Item *item;
164     Evas_Object *widget = eail_widget_get_widget(EAIL_WIDGET(genlist));
165
166     item = elm_genlist_first_item_get(widget);
167     while (item) {
168         items = eina_list_append(items, item);
169         item = elm_genlist_item_next_get(item);
170     }
171
172     return items;
173 }
174
175 /**
176  * @brief Gets the number of accessible children of the accessible
177  *
178  * Implementation of AtkObject->get_n_children callback.
179  *
180  * @param obj AtkObject instance
181  *
182  * @returns integer representing the number of accessible children of
183  * the accessible
184  */
185 static gint
186 eail_genlist_get_n_children(AtkObject *obj)
187 {
188     gint n_items;
189     Eina_List *items;
190
191     items = eail_genlist_get_items(EAIL_GENLIST(obj));
192     n_items = eina_list_count(items);
193
194     eina_list_free(items);
195
196     return n_items;
197 }
198
199 /**
200  * @brief Gets a reference to the specified accessible child of the object.
201  *
202  * The accessible children are 0-based so the first accessible child is
203  * at index 0, the second at index 1 and so on.
204  *
205  * Implementation of AtkObject->ref_child callback.
206  *
207  * @param obj AtkObject instance
208  * @param i child's index
209  *
210  * @returns AtkObject representing the specified accessible child of the
211  * accessible
212  */
213 static AtkObject *
214 eail_genlist_ref_child(AtkObject *obj, gint i)
215 {
216     Eina_List *items;
217     AtkObject *child = NULL;
218
219     items = eail_genlist_get_items(EAIL_GENLIST(obj));
220     if (eina_list_count(items) > i) {
221        child = eail_factory_get_item_atk_obj
222                              (eina_list_nth(items, i), ATK_ROLE_LIST_ITEM, obj);
223
224        g_object_ref(child);
225     }
226
227     eina_list_free(items);
228
229     return child;
230 }
231
232 /**
233  * @brief Gets a reference to the state set of the accessible
234  *
235  * The caller must unreference it when it is no longer needed.
236  *
237  * Implementation of AtkObject->ref_state_set callback.
238  *
239  * @param obj AtkObject instance
240  *
241  * @returns AtkStateSet representing the state set of the
242  * accessible
243  */
244 static AtkStateSet *
245 eail_genlist_ref_state_set(AtkObject *obj)
246 {
247     AtkStateSet *state_set;
248     Evas_Object *widget = eail_widget_get_widget(EAIL_WIDGET(obj));
249
250     state_set = ATK_OBJECT_CLASS(eail_genlist_parent_class)->ref_state_set(obj);
251
252     if (!widget) {
253         return state_set;
254     }
255
256     if (elm_genlist_multi_select_get(widget)) {
257         atk_state_set_add_state(state_set, ATK_STATE_MULTISELECTABLE);
258     }
259
260     return state_set;
261 }
262
263 /**
264  * @brief Initializer for GObject EailList instance
265  *
266  * @param genlist EailGenlist instance
267  */
268 static void
269 eail_genlist_init(EailGenlist *genlist)
270 {
271 }
272
273 /**
274  * @brief Initializer for GObject class
275  *
276  * Defines callbacks for base AtkObject.
277  *
278  * @param klass EailGenlistClass instance
279  */
280 static void
281 eail_genlist_class_init(EailGenlistClass *klass)
282 {
283     AtkObjectClass *atk_class = ATK_OBJECT_CLASS(klass);
284
285     atk_class->initialize = eail_genlist_initialize;
286     atk_class->get_n_children = eail_genlist_get_n_children;
287     atk_class->ref_child = eail_genlist_ref_child;
288     atk_class->ref_state_set = eail_genlist_ref_state_set;
289 }
290
291 /*
292  * Implementation of the *EailItemParent* interface
293  */
294
295 /**
296  * @brief Gets the name of EailItem
297  *
298  * Implementation of EailItemParent->get_name callback.
299  *
300  * @param parent EailItemParent instance
301  * @param item EailItem instance
302  *
303  * @returns character string representing the accessible name of
304  * the item
305  */
306 static const gchar *
307 eail_genlist_get_item_name(EailItemParent   *parent,
308                            EailItem         *item)
309 {
310    Evas_Object *widget = eail_widget_get_widget(EAIL_WIDGET(parent));
311    Elm_Object_Item *it = eail_item_get_item(item);
312
313    if (!widget || !it) return NULL;
314
315    return elm_object_item_part_text_get(it, EAIL_GENLIST_TEXT);
316 }
317
318 /**
319  * @brief Gets index of element in parent object
320  *
321  * Implementation of EailItemParent->get_item_index_in_parent.
322  *
323  * @param parent EailItemParent instance
324  * @param item EailItem instance
325  *
326  * @returns integer representing the index of the accessible in its parent
327  */
328 static gint
329 eail_genlist_get_item_index_in_parent(EailItemParent    *parent,
330                                       EailItem          *item)
331 {
332     Evas_Object *widget = eail_widget_get_widget(EAIL_WIDGET(parent));
333     Elm_Object_Item *it = eail_item_get_item(item);
334
335     if (!widget || !it) {
336         return -1;
337     }
338
339     return elm_genlist_item_index_get(it)-1;
340 }
341
342 /**
343  * @brief Gets a reference to the state set of the accessible
344  *
345  * The caller must unreference it when it is no longer needed.
346  *
347  * Implementation EailItemParent->ref_state_set callback.
348  *
349  * @param parent object that holds EailItem in its content
350  * @param item EailItem object
351  * @param state_set current state set
352  *
353  * @returns AtkStateSet representing the state set of the accessible
354  */
355 static AtkStateSet *
356 eail_genlist_ref_item_state_set(EailItemParent  *parent,
357                                 EailItem        *item,
358                                 AtkStateSet       *state_set)
359 {
360     Evas_Object *widget = eail_widget_get_widget(EAIL_WIDGET(parent));
361     Elm_Object_Item *it = eail_item_get_item(item);
362     Eina_List *list;
363
364     if (!widget || !it) {
365         return state_set;
366     }
367
368     if (elm_genlist_select_mode_get(widget) != ELM_OBJECT_SELECT_MODE_NONE &&
369         elm_genlist_item_select_mode_get(it) != ELM_OBJECT_SELECT_MODE_NONE) {
370         atk_state_set_add_state(state_set, ATK_STATE_SELECTABLE);
371
372         if (elm_genlist_item_selected_get(it)) {
373             atk_state_set_add_state(state_set, ATK_STATE_SELECTED);
374         }
375     }
376
377     if (elm_genlist_item_type_get(it) == ELM_GENLIST_ITEM_TREE) {
378         atk_state_set_add_state(state_set, ATK_STATE_EXPANDABLE);
379
380         if (elm_genlist_item_expanded_get(it)) {
381             atk_state_set_add_state(state_set, ATK_STATE_EXPANDED);
382         }
383     }
384
385     atk_state_set_add_state(state_set, ATK_STATE_VISIBLE);
386
387     list = elm_genlist_realized_items_get(widget);
388     if (eina_list_data_find(list, it)) {
389         atk_state_set_add_state(state_set, ATK_STATE_SHOWING);
390     }
391     eina_list_free(list);
392
393     return state_set;
394 }
395
396 /**
397  * @brief Grabs focus of item
398  *
399  * Implementation of EailItemParent->grab_item_focus callback.
400  *
401  * @param parent object that holds EailItem in its content
402  * @param item EailItem instance
403  *
404  * @returns TRUE if operation was successfull, FALSE otherwise
405  */
406 static gboolean
407 eail_genlist_grab_item_focus(EailItemParent *parent,
408                              EailItem       *item)
409 {
410     Evas_Object *widget = eail_widget_get_widget(EAIL_WIDGET(parent));
411     Elm_Object_Item *it = eail_item_get_item(item);
412
413     if (!widget || !it) {
414         return FALSE;
415     }
416
417     return FALSE;
418 }
419
420 /**
421  * @brief Gets the rectangle which gives the extent of the component
422  *
423  * Implementation of get_extents from AtkComponent.
424  *
425  * @param parent object that holds EailItem in its content
426  * @param item EailItem object instance
427  * @param [out] x x coordinate
428  * @param [out] y y coordinate
429  * @param [out] width width of the rectangle
430  * @param [out] height height of the rectangle
431  * @param coord_type specifies whether the coordinates are relative to the
432  * screen or to the components top level window
433  */
434 static void
435 eail_genlist_get_item_extents(EailItemParent    *parent,
436                               EailItem          *item,
437                               gint                *x,
438                               gint                *y,
439                               gint                *width,
440                               gint                *height,
441                               AtkCoordType         coord_type)
442 {
443     Evas_Object *widget = eail_widget_get_widget(EAIL_WIDGET(parent));
444     Elm_Object_Item *it = eail_item_get_item(item);
445
446     if (!widget || !it) {
447         return;
448     }
449 }
450
451 /**
452  * @brief Gets supported actions
453  *
454  * Implementation of EailItemParent->get_actions_supported callback.
455  *
456  * @param parent EailItemParent instance
457  * @param item EailItem instance
458  * @returns integer representing supported actions
459  */
460 static gint
461 eail_genlist_get_actions_supported(EailItemParent   *parent,
462                                    EailItem         *item)
463 {
464    return EAIL_ACTION_SUPPORTED_CLICK | EAIL_ACTION_SUPPORTED_PRESS |
465             EAIL_ACTION_SUPPORTED_RELEASE | EAIL_ACTION_SUPPORTED_EXPAND |
466               EAIL_ACTION_SUPPORTED_SHRINK;
467 }
468
469 /**
470  * @brief Initialization of EailItemParentIface callbacks
471  *
472  * @param iface EailItemParentIface instance
473  */
474 static void
475 eail_item_parent_interface_init(EailItemParentIface *iface)
476 {
477     iface->get_item_name = eail_genlist_get_item_name;
478     iface->get_item_index_in_parent = eail_genlist_get_item_index_in_parent;
479     iface->ref_item_state_set = eail_genlist_ref_item_state_set;
480     iface->grab_item_focus = eail_genlist_grab_item_focus;
481     iface->get_item_extents = eail_genlist_get_item_extents;
482     iface->get_actions_supported = eail_genlist_get_actions_supported;
483 }
484
485 /*
486  * AtkSelection
487  */
488
489 /**
490  * @brief Adds the specified accessible child of the object to the object's selection
491  *
492  * Implementation of AtkSelection->add_selection callback.
493  *
494  * @param selection AtkSelection instance
495  * @param i selection index
496  *
497  * @returns TRUE if operation was successfull, FALSE otherwise
498  */
499 static gboolean
500 eail_genlist_add_selection(AtkSelection *selection,
501                            gint i)
502 {
503    g_return_val_if_fail(EAIL_IS_WIDGET(selection), FALSE);
504    Eina_List *items = eail_genlist_get_items(EAIL_GENLIST(selection));
505    Elm_Object_Item *item = eina_list_nth(items, i);
506
507    if (!item) return FALSE;
508
509    elm_genlist_item_selected_set(item, EINA_TRUE);
510
511    eina_list_free(items);
512
513    return TRUE;
514 }
515
516 /**
517  * @brief Gets a reference to the accessible object representing the specified selected
518  * child of the object.
519  *
520  * Note: callers should not rely on NULL or on a zero value
521  * for indication of whether AtkSelectionIface is implemented, they should use
522  * type checking/interface checking macros or the atk_get_accessible_value()
523  * convenience method.
524  *
525  * Implementation of AtkSelection->ref_selection callback.
526  *
527  * @param selection AtkSelection instance
528  * @param i selection index
529  *
530  * @returns AtkObject representing the selected accessible or NULL if
531  * selection does not implement this interface
532  */
533 static AtkObject *
534 eail_genlist_ref_selection(AtkSelection *selection,
535                         gint i)
536 {
537    g_return_val_if_fail(EAIL_IS_WIDGET(selection), NULL);
538
539    return eail_genlist_ref_child(ATK_OBJECT(selection), i);
540 }
541
542 /**
543  * @brief Gets the number of accessible children currently selected.
544  *
545  * Note: callers should not rely on NULL or on a zero value for indication of whether
546  * AtkSelectionIface is implemented, they should use type checking/interface
547  * checking macros or the atk_get_accessible_value() convenience method.
548  *
549  * Implementation of AtkSelection->get_selection_count callback.
550  *
551  * @param selection AtkSelection instance
552  *
553  * @returns integer representing the number of selected children
554  */
555 static gint
556 eail_genlist_get_selection_count(AtkSelection *selection)
557 {
558    g_return_val_if_fail(EAIL_IS_WIDGET(selection), FALSE);
559    Evas_Object *object = eail_widget_get_widget(EAIL_WIDGET(selection));
560    const Eina_List *sel_objs = elm_genlist_selected_items_get(object);
561
562    if (!sel_objs) return 0;
563
564    return eina_list_count(sel_objs);
565 }
566
567 /**
568  * @brief Determines if the current child of this object is selected.
569  *
570  * Note: callers should not rely on NULL or on a zero value for indication of whether
571  * AtkSelectionIface is implemented, they should use type checking/interface
572  * checking macros or the atk_get_accessible_value() convenience method.
573  *
574  * Implementation of AtkSelection->is_child_selected callback.
575  *
576  * @param selection AtkSelection instance
577  * @param i index of child
578  *
579  * @returns gboolean representing whether the specified child is selected, or 0 if
580  * selection does not implement this interface
581  */
582 static gboolean
583 eail_genlist_is_child_selected(AtkSelection *selection,
584                                gint i)
585 {
586    g_return_val_if_fail(EAIL_IS_WIDGET(selection), FALSE);
587    Evas_Object *object = eail_widget_get_widget(EAIL_WIDGET(selection));
588    Elm_Object_Item *selected_item = elm_genlist_selected_item_get(object);
589    Elm_Object_Item *last_item = elm_genlist_last_item_get(object);
590    Elm_Object_Item *item = elm_genlist_first_item_get(object);
591    gint n = 0;
592
593    /* of course if there is no selected object, that naturally means that none
594     * of children is selected */
595    if (!selected_item) return FALSE;
596
597    do
598      {
599         if (selected_item == item) break;
600
601         item = elm_genlist_item_next_get(item);
602         n++;
603      } while (item != last_item);
604
605    return i == n;
606 }
607
608 /**
609  * @brief Removes the specified child of the object from the object's selection
610  *
611  * Implementation of AtkSelection->remove_selection callback.
612  *
613  * @param selection AtkSelection instance
614  * @param i index of child
615  *
616  * @returns TRUE on success, FALSE otherwise
617  */
618 static gboolean
619 eail_genlist_remove_selection(AtkSelection *selection,
620                               gint i)
621 {
622    Eina_List *items = NULL;
623    Evas_Object *object = NULL;
624    Elm_Object_Item *item = NULL;
625
626    g_return_val_if_fail(EAIL_IS_WIDGET(selection), FALSE);
627    object = eail_widget_get_widget(EAIL_WIDGET(selection));
628    g_return_val_if_fail(object, FALSE);
629
630    items = eail_genlist_get_items(EAIL_GENLIST(selection));
631    g_return_val_if_fail((eina_list_count(items) > i), FALSE);
632
633    item = eina_list_nth(items, i);
634    elm_genlist_item_selected_set(item, EINA_FALSE);
635
636    eina_list_free(items);
637
638    /* if change was successful, then selected item now should be different */
639    return (elm_genlist_selected_item_get(object) != item);
640 }
641
642 /**
643  * @brief Helper function for selecting/unselecting all items in the list depending
644  * on "selected" param value
645  *
646  * @param selection AtkSelection instance
647  * @param selected selected state (TRUE means selected)
648  *
649  * @returns TRUE on success, FALSE otherwise
650  */
651 static gboolean
652 eail_genlist_mark_all_selected_state(AtkSelection *selection,
653                                      Eina_Bool selected)
654 {
655    Eina_List *items = NULL;
656    int i = 0;
657
658    items = eail_genlist_get_items(EAIL_GENLIST(selection));
659    for (i = 0; i < eina_list_count(items); ++i)
660      {
661         Elm_Object_Item *item = eina_list_nth(items, i);
662         if (!item) return FALSE;
663
664         elm_genlist_item_selected_set(item, selected);
665      }
666
667    eina_list_free(items);
668
669    return TRUE;
670 }
671
672 /**
673  * @brief Causes every child of the object to be selected if the object supports
674  * multiple selections
675  *
676  * Implementation of AtkSelection->select_all_selection callback.
677  *
678  * @param selection AtkSelection instance
679  *
680  * @returns TRUE on success, FALSE otherwise
681  */
682 static gboolean
683 eail_genlist_select_all_selection(AtkSelection *selection)
684 {
685    g_return_val_if_fail(EAIL_IS_WIDGET(selection), FALSE);
686    Evas_Object *object = eail_widget_get_widget(EAIL_WIDGET(selection));
687
688    /* can select all only when multiselect is enabled */
689    if (!elm_genlist_multi_select_get(object)) return FALSE;
690
691    return eail_genlist_mark_all_selected_state(selection, EINA_TRUE);
692 }
693
694 /**
695  * @brief Clears the selection in the object so that no children in the object are
696  * selected
697  *
698  * Implementation of AtkSelection->clear_selection callback.
699  *
700  * @param selection AtkSelection instance
701  *
702  * @returns TRUE on success, FALSE otherwise
703  */
704 static gboolean
705 eail_genlist_clear_selection(AtkSelection *selection)
706 {
707    g_return_val_if_fail(EAIL_IS_WIDGET(selection), FALSE);
708
709    return eail_genlist_mark_all_selected_state(selection, EINA_FALSE);
710 }
711
712 /**
713  * @brief Initializer of AtkSelectionIface interface
714  *
715  * @param iface AtkSelectionIface instance
716  */
717 static void atk_selection_interface_init(AtkSelectionIface *iface)
718 {
719    iface->add_selection        = eail_genlist_add_selection;
720    iface->clear_selection      = eail_genlist_clear_selection;
721    iface->ref_selection        = eail_genlist_ref_selection;
722    iface->get_selection_count  = eail_genlist_get_selection_count;
723    iface->is_child_selected    = eail_genlist_is_child_selected;
724    iface->remove_selection     = eail_genlist_remove_selection;
725    iface->select_all_selection = eail_genlist_select_all_selection;
726 }