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