2 * vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2
4 #include <Elementary.h>
8 * @defgroup Grid Scrolled Grid
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.
15 * Signals that you can add callbacks for are:
17 * clicked - The user has double-clicked a cell. The event_info parameter is
18 * the grid cell that was double-clicked.
20 * selected - The user has made an item selected. The event_info parameter is
21 * the grid cell that was selected.
23 * unselected - The user has made an item unselected. The event_info parameter
24 * is the grid cell that was unselected.
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
32 * drag,start,up - Called when the cell in the grid has been dragged (not
35 * drag,start,down - Called when the cell in the grid has been dragged (not
38 * drag,start,left - Called when the cell in the grid has been dragged (not
41 * drag,start,right - Called when the cell in the grid has been dragged (not
44 * drag,stop - Called when the cell in the grid has stopped being dragged.
46 * drag - Called when the cell in the grid is being dragged.
48 * scroll - called when the content has been scrolled (moved).
50 * scroll,drag,start - called when dragging the content has started.
52 * scroll,drag,stop - called when dragging the content has stopped.
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).
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:
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".
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.
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.
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.
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.
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).
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.
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.
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
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.
138 * * Handle non-homogeneous objects too.
141 typedef struct _Widget_Data Widget_Data;
142 typedef struct _Pan Pan;
146 struct _Elm_Grid_Cell
148 Evas_Object *base, *spacer;
149 const Elm_Grid_Cell_Class *gcc;
150 Ecore_Timer *long_timer;
152 Eina_List *labels, *icons, *states, *icon_objs;
160 Evas_Coord x, y, dx, dy;
163 Eina_Bool want_unrealize : 1;
164 Eina_Bool realized : 1;
165 Eina_Bool dragging : 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;
177 Evas_Object *self, *scr;
178 Evas_Object *pan_smart;
183 double align_x, align_y;
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 */
190 Eina_Bool horizontal : 1;
191 Eina_Bool on_hold : 1;
192 Eina_Bool longpressed : 1;
194 Eina_Bool no_select : 1;
195 Eina_Bool wasselected : 1;
196 Eina_Bool always_select : 1;
201 Evas_Object_Smart_Clipped_Data __clipped_data;
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);
212 static Evas_Smart_Class _pan_sc = EVAS_SMART_CLASS_INIT_VERSION;
215 _theme_hook(Evas_Object *obj)
217 Widget_Data *wd = elm_widget_data_get(obj);
219 elm_smart_scroller_object_theme_set(obj, wd->scr, "grid", "base",
220 elm_widget_style_get(obj));
225 _sizing_eval(Evas_Object *obj)
227 Widget_Data *wd = elm_widget_data_get(obj);
228 Evas_Coord minw = -1, minh = -1, maxw = -1, maxh = -1;
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);
236 _del_hook(Evas_Object *obj)
238 Widget_Data *wd = elm_widget_data_get(obj);
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);
248 _mouse_move(void *data, Evas *evas __UNUSED__, Evas_Object *obj, void *event_info)
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;
254 if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD)
256 if (!cell->wd->on_hold) {
257 cell->wd->on_hold = EINA_TRUE;
258 _cell_unselect(cell);
261 if ((cell->dragging) && (cell->down))
263 if (cell->long_timer)
265 ecore_timer_del(cell->long_timer);
266 cell->long_timer = NULL;
268 evas_object_smart_callback_call(cell->wd->self, "drag", cell);
271 if ((!cell->down) || (cell->wd->longpressed))
273 if (cell->long_timer)
275 ecore_timer_del(cell->long_timer);
276 cell->long_timer = NULL;
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;
287 if (adx < 0) adx = -dx;
290 if (ady < 0) ady = -dy;
293 if ((adx > minw) || (ady > minh))
296 if (cell->long_timer)
298 ecore_timer_del(cell->long_timer);
299 cell->long_timer = NULL;
301 if (cell->wd->wasselected)
302 _cell_unselect(cell);
303 cell->wd->wasselected = 0;
307 evas_object_smart_callback_call(cell->wd->self, "drag,start,up",
312 evas_object_smart_callback_call(cell->wd->self,
313 "drag,start,left", cell);
319 evas_object_smart_callback_call(cell->wd->self,
320 "drag,start,down", cell);
324 evas_object_smart_callback_call(cell->wd->self,
325 "drag,start,left", cell);
327 evas_object_smart_callback_call(cell->wd->self,
328 "drag,start,right", cell);
335 _long_press(void *data)
337 Elm_Grid_Cell *cell = data;
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);
347 _mouse_down(void *data, Evas *evas __UNUSED__, Evas_Object *obj, void *event_info)
349 Elm_Grid_Cell *cell = data;
350 Evas_Event_Mouse_Down *ev = event_info;
353 if (ev->button != 1) return;
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;
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);
368 cell->long_timer = ecore_timer_add(1.0, _long_press, cell);
370 cell->long_timer = NULL;
374 _mouse_up(void *data, Evas *evas __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info)
376 Elm_Grid_Cell *cell = data;
377 Evas_Event_Mouse_Up *ev = event_info;
378 Eina_Bool dragged = EINA_FALSE;
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)
386 ecore_timer_del(cell->long_timer);
387 cell->long_timer = NULL;
391 cell->dragging = EINA_FALSE;
392 evas_object_smart_callback_call(cell->wd->self, "drag,stop", cell);
395 if (cell->wd->on_hold)
397 cell->wd->longpressed = EINA_FALSE;
398 cell->wd->on_hold = EINA_FALSE;
401 if (cell->wd->longpressed)
403 cell->wd->longpressed = EINA_FALSE;
404 if (!cell->wd->wasselected)
405 _cell_unselect(cell);
406 cell->wd->wasselected = EINA_FALSE;
411 if (cell->want_unrealize)
412 _cell_unrealize(cell);
414 if ((cell->disabled) || dragged) return;
422 else _cell_unselect(cell);
428 while (cell->wd->selected) _cell_unselect(cell->wd->selected->data);
432 const Eina_List *l, *l_next;
433 Elm_Grid_Cell *cell2;
435 EINA_LIST_FOREACH_SAFE(cell->wd->selected, l, l_next, cell2)
436 if (cell2 != cell) _cell_unselect(cell2);
444 _cell_hilight(Elm_Grid_Cell *cell)
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;
452 _cell_realize(Elm_Grid_Cell *cell)
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) *
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);
470 if (cell->gcc->func.label_get)
475 cell->labels = _elm_stringlist_get(edje_object_data_get(cell->base,
477 EINA_LIST_FOREACH(cell->labels, l, key)
479 char *s = cell->gcc->func.label_get(cell->data, cell->wd->self,
483 edje_object_part_text_set(cell->base, l->data, s);
489 if (cell->gcc->func.icon_get)
494 cell->icons = _elm_stringlist_get(edje_object_data_get(cell->base,
496 EINA_LIST_FOREACH(cell->icons, l, key)
498 Evas_Object *ic = cell->gcc->func.icon_get(cell->data,
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);
511 if (cell->gcc->func.state_get)
516 cell->states = _elm_stringlist_get(edje_object_data_get(cell->base,
518 EINA_LIST_FOREACH(cell->states, l, key)
520 Eina_Bool on = cell->gcc->func.state_get(cell->data,
521 cell->wd->self, l->data);
524 snprintf(buf, sizeof(buf), "elm,state,%s,active", key);
525 edje_object_signal_emit(cell->base, buf, "elm");
530 if (!cell->wd->cell_width && !cell->wd->cell_height)
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);
539 evas_object_event_callback_add(cell->base, EVAS_CALLBACK_MOUSE_DOWN,
541 evas_object_event_callback_add(cell->base, EVAS_CALLBACK_MOUSE_UP,
543 evas_object_event_callback_add(cell->base, EVAS_CALLBACK_MOUSE_MOVE,
547 edje_object_signal_emit(cell->base, "elm,state,selected", "elm");
549 edje_object_signal_emit(cell->base, "elm,state,disabled", "elm");
551 evas_object_show(cell->base);
552 cell->realized = EINA_TRUE;
553 cell->want_unrealize = EINA_FALSE;
557 _cell_unrealize(Elm_Grid_Cell *cell)
561 if (!cell->realized) return;
562 if (cell->long_timer)
564 ecore_timer_del(cell->long_timer);
565 cell->long_timer = NULL;
567 evas_object_del(cell->base);
569 evas_object_del(cell->spacer);
571 _elm_stringlist_free(cell->labels);
573 _elm_stringlist_free(cell->icons);
575 _elm_stringlist_free(cell->states);
577 EINA_LIST_FREE(cell->icon_objs, icon)
578 evas_object_del(icon);
581 cell->realized = EINA_FALSE;
582 cell->want_unrealize = EINA_FALSE;
586 _cell_place(Elm_Grid_Cell *cell, Evas_Coord cx, Evas_Coord cy)
588 Evas_Coord x, y, ox, oy, cvx, cvy, cvw, cvh;
589 Evas_Coord tch, tcw, alignw = 0, alignh = 0, vw, vh;
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);
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;
603 tch = ((vh/cell->wd->cell_height)*cell->wd->cell_height);
604 alignh = (vh - tch)*cell->wd->align_y;
606 tcw = ((vw/cell->wd->cell_width)*cell->wd->cell_width);
607 alignw = (vw - tcw)*cell->wd->align_x;
609 if (cell->wd->horizontal && cell->wd->minw < vw)
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))
617 tcw = cell->wd->cell_width * columns;
618 alignw = (vw - tcw)*cell->wd->align_x;
620 else if (cell->wd->horizontal && cell->wd->minw > vw)
622 if (!cell->wd->horizontal && cell->wd->minh < vh)
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))
630 tch = cell->wd->cell_height * rows;
631 alignh = (vh - tch)*cell->wd->align_y;
633 else if (!cell->wd->horizontal && cell->wd->minh > vh)
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;
638 if (ELM_RECTS_INTERSECT(x, y, cell->wd->cell_width, cell->wd->cell_height,
641 Eina_Bool was_realized = cell->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);
650 _cell_unrealize(cell);
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)
659 cell = calloc(1, sizeof(*cell));
660 if (!cell) return NULL;
664 cell->func.func = func;
665 cell->func.data = func_data;
670 _cell_del(Elm_Grid_Cell *cell)
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);
684 _cell_select(Elm_Grid_Cell *cell)
686 if ((cell->wd->no_select) || (cell->delete_me)) return;
689 if (cell->wd->always_select) goto call;
692 cell->selected = EINA_TRUE;
693 cell->wd->selected = eina_list_append(cell->wd->selected, cell);
696 if (cell->func.func) cell->func.func((void *)cell->func.data, cell->wd->self,
698 if (!cell->delete_me)
699 evas_object_smart_callback_call(cell->wd->self, "selected", cell);
701 if ((cell->walking == 0) && (cell->delete_me))
702 if (cell->relcount == 0) _cell_del(cell);
706 _cell_unselect(Elm_Grid_Cell *cell)
708 if ((cell->delete_me) || (!cell->hilighted)) return;
709 edje_object_signal_emit(cell->base, "elm,state,unselected", "elm");
710 cell->hilighted = EINA_FALSE;
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);
720 _calc_job(void *data)
722 Widget_Data *wd = data;
723 Evas_Coord minw = 0, minh = 0, nmax = 0, cvw, cvh;
726 evas_output_viewport_get(evas_object_evas_get(wd->self), NULL, NULL,
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;
735 count = eina_list_count(wd->cells);
738 minw = ceil(count / (float)nmax) * wd->cell_width;
739 minh = nmax * wd->cell_height;
743 minw = nmax * wd->cell_width;
744 minh = ceil(count / (float)nmax) * wd->cell_height;
748 if ((minw != wd->minw) || (minh != wd->minh))
752 evas_object_smart_callback_call(wd->pan_smart, "changed", NULL);
753 _sizing_eval(wd->self);
758 evas_object_smart_changed(wd->pan_smart);
762 _pan_add(Evas_Object *obj)
765 Evas_Object_Smart_Clipped_Data *cd;
768 cd = evas_object_smart_data_get(obj);
771 sd->__clipped_data = *cd;
773 evas_object_smart_data_set(obj, sd);
777 _pan_del(Evas_Object *obj)
779 Pan *sd = evas_object_smart_data_get(obj);
786 _pan_set(Evas_Object *obj, Evas_Coord x, Evas_Coord y)
788 Pan *sd = evas_object_smart_data_get(obj);
789 if ((x == sd->wd->pan_x) && (y == sd->wd->pan_y)) return;
792 evas_object_smart_changed(obj);
796 _pan_get(Evas_Object *obj, Evas_Coord *x, Evas_Coord *y)
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;
804 _pan_child_size_get(Evas_Object *obj, Evas_Coord *w, Evas_Coord *h)
806 Pan *sd = evas_object_smart_data_get(obj);
807 if (w) *w = sd->wd->minw;
808 if (h) *h = sd->wd->minh;
812 _pan_max_get(Evas_Object *obj, Evas_Coord *x, Evas_Coord *y)
814 Pan *sd = evas_object_smart_data_get(obj);
818 evas_object_geometry_get(obj, NULL, NULL, &ow, &oh);
820 *x = (ow < sd->wd->minw) ? sd->wd->minw - ow : 0;
822 *y = (oh < sd->wd->minh) ? sd->wd->minh - oh : 0;
826 _pan_resize(Evas_Object *obj, Evas_Coord w, Evas_Coord h)
828 Pan *sd = evas_object_smart_data_get(obj);
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);
838 _pan_calculate(Evas_Object *obj)
840 Pan *sd = evas_object_smart_data_get(obj);
841 Evas_Coord cx = 0, cy = 0;
846 if (!sd->wd->nmax) return;
848 EINA_LIST_FOREACH(sd->wd->cells, l, cell)
850 _cell_place(cell, cx, cy);
851 if (sd->wd->horizontal)
853 cy = (cy + 1) % sd->wd->nmax;
858 cx = (cx + 1) % sd->wd->nmax;
865 _pan_move(Evas_Object *obj, Evas_Coord x __UNUSED__, Evas_Coord y __UNUSED__)
867 Pan *sd = evas_object_smart_data_get(obj);
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);
874 _hold_on(void *data __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
876 Widget_Data *wd = elm_widget_data_get(obj);
878 elm_smart_scroller_hold_set(wd->scr, 1);
882 _hold_off(void *data __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
884 Widget_Data *wd = elm_widget_data_get(obj);
886 elm_smart_scroller_hold_set(wd->scr, 0);
890 _freeze_on(void *data __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
892 Widget_Data *wd = elm_widget_data_get(obj);
894 elm_smart_scroller_freeze_set(wd->scr, 1);
898 _freeze_off(void *data __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
900 Widget_Data *wd = elm_widget_data_get(obj);
902 elm_smart_scroller_freeze_set(wd->scr, 0);
906 _scr_drag_start(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
908 evas_object_smart_callback_call(data, "scroll,drag,start", NULL);
912 _scr_drag_stop(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
914 evas_object_smart_callback_call(data, "scroll,drag,stop", NULL);
918 _scr_scroll(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
920 evas_object_smart_callback_call(data, "scroll", NULL);
924 * Add a new Scrolled Grid object.
926 * @param parent The parent object.
927 * @return The new object or NULL if it cannot be created.
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()
938 elm_scrolled_grid_add(Evas_Object *parent)
942 Evas_Coord minw, minh;
944 static Evas_Smart *smart = NULL;
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);
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);
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);
965 elm_smart_scroller_bounce_allow_set(wd->scr, 1, 1);
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);
978 static Evas_Smart_Class sc;
980 evas_object_smart_clipped_smart_set(&_pan_sc);
982 sc.name = "elm_scrolled_grid_pan";
983 sc.version = EVAS_SMART_CLASS_VERSION;
986 sc.resize = _pan_resize;
988 sc.calculate = _pan_calculate;
989 smart = evas_smart_class_new(&sc);
993 wd->pan_smart = evas_object_smart_add(e, smart);
994 wd->pan = evas_object_smart_data_get(wd->pan_smart);
998 elm_smart_scroller_extern_pan_set(wd->scr, wd->pan_smart,
1000 _pan_max_get, _pan_child_size_get);
1008 * Set the size for the cell of the Grid.
1010 * @param obj The Grid object.
1011 * @param w The cell's width.
1012 * @param h The cell's height;
1014 * @see elm_scrolled_grid_cell_size_get()
1019 elm_scrolled_grid_cell_size_set(Evas_Object *obj, Evas_Coord w, Evas_Coord h)
1021 ELM_CHECK_WIDTYPE(obj, widtype);
1022 Widget_Data *wd = elm_widget_data_get(obj);
1024 if (wd->cell_width == w && wd->cell_height == h) return;
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);
1032 * Get the size of the cell of the Grid.
1034 * @param obj The Grid object.
1035 * @param w Pointer to the cell's width.
1036 * @param h Pointer to the cell's height.
1038 * @see elm_scrolled_grid_cell_size_get()
1043 elm_scrolled_grid_cell_size_get(const Evas_Object *obj, Evas_Coord *w, Evas_Coord *h)
1045 ELM_CHECK_WIDTYPE(obj, widtype);
1046 Widget_Data *wd = elm_widget_data_get(obj);
1048 if (w) *w = wd->cell_width;
1049 if (h) *h = wd->cell_height;
1053 * Set cell's alignment within the scroller.
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).
1059 * @see elm_scrolled_grid_align_get()
1064 elm_scrolled_grid_align_set(Evas_Object *obj, double align_x, double align_y)
1066 ELM_CHECK_WIDTYPE(obj, widtype);
1067 Widget_Data *wd = elm_widget_data_get(obj);
1071 else if (align_x < 0.0)
1073 wd->align_x = align_x;
1077 else if (align_y < 0.0)
1079 wd->align_y = align_y;
1083 * Get the alignenment set for the grid object.
1085 * @param obj The grid object.
1086 * @param align_x Pointer to x alignenment.
1087 * @param align_y Pointer to y alignenment.
1089 * @see elm_scrolled_grid_align_set()
1094 elm_scrolled_grid_align_get(const Evas_Object *obj, double *align_x, double *align_y)
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;
1103 * Add cell to the end of the Grid.
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.
1112 * @see elm_scrolled_grid_cell_del()
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)
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;
1126 cell = _cell_create(wd, gcc, data, func, func_data);
1127 if (!cell) return NULL;
1129 wd->cells = eina_list_append(wd->cells, cell);
1130 wd->no_select = EINA_FALSE;
1132 if (wd->calc_job) ecore_job_del(wd->calc_job);
1133 wd->calc_job = ecore_job_add(_calc_job, wd);
1139 * Remove a cell from the Grid.
1141 * @param cell The cell to be removed.
1142 * @return @c EINA_TRUE on success or @c EINA_FALSE otherwise.
1144 * @see elm_scrolled_grid_clear() to remove all cells of the grid.
1149 elm_scrolled_grid_cell_del(Elm_Grid_Cell *cell)
1152 if ((cell->relcount > 0) || (cell->walking > 0))
1154 cell->delete_me = EINA_TRUE;
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);
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);
1168 * Set for what direction the grid will expand.
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.
1177 elm_scrolled_grid_horizontal_set(Evas_Object *obj, Eina_Bool setting)
1179 ELM_CHECK_WIDTYPE(obj, widtype);
1180 Widget_Data *wd = elm_widget_data_get(obj);
1182 if (setting == wd->horizontal) return;
1183 wd->horizontal = setting;
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);
1193 * This clears all cells in the grid, leaving it empty.
1195 * @param obj The Grid object.
1197 * @see elm_scrolled_grid_cell_del() to remove just one cell.
1202 elm_scrolled_grid_clear(Evas_Object *obj)
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);
1212 ecore_job_del(wd->calc_job);
1213 wd->calc_job = NULL;
1216 EINA_LIST_FOREACH_SAFE(wd->cells, l, l_next, cell)
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);
1222 wd->cells = eina_list_remove_list(wd->cells, l);
1227 eina_list_free(wd->selected);
1228 wd->selected = NULL;
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);
1241 * Get the real evas object of the grid cell
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.
1250 * @param cell The Grid cell.
1251 * @return the evas object associated to this cell.
1253 * @see elm_scrolled_grid_cell_data_get()
1257 EAPI const Evas_Object *
1258 elm_scrolled_grid_cell_object_get(Elm_Grid_Cell *cell)
1260 if (!cell) return NULL;
1265 * Returns the data associated to a cell
1267 * This returns the data value passed on the elm_scrolled_grid_cell_add() and
1268 * related cell addition calls.
1270 * @param cell The Grid cell.
1271 * @return the data associated to this cell.
1273 * @see elm_scrolled_grid_cell_add()
1274 * @see elm_scrolled_grid_cell_object_get()
1279 elm_scrolled_grid_cell_data_get(Elm_Grid_Cell *cell)
1281 if (!cell) return NULL;
1282 return (void *)cell->data;
1286 * Get the cell's coordinates.
1288 * This returns the logical position of the cell whithin the Grid.
1290 * @param cell The Grid cell.
1291 * @param x The x-axis coordinate pointer.
1292 * @param y The y-axis coordinate pointer.
1297 elm_scrolled_grid_cell_pos_get(const Elm_Grid_Cell *cell, unsigned int *x, unsigned int *y)
1300 if (x) *x = cell->x;
1301 if (y) *y = cell->y;
1305 * Enable or disable multi-select in the grid.
1307 * This enables (EINA_TRUE) or disables (EINA_FALSE) multi-select in the grid.
1308 * This allows more than 1 cell to be selected.
1310 * @param obj The grid object.
1311 * @param multi Multi-select enabled/disabled
1316 elm_scrolled_grid_multi_select_set(Evas_Object *obj, Eina_Bool multi)
1318 ELM_CHECK_WIDTYPE(obj, widtype);
1319 Widget_Data *wd = elm_widget_data_get(obj);
1325 * Get if multi-select in grid is enabled or disabled
1327 * @param obj The grid object
1328 * @return Multi-select enable/disable
1329 * (EINA_TRUE = enabled / EINA_FALSE = disabled)
1334 elm_scrolled_grid_multi_select_get(const Evas_Object *obj)
1336 ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
1337 Widget_Data *wd = elm_widget_data_get(obj);
1338 if (!wd) return EINA_FALSE;
1343 * Get the selected cell in the grid
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).
1349 * If no cell is selected, NULL is returned.
1351 * @param obj The grid object.
1352 * @return The selected cell, or NULL if none.
1356 EAPI Elm_Grid_Cell *
1357 elm_scrolled_grid_selected_cell_get(const Evas_Object *obj)
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;
1367 * Get a list of selected cells in the grid.
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.
1373 * @param obj The grid object.
1374 * @return The list of selected cells, or NULL if none are selected.
1378 EAPI const Eina_List *
1379 elm_scrolled_grid_selected_cells_get(const Evas_Object *obj)
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;
1388 * Get the selected state of a cell.
1390 * This gets the selected state of a cell (1 selected, 0 not selected).
1392 * @param cell The cell
1393 * @return The selected state
1398 elm_scrolled_grid_cell_selected_get(const Elm_Grid_Cell *cell)
1400 if (!cell) return EINA_FALSE;
1401 return cell->selected;
1405 * Sets the disabled state of a cell.
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
1411 * @param cell The cell
1412 * @param disabled The disabled state
1417 elm_scrolled_grid_cell_disabled_set(Elm_Grid_Cell *cell, Eina_Bool disabled)
1420 if (cell->disabled == disabled) return;
1421 if (cell->delete_me) return;
1422 cell->disabled = disabled;
1426 edje_object_signal_emit(cell->base, "elm,state,disabled", "elm");
1428 edje_object_signal_emit(cell->base, "elm,state,enabled", "elm");
1433 * Get the disabled state of a cell.
1435 * This gets the disabled state of the given cell.
1437 * @param cell The cell
1438 * @return The disabled state
1443 elm_scrolled_grid_cell_disabled_get(const Elm_Grid_Cell *cell)
1445 if (!cell) return EINA_FALSE;
1446 if (cell->delete_me) return EINA_FALSE;
1447 return cell->disabled;
1451 * Set the always select mode.
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.
1458 * @param obj The grid object
1459 * @param always_select The always select mode (EINA_TRUE = on, EINA_FALSE = off)
1464 elm_scrolled_grid_always_select_mode_set(Evas_Object *obj, Eina_Bool always_select)
1466 ELM_CHECK_WIDTYPE(obj, widtype);
1467 Widget_Data *wd = elm_widget_data_get(obj);
1469 wd->always_select = always_select;
1473 * Get the always select mode.
1475 * @param obj The grid object.
1476 * @return The always select mode (EINA_TRUE = on, EINA_FALSE = off)
1481 elm_scrolled_grid_always_select_mode_get(const Evas_Object *obj)
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;
1490 * Set no select mode.
1492 * This will turn off the ability to select items entirely and they will
1493 * neither appear selected nor call selected callback functions.
1495 * @param obj The grid object
1496 * @param no_select The no select mode (EINA_TRUE = on, EINA_FALSE = off)
1501 elm_scrolled_grid_no_select_mode_set(Evas_Object *obj, Eina_Bool no_select)
1503 ELM_CHECK_WIDTYPE(obj, widtype);
1504 Widget_Data *wd = elm_widget_data_get(obj);
1506 wd->no_select = no_select;
1510 * Gets no select mode.
1512 * @param obj The grid object
1513 * @return The no select mode (EINA_TRUE = on, EINA_FALSE = off)
1518 elm_scrolled_grid_no_select_mode_get(const Evas_Object *obj)
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;
1529 * This will enable or disable the scroller bounce mode for the grid. See
1530 * elm_scroller_bounce_set() for details.
1532 * @param obj The grid object
1533 * @param h_bounce Allow bounce horizontally
1534 * @param v_bounce Allow bounce vertically
1539 elm_scrolled_grid_bounce_set(Evas_Object *obj, Eina_Bool h_bounce, Eina_Bool v_bounce)
1541 ELM_CHECK_WIDTYPE(obj, widtype);
1542 Widget_Data *wd = elm_widget_data_get(obj);
1544 elm_smart_scroller_bounce_allow_set(wd->scr, h_bounce, v_bounce);
1548 * Get the bounce mode
1550 * @param obj The grid object
1551 * @param h_bounce Allow bounce horizontally
1552 * @param v_bounce Allow bounce vertically
1557 elm_scrolled_grid_bounce_get(const Evas_Object *obj, Eina_Bool *h_bounce, Eina_Bool *v_bounce)
1559 ELM_CHECK_WIDTYPE(obj, widtype);
1560 Widget_Data *wd = elm_widget_data_get(obj);
1562 elm_smart_scroller_bounce_allow_get(wd->scr, h_bounce, v_bounce);