svn update: 48945 (latest:48959)
[framework/uifw/elementary.git] / src / lib / elm_scrolled_grid.c
1 /*
2  * vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2
3  */
4 #include <Elementary.h>
5 #include "elm_priv.h"
6
7 /**
8  * @defgroup Grid Scrolled Grid
9  *
10  * This widget aims to position objects in a grid layout while actually
11  * building only the visible ones, using the same idea as genlist: the user
12  * define a class for each cell, specifying functions that will be called at
13  * object creation and deletion.
14  *
15  * Signals that you can add callbacks for are:
16  *
17  * clicked - The user has double-clicked a cell. The event_info parameter is
18  * the grid cell that was double-clicked.
19  *
20  * selected - The user has made an item selected. The event_info parameter is
21  * the grid cell that was selected.
22  *
23  * unselected - The user has made an item unselected. The event_info parameter
24  * is the grid cell that was unselected.
25  *
26  * realized - This is called when the cell in the grid is created as a real
27  * evas object. event_info is the grid cell that was created. The object may be
28  * deleted at any time, so it is up to the caller to not use the object pointer
29  * from elm_scrolled_grid_cell_object_get() in a way where it may point to
30  * freed objects.
31  *
32  * drag,start,up - Called when the cell in the grid has been dragged (not
33  * scrolled) up.
34  *
35  * drag,start,down - Called when the cell in the grid has been dragged (not
36  * scrolled) down.
37  *
38  * drag,start,left - Called when the cell in the grid has been dragged (not
39  * scrolled) left.
40  *
41  * drag,start,right - Called when the cell in the grid has been dragged (not
42  * scrolled) right.
43  *
44  * drag,stop - Called when the cell in the grid has stopped being dragged.
45  *
46  * drag - Called when the cell in the grid is being dragged.
47  *
48  * scroll - called when the content has been scrolled (moved).
49  *
50  * scroll,drag,start - called when dragging the content has started.
51  *
52  * scroll,drag,stop - called when dragging the content has stopped.
53  *
54  *
55  * A cell in the grid can have 0 or more text labels (they can be regular text
56  * or textblock - that's up to the style to determine), 0 or more icons (which
57  * are simply objects swallowed into the grid cell) and 0 or more boolean states
58  * that can be used for check, radio or other indicators by the edje theme style.
59  * A cell may be one of several styles (Elementary provides 1 by default -
60  * "default", but this can be extended by system or application custom
61  * themes/overlays/extensions).
62  *
63  * In order to implement the ability to add and delete cells on the fly, Grid
64  * implements a class/callback system where the application provides a structure
65  * with information about that type of cell (grid may contain multiple different
66  * cells with different classes, states and styles). Grid will call the functions
67  * in this struct (methods) when a cell is "realized" (that is created
68  * dynamically while scrolling). All objects will simply be deleted when no
69  * longer needed with evas_object_del(). The Elm_Genlist_Item_Class structure
70  * contains the following members:
71  *
72  * cell_style - This is a constant string and simply defines the name of the
73  * cell style. It must be specified and the default should be "default".
74  *
75  * func.label_get - This function is called when an actual cell object is
76  * created. The data parameter is the one passed to elm_scrolled_grid_cell_add()
77  * and related cell creation functions. The obj parameter is the grid object and
78  * the part parameter is the string name of the text part in the edje design that
79  * is listed as one of the possible labels that can be set. This function must
80  * return a strdup'()ed string as the caller will free() it when done.
81  *
82  * func.icon_get - This function is called when an actual item object is
83  * created. The data parameter is the one passed to elm_scrolled_grid_cell_add()
84  * and related cell creation functions. The obj parameter is the grid object and
85  * the part parameter is the string name of the icon part in the edje design that
86  * is listed as one of the possible icons that can be set. This must return NULL
87  * for no object or a valid object. The object will be deleted by grid on
88  * shutdown or when the cell is unrealized.
89  *
90  * func.state_get - This function is called when an actual cell object is
91  * created. The data parameter is the one passed to elm_scrolled_grid_cell_add()
92  * and related cell creation functions. The obj parameter is the grid object and
93  * the part parameter is the string name of th state part in the edje design that
94  * is listed as one of the possible states that can be set. Return 0 for false
95  * and 1 for true. Grid will emit a signal to the edje object with
96  * "elm,state,XXX,active" "elm" when true (the default is false), where XXX is
97  * the name of the part.
98  *
99  * func.del - This is called when elm_scrolled_grid_cell_del() is called on a
100  * cell or elm_scrolled_grid_clear() is called on the grid. This is intended for
101  * use when actual grid cells are deleted, so any backing data attached to the
102  * cell (e.g. its data parameter on creation) can be deleted.
103  *
104  * If the application wants multiple cells to be able to be selected,
105  * elm_scrolled_grid_multi_select_set() can enable this. If the grid is
106  * single-selection only (the default), then elm_scrolled_grid_select_cell_get()
107  * will return the selected cell, if any, or NULL if none is selected. If the
108  * grid is multi-select then elm_scrolled_grid_selected_cells_get() will return a
109  * list (that is only valid as long as no cells are modified (added, deleted,
110  * selected or unselected).
111  *
112  * If a cell changes (state of boolean changes, label or icons change), then use
113  * elm_scrolled_grid_cell_update() to have grid update the cell with the new
114  * state. Grid will re-realize the cell thus call the functions in the
115  * _Elm_Grid_Cell_Class for that cell.
116  *
117  * To programmatically (un)select a cell use elm_scrolled_grid_cell_selected_set().
118  * To get its selected state use elm_scrolled_grid_cell_selected_get(). To make a
119  * cell disabled (unable to be selected and appear differently) use
120  * elm_scrolled_grid_cell_disable_set() to set this and
121  * elm_scrolled_grid_cell_disable_get() to get the disabled state.
122  *
123  * Cells will only call their selection func and callback when first becoming
124  * selected. Any further clicks will do nothing, unless you enable always
125  * select with elm_scrolled_grid_always_select_mode_set(). This means event if
126  * selected, every click will make the selected callbacks be called.
127  * elm_scrolled_grid_no_select_mode_set() will turn off the ability to select
128  * items entirely and they will neither appear selected nor call selected
129  * callback function.
130  *
131  * Remember that you can create new styles and add your own theme augmentation
132  * per application with elm_theme_extension_add(). If you absolutely must have a
133  * specific style that overrides any theme the user or system sets up you can use
134  * elm_theme_overlay_add() to add such a file.
135  *
136  * --
137  * TODO:
138  *  * Handle non-homogeneous objects too.
139  */
140
141 typedef struct _Widget_Data Widget_Data;
142 typedef struct _Pan Pan;
143
144 #define PRELOAD 1
145
146 struct _Elm_Grid_Cell
147 {
148    Evas_Object *base, *spacer;
149    const Elm_Grid_Cell_Class *gcc;
150    Ecore_Timer *long_timer;
151    Widget_Data *wd;
152    Eina_List *labels, *icons, *states, *icon_objs;
153    const void *data;
154    struct
155      {
156         Evas_Smart_Cb func;
157         const void *data;
158      } func;
159
160    Evas_Coord x, y, dx, dy;
161    int relcount;
162
163    Eina_Bool want_unrealize : 1;
164    Eina_Bool realized : 1;
165    Eina_Bool dragging : 1;
166    Eina_Bool down : 1;
167    Eina_Bool delete_me : 1;
168    Eina_Bool display_only : 1;
169    Eina_Bool disabled : 1;
170    Eina_Bool selected : 1;
171    Eina_Bool hilighted : 1;
172    Eina_Bool walking : 1;
173 };
174
175 struct _Widget_Data
176 {
177    Evas_Object *self, *scr;
178    Evas_Object *pan_smart;
179    Pan *pan;
180    Eina_List *cells;
181    Ecore_Job *calc_job;
182    Eina_List *selected;
183    double align_x, align_y;
184
185    Evas_Coord pan_x, pan_y;
186    Evas_Coord cell_width, cell_height;  /* Each cell size */
187    Evas_Coord minw, minh;               /* Total obj size */
188    unsigned int nmax;
189
190    Eina_Bool horizontal : 1;
191    Eina_Bool on_hold : 1;
192    Eina_Bool longpressed : 1;
193    Eina_Bool multi : 1;
194    Eina_Bool no_select : 1;
195    Eina_Bool wasselected : 1;
196    Eina_Bool always_select : 1;
197 };
198
199 struct _Pan
200 {
201    Evas_Object_Smart_Clipped_Data __clipped_data;
202    Widget_Data *wd;
203 };
204
205 static const char *widtype = NULL;
206 static void _sizing_eval(Evas_Object *obj);
207 static void _cell_hilight(Elm_Grid_Cell *cell);
208 static void _cell_unrealize(Elm_Grid_Cell *cell);
209 static void _cell_select(Elm_Grid_Cell *cell);
210 static void _cell_unselect(Elm_Grid_Cell *cell);
211
212 static Evas_Smart_Class _pan_sc = EVAS_SMART_CLASS_INIT_VERSION;
213
214 static void
215 _theme_hook(Evas_Object *obj)
216 {
217    Widget_Data *wd = elm_widget_data_get(obj);
218    if (!wd) return;
219    elm_smart_scroller_object_theme_set(obj, wd->scr, "grid", "base",
220                                        elm_widget_style_get(obj));
221    _sizing_eval(obj);
222 }
223
224 static void
225 _sizing_eval(Evas_Object *obj)
226 {
227    Widget_Data *wd = elm_widget_data_get(obj);
228    Evas_Coord minw = -1, minh = -1, maxw = -1, maxh = -1;
229    if (!wd) return;
230    evas_object_size_hint_max_get(wd->scr, &maxw, &maxh);
231    evas_object_size_hint_min_set(obj, minw, minh);
232    evas_object_size_hint_max_set(obj, maxw, maxh);
233 }
234
235 static void
236 _del_hook(Evas_Object *obj)
237 {
238    Widget_Data *wd = elm_widget_data_get(obj);
239    if (!wd) return;
240    if (wd->calc_job) ecore_job_del(wd->calc_job);
241    evas_object_del(wd->pan_smart);
242    wd->pan_smart = NULL;
243    elm_scrolled_grid_clear(obj);
244    free(wd);
245 }
246
247 static void
248 _mouse_move(void *data, Evas *evas __UNUSED__, Evas_Object *obj, void *event_info)
249 {
250    Elm_Grid_Cell *cell = data;
251    Evas_Event_Mouse_Move *ev = event_info;
252    Evas_Coord minw = 0, minh = 0, x, y, dx, dy, adx, ady;
253
254    if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD)
255      {
256         if (!cell->wd->on_hold) {
257              cell->wd->on_hold = EINA_TRUE;
258              _cell_unselect(cell);
259         }
260      }
261    if ((cell->dragging) && (cell->down))
262      {
263         if (cell->long_timer)
264           {
265              ecore_timer_del(cell->long_timer);
266              cell->long_timer = NULL;
267           }
268         evas_object_smart_callback_call(cell->wd->self, "drag", cell);
269         return;
270      }
271    if ((!cell->down) || (cell->wd->longpressed))
272      {
273         if (cell->long_timer)
274           {
275              ecore_timer_del(cell->long_timer);
276              cell->long_timer = NULL;
277           }
278         return;
279      }
280    if (!cell->display_only)
281      elm_coords_finger_size_adjust(1, &minw, 1, &minh);
282    evas_object_geometry_get(obj, &x, &y, NULL, NULL);
283    x = ev->cur.canvas.x - x;
284    y = ev->cur.canvas.y - y;
285    dx = x - cell->dx;
286    adx = dx;
287    if (adx < 0) adx = -dx;
288    dy = y - cell->dy;
289    ady = dy;
290    if (ady < 0) ady = -dy;
291    minw /= 2;
292    minh /= 2;
293    if ((adx > minw) || (ady > minh))
294      {
295         cell->dragging = 1;
296         if (cell->long_timer)
297           {
298              ecore_timer_del(cell->long_timer);
299              cell->long_timer = NULL;
300           }
301         if (cell->wd->wasselected)
302           _cell_unselect(cell);
303         cell->wd->wasselected = 0;
304         if (dy < 0)
305           {
306              if (ady > adx)
307                evas_object_smart_callback_call(cell->wd->self, "drag,start,up",
308                                                cell);
309              else
310                {
311                   if (dx < 0)
312                     evas_object_smart_callback_call(cell->wd->self,
313                                                     "drag,start,left", cell);
314                }
315           }
316         else
317           {
318              if (ady > adx)
319                evas_object_smart_callback_call(cell->wd->self,
320                                                "drag,start,down", cell);
321              else
322                {
323                   if (dx < 0)
324                     evas_object_smart_callback_call(cell->wd->self,
325                                                     "drag,start,left", cell);
326                   else
327                     evas_object_smart_callback_call(cell->wd->self,
328                                                     "drag,start,right", cell);
329                }
330           }
331      }
332 }
333
334 static int
335 _long_press(void *data)
336 {
337    Elm_Grid_Cell *cell = data;
338
339    cell->long_timer = NULL;
340    if ((cell->disabled) || (cell->dragging)) return 0;
341    cell->wd->longpressed = EINA_TRUE;
342    evas_object_smart_callback_call(cell->wd->self, "longpressed", cell);
343    return 0;
344 }
345
346 static void
347 _mouse_down(void *data, Evas *evas __UNUSED__, Evas_Object *obj, void *event_info)
348 {
349    Elm_Grid_Cell *cell = data;
350    Evas_Event_Mouse_Down *ev = event_info;
351    Evas_Coord x, y;
352
353    if (ev->button != 1) return;
354    cell->down = 1;
355    cell->dragging = 0;
356    evas_object_geometry_get(obj, &x, &y, NULL, NULL);
357    cell->dx = ev->canvas.x - x;
358    cell->dy = ev->canvas.y - y;
359    cell->wd->longpressed = EINA_FALSE;
360    if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) cell->wd->on_hold = EINA_TRUE;
361    else cell->wd->on_hold = EINA_FALSE;
362    cell->wd->wasselected = cell->selected;
363    _cell_hilight(cell);
364    if (ev->flags & EVAS_BUTTON_DOUBLE_CLICK)
365      evas_object_smart_callback_call(cell->wd->self, "clicked", cell);
366    if (cell->long_timer) ecore_timer_del(cell->long_timer);
367    if (cell->realized)
368      cell->long_timer = ecore_timer_add(1.0, _long_press, cell);
369    else
370      cell->long_timer = NULL;
371 }
372
373 static void
374 _mouse_up(void *data, Evas *evas __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info)
375 {
376    Elm_Grid_Cell *cell = data;
377    Evas_Event_Mouse_Up *ev = event_info;
378    Eina_Bool dragged = EINA_FALSE;
379
380    if (ev->button != 1) return;
381    cell->down = EINA_FALSE;
382    if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) cell->wd->on_hold = EINA_TRUE;
383    else cell->wd->on_hold = EINA_FALSE;
384    if (cell->long_timer)
385      {
386         ecore_timer_del(cell->long_timer);
387         cell->long_timer = NULL;
388      }
389    if (cell->dragging)
390      {
391         cell->dragging = EINA_FALSE;
392         evas_object_smart_callback_call(cell->wd->self, "drag,stop", cell);
393         dragged = EINA_TRUE;
394      }
395    if (cell->wd->on_hold)
396      {
397         cell->wd->longpressed = EINA_FALSE;
398         cell->wd->on_hold = EINA_FALSE;
399         return;
400      }
401    if (cell->wd->longpressed)
402      {
403         cell->wd->longpressed = EINA_FALSE;
404         if (!cell->wd->wasselected)
405           _cell_unselect(cell);
406         cell->wd->wasselected = EINA_FALSE;
407         return;
408      }
409    if (dragged)
410      {
411         if (cell->want_unrealize)
412              _cell_unrealize(cell);
413      }
414    if ((cell->disabled) || dragged) return;
415    if (cell->wd->multi)
416      {
417         if (!cell->selected)
418           {
419              _cell_hilight(cell);
420              _cell_select(cell);
421           }
422         else _cell_unselect(cell);
423      }
424    else
425      {
426         if (!cell->selected)
427           {
428              while (cell->wd->selected) _cell_unselect(cell->wd->selected->data);
429           }
430         else
431           {
432              const Eina_List *l, *l_next;
433              Elm_Grid_Cell *cell2;
434
435              EINA_LIST_FOREACH_SAFE(cell->wd->selected, l, l_next, cell2)
436                 if (cell2 != cell) _cell_unselect(cell2);
437           }
438         _cell_hilight(cell);
439         _cell_select(cell);
440      }
441 }
442
443 static void
444 _cell_hilight(Elm_Grid_Cell *cell)
445 {
446    if ((cell->wd->no_select) || (cell->delete_me) || (cell->hilighted)) return;
447    edje_object_signal_emit(cell->base, "elm,state,selected", "elm");
448    cell->hilighted = EINA_TRUE;
449 }
450
451 static void
452 _cell_realize(Elm_Grid_Cell *cell)
453 {
454    char buf[1024];
455
456    if ((cell->realized) || (cell->delete_me)) return;
457    cell->base = edje_object_add(evas_object_evas_get(cell->wd->self));
458    edje_object_scale_set(cell->base, elm_widget_scale_get(cell->wd->self) *
459                          _elm_config->scale);
460    evas_object_smart_member_add(cell->base, cell->wd->pan_smart);
461    elm_widget_sub_object_add(cell->wd->self, cell->base);
462    _elm_theme_object_set(cell->wd->self, cell->base, "grid", "cell/default",
463                          elm_widget_style_get(cell->wd->self));
464    cell->spacer = evas_object_rectangle_add(evas_object_evas_get(cell->wd->self));
465    evas_object_color_set(cell->spacer, 0, 0, 0, 0);
466    elm_widget_sub_object_add(cell->wd->self, cell->spacer);
467    evas_object_size_hint_min_set(cell->spacer, 2 * _elm_config->scale, 1);
468    edje_object_part_swallow(cell->base, "elm.swallow.pad", cell->spacer);
469
470    if (cell->gcc->func.label_get)
471      {
472         const Eina_List *l;
473         const char *key;
474
475         cell->labels = _elm_stringlist_get(edje_object_data_get(cell->base,
476                                                                 "labels"));
477         EINA_LIST_FOREACH(cell->labels, l, key)
478           {
479              char *s = cell->gcc->func.label_get(cell->data, cell->wd->self,
480                                                  l->data);
481              if (s)
482                {
483                   edje_object_part_text_set(cell->base, l->data, s);
484                   free(s);
485                }
486           }
487      }
488
489    if (cell->gcc->func.icon_get)
490      {
491         const Eina_List *l;
492         const char *key;
493
494         cell->icons = _elm_stringlist_get(edje_object_data_get(cell->base,
495                                                                "icons"));
496         EINA_LIST_FOREACH(cell->icons, l, key)
497           {
498              Evas_Object *ic = cell->gcc->func.icon_get(cell->data,
499                                                         cell->wd->self,
500                                                         l->data);
501              if (ic)
502                {
503                   cell->icon_objs = eina_list_append(cell->icon_objs, ic);
504                   edje_object_part_swallow(cell->base, key, ic);
505                   evas_object_show(ic);
506                   elm_widget_sub_object_add(cell->wd->self, ic);
507                }
508           }
509      }
510
511    if (cell->gcc->func.state_get)
512      {
513         const Eina_List *l;
514         const char *key;
515
516         cell->states = _elm_stringlist_get(edje_object_data_get(cell->base,
517                                                                 "states"));
518         EINA_LIST_FOREACH(cell->states, l, key)
519           {
520              Eina_Bool on = cell->gcc->func.state_get(cell->data,
521                    cell->wd->self, l->data);
522              if (on)
523                {
524                   snprintf(buf, sizeof(buf), "elm,state,%s,active", key);
525                   edje_object_signal_emit(cell->base, buf, "elm");
526                }
527           }
528      }
529
530    if (!cell->wd->cell_width && !cell->wd->cell_height)
531      {
532         edje_object_size_min_restricted_calc(cell->base,
533               &cell->wd->cell_width, &cell->wd->cell_height,
534               cell->wd->cell_width, cell->wd->cell_height);
535         elm_coords_finger_size_adjust(1, &cell->wd->cell_width,
536                                       1, &cell->wd->cell_height);
537      }
538
539    evas_object_event_callback_add(cell->base, EVAS_CALLBACK_MOUSE_DOWN,
540                                   _mouse_down, cell);
541    evas_object_event_callback_add(cell->base, EVAS_CALLBACK_MOUSE_UP,
542                                   _mouse_up, cell);
543    evas_object_event_callback_add(cell->base, EVAS_CALLBACK_MOUSE_MOVE,
544                                   _mouse_move, cell);
545
546    if (cell->selected)
547      edje_object_signal_emit(cell->base, "elm,state,selected", "elm");
548    if (cell->disabled)
549      edje_object_signal_emit(cell->base, "elm,state,disabled", "elm");
550
551    evas_object_show(cell->base);
552    cell->realized = EINA_TRUE;
553    cell->want_unrealize = EINA_FALSE;
554 }
555
556 static void
557 _cell_unrealize(Elm_Grid_Cell *cell)
558 {
559    Evas_Object *icon;
560
561    if (!cell->realized) return;
562    if (cell->long_timer)
563      {
564         ecore_timer_del(cell->long_timer);
565         cell->long_timer = NULL;
566      }
567    evas_object_del(cell->base);
568    cell->base = NULL;
569    evas_object_del(cell->spacer);
570    cell->spacer = NULL;
571    _elm_stringlist_free(cell->labels);
572    cell->labels = NULL;
573    _elm_stringlist_free(cell->icons);
574    cell->icons = NULL;
575    _elm_stringlist_free(cell->states);
576
577    EINA_LIST_FREE(cell->icon_objs, icon)
578       evas_object_del(icon);
579
580    cell->states = NULL;
581    cell->realized = EINA_FALSE;
582    cell->want_unrealize = EINA_FALSE;
583 }
584
585 static void
586 _cell_place(Elm_Grid_Cell *cell, Evas_Coord cx, Evas_Coord cy)
587 {
588    Evas_Coord x, y, ox, oy, cvx, cvy, cvw, cvh;
589    Evas_Coord tch, tcw, alignw = 0, alignh = 0, vw, vh;
590
591    cell->x = cx;
592    cell->y = cy;
593    evas_object_geometry_get(cell->wd->self, &ox, &oy, &vw, &vh);
594    evas_output_viewport_get(evas_object_evas_get(cell->wd->self),
595                             &cvx, &cvy, &cvw, &cvh);
596
597    /* Preload rows/columns at each side of the Grid */
598    cvx -= PRELOAD * cell->wd->cell_width;
599    cvy -= PRELOAD * cell->wd->cell_height;
600    cvw += 2 * PRELOAD * cell->wd->cell_width;
601    cvh += 2 * PRELOAD * cell->wd->cell_height;
602
603    tch = ((vh/cell->wd->cell_height)*cell->wd->cell_height);
604    alignh = (vh - tch)*cell->wd->align_y;
605
606    tcw = ((vw/cell->wd->cell_width)*cell->wd->cell_width);
607    alignw = (vw - tcw)*cell->wd->align_x;
608
609    if (cell->wd->horizontal && cell->wd->minw < vw)
610      {
611         int columns;
612
613         columns = eina_list_count(cell->wd->cells)/(vh/cell->wd->cell_height);
614         if (eina_list_count(cell->wd->cells) % (vh/cell->wd->cell_height))
615              columns++;
616
617         tcw = cell->wd->cell_width * columns;
618         alignw = (vw - tcw)*cell->wd->align_x;
619      }
620    else if (cell->wd->horizontal && cell->wd->minw > vw)
621         alignw = 0;   
622    if (!cell->wd->horizontal && cell->wd->minh < vh)
623      {
624         int rows;
625
626         rows = eina_list_count(cell->wd->cells)/(vw/cell->wd->cell_width);
627         if (eina_list_count(cell->wd->cells) % (vw/cell->wd->cell_width))
628              rows++;
629
630         tch = cell->wd->cell_height * rows;
631         alignh = (vh - tch)*cell->wd->align_y;
632      }
633    else if (!cell->wd->horizontal && cell->wd->minh > vh)
634         alignh = 0;
635    x = cx * cell->wd->cell_width - cell->wd->pan_x + ox + alignw;
636    y = cy * cell->wd->cell_height - cell->wd->pan_y + oy + alignh;
637
638    if (ELM_RECTS_INTERSECT(x, y, cell->wd->cell_width, cell->wd->cell_height,
639                            cvx, cvy, cvw, cvh))
640      {
641         Eina_Bool was_realized = cell->realized;
642         _cell_realize(cell);
643         if (!was_realized)
644           evas_object_smart_callback_call(cell->wd->self, "realized", cell);
645         evas_object_move(cell->base, x, y);
646         evas_object_resize(cell->base, cell->wd->cell_width,
647                            cell->wd->cell_height);
648      }
649    else
650      _cell_unrealize(cell);
651 }
652
653 static Elm_Grid_Cell *
654 _cell_create(Widget_Data *wd, const Elm_Grid_Cell_Class *gcc,
655       const void *data, Evas_Smart_Cb func, const void *func_data)
656 {
657    Elm_Grid_Cell *cell;
658
659    cell = calloc(1, sizeof(*cell));
660    if (!cell) return NULL;
661    cell->wd = wd;
662    cell->gcc = gcc;
663    cell->data = data;
664    cell->func.func = func;
665    cell->func.data = func_data;
666    return cell;
667 }
668
669 static void
670 _cell_del(Elm_Grid_Cell *cell)
671 {
672    if (cell->selected)
673      cell->wd->selected = eina_list_remove(cell->wd->selected, cell);
674    if (cell->realized) _cell_unrealize(cell);
675    if ((!cell->delete_me) && (cell->gcc->func.del))
676      cell->gcc->func.del(cell->data, cell->wd->self);
677    cell->delete_me = EINA_TRUE;
678    cell->wd->cells = eina_list_remove(cell->wd->cells, cell);
679    if (cell->long_timer) ecore_timer_del(cell->long_timer);
680    free(cell);
681 }
682
683 static void
684 _cell_select(Elm_Grid_Cell *cell)
685 {
686    if ((cell->wd->no_select) || (cell->delete_me)) return;
687    if (cell->selected)
688      {
689         if (cell->wd->always_select) goto call;
690         return;
691      }
692    cell->selected = EINA_TRUE;
693    cell->wd->selected = eina_list_append(cell->wd->selected, cell);
694 call:
695    cell->walking++;
696    if (cell->func.func) cell->func.func((void *)cell->func.data, cell->wd->self,
697                                         cell);
698    if (!cell->delete_me)
699      evas_object_smart_callback_call(cell->wd->self, "selected", cell);
700    cell->walking--;
701    if ((cell->walking == 0) && (cell->delete_me))
702      if (cell->relcount == 0) _cell_del(cell);
703 }
704
705 static void
706 _cell_unselect(Elm_Grid_Cell *cell)
707 {
708    if ((cell->delete_me) || (!cell->hilighted)) return;
709    edje_object_signal_emit(cell->base, "elm,state,unselected", "elm");
710    cell->hilighted = EINA_FALSE;
711    if (cell->selected)
712      {
713         cell->selected = EINA_FALSE;
714         cell->wd->selected = eina_list_remove(cell->wd->selected, cell);
715         evas_object_smart_callback_call(cell->wd->self, "unselected", cell);
716      }
717 }
718
719 static void
720 _calc_job(void *data)
721 {
722    Widget_Data *wd = data;
723    Evas_Coord minw = 0, minh = 0, nmax = 0, cvw, cvh;
724    int count;
725
726    evas_output_viewport_get(evas_object_evas_get(wd->self), NULL, NULL,
727                             &cvw, &cvh);
728    if (wd->horizontal && wd->cell_height)
729      nmax = cvh / wd->cell_height;
730    else if (wd->cell_width)
731      nmax = cvw / wd->cell_width;
732
733    if (nmax)
734      {
735         count = eina_list_count(wd->cells);
736         if (wd->horizontal)
737           {
738              minw = ceil(count  / (float)nmax) * wd->cell_width;
739              minh = nmax * wd->cell_height;
740           }
741         else
742           {
743              minw = nmax * wd->cell_width;
744              minh = ceil(count / (float)nmax) * wd->cell_height;
745           }
746      }
747
748    if ((minw != wd->minw) || (minh != wd->minh))
749      {
750         wd->minh = minh;
751         wd->minw = minw;
752         evas_object_smart_callback_call(wd->pan_smart, "changed", NULL);
753         _sizing_eval(wd->self);
754      }
755
756    wd->nmax = nmax;
757    wd->calc_job = NULL;
758    evas_object_smart_changed(wd->pan_smart);
759 }
760
761 static void
762 _pan_add(Evas_Object *obj)
763 {
764    Pan *sd;
765    Evas_Object_Smart_Clipped_Data *cd;
766
767    _pan_sc.add(obj);
768    cd = evas_object_smart_data_get(obj);
769    sd = ELM_NEW(Pan);
770    if (!sd) return;
771    sd->__clipped_data = *cd;
772    free(cd);
773    evas_object_smart_data_set(obj, sd);
774 }
775
776 static void
777 _pan_del(Evas_Object *obj)
778 {
779    Pan *sd = evas_object_smart_data_get(obj);
780
781    if (!sd) return;
782    _pan_sc.del(obj);
783 }
784
785 static void
786 _pan_set(Evas_Object *obj, Evas_Coord x, Evas_Coord y)
787 {
788    Pan *sd = evas_object_smart_data_get(obj);
789    if ((x == sd->wd->pan_x) && (y == sd->wd->pan_y)) return;
790    sd->wd->pan_x = x;
791    sd->wd->pan_y = y;
792    evas_object_smart_changed(obj);
793 }
794
795 static void
796 _pan_get(Evas_Object *obj, Evas_Coord *x, Evas_Coord *y)
797 {
798    Pan *sd = evas_object_smart_data_get(obj);
799    if (x) *x = sd->wd->pan_x;
800    if (y) *y = sd->wd->pan_y;
801 }
802
803 static void
804 _pan_child_size_get(Evas_Object *obj, Evas_Coord *w, Evas_Coord *h)
805 {
806    Pan *sd = evas_object_smart_data_get(obj);
807    if (w) *w = sd->wd->minw;
808    if (h) *h = sd->wd->minh;
809 }
810
811 static void
812 _pan_max_get(Evas_Object *obj, Evas_Coord *x, Evas_Coord *y)
813 {
814    Pan *sd = evas_object_smart_data_get(obj);
815    Evas_Coord ow, oh;
816
817    if (!sd) return;
818    evas_object_geometry_get(obj, NULL, NULL, &ow, &oh);
819    if (x)
820      *x = (ow < sd->wd->minw) ? sd->wd->minw - ow : 0;
821    if (y)
822      *y = (oh < sd->wd->minh) ? sd->wd->minh - oh : 0;
823 }
824
825 static void
826 _pan_resize(Evas_Object *obj, Evas_Coord w, Evas_Coord h)
827 {
828    Pan *sd = evas_object_smart_data_get(obj);
829    Evas_Coord ow, oh;
830
831    evas_object_geometry_get(obj, NULL, NULL, &ow, &oh);
832    if ((ow == w) && (oh == h)) return;
833    if (sd->wd->calc_job) ecore_job_del(sd->wd->calc_job);
834    sd->wd->calc_job = ecore_job_add(_calc_job, sd->wd);
835 }
836
837 static void
838 _pan_calculate(Evas_Object *obj)
839 {
840    Pan *sd = evas_object_smart_data_get(obj);
841    Evas_Coord cx = 0, cy = 0;
842    Eina_List *l;
843    Elm_Grid_Cell *cell;
844
845    if (!sd) return;
846    if (!sd->wd->nmax) return;
847
848    EINA_LIST_FOREACH(sd->wd->cells, l, cell)
849      {
850         _cell_place(cell, cx, cy);
851         if (sd->wd->horizontal)
852           {
853              cy = (cy + 1) % sd->wd->nmax;
854              if (!cy) cx++;
855           }
856         else
857           {
858              cx = (cx + 1) % sd->wd->nmax;
859              if (!cx) cy++;
860           }
861      }
862 }
863
864 static void
865 _pan_move(Evas_Object *obj, Evas_Coord x __UNUSED__, Evas_Coord y __UNUSED__)
866 {
867    Pan *sd = evas_object_smart_data_get(obj);
868    if (!sd) return;
869    if (sd->wd->calc_job) ecore_job_del(sd->wd->calc_job);
870    sd->wd->calc_job = ecore_job_add(_calc_job, sd->wd);
871 }
872
873 static void
874 _hold_on(void *data __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
875 {
876    Widget_Data *wd = elm_widget_data_get(obj);
877    if (!wd) return;
878    elm_smart_scroller_hold_set(wd->scr, 1);
879 }
880
881 static void
882 _hold_off(void *data __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
883 {
884    Widget_Data *wd = elm_widget_data_get(obj);
885    if (!wd) return;
886    elm_smart_scroller_hold_set(wd->scr, 0);
887 }
888
889 static void
890 _freeze_on(void *data __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
891 {
892    Widget_Data *wd = elm_widget_data_get(obj);
893    if (!wd) return;
894    elm_smart_scroller_freeze_set(wd->scr, 1);
895 }
896
897 static void
898 _freeze_off(void *data __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
899 {
900    Widget_Data *wd = elm_widget_data_get(obj);
901    if (!wd) return;
902    elm_smart_scroller_freeze_set(wd->scr, 0);
903 }
904
905 static void
906 _scr_drag_start(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
907 {
908    evas_object_smart_callback_call(data, "scroll,drag,start", NULL);
909 }
910
911 static void
912 _scr_drag_stop(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
913 {
914    evas_object_smart_callback_call(data, "scroll,drag,stop", NULL);
915 }
916
917 static void
918 _scr_scroll(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
919 {
920    evas_object_smart_callback_call(data, "scroll", NULL);
921 }
922
923 /**
924  * Add a new Scrolled Grid object.
925  *
926  * @param parent The parent object.
927  * @return  The new object or NULL if it cannot be created.
928  *
929  * @see elm_scrolled_grid_cell_size_set()
930  * @see elm_scrolled_grid_horizontal_set()
931  * @see elm_scrolled_grid_cell_add()
932  * @see elm_scrolled_grid_cell_del()
933  * @see elm_scrolled_grid_clear()
934  *
935  * @ingroup Grid
936  */
937 EAPI Evas_Object *
938 elm_scrolled_grid_add(Evas_Object *parent)
939 {
940    Evas_Object *obj;
941    Evas *e;
942    Evas_Coord minw, minh;
943    Widget_Data *wd;
944    static Evas_Smart *smart = NULL;
945
946    wd = ELM_NEW(Widget_Data);
947    e = evas_object_evas_get(parent);
948    obj = elm_widget_add(e);
949    ELM_SET_WIDTYPE(widtype, "grid");
950    elm_widget_type_set(obj, "grid");
951    elm_widget_sub_object_add(parent, obj);
952    elm_widget_data_set(obj, wd);
953    elm_widget_del_hook_set(obj, _del_hook);
954    elm_widget_theme_hook_set(obj, _theme_hook);
955
956    wd->scr = elm_smart_scroller_add(e);
957    elm_smart_scroller_widget_set(wd->scr, obj);
958    elm_smart_scroller_object_theme_set(obj, wd->scr, "grid", "base", "default");
959    elm_widget_resize_object_set(obj, wd->scr);
960
961    evas_object_smart_callback_add(wd->scr, "drag,start", _scr_drag_start, obj);
962    evas_object_smart_callback_add(wd->scr, "drag,stop", _scr_drag_stop, obj);
963    evas_object_smart_callback_add(wd->scr, "scroll", _scr_scroll, obj);
964
965    elm_smart_scroller_bounce_allow_set(wd->scr, 1, 1);
966
967    wd->self = obj;
968    wd->align_x = 0.5;
969    wd->align_y = 0.5;
970
971    evas_object_smart_callback_add(obj, "scroll-hold-on", _hold_on, obj);
972    evas_object_smart_callback_add(obj, "scroll-hold-off", _hold_off, obj);
973    evas_object_smart_callback_add(obj, "scroll-freeze-on", _freeze_on, obj);
974    evas_object_smart_callback_add(obj, "scroll-freeze-off", _freeze_off, obj);
975
976    if (!smart)
977      {
978         static Evas_Smart_Class sc;
979
980         evas_object_smart_clipped_smart_set(&_pan_sc);
981         sc = _pan_sc;
982         sc.name = "elm_scrolled_grid_pan";
983         sc.version = EVAS_SMART_CLASS_VERSION;
984         sc.add = _pan_add;
985         sc.del = _pan_del;
986         sc.resize = _pan_resize;
987         sc.move = _pan_move;
988         sc.calculate = _pan_calculate;
989         smart = evas_smart_class_new(&sc);
990      }
991    if (smart)
992      {
993         wd->pan_smart = evas_object_smart_add(e, smart);
994         wd->pan = evas_object_smart_data_get(wd->pan_smart);
995         wd->pan->wd = wd;
996      }
997
998    elm_smart_scroller_extern_pan_set(wd->scr, wd->pan_smart,
999                                      _pan_set, _pan_get,
1000                                      _pan_max_get, _pan_child_size_get);
1001
1002    _sizing_eval(obj);
1003
1004    return obj;
1005 }
1006
1007 /**
1008  * Set the size for the cell of the Grid.
1009  *
1010  * @param obj The Grid object.
1011  * @param w The cell's width.
1012  * @param h The cell's height;
1013  *
1014  * @see elm_scrolled_grid_cell_size_get()
1015  *
1016  * @ingroup Grid
1017  */
1018 EAPI void
1019 elm_scrolled_grid_cell_size_set(Evas_Object *obj, Evas_Coord w, Evas_Coord h)
1020 {
1021    ELM_CHECK_WIDTYPE(obj, widtype);
1022    Widget_Data *wd = elm_widget_data_get(obj);
1023    if (!wd) return;
1024    if (wd->cell_width == w && wd->cell_height == h) return;
1025    wd->cell_width = w;
1026    wd->cell_height = h;
1027    if (wd->calc_job) ecore_job_del(wd->calc_job);
1028    wd->calc_job = ecore_job_add(_calc_job, wd);
1029 }
1030
1031 /**
1032  * Get the size of the cell of the Grid.
1033  *
1034  * @param obj The Grid object.
1035  * @param w Pointer to the cell's width.
1036  * @param h Pointer to the cell's height.
1037  *
1038  * @see elm_scrolled_grid_cell_size_get()
1039  *
1040  * @ingroup Grid
1041  */
1042 EAPI void
1043 elm_scrolled_grid_cell_size_get(const Evas_Object *obj, Evas_Coord *w, Evas_Coord *h)
1044 {
1045    ELM_CHECK_WIDTYPE(obj, widtype);
1046    Widget_Data *wd = elm_widget_data_get(obj);
1047    if (!wd) return;
1048    if (w) *w = wd->cell_width;
1049    if (h) *h = wd->cell_height;
1050 }
1051
1052 /**
1053  * Set cell's alignment within the scroller.
1054  *
1055  * @param obj The grid object.
1056  * @param align_x The x alignment (0 <= x <= 1).
1057  * @param align_y The y alignment (0 <= y <= 1).
1058  *
1059  * @see elm_scrolled_grid_align_get()
1060  *
1061  * @ingroup Grid
1062  */
1063 EAPI void
1064 elm_scrolled_grid_align_set(Evas_Object *obj, double align_x, double align_y)
1065 {
1066    ELM_CHECK_WIDTYPE(obj, widtype);
1067    Widget_Data *wd = elm_widget_data_get(obj);
1068
1069    if (align_x > 1.0)
1070      align_x = 1.0;
1071    else if (align_x < 0.0)
1072      align_x = 0.0;
1073    wd->align_x = align_x;
1074
1075    if (align_y > 1.0)
1076      align_y = 1.0;
1077    else if (align_y < 0.0)
1078      align_y = 0.0;
1079    wd->align_y = align_y;
1080 }
1081
1082 /**
1083  * Get the alignenment set for the grid object.
1084  *
1085  * @param obj The grid object.
1086  * @param align_x Pointer to x alignenment.
1087  * @param align_y Pointer to y alignenment.
1088  *
1089  * @see elm_scrolled_grid_align_set()
1090  *
1091  * @ingroup Grid
1092  */
1093 EAPI void
1094 elm_scrolled_grid_align_get(const Evas_Object *obj, double *align_x, double *align_y)
1095 {
1096     ELM_CHECK_WIDTYPE(obj, widtype);
1097     Widget_Data *wd = elm_widget_data_get(obj);
1098     if (align_x) *align_x = wd->align_x;
1099     if (align_y) *align_y = wd->align_y;
1100 }
1101
1102 /**
1103  * Add cell to the end of the Grid.
1104  *
1105  * @param obj The Grid object.
1106  * @param gcc The cell class for the cell.
1107  * @param data The cell data.
1108  * @param func Convenience function called when cell is selected.
1109  * @param func_data Data passed to @p func above.
1110  * @return A handle to the cell added or NULL if not possible.
1111  *
1112  * @see elm_scrolled_grid_cell_del()
1113  *
1114  * @ingroup Grid
1115  */
1116 EAPI Elm_Grid_Cell *
1117 elm_scrolled_grid_cell_add(Evas_Object *obj, const Elm_Grid_Cell_Class *gcc,
1118                            const void *data, Evas_Smart_Cb func,
1119                            const void *func_data)
1120 {
1121    Elm_Grid_Cell *cell;
1122    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
1123    Widget_Data *wd = elm_widget_data_get(obj);
1124    if (!wd) return NULL;
1125
1126    cell = _cell_create(wd, gcc, data, func, func_data);
1127    if (!cell) return NULL;
1128
1129    wd->cells = eina_list_append(wd->cells, cell);
1130    wd->no_select = EINA_FALSE;
1131
1132    if (wd->calc_job) ecore_job_del(wd->calc_job);
1133    wd->calc_job = ecore_job_add(_calc_job, wd);
1134
1135    return cell;
1136 }
1137
1138 /**
1139  * Remove a cell from the Grid.
1140  *
1141  * @param cell The cell to be removed.
1142  * @return @c EINA_TRUE on success or @c EINA_FALSE otherwise.
1143  *
1144  * @see elm_scrolled_grid_clear() to remove all cells of the grid.
1145  *
1146  * @ingroup Grid
1147  */
1148 EAPI void
1149 elm_scrolled_grid_cell_del(Elm_Grid_Cell *cell)
1150 {
1151    if (!cell) return;
1152    if ((cell->relcount > 0) || (cell->walking > 0))
1153      {
1154         cell->delete_me = EINA_TRUE;
1155         if (cell->selected)
1156           cell->wd->selected = eina_list_remove(cell->wd->selected, cell);
1157         if (cell->gcc->func.del) cell->gcc->func.del(cell->data, cell->wd->self);
1158         return;
1159      }
1160
1161    _cell_del(cell);
1162
1163    if (cell->wd->calc_job) ecore_job_del(cell->wd->calc_job);
1164    cell->wd->calc_job = ecore_job_add(_calc_job, cell->wd);
1165 }
1166
1167 /**
1168  * Set for what direction the grid will expand.
1169  *
1170  * @param obj The Grid object.
1171  * @param setting If @c EINA_TRUE the grid will expand horizontally or
1172  * vertically if @c EINA_FALSE.
1173  *
1174  * @ingroup Grid
1175  */
1176 EAPI void
1177 elm_scrolled_grid_horizontal_set(Evas_Object *obj, Eina_Bool setting)
1178 {
1179    ELM_CHECK_WIDTYPE(obj, widtype);
1180    Widget_Data *wd = elm_widget_data_get(obj);
1181    if (!wd) return;
1182    if (setting == wd->horizontal) return;
1183    wd->horizontal = setting;
1184
1185    /* Update the cells to conform to the new layout */
1186    if (wd->calc_job) ecore_job_del(wd->calc_job);
1187    wd->calc_job = ecore_job_add(_calc_job, wd);
1188 }
1189
1190 /**
1191  * Clear the Grid
1192  *
1193  * This clears all cells in the grid, leaving it empty.
1194  *
1195  * @param obj The Grid object.
1196  *
1197  * @see elm_scrolled_grid_cell_del() to remove just one cell.
1198  *
1199  * @ingroup Grid
1200  */
1201 EAPI void
1202 elm_scrolled_grid_clear(Evas_Object *obj)
1203 {
1204    Eina_List *l, *l_next;
1205    Elm_Grid_Cell *cell;
1206    ELM_CHECK_WIDTYPE(obj, widtype);
1207    Widget_Data *wd = elm_widget_data_get(obj);
1208    if (!wd) return;
1209
1210    if (wd->calc_job)
1211      {
1212         ecore_job_del(wd->calc_job);
1213         wd->calc_job = NULL;
1214      }
1215
1216    EINA_LIST_FOREACH_SAFE(wd->cells, l, l_next, cell)
1217      {
1218         if (cell->realized) _cell_unrealize(cell);
1219         if (cell->gcc->func.del) cell->gcc->func.del(cell->data, wd->self);
1220         if (cell->long_timer) ecore_timer_del(cell->long_timer);
1221         free(cell);
1222         wd->cells = eina_list_remove_list(wd->cells, l);
1223      }
1224
1225    if (wd->selected)
1226      {
1227         eina_list_free(wd->selected);
1228         wd->selected = NULL;
1229      }
1230
1231    wd->pan_x = 0;
1232    wd->pan_y = 0;
1233    wd->minw = 0;
1234    wd->minh = 0;
1235    evas_object_size_hint_min_set(wd->pan_smart, wd->minw, wd->minh);
1236    evas_object_smart_callback_call(wd->pan_smart, "changed", NULL);
1237    _sizing_eval(obj);
1238 }
1239
1240 /**
1241  * Get the real evas object of the grid cell
1242  *
1243  * This returns the actual evas object used for the specified grid cell.
1244  * This may be NULL as it may not be created, and may be deleted at any time
1245  * by grid. Do not modify this object (move, resize, show, hide etc.) as grid
1246  * is controlling it. This function is for querying, emitting custom signals
1247  * or hooking lower level callbacks for events. Do not delete this object
1248  * under any circumstances.
1249  *
1250  * @param cell The Grid cell.
1251  * @return the evas object associated to this cell.
1252  *
1253  * @see elm_scrolled_grid_cell_data_get()
1254  *
1255  * @ingroup Grid
1256  */
1257 EAPI const Evas_Object *
1258 elm_scrolled_grid_cell_object_get(Elm_Grid_Cell *cell)
1259 {
1260    if (!cell) return NULL;
1261    return cell->base;
1262 }
1263
1264 /**
1265  * Returns the data associated to a cell
1266  *
1267  * This returns the data value passed on the elm_scrolled_grid_cell_add() and
1268  * related cell addition calls.
1269  *
1270  * @param cell The Grid cell.
1271  * @return the data associated to this cell.
1272  *
1273  * @see elm_scrolled_grid_cell_add()
1274  * @see elm_scrolled_grid_cell_object_get()
1275  *
1276  * @ingroup Grid
1277  */
1278 EAPI void *
1279 elm_scrolled_grid_cell_data_get(Elm_Grid_Cell *cell)
1280 {
1281    if (!cell) return NULL;
1282    return (void *)cell->data;
1283 }
1284
1285 /**
1286  * Get the cell's coordinates.
1287  *
1288  * This returns the logical position of the cell whithin the Grid.
1289  *
1290  * @param cell The Grid cell.
1291  * @param x The x-axis coordinate pointer.
1292  * @param y The y-axis coordinate pointer.
1293  *
1294  * @ingroup Grid
1295  */
1296 EAPI void
1297 elm_scrolled_grid_cell_pos_get(const Elm_Grid_Cell *cell, unsigned int *x, unsigned int *y)
1298 {
1299    if (!cell) return;
1300    if (x) *x = cell->x;
1301    if (y) *y = cell->y;
1302 }
1303
1304 /**
1305  * Enable or disable multi-select in the grid.
1306  *
1307  * This enables (EINA_TRUE) or disables (EINA_FALSE) multi-select in the grid.
1308  * This allows more than 1 cell to be selected.
1309  *
1310  * @param obj The grid object.
1311  * @param multi Multi-select enabled/disabled
1312  *
1313  * @ingroup Grid
1314  */
1315 EAPI void
1316 elm_scrolled_grid_multi_select_set(Evas_Object *obj, Eina_Bool multi)
1317 {
1318    ELM_CHECK_WIDTYPE(obj, widtype);
1319    Widget_Data *wd = elm_widget_data_get(obj);
1320    if (!wd) return;
1321    wd->multi = multi;
1322 }
1323
1324 /**
1325  * Get if multi-select in grid is enabled or disabled
1326  *
1327  * @param obj The grid object
1328  * @return Multi-select enable/disable
1329  * (EINA_TRUE = enabled / EINA_FALSE = disabled)
1330  *
1331  * @ingroup Grid
1332  */
1333 EAPI Eina_Bool
1334 elm_scrolled_grid_multi_select_get(const Evas_Object *obj)
1335 {
1336    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
1337    Widget_Data *wd = elm_widget_data_get(obj);
1338    if (!wd) return EINA_FALSE;
1339    return wd->multi;
1340 }
1341
1342 /**
1343  * Get the selected cell in the grid
1344  *
1345  * This gets the selected cell in the grid (if multi-select is enabled only
1346  * the first cell in the list is selected - which is not very useful, so see
1347  * elm_scrolled_grid_selected_cells_get() for when multi-select is used).
1348  *
1349  * If no cell is selected, NULL is returned.
1350  *
1351  * @param obj The grid object.
1352  * @return The selected cell, or NULL if none.
1353  *
1354  * @ingroup Grid
1355  */
1356 EAPI Elm_Grid_Cell *
1357 elm_scrolled_grid_selected_cell_get(const Evas_Object *obj)
1358 {
1359    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
1360    Widget_Data *wd = elm_widget_data_get(obj);
1361    if (!wd) return NULL;
1362    if (wd->selected) return wd->selected->data;
1363    return NULL;
1364 }
1365
1366 /**
1367  * Get a list of selected cells in the grid.
1368  *
1369  * This returns a list of the selected cells. This list pointer is only valid
1370  * so long as no cells are selected or unselected (or unselected implictly by
1371  * deletion). The list contains Elm_Grid_Cell pointers.
1372  *
1373  * @param obj The grid object.
1374  * @return The list of selected cells, or NULL if none are selected.
1375  *
1376  * @ingroup Grid
1377  */
1378 EAPI const Eina_List *
1379 elm_scrolled_grid_selected_cells_get(const Evas_Object *obj)
1380 {
1381    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
1382    Widget_Data *wd = elm_widget_data_get(obj);
1383    if (!wd) return NULL;
1384    return wd->selected;
1385 }
1386
1387 /**
1388  * Get the selected state of a cell.
1389  *
1390  * This gets the selected state of a cell (1 selected, 0 not selected).
1391  *
1392  * @param cell The cell
1393  * @return The selected state
1394  *
1395  * @ingroup Grid
1396  */
1397 EAPI Eina_Bool
1398 elm_scrolled_grid_cell_selected_get(const Elm_Grid_Cell *cell)
1399 {
1400    if (!cell) return EINA_FALSE;
1401    return cell->selected;
1402 }
1403
1404 /**
1405  * Sets the disabled state of a cell.
1406  *
1407  * A disabled cell cannot be selected or unselected. It will also change
1408  * appearance to disabled. This sets the disabled state (1 disabled, 0 not
1409  * disabled).
1410  *
1411  * @param cell The cell
1412  * @param disabled The disabled state
1413  *
1414  * @ingroup Grid
1415  */
1416 EAPI void
1417 elm_scrolled_grid_cell_disabled_set(Elm_Grid_Cell *cell, Eina_Bool disabled)
1418 {
1419    if (!cell) return;
1420    if (cell->disabled == disabled) return;
1421    if (cell->delete_me) return;
1422    cell->disabled = disabled;
1423    if (cell->realized)
1424      {
1425         if (cell->disabled)
1426           edje_object_signal_emit(cell->base, "elm,state,disabled", "elm");
1427         else
1428           edje_object_signal_emit(cell->base, "elm,state,enabled", "elm");
1429      }
1430 }
1431
1432 /**
1433  * Get the disabled state of a cell.
1434  *
1435  * This gets the disabled state of the given cell.
1436  *
1437  * @param cell The cell
1438  * @return The disabled state
1439  *
1440  * @ingroup Grid
1441  */
1442 EAPI Eina_Bool
1443 elm_scrolled_grid_cell_disabled_get(const Elm_Grid_Cell *cell)
1444 {
1445    if (!cell) return EINA_FALSE;
1446    if (cell->delete_me) return EINA_FALSE;
1447    return cell->disabled;
1448 }
1449
1450 /**
1451  * Set the always select mode.
1452  *
1453  * Cells will only call their selection func and callback when first becoming
1454  * selected. Any further clicks will do nothing, unless you enable always select
1455  * with elm_scrolled_grid_always_select_mode_set(). This means even if selected,
1456  * every click will make the selected callbacks be called.
1457  *
1458  * @param obj The grid object
1459  * @param always_select The always select mode (EINA_TRUE = on, EINA_FALSE = off)
1460  *
1461  * @ingroup Grid
1462  */
1463 EAPI void
1464 elm_scrolled_grid_always_select_mode_set(Evas_Object *obj, Eina_Bool always_select)
1465 {
1466    ELM_CHECK_WIDTYPE(obj, widtype);
1467    Widget_Data *wd = elm_widget_data_get(obj);
1468    if (!wd) return;
1469    wd->always_select = always_select;
1470 }
1471
1472 /**
1473  * Get the always select mode.
1474  *
1475  * @param obj The grid object.
1476  * @return The always select mode (EINA_TRUE = on, EINA_FALSE = off)
1477  *
1478  * @ingroup Grid
1479  */
1480 EAPI Eina_Bool
1481 elm_scrolled_grid_always_select_mode_get(const Evas_Object *obj)
1482 {
1483    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
1484    Widget_Data *wd = elm_widget_data_get(obj);
1485    if (!wd) return EINA_FALSE;
1486    return wd->always_select;
1487 }
1488
1489 /**
1490  * Set no select mode.
1491  *
1492  * This will turn off the ability to select items entirely and they will
1493  * neither appear selected nor call selected callback functions.
1494  *
1495  * @param obj The grid object
1496  * @param no_select The no select mode (EINA_TRUE = on, EINA_FALSE = off)
1497  *
1498  * @ingroup Grid
1499  */
1500 EAPI void
1501 elm_scrolled_grid_no_select_mode_set(Evas_Object *obj, Eina_Bool no_select)
1502 {
1503    ELM_CHECK_WIDTYPE(obj, widtype);
1504    Widget_Data *wd = elm_widget_data_get(obj);
1505    if (!wd) return;
1506    wd->no_select = no_select;
1507 }
1508
1509 /**
1510  * Gets no select mode.
1511  *
1512  * @param obj The grid object
1513  * @return The no select mode (EINA_TRUE = on, EINA_FALSE = off)
1514  *
1515  * @ingroup Grid
1516  */
1517 EAPI Eina_Bool
1518 elm_scrolled_grid_no_select_mode_get(const Evas_Object *obj)
1519 {
1520    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
1521    Widget_Data *wd = elm_widget_data_get(obj);
1522    if (!wd) return EINA_FALSE;
1523    return wd->no_select;
1524 }
1525
1526 /**
1527  * Set bounce mode.
1528  *
1529  * This will enable or disable the scroller bounce mode for the grid. See
1530  * elm_scroller_bounce_set() for details.
1531  *
1532  * @param obj The grid object
1533  * @param h_bounce Allow bounce horizontally
1534  * @param v_bounce Allow bounce vertically
1535  *
1536  * @ingroup Grid
1537  */
1538 EAPI void
1539 elm_scrolled_grid_bounce_set(Evas_Object *obj, Eina_Bool h_bounce, Eina_Bool v_bounce)
1540 {
1541    ELM_CHECK_WIDTYPE(obj, widtype);
1542    Widget_Data *wd = elm_widget_data_get(obj);
1543    if (!wd) return;
1544    elm_smart_scroller_bounce_allow_set(wd->scr, h_bounce, v_bounce);
1545 }
1546
1547 /**
1548  * Get the bounce mode
1549  *
1550  * @param obj The grid object
1551  * @param h_bounce Allow bounce horizontally
1552  * @param v_bounce Allow bounce vertically
1553  *
1554  * @ingroup Grid
1555  */
1556 EAPI void
1557 elm_scrolled_grid_bounce_get(const Evas_Object *obj, Eina_Bool *h_bounce, Eina_Bool *v_bounce)
1558 {
1559    ELM_CHECK_WIDTYPE(obj, widtype);
1560    Widget_Data *wd = elm_widget_data_get(obj);
1561    if (!wd) return;
1562    elm_smart_scroller_bounce_allow_get(wd->scr, h_bounce, v_bounce);
1563 }