From 24feacfa4ceaa5bfda95e4cf2fe5e003270df939 Mon Sep 17 00:00:00 2001 From: Rafael Fonseca Date: Thu, 3 Jun 2010 12:53:14 +0000 Subject: [PATCH] From: Rafael Fonseca As requested, I've changed the widget name. Let me know about any issues. SVN revision: 49411 --- legacy/elementary/data/themes/default.edc | 14 +- legacy/elementary/src/bin/Makefile.am | 2 +- legacy/elementary/src/bin/test.c | 4 +- .../src/bin/{test_grid.c => test_gengrid.c} | 57 +- legacy/elementary/src/lib/Elementary.h.in | 92 +- legacy/elementary/src/lib/Makefile.am | 2 +- legacy/elementary/src/lib/elm_gengrid.c | 1579 +++++++++++++++++ legacy/elementary/src/lib/elm_scrolled_grid.c | 1579 ----------------- 8 files changed, 1664 insertions(+), 1665 deletions(-) rename legacy/elementary/src/bin/{test_grid.c => test_gengrid.c} (74%) create mode 100644 legacy/elementary/src/lib/elm_gengrid.c delete mode 100644 legacy/elementary/src/lib/elm_scrolled_grid.c diff --git a/legacy/elementary/data/themes/default.edc b/legacy/elementary/data/themes/default.edc index 3fc08dc09a..c64d1922c5 100644 --- a/legacy/elementary/data/themes/default.edc +++ b/legacy/elementary/data/themes/default.edc @@ -80,7 +80,7 @@ collections { alias: "elm/list/base/default"; alias: "elm/genlist/base/default"; alias: "elm/carousel/base/default"; - alias: "elm/grid/base/default"; + alias: "elm/gengrid/base/default"; script { public sbvis_v, sbvis_h, sbalways_v, sbalways_h, sbvis_timer; @@ -21072,7 +21072,7 @@ collections { } /////////////////////////////////////////////////////////////////////////////// - group { name: "elm/grid/cell/default/default"; + group { name: "elm/gengrid/item/default/default"; data.item: "labels" "elm.text"; data.item: "icons" "elm.swallow.icon elm.swallow.end"; images { @@ -21338,17 +21338,17 @@ collections { } } } - group { name: "elm/grid/cell/default_style/default"; + group { name: "elm/gengrid/item/default_style/default"; styles { - style { name: "grid_style"; + style { name: "gengrid_style"; base: "font=Sans font_size=10 align=left valign=0.5 color=#000"; tag: "br" "\n"; tag: "hilight" "+ font=Sans:style=Bold"; tag: "b" "+ font=Sans:style=Bold"; tag: "tab" "\t"; } - style { name: "grid_selected_style"; + style { name: "gengrid_selected_style"; base: "font=Sans font_size=10 align=left valign=0.5 color=#fff"; tag: "br" "\n"; tag: "hilight" "+ font=Sans:style=Bold"; @@ -21519,14 +21519,14 @@ collections { offset: -1 -5; } text { - style: "grid_style"; + style: "gengrid_style"; min: 1 1; } } description { state: "selected" 0.0; inherit: "default" 0.0; text { - style: "grid_selected_style"; + style: "gengrid_selected_style"; } } } diff --git a/legacy/elementary/src/bin/Makefile.am b/legacy/elementary/src/bin/Makefile.am index 172287dd61..933e5fe1e7 100644 --- a/legacy/elementary/src/bin/Makefile.am +++ b/legacy/elementary/src/bin/Makefile.am @@ -36,7 +36,7 @@ test_button.c \ test_fileselector_button.c \ test_toggle.c \ test_table.c \ -test_grid.c \ +test_gengrid.c \ test_clock.c \ test_layout.c \ test_hover.c \ diff --git a/legacy/elementary/src/bin/test.c b/legacy/elementary/src/bin/test.c index ebae8bcc9e..bd9806d953 100644 --- a/legacy/elementary/src/bin/test.c +++ b/legacy/elementary/src/bin/test.c @@ -43,7 +43,7 @@ void test_genlist4(void *data, Evas_Object *obj, void *event_info); void test_genlist5(void *data, Evas_Object *obj, void *event_info); void test_genlist6(void *data, Evas_Object *obj, void *event_info); void test_table(void *data, Evas_Object *obj, void *event_info); -void test_grid(void *data, Evas_Object *obj, void *event_info); +void test_gengrid(void *data, Evas_Object *obj, void *event_info); void test_pager(void *data, Evas_Object *obj, void *event_info); void test_win_state(void *data, Evas_Object *obj, void *event_info); void test_win_state2(void *data, Evas_Object *obj, void *event_info); @@ -236,7 +236,7 @@ my_win_main(void) ADD_TEST("Genlist 4", test_genlist4); ADD_TEST("Genlist 5", test_genlist5); ADD_TEST("Genlist Tree", test_genlist6); - ADD_TEST("Scrolled Grid", test_grid); + ADD_TEST("GenGrid", test_gengrid); ADD_TEST("Checks", test_check); ADD_TEST("Radios", test_radio); ADD_TEST("Pager", test_pager); diff --git a/legacy/elementary/src/bin/test_grid.c b/legacy/elementary/src/bin/test_gengrid.c similarity index 74% rename from legacy/elementary/src/bin/test_grid.c rename to legacy/elementary/src/bin/test_gengrid.c index 6d5f591a49..9a3ecff8b1 100644 --- a/legacy/elementary/src/bin/test_grid.c +++ b/legacy/elementary/src/bin/test_gengrid.c @@ -2,13 +2,13 @@ #ifndef ELM_LIB_QUICKLAUNCH typedef struct _Testitem { - Elm_Grid_Cell *cell; + Elm_Gengrid_Item *item; const char *path; int mode; int onoff; } Testitem; -static Elm_Grid_Cell_Class gcc; +static Elm_Gengrid_Item_Class gic; static void grid_drag_up(void *data, Evas_Object *obj, void *event_info) @@ -59,31 +59,31 @@ grid_longpress(void *data, Evas_Object *obj, void *event_info) } static void -grid_cell_check_changed(void *data, Evas_Object *obj, void *event_info) +grid_item_check_changed(void *data, Evas_Object *obj, void *event_info) { - Testitem *tit = data; - tit->onoff = elm_check_state_get(obj); - printf("cell %p onoff = %i\n", tit, tit->onoff); + Testitem *ti = data; + ti->onoff = elm_check_state_get(obj); + printf("item %p onoff = %i\n", ti, ti->onoff); } char * grid_label_get(const void *data, Evas_Object *obj, const char *part) { - const Testitem *tit = data; + const Testitem *ti = data; char buf[256]; - snprintf(buf, sizeof(buf), "Photo %s", tit->path); + snprintf(buf, sizeof(buf), "Photo %s", ti->path); return strdup(buf); } Evas_Object * grid_icon_get(const void *data, Evas_Object *obj, const char *part) { - const Testitem *tit = data; + const Testitem *ti = data; if (!strcmp(part, "elm.swallow.icon")) { Evas_Object *icon = elm_bg_add(obj); - elm_bg_file_set(icon, tit->path, NULL); + elm_bg_file_set(icon, ti->path, NULL); evas_object_size_hint_aspect_set(icon, EVAS_ASPECT_CONTROL_VERTICAL, 1, 1); evas_object_show(icon); return icon; @@ -93,8 +93,8 @@ grid_icon_get(const void *data, Evas_Object *obj, const char *part) Evas_Object *ck; ck = elm_check_add(obj); evas_object_propagate_events_set(ck, 0); - elm_check_state_set(ck, tit->onoff); - evas_object_smart_callback_add(ck, "changed", grid_cell_check_changed, data); + elm_check_state_set(ck, ti->onoff); + evas_object_smart_callback_add(ck, "changed", grid_item_check_changed, data); evas_object_show(ck); return ck; } @@ -119,10 +119,10 @@ grid_sel(void *data, Evas_Object *obj, void *event_info) } void -test_grid(void *data, Evas_Object *obj, void *event_info) +test_gengrid(void *data, Evas_Object *obj, void *event_info) { Evas_Object *win, *bg, *grid; - static Testitem item[144]; + static Testitem ti[144]; int i, j, n; char buf[PATH_MAX]; const char *img[9] = @@ -147,10 +147,10 @@ test_grid(void *data, Evas_Object *obj, void *event_info) elm_win_resize_object_add(win, bg); evas_object_show(bg); - grid = elm_scrolled_grid_add(win); - elm_scrolled_grid_cell_size_set(grid, 150, 150); - elm_scrolled_grid_horizontal_set(grid, EINA_FALSE); - elm_scrolled_grid_multi_select_set(grid, EINA_TRUE); + grid = elm_gengrid_add(win); + elm_gengrid_item_size_set(grid, 150, 150); + elm_gengrid_horizontal_set(grid, EINA_FALSE); + elm_gengrid_multi_select_set(grid, EINA_TRUE); evas_object_smart_callback_add(grid, "selected", grid_selected, NULL); evas_object_smart_callback_add(grid, "clicked", grid_clicked, NULL); evas_object_smart_callback_add(grid, "longpressed", grid_longpress, NULL); @@ -161,23 +161,22 @@ test_grid(void *data, Evas_Object *obj, void *event_info) evas_object_smart_callback_add(grid, "drag,stop", grid_drag_stop, NULL); evas_object_size_hint_weight_set(grid, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); - gcc.item_style = "default"; - gcc.func.label_get = grid_label_get; - gcc.func.icon_get = grid_icon_get; - gcc.func.state_get = grid_state_get; - gcc.func.del = grid_del; + gic.item_style = "default"; + gic.func.label_get = grid_label_get; + gic.func.icon_get = grid_icon_get; + gic.func.state_get = grid_state_get; + gic.func.del = grid_del; n = 0; for (i = 0; i < 12 * 12; i++) { - snprintf(buf, sizeof(buf), "%s/images/%s", PACKAGE_DATA_DIR, - img[n]); + snprintf(buf, sizeof(buf), "%s/images/%s", PACKAGE_DATA_DIR, img[n]); n = (n + 1) % 9; - item[i].mode = i; - item[i].path = eina_stringshare_add(buf); - item[i].cell = elm_scrolled_grid_cell_add(grid, &gcc, &(item[i]), grid_sel, NULL); + ti[i].mode = i; + ti[i].path = eina_stringshare_add(buf); + ti[i].item = elm_gengrid_item_append(grid, &gic, &(ti[i]), grid_sel, NULL); if (!(i % 5)) - elm_scrolled_grid_cell_selected_set(item[i].cell, EINA_TRUE); + elm_gengrid_item_selected_set(ti[i].item, EINA_TRUE); } evas_object_show(grid); diff --git a/legacy/elementary/src/lib/Elementary.h.in b/legacy/elementary/src/lib/Elementary.h.in index 84a6b16f9e..7c3cd46bdf 100644 --- a/legacy/elementary/src/lib/Elementary.h.in +++ b/legacy/elementary/src/lib/Elementary.h.in @@ -563,64 +563,64 @@ extern "C" { EAPI void elm_table_unpack(Evas_Object *obj, Evas_Object *subobj); EAPI void elm_table_clear(Evas_Object *obj, Eina_Bool clear); - typedef struct _Elm_Grid_Cell_Class Elm_Grid_Cell_Class; - typedef struct _Elm_Grid_Cell_Class_Func Elm_Grid_Cell_Class_Func; - typedef struct _Elm_Grid_Cell Elm_Grid_Cell; - typedef char *(*GridCellLabelGetFunc) (const void *data, Evas_Object *obj, const char *part); - typedef Evas_Object *(*GridCellIconGetFunc) (const void *data, Evas_Object *obj, const char *part); - typedef Eina_Bool (*GridCellStateGetFunc) (const void *data, Evas_Object *obj, const char *part); - typedef void (*GridCellDelFunc) (const void *data, Evas_Object *obj); + typedef struct _Elm_Gengrid_Item_Class Elm_Gengrid_Item_Class; + typedef struct _Elm_Gengrid_Item_Class_Func Elm_Gengrid_Item_Class_Func; + typedef struct _Elm_Gengrid_Item Elm_Gengrid_Item; + typedef char *(*GridItemLabelGetFunc) (const void *data, Evas_Object *obj, const char *part); + typedef Evas_Object *(*GridItemIconGetFunc) (const void *data, Evas_Object *obj, const char *part); + typedef Eina_Bool (*GridItemStateGetFunc) (const void *data, Evas_Object *obj, const char *part); + typedef void (*GridItemDelFunc) (const void *data, Evas_Object *obj); - struct _Elm_Grid_Cell_Class + struct _Elm_Gengrid_Item_Class { const char *item_style; - struct _Elm_Grid_Cell_Class_Func + struct _Elm_Gengrid_Item_Class_Func { - GridCellLabelGetFunc label_get; - GridCellIconGetFunc icon_get; - GridCellStateGetFunc state_get; - GridCellDelFunc del; + GridItemLabelGetFunc label_get; + GridItemIconGetFunc icon_get; + GridItemStateGetFunc state_get; + GridItemDelFunc del; } func; }; - EAPI Evas_Object *elm_scrolled_grid_add(Evas_Object *parent); - EAPI void elm_scrolled_grid_cell_size_set(Evas_Object *obj, Evas_Coord w, Evas_Coord h); - EAPI void elm_scrolled_grid_cell_size_get(const Evas_Object *obj, Evas_Coord *w, Evas_Coord *h); - EAPI void elm_scrolled_grid_align_set(Evas_Object *obj, double align_x, double align_y); - EAPI void elm_scrolled_grid_align_get(const Evas_Object *obj, double *align_x, double *align_y); - EAPI void elm_scrolled_grid_cell_del(Elm_Grid_Cell *cell); - EAPI void elm_scrolled_grid_always_select_mode_set(Evas_Object *obj, Eina_Bool always_select); - EAPI Eina_Bool elm_scrolled_grid_always_select_mode_get(const Evas_Object *obj); - EAPI void elm_scrolled_grid_no_select_mode_set(Evas_Object *obj, Eina_Bool no_select); - EAPI Eina_Bool elm_scrolled_grid_no_select_mode_get(const Evas_Object *obj); - EAPI void elm_scrolled_grid_multi_select_set(Evas_Object *obj, Eina_Bool multi); - EAPI Eina_Bool elm_scrolled_grid_multi_select_get(const Evas_Object *obj); - EAPI void elm_scrolled_grid_bounce_set(Evas_Object *obj, Eina_Bool h_bounce, Eina_Bool v_bounce); - EAPI void elm_scrolled_grid_bounce_get(const Evas_Object *obj, Eina_Bool *h_bounce, Eina_Bool *v_bounce); - EAPI void elm_scrolled_grid_horizontal_set(Evas_Object *obj, Eina_Bool setting); - EAPI void elm_scrolled_grid_clear(Evas_Object *obj); - EAPI void *elm_scrolled_grid_cell_data_get(Elm_Grid_Cell *cell); - EAPI void elm_scrolled_grid_cell_pos_get(const Elm_Grid_Cell *cell, unsigned int *x, unsigned int *y); - EAPI void elm_scrolled_grid_cell_selected_set(Elm_Grid_Cell *cell, Eina_Bool selected); - EAPI Eina_Bool elm_scrolled_grid_cell_selected_get(const Elm_Grid_Cell *cell); - EAPI const Evas_Object *elm_scrolled_grid_cell_object_get(Elm_Grid_Cell *cell); - EAPI const Eina_List *elm_scrolled_grid_selected_cells_get(const Evas_Object *obj); - EAPI Elm_Grid_Cell *elm_scrolled_grid_cell_add(Evas_Object *obj, const Elm_Grid_Cell_Class *gcc, const void *data, Evas_Smart_Cb func, const void *func_data); + EAPI Evas_Object *elm_gengrid_add(Evas_Object *parent); + EAPI void elm_gengrid_item_size_set(Evas_Object *obj, Evas_Coord w, Evas_Coord h); + EAPI void elm_gengrid_item_size_get(const Evas_Object *obj, Evas_Coord *w, Evas_Coord *h); + EAPI void elm_gengrid_align_set(Evas_Object *obj, double align_x, double align_y); + EAPI void elm_gengrid_align_get(const Evas_Object *obj, double *align_x, double *align_y); + EAPI void elm_gengrid_item_del(Elm_Gengrid_Item *item); + EAPI void elm_gengrid_always_select_mode_set(Evas_Object *obj, Eina_Bool always_select); + EAPI Eina_Bool elm_gengrid_always_select_mode_get(const Evas_Object *obj); + EAPI void elm_gengrid_no_select_mode_set(Evas_Object *obj, Eina_Bool no_select); + EAPI Eina_Bool elm_gengrid_no_select_mode_get(const Evas_Object *obj); + EAPI void elm_gengrid_multi_select_set(Evas_Object *obj, Eina_Bool multi); + EAPI Eina_Bool elm_gengrid_multi_select_get(const Evas_Object *obj); + EAPI void elm_gengrid_bounce_set(Evas_Object *obj, Eina_Bool h_bounce, Eina_Bool v_bounce); + EAPI void elm_gengrid_bounce_get(const Evas_Object *obj, Eina_Bool *h_bounce, Eina_Bool *v_bounce); + EAPI void elm_gengrid_horizontal_set(Evas_Object *obj, Eina_Bool setting); + EAPI void elm_gengrid_clear(Evas_Object *obj); + EAPI void *elm_gengrid_item_data_get(Elm_Gengrid_Item *item); + EAPI void elm_gengrid_item_pos_get(const Elm_Gengrid_Item *item, unsigned int *x, unsigned int *y); + EAPI void elm_gengrid_item_selected_set(Elm_Gengrid_Item *item, Eina_Bool selected); + EAPI Eina_Bool elm_gengrid_item_selected_get(const Elm_Gengrid_Item *item); + EAPI const Evas_Object *elm_gengrid_item_object_get(Elm_Gengrid_Item *item); + EAPI const Eina_List *elm_gengrid_selected_items_get(const Evas_Object *obj); + EAPI Elm_Gengrid_Item *elm_gengrid_item_append(Evas_Object *obj, const Elm_Gengrid_Item_Class *gcc, const void *data, Evas_Smart_Cb func, const void *func_data); /* smart callbacks called: * - * selected - User has selected a cell. - * unselected - User has unselected a cell. - * clicked - User has double-clicked a cell. - * realized - An evas object for a cell was built. + * selected - User has selected a item. + * unselected - User has unselected a item. + * clicked - User has double-clicked a item. + * realized - An evas object for a item was built. * scroll - the content has been scrolled (moved). * "scroll,drag,start" - dragging the contents around has started. * "scroll,drat,stop" - dragging the contents around has stopped. - * drag - grid is being dragged. - * "drag,start,up" - grid has been dragged (not scrolled) up. - * "drag,start,down" - grid has been dragged (not scrolled) down. - * "drag,start,left" - grid has been dragged (not scrolled) left. - * "drag,start,rigth" - grid has been dragged (nto scrolled) right. - * "drag,stop" - grid is not being dragged. + * drag - Gengrid is being dragged. + * "drag,start,up" - Gengrid has been dragged (not scrolled) up. + * "drag,start,down" - Gengrid has been dragged (not scrolled) down. + * "drag,start,left" - Gengrid has been dragged (not scrolled) left. + * "drag,start,rigth" - Gengrid has been dragged (nto scrolled) right. + * "drag,stop" - Gengrid is not being dragged. */ EAPI Evas_Object *elm_clock_add(Evas_Object *parent); diff --git a/legacy/elementary/src/lib/Makefile.am b/legacy/elementary/src/lib/Makefile.am index f2711bf07f..b8a515d38a 100644 --- a/legacy/elementary/src/lib/Makefile.am +++ b/legacy/elementary/src/lib/Makefile.am @@ -45,7 +45,7 @@ elm_label.c \ elm_toggle.c \ elm_frame.c \ elm_table.c \ -elm_scrolled_grid.c \ +elm_gengrid.c \ elm_clock.c \ elm_layout.c \ elm_hover.c \ diff --git a/legacy/elementary/src/lib/elm_gengrid.c b/legacy/elementary/src/lib/elm_gengrid.c new file mode 100644 index 0000000000..6a54fa1151 --- /dev/null +++ b/legacy/elementary/src/lib/elm_gengrid.c @@ -0,0 +1,1579 @@ +/* + * vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2 + */ +#include +#include "elm_priv.h" + +/** + * @defgroup Gengrid Gengrid + * + * This widget aims to position objects in a grid layout while actually + * building only the visible ones, using the same idea as genlist: the user + * define a class for each item, specifying functions that will be called at + * object creation and deletion. + * + * Signals that you can add callbacks for are: + * + * clicked - The user has double-clicked a item. The event_info parameter is + * the Gengrid item that was double-clicked. + * + * selected - The user has made an item selected. The event_info parameter is + * the Gengrid item that was selected. + * + * unselected - The user has made an item unselected. The event_info parameter + * is the Gengrid item that was unselected. + * + * realized - This is called when the item in the Gengrid is created as a real + * evas object. event_info is the Gengrid item that was created. The object may be + * deleted at any time, so it is up to the caller to not use the object pointer + * from elm_gengrid_item_object_get() in a way where it may point to + * freed objects. + * + * drag,start,up - Called when the item in the Gengrid has been dragged (not + * scrolled) up. + * + * drag,start,down - Called when the item in the Gengrid has been dragged (not + * scrolled) down. + * + * drag,start,left - Called when the item in the Gengrid has been dragged (not + * scrolled) left. + * + * drag,start,right - Called when the item in the Gengrid has been dragged (not + * scrolled) right. + * + * drag,stop - Called when the item in the Gengrid has stopped being dragged. + * + * drag - Called when the item in the Gengrid is being dragged. + * + * scroll - called when the content has been scrolled (moved). + * + * scroll,drag,start - called when dragging the content has started. + * + * scroll,drag,stop - called when dragging the content has stopped. + * + * + * A item in the Gengrid can have 0 or more text labels (they can be regular text + * or textblock - that's up to the style to determine), 0 or more icons (which + * are simply objects swallowed into the Gengrid item) and 0 or more boolean states + * that can be used for check, radio or other indicators by the edje theme style. + * A item may be one of several styles (Elementary provides 1 by default - + * "default", but this can be extended by system or application custom + * themes/overlays/extensions). + * + * In order to implement the ability to add and delete items on the fly, Gengrid + * implements a class/callback system where the application provides a structure + * with information about that type of item (Gengrid may contain multiple different + * items with different classes, states and styles). Gengrid will call the functions + * in this struct (methods) when a item is "realized" (that is created + * dynamically while scrolling). All objects will simply be deleted when no + * longer needed with evas_object_del(). The Elm_GenGrid_Item_Class structure + * contains the following members: + * + * item_style - This is a constant string and simply defines the name of the + * item style. It must be specified and the default should be "default". + * + * func.label_get - This function is called when an actual item object is + * created. The data parameter is the one passed to elm_gengrid_item_append() + * and related item creation functions. The obj parameter is the Gengrid object and + * the part parameter is the string name of the text part in the edje design that + * is listed as one of the possible labels that can be set. This function must + * return a strdup'()ed string as the caller will free() it when done. + * + * func.icon_get - This function is called when an actual item object is + * created. The data parameter is the one passed to elm_gengrid_item_append() + * and related item creation functions. The obj parameter is the Gengrid object and + * the part parameter is the string name of the icon part in the edje design that + * is listed as one of the possible icons that can be set. This must return NULL + * for no object or a valid object. The object will be deleted by Gengrid on + * shutdown or when the item is unrealized. + * + * func.state_get - This function is called when an actual item object is + * created. The data parameter is the one passed to elm_gengrid_item_append() + * and related item creation functions. The obj parameter is the Gengrid object and + * the part parameter is the string name of th state part in the edje design that + * is listed as one of the possible states that can be set. Return 0 for false + * and 1 for true. Gengrid will emit a signal to the edje object with + * "elm,state,XXX,active" "elm" when true (the default is false), where XXX is + * the name of the part. + * + * func.del - This is called when elm_gengrid_item_del() is called on a + * item or elm_gengrid_clear() is called on the Gengrid. This is intended for + * use when actual Gengrid items are deleted, so any backing data attached to the + * item (e.g. its data parameter on creation) can be deleted. + * + * If the application wants multiple items to be able to be selected, + * elm_gengrid_multi_select_set() can enable this. If the Gengrid is + * single-selection only (the default), then elm_gengrid_select_item_get() + * will return the selected item, if any, or NULL if none is selected. If the + * Gengrid is multi-select then elm_gengrid_selected_items_get() will return a + * list (that is only valid as long as no items are modified (added, deleted, + * selected or unselected). + * + * If a item changes (state of boolean changes, label or icons change), then use + * elm_gengrid_item_update() to have Gengrid update the item with the new + * state. Gengrid will re-realize the item thus call the functions in the + * _Elm_Gengrid_Item_Class for that item. + * + * To programmatically (un)select a item use elm_gengrid_item_selected_set(). + * To get its selected state use elm_gengrid_item_selected_get(). To make a + * item disabled (unable to be selected and appear differently) use + * elm_gengrid_item_disable_set() to set this and + * elm_gengrid_item_disable_get() to get the disabled state. + * + * Cells will only call their selection func and callback when first becoming + * selected. Any further clicks will do nothing, unless you enable always + * select with elm_gengrid_always_select_mode_set(). This means event if + * selected, every click will make the selected callbacks be called. + * elm_gengrid_no_select_mode_set() will turn off the ability to select + * items entirely and they will neither appear selected nor call selected + * callback function. + * + * Remember that you can create new styles and add your own theme augmentation + * per application with elm_theme_extension_add(). If you absolutely must have a + * specific style that overrides any theme the user or system sets up you can use + * elm_theme_overlay_add() to add such a file. + * + * -- + * TODO: + * * Handle non-homogeneous objects too. + */ + +typedef struct _Widget_Data Widget_Data; +typedef struct _Pan Pan; + +#define PRELOAD 1 + +struct _Elm_Gengrid_Item +{ + Evas_Object *base, *spacer; + const Elm_Gengrid_Item_Class *gic; + Ecore_Timer *long_timer; + Widget_Data *wd; + Eina_List *labels, *icons, *states, *icon_objs; + const void *data; + struct + { + Evas_Smart_Cb func; + const void *data; + } func; + + Evas_Coord x, y, dx, dy; + int relcount; + + Eina_Bool want_unrealize : 1; + Eina_Bool realized : 1; + Eina_Bool dragging : 1; + Eina_Bool down : 1; + Eina_Bool delete_me : 1; + Eina_Bool display_only : 1; + Eina_Bool disabled : 1; + Eina_Bool selected : 1; + Eina_Bool hilighted : 1; + Eina_Bool walking : 1; +}; + +struct _Widget_Data +{ + Evas_Object *self, *scr; + Evas_Object *pan_smart; + Pan *pan; + Eina_List *items; + Ecore_Job *calc_job; + Eina_List *selected; + double align_x, align_y; + + Evas_Coord pan_x, pan_y; + Evas_Coord item_width, item_height; /* Each item size */ + Evas_Coord minw, minh; /* Total obj size */ + unsigned int nmax; + + Eina_Bool horizontal : 1; + Eina_Bool on_hold : 1; + Eina_Bool longpressed : 1; + Eina_Bool multi : 1; + Eina_Bool no_select : 1; + Eina_Bool wasselected : 1; + Eina_Bool always_select : 1; +}; + +struct _Pan +{ + Evas_Object_Smart_Clipped_Data __clipped_data; + Widget_Data *wd; +}; + +static const char *widtype = NULL; +static void _item_hilight(Elm_Gengrid_Item *item); +static void _item_unrealize(Elm_Gengrid_Item *item); +static void _item_select(Elm_Gengrid_Item *item); +static void _item_unselect(Elm_Gengrid_Item *item); + +static Evas_Smart_Class _pan_sc = EVAS_SMART_CLASS_INIT_VERSION; + +static void +_theme_hook(Evas_Object *obj) +{ + Widget_Data *wd = elm_widget_data_get(obj); + if (!wd) return; + elm_smart_scroller_object_theme_set(obj, wd->scr, "gengrid", "base", + elm_widget_style_get(obj)); +} + +static void +_del_hook(Evas_Object *obj) +{ + Widget_Data *wd = elm_widget_data_get(obj); + if (!wd) return; + if (wd->calc_job) ecore_job_del(wd->calc_job); + evas_object_del(wd->pan_smart); + wd->pan_smart = NULL; + elm_gengrid_clear(obj); + free(wd); +} + +static void +_mouse_move(void *data, Evas *evas __UNUSED__, Evas_Object *obj, void *event_info) +{ + Elm_Gengrid_Item *item = data; + Evas_Event_Mouse_Move *ev = event_info; + Evas_Coord minw = 0, minh = 0, x, y, dx, dy, adx, ady; + + if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) + { + if (!item->wd->on_hold) { + item->wd->on_hold = EINA_TRUE; + _item_unselect(item); + } + } + if ((item->dragging) && (item->down)) + { + if (item->long_timer) + { + ecore_timer_del(item->long_timer); + item->long_timer = NULL; + } + evas_object_smart_callback_call(item->wd->self, "drag", item); + return; + } + if ((!item->down) || (item->wd->longpressed)) + { + if (item->long_timer) + { + ecore_timer_del(item->long_timer); + item->long_timer = NULL; + } + return; + } + if (!item->display_only) + elm_coords_finger_size_adjust(1, &minw, 1, &minh); + evas_object_geometry_get(obj, &x, &y, NULL, NULL); + x = ev->cur.canvas.x - x; + y = ev->cur.canvas.y - y; + dx = x - item->dx; + adx = dx; + if (adx < 0) adx = -dx; + dy = y - item->dy; + ady = dy; + if (ady < 0) ady = -dy; + minw /= 2; + minh /= 2; + if ((adx > minw) || (ady > minh)) + { + item->dragging = 1; + if (item->long_timer) + { + ecore_timer_del(item->long_timer); + item->long_timer = NULL; + } + if (item->wd->wasselected) + _item_unselect(item); + item->wd->wasselected = 0; + if (dy < 0) + { + if (ady > adx) + evas_object_smart_callback_call(item->wd->self, "drag,start,up", + item); + else + { + if (dx < 0) + evas_object_smart_callback_call(item->wd->self, + "drag,start,left", item); + } + } + else + { + if (ady > adx) + evas_object_smart_callback_call(item->wd->self, + "drag,start,down", item); + else + { + if (dx < 0) + evas_object_smart_callback_call(item->wd->self, + "drag,start,left", item); + else + evas_object_smart_callback_call(item->wd->self, + "drag,start,right", item); + } + } + } +} + +static int +_long_press(void *data) +{ + Elm_Gengrid_Item *item = data; + + item->long_timer = NULL; + if ((item->disabled) || (item->dragging)) return 0; + item->wd->longpressed = EINA_TRUE; + evas_object_smart_callback_call(item->wd->self, "longpressed", item); + return 0; +} + +static void +_mouse_down(void *data, Evas *evas __UNUSED__, Evas_Object *obj, void *event_info) +{ + Elm_Gengrid_Item *item = data; + Evas_Event_Mouse_Down *ev = event_info; + Evas_Coord x, y; + + if (ev->button != 1) return; + item->down = 1; + item->dragging = 0; + evas_object_geometry_get(obj, &x, &y, NULL, NULL); + item->dx = ev->canvas.x - x; + item->dy = ev->canvas.y - y; + item->wd->longpressed = EINA_FALSE; + if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) item->wd->on_hold = EINA_TRUE; + else item->wd->on_hold = EINA_FALSE; + item->wd->wasselected = item->selected; + _item_hilight(item); + if (ev->flags & EVAS_BUTTON_DOUBLE_CLICK) + evas_object_smart_callback_call(item->wd->self, "clicked", item); + if (item->long_timer) ecore_timer_del(item->long_timer); + if (item->realized) + item->long_timer = ecore_timer_add(1.0, _long_press, item); + else + item->long_timer = NULL; +} + +static void +_mouse_up(void *data, Evas *evas __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info) +{ + Elm_Gengrid_Item *item = data; + Evas_Event_Mouse_Up *ev = event_info; + Eina_Bool dragged = EINA_FALSE; + + if (ev->button != 1) return; + item->down = EINA_FALSE; + if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) item->wd->on_hold = EINA_TRUE; + else item->wd->on_hold = EINA_FALSE; + if (item->long_timer) + { + ecore_timer_del(item->long_timer); + item->long_timer = NULL; + } + if (item->dragging) + { + item->dragging = EINA_FALSE; + evas_object_smart_callback_call(item->wd->self, "drag,stop", item); + dragged = EINA_TRUE; + } + if (item->wd->on_hold) + { + item->wd->longpressed = EINA_FALSE; + item->wd->on_hold = EINA_FALSE; + return; + } + if (item->wd->longpressed) + { + item->wd->longpressed = EINA_FALSE; + if (!item->wd->wasselected) + _item_unselect(item); + item->wd->wasselected = EINA_FALSE; + return; + } + if (dragged) + { + if (item->want_unrealize) + _item_unrealize(item); + } + if ((item->disabled) || dragged) return; + if (item->wd->multi) + { + if (!item->selected) + { + _item_hilight(item); + _item_select(item); + } + else _item_unselect(item); + } + else + { + if (!item->selected) + { + while (item->wd->selected) _item_unselect(item->wd->selected->data); + } + else + { + const Eina_List *l, *l_next; + Elm_Gengrid_Item *item2; + + EINA_LIST_FOREACH_SAFE(item->wd->selected, l, l_next, item2) + if (item2 != item) _item_unselect(item2); + } + _item_hilight(item); + _item_select(item); + } +} + +static void +_item_hilight(Elm_Gengrid_Item *item) +{ + if ((item->wd->no_select) || (item->delete_me) || (item->hilighted)) return; + edje_object_signal_emit(item->base, "elm,state,selected", "elm"); + item->hilighted = EINA_TRUE; +} + +static void +_item_realize(Elm_Gengrid_Item *item) +{ + char buf[1024]; + + if ((item->realized) || (item->delete_me)) return; + item->base = edje_object_add(evas_object_evas_get(item->wd->self)); + edje_object_scale_set(item->base, elm_widget_scale_get(item->wd->self) * + _elm_config->scale); + evas_object_smart_member_add(item->base, item->wd->pan_smart); + elm_widget_sub_object_add(item->wd->self, item->base); + _elm_theme_object_set(item->wd->self, item->base, "gengrid", "item/default", + elm_widget_style_get(item->wd->self)); + item->spacer = evas_object_rectangle_add(evas_object_evas_get(item->wd->self)); + evas_object_color_set(item->spacer, 0, 0, 0, 0); + elm_widget_sub_object_add(item->wd->self, item->spacer); + evas_object_size_hint_min_set(item->spacer, 2 * _elm_config->scale, 1); + edje_object_part_swallow(item->base, "elm.swallow.pad", item->spacer); + + if (item->gic->func.label_get) + { + const Eina_List *l; + const char *key; + + item->labels = _elm_stringlist_get(edje_object_data_get(item->base, + "labels")); + EINA_LIST_FOREACH(item->labels, l, key) + { + char *s = item->gic->func.label_get(item->data, item->wd->self, + l->data); + if (s) + { + edje_object_part_text_set(item->base, l->data, s); + free(s); + } + } + } + + if (item->gic->func.icon_get) + { + const Eina_List *l; + const char *key; + + item->icons = _elm_stringlist_get(edje_object_data_get(item->base, + "icons")); + EINA_LIST_FOREACH(item->icons, l, key) + { + Evas_Object *ic = item->gic->func.icon_get(item->data, + item->wd->self, + l->data); + if (ic) + { + item->icon_objs = eina_list_append(item->icon_objs, ic); + edje_object_part_swallow(item->base, key, ic); + evas_object_show(ic); + elm_widget_sub_object_add(item->wd->self, ic); + } + } + } + + if (item->gic->func.state_get) + { + const Eina_List *l; + const char *key; + + item->states = _elm_stringlist_get(edje_object_data_get(item->base, + "states")); + EINA_LIST_FOREACH(item->states, l, key) + { + Eina_Bool on = item->gic->func.state_get(item->data, + item->wd->self, l->data); + if (on) + { + snprintf(buf, sizeof(buf), "elm,state,%s,active", key); + edje_object_signal_emit(item->base, buf, "elm"); + } + } + } + + if (!item->wd->item_width && !item->wd->item_height) + { + edje_object_size_min_restricted_calc(item->base, + &item->wd->item_width, &item->wd->item_height, + item->wd->item_width, item->wd->item_height); + elm_coords_finger_size_adjust(1, &item->wd->item_width, + 1, &item->wd->item_height); + } + + evas_object_event_callback_add(item->base, EVAS_CALLBACK_MOUSE_DOWN, + _mouse_down, item); + evas_object_event_callback_add(item->base, EVAS_CALLBACK_MOUSE_UP, + _mouse_up, item); + evas_object_event_callback_add(item->base, EVAS_CALLBACK_MOUSE_MOVE, + _mouse_move, item); + + if (item->selected) + edje_object_signal_emit(item->base, "elm,state,selected", "elm"); + if (item->disabled) + edje_object_signal_emit(item->base, "elm,state,disabled", "elm"); + + evas_object_show(item->base); + item->realized = EINA_TRUE; + item->want_unrealize = EINA_FALSE; +} + +static void +_item_unrealize(Elm_Gengrid_Item *item) +{ + Evas_Object *icon; + + if (!item->realized) return; + if (item->long_timer) + { + ecore_timer_del(item->long_timer); + item->long_timer = NULL; + } + evas_object_del(item->base); + item->base = NULL; + evas_object_del(item->spacer); + item->spacer = NULL; + _elm_stringlist_free(item->labels); + item->labels = NULL; + _elm_stringlist_free(item->icons); + item->icons = NULL; + _elm_stringlist_free(item->states); + + EINA_LIST_FREE(item->icon_objs, icon) + evas_object_del(icon); + + item->states = NULL; + item->realized = EINA_FALSE; + item->want_unrealize = EINA_FALSE; +} + +static void +_item_place(Elm_Gengrid_Item *item, Evas_Coord cx, Evas_Coord cy) +{ + Evas_Coord x, y, ox, oy, cvx, cvy, cvw, cvh; + Evas_Coord tch, tcw, alignw = 0, alignh = 0, vw, vh; + + item->x = cx; + item->y = cy; + evas_object_geometry_get(item->wd->self, &ox, &oy, &vw, &vh); + evas_output_viewport_get(evas_object_evas_get(item->wd->self), + &cvx, &cvy, &cvw, &cvh); + + /* Preload rows/columns at each side of the Gengrid */ + cvx -= PRELOAD * item->wd->item_width; + cvy -= PRELOAD * item->wd->item_height; + cvw += 2 * PRELOAD * item->wd->item_width; + cvh += 2 * PRELOAD * item->wd->item_height; + + tch = ((vh/item->wd->item_height)*item->wd->item_height); + alignh = (vh - tch)*item->wd->align_y; + + tcw = ((vw/item->wd->item_width)*item->wd->item_width); + alignw = (vw - tcw)*item->wd->align_x; + + if (item->wd->horizontal && item->wd->minw < vw) + { + int columns; + + columns = eina_list_count(item->wd->items)/(vh/item->wd->item_height); + if (eina_list_count(item->wd->items) % (vh/item->wd->item_height)) + columns++; + + tcw = item->wd->item_width * columns; + alignw = (vw - tcw)*item->wd->align_x; + } + else if (item->wd->horizontal && item->wd->minw > vw) + alignw = 0; + if (!item->wd->horizontal && item->wd->minh < vh) + { + int rows; + + rows = eina_list_count(item->wd->items)/(vw/item->wd->item_width); + if (eina_list_count(item->wd->items) % (vw/item->wd->item_width)) + rows++; + + tch = item->wd->item_height * rows; + alignh = (vh - tch)*item->wd->align_y; + } + else if (!item->wd->horizontal && item->wd->minh > vh) + alignh = 0; + x = cx * item->wd->item_width - item->wd->pan_x + ox + alignw; + y = cy * item->wd->item_height - item->wd->pan_y + oy + alignh; + + if (ELM_RECTS_INTERSECT(x, y, item->wd->item_width, item->wd->item_height, + cvx, cvy, cvw, cvh)) + { + Eina_Bool was_realized = item->realized; + _item_realize(item); + if (!was_realized) + evas_object_smart_callback_call(item->wd->self, "realized", item); + evas_object_move(item->base, x, y); + evas_object_resize(item->base, item->wd->item_width, + item->wd->item_height); + } + else + _item_unrealize(item); +} + +static Elm_Gengrid_Item * +_item_create(Widget_Data *wd, const Elm_Gengrid_Item_Class *gic, + const void *data, Evas_Smart_Cb func, const void *func_data) +{ + Elm_Gengrid_Item *item; + + item = calloc(1, sizeof(*item)); + if (!item) return NULL; + item->wd = wd; + item->gic = gic; + item->data = data; + item->func.func = func; + item->func.data = func_data; + return item; +} + +static void +_item_del(Elm_Gengrid_Item *item) +{ + if (item->selected) + item->wd->selected = eina_list_remove(item->wd->selected, item); + if (item->realized) _item_unrealize(item); + if ((!item->delete_me) && (item->gic->func.del)) + item->gic->func.del(item->data, item->wd->self); + item->delete_me = EINA_TRUE; + item->wd->items = eina_list_remove(item->wd->items, item); + if (item->long_timer) ecore_timer_del(item->long_timer); + free(item); +} + +static void +_item_select(Elm_Gengrid_Item *item) +{ + if ((item->wd->no_select) || (item->delete_me)) return; + if (item->selected) + { + if (item->wd->always_select) goto call; + return; + } + item->selected = EINA_TRUE; + item->wd->selected = eina_list_append(item->wd->selected, item); +call: + item->walking++; + if (item->func.func) item->func.func((void *)item->func.data, item->wd->self, + item); + if (!item->delete_me) + evas_object_smart_callback_call(item->wd->self, "selected", item); + item->walking--; + if ((item->walking == 0) && (item->delete_me)) + if (item->relcount == 0) _item_del(item); +} + +static void +_item_unselect(Elm_Gengrid_Item *item) +{ + if ((item->delete_me) || (!item->hilighted)) return; + edje_object_signal_emit(item->base, "elm,state,unselected", "elm"); + item->hilighted = EINA_FALSE; + if (item->selected) + { + item->selected = EINA_FALSE; + item->wd->selected = eina_list_remove(item->wd->selected, item); + evas_object_smart_callback_call(item->wd->self, "unselected", item); + } +} + +static void +_calc_job(void *data) +{ + Widget_Data *wd = data; + Evas_Coord minw = 0, minh = 0, nmax = 0, cvw, cvh; + int count; + + evas_object_geometry_get(wd->self, NULL, NULL, &cvw, &cvh); + if (wd->horizontal && wd->item_height) + nmax = cvh / wd->item_height; + else if (wd->item_width) + nmax = cvw / wd->item_width; + + if (nmax) + { + count = eina_list_count(wd->items); + if (wd->horizontal) + { + minw = ceil(count / (float)nmax) * wd->item_width; + minh = nmax * wd->item_height; + } + else + { + minw = nmax * wd->item_width; + minh = ceil(count / (float)nmax) * wd->item_height; + } + } + + if ((minw != wd->minw) || (minh != wd->minh)) + { + wd->minh = minh; + wd->minw = minw; + evas_object_smart_callback_call(wd->pan_smart, "changed", NULL); + } + + wd->nmax = nmax; + wd->calc_job = NULL; + evas_object_smart_changed(wd->pan_smart); +} + +static void +_pan_add(Evas_Object *obj) +{ + Pan *sd; + Evas_Object_Smart_Clipped_Data *cd; + + _pan_sc.add(obj); + cd = evas_object_smart_data_get(obj); + sd = ELM_NEW(Pan); + if (!sd) return; + sd->__clipped_data = *cd; + free(cd); + evas_object_smart_data_set(obj, sd); +} + +static void +_pan_del(Evas_Object *obj) +{ + Pan *sd = evas_object_smart_data_get(obj); + + if (!sd) return; + _pan_sc.del(obj); +} + +static void +_pan_set(Evas_Object *obj, Evas_Coord x, Evas_Coord y) +{ + Pan *sd = evas_object_smart_data_get(obj); + if ((x == sd->wd->pan_x) && (y == sd->wd->pan_y)) return; + sd->wd->pan_x = x; + sd->wd->pan_y = y; + evas_object_smart_changed(obj); +} + +static void +_pan_get(Evas_Object *obj, Evas_Coord *x, Evas_Coord *y) +{ + Pan *sd = evas_object_smart_data_get(obj); + if (x) *x = sd->wd->pan_x; + if (y) *y = sd->wd->pan_y; +} + +static void +_pan_child_size_get(Evas_Object *obj, Evas_Coord *w, Evas_Coord *h) +{ + Pan *sd = evas_object_smart_data_get(obj); + if (w) *w = sd->wd->minw; + if (h) *h = sd->wd->minh; +} + +static void +_pan_max_get(Evas_Object *obj, Evas_Coord *x, Evas_Coord *y) +{ + Pan *sd = evas_object_smart_data_get(obj); + Evas_Coord ow, oh; + + if (!sd) return; + evas_object_geometry_get(obj, NULL, NULL, &ow, &oh); + if (x) + *x = (ow < sd->wd->minw) ? sd->wd->minw - ow : 0; + if (y) + *y = (oh < sd->wd->minh) ? sd->wd->minh - oh : 0; +} + +static void +_pan_resize(Evas_Object *obj, Evas_Coord w, Evas_Coord h) +{ + Pan *sd = evas_object_smart_data_get(obj); + Evas_Coord ow, oh; + + evas_object_geometry_get(obj, NULL, NULL, &ow, &oh); + if ((ow == w) && (oh == h)) return; + if (sd->wd->calc_job) ecore_job_del(sd->wd->calc_job); + sd->wd->calc_job = ecore_job_add(_calc_job, sd->wd); +} + +static void +_pan_calculate(Evas_Object *obj) +{ + Pan *sd = evas_object_smart_data_get(obj); + Evas_Coord cx = 0, cy = 0; + Eina_List *l; + Elm_Gengrid_Item *item; + + if (!sd) return; + if (!sd->wd->nmax) return; + + EINA_LIST_FOREACH(sd->wd->items, l, item) + { + _item_place(item, cx, cy); + if (sd->wd->horizontal) + { + cy = (cy + 1) % sd->wd->nmax; + if (!cy) cx++; + } + else + { + cx = (cx + 1) % sd->wd->nmax; + if (!cx) cy++; + } + } +} + +static void +_pan_move(Evas_Object *obj, Evas_Coord x __UNUSED__, Evas_Coord y __UNUSED__) +{ + Pan *sd = evas_object_smart_data_get(obj); + if (!sd) return; + if (sd->wd->calc_job) ecore_job_del(sd->wd->calc_job); + sd->wd->calc_job = ecore_job_add(_calc_job, sd->wd); +} + +static void +_hold_on(void *data __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__) +{ + Widget_Data *wd = elm_widget_data_get(obj); + if (!wd) return; + elm_smart_scroller_hold_set(wd->scr, 1); +} + +static void +_hold_off(void *data __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__) +{ + Widget_Data *wd = elm_widget_data_get(obj); + if (!wd) return; + elm_smart_scroller_hold_set(wd->scr, 0); +} + +static void +_freeze_on(void *data __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__) +{ + Widget_Data *wd = elm_widget_data_get(obj); + if (!wd) return; + elm_smart_scroller_freeze_set(wd->scr, 1); +} + +static void +_freeze_off(void *data __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__) +{ + Widget_Data *wd = elm_widget_data_get(obj); + if (!wd) return; + elm_smart_scroller_freeze_set(wd->scr, 0); +} + +static void +_scr_drag_start(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__) +{ + evas_object_smart_callback_call(data, "scroll,drag,start", NULL); +} + +static void +_scr_drag_stop(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__) +{ + evas_object_smart_callback_call(data, "scroll,drag,stop", NULL); +} + +static void +_scr_scroll(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__) +{ + evas_object_smart_callback_call(data, "scroll", NULL); +} + +/** + * Add a new Gengrid object. + * + * @param parent The parent object. + * @return The new object or NULL if it cannot be created. + * + * @see elm_gengrid_item_size_set() + * @see elm_gengrid_horizontal_set() + * @see elm_gengrid_item_append() + * @see elm_gengrid_item_del() + * @see elm_gengrid_clear() + * + * @ingroup Gengrid + */ +EAPI Evas_Object * +elm_gengrid_add(Evas_Object *parent) +{ + Evas_Object *obj; + Evas *e; + Evas_Coord minw, minh; + Widget_Data *wd; + static Evas_Smart *smart = NULL; + + wd = ELM_NEW(Widget_Data); + e = evas_object_evas_get(parent); + obj = elm_widget_add(e); + ELM_SET_WIDTYPE(widtype, "gengrid"); + elm_widget_type_set(obj, "gengrid"); + elm_widget_sub_object_add(parent, obj); + elm_widget_data_set(obj, wd); + elm_widget_del_hook_set(obj, _del_hook); + elm_widget_theme_hook_set(obj, _theme_hook); + + wd->scr = elm_smart_scroller_add(e); + elm_smart_scroller_widget_set(wd->scr, obj); + elm_smart_scroller_object_theme_set(obj, wd->scr, "gengrid", "base", "default"); + elm_widget_resize_object_set(obj, wd->scr); + + evas_object_smart_callback_add(wd->scr, "drag,start", _scr_drag_start, obj); + evas_object_smart_callback_add(wd->scr, "drag,stop", _scr_drag_stop, obj); + evas_object_smart_callback_add(wd->scr, "scroll", _scr_scroll, obj); + + elm_smart_scroller_bounce_allow_set(wd->scr, 1, 1); + + wd->self = obj; + wd->align_x = 0.5; + wd->align_y = 0.5; + + evas_object_smart_callback_add(obj, "scroll-hold-on", _hold_on, obj); + evas_object_smart_callback_add(obj, "scroll-hold-off", _hold_off, obj); + evas_object_smart_callback_add(obj, "scroll-freeze-on", _freeze_on, obj); + evas_object_smart_callback_add(obj, "scroll-freeze-off", _freeze_off, obj); + + if (!smart) + { + static Evas_Smart_Class sc; + + evas_object_smart_clipped_smart_set(&_pan_sc); + sc = _pan_sc; + sc.name = "elm_gengrid_pan"; + sc.version = EVAS_SMART_CLASS_VERSION; + sc.add = _pan_add; + sc.del = _pan_del; + sc.resize = _pan_resize; + sc.move = _pan_move; + sc.calculate = _pan_calculate; + smart = evas_smart_class_new(&sc); + } + if (smart) + { + wd->pan_smart = evas_object_smart_add(e, smart); + wd->pan = evas_object_smart_data_get(wd->pan_smart); + wd->pan->wd = wd; + } + + elm_smart_scroller_extern_pan_set(wd->scr, wd->pan_smart, + _pan_set, _pan_get, + _pan_max_get, _pan_child_size_get); + + return obj; +} + +/** + * Set the size for the item of the Gengrid. + * + * @param obj The Gengrid object. + * @param w The item's width. + * @param h The item's height; + * + * @see elm_gengrid_item_size_get() + * + * @ingroup Gengrid + */ +EAPI void +elm_gengrid_item_size_set(Evas_Object *obj, Evas_Coord w, Evas_Coord h) +{ + ELM_CHECK_WIDTYPE(obj, widtype); + Widget_Data *wd = elm_widget_data_get(obj); + if (!wd) return; + if (wd->item_width == w && wd->item_height == h) return; + wd->item_width = w; + wd->item_height = h; + if (wd->calc_job) ecore_job_del(wd->calc_job); + wd->calc_job = ecore_job_add(_calc_job, wd); +} + +/** + * Get the size of the item of the Gengrid. + * + * @param obj The Gengrid object. + * @param w Pointer to the item's width. + * @param h Pointer to the item's height. + * + * @see elm_gengrid_item_size_get() + * + * @ingroup Gengrid + */ +EAPI void +elm_gengrid_item_size_get(const Evas_Object *obj, Evas_Coord *w, Evas_Coord *h) +{ + ELM_CHECK_WIDTYPE(obj, widtype); + Widget_Data *wd = elm_widget_data_get(obj); + if (!wd) return; + if (w) *w = wd->item_width; + if (h) *h = wd->item_height; +} + +/** + * Set item's alignment within the scroller. + * + * @param obj The Gengrid object. + * @param align_x The x alignment (0 <= x <= 1). + * @param align_y The y alignment (0 <= y <= 1). + * + * @see elm_gengrid_align_get() + * + * @ingroup Gengrid + */ +EAPI void +elm_gengrid_align_set(Evas_Object *obj, double align_x, double align_y) +{ + ELM_CHECK_WIDTYPE(obj, widtype); + Widget_Data *wd = elm_widget_data_get(obj); + + if (align_x > 1.0) + align_x = 1.0; + else if (align_x < 0.0) + align_x = 0.0; + wd->align_x = align_x; + + if (align_y > 1.0) + align_y = 1.0; + else if (align_y < 0.0) + align_y = 0.0; + wd->align_y = align_y; +} + +/** + * Get the alignenment set for the Gengrid object. + * + * @param obj The Gengrid object. + * @param align_x Pointer to x alignenment. + * @param align_y Pointer to y alignenment. + * + * @see elm_gengrid_align_set() + * + * @ingroup Gengrid + */ +EAPI void +elm_gengrid_align_get(const Evas_Object *obj, double *align_x, double *align_y) +{ + ELM_CHECK_WIDTYPE(obj, widtype); + Widget_Data *wd = elm_widget_data_get(obj); + if (align_x) *align_x = wd->align_x; + if (align_y) *align_y = wd->align_y; +} + +/** + * Add item to the end of the Gengrid. + * + * @param obj The Gengrid object. + * @param gic The item class for the item. + * @param data The item data. + * @param func Convenience function called when item is selected. + * @param func_data Data passed to @p func above. + * @return A handle to the item added or NULL if not possible. + * + * @see elm_gengrid_item_del() + * + * @ingroup Gengrid + */ +EAPI Elm_Gengrid_Item * +elm_gengrid_item_append(Evas_Object *obj, const Elm_Gengrid_Item_Class *gic, + const void *data, Evas_Smart_Cb func, + const void *func_data) +{ + Elm_Gengrid_Item *item; + ELM_CHECK_WIDTYPE(obj, widtype) NULL; + Widget_Data *wd = elm_widget_data_get(obj); + if (!wd) return NULL; + + item = _item_create(wd, gic, data, func, func_data); + if (!item) return NULL; + + wd->items = eina_list_append(wd->items, item); + wd->no_select = EINA_FALSE; + + if (wd->calc_job) ecore_job_del(wd->calc_job); + wd->calc_job = ecore_job_add(_calc_job, wd); + + return item; +} + +/** + * Remove a item from the Gengrid. + * + * @param item The item to be removed. + * @return @c EINA_TRUE on success or @c EINA_FALSE otherwise. + * + * @see elm_gengrid_clear() to remove all items of the Gengrid. + * + * @ingroup Gengrid + */ +EAPI void +elm_gengrid_item_del(Elm_Gengrid_Item *item) +{ + if (!item) return; + if ((item->relcount > 0) || (item->walking > 0)) + { + item->delete_me = EINA_TRUE; + if (item->selected) + item->wd->selected = eina_list_remove(item->wd->selected, item); + if (item->gic->func.del) item->gic->func.del(item->data, item->wd->self); + return; + } + + _item_del(item); + + if (item->wd->calc_job) ecore_job_del(item->wd->calc_job); + item->wd->calc_job = ecore_job_add(_calc_job, item->wd); +} + +/** + * Set for what direction the Gengrid will expand. + * + * @param obj The Gengrid object. + * @param setting If @c EINA_TRUE the Gengrid will expand horizontally or + * vertically if @c EINA_FALSE. + * + * @ingroup Gengrid + */ +EAPI void +elm_gengrid_horizontal_set(Evas_Object *obj, Eina_Bool setting) +{ + ELM_CHECK_WIDTYPE(obj, widtype); + Widget_Data *wd = elm_widget_data_get(obj); + if (!wd) return; + if (setting == wd->horizontal) return; + wd->horizontal = setting; + + /* Update the items to conform to the new layout */ + if (wd->calc_job) ecore_job_del(wd->calc_job); + wd->calc_job = ecore_job_add(_calc_job, wd); +} + +/** + * Clear the Gengrid + * + * This clears all items in the Gengrid, leaving it empty. + * + * @param obj The Gengrid object. + * + * @see elm_gengrid_item_del() to remove just one item. + * + * @ingroup Gengrid + */ +EAPI void +elm_gengrid_clear(Evas_Object *obj) +{ + Eina_List *l, *l_next; + Elm_Gengrid_Item *item; + ELM_CHECK_WIDTYPE(obj, widtype); + Widget_Data *wd = elm_widget_data_get(obj); + if (!wd) return; + + if (wd->calc_job) + { + ecore_job_del(wd->calc_job); + wd->calc_job = NULL; + } + + EINA_LIST_FOREACH_SAFE(wd->items, l, l_next, item) + { + if (item->realized) _item_unrealize(item); + if (item->gic->func.del) item->gic->func.del(item->data, wd->self); + if (item->long_timer) ecore_timer_del(item->long_timer); + free(item); + wd->items = eina_list_remove_list(wd->items, l); + } + + if (wd->selected) + { + eina_list_free(wd->selected); + wd->selected = NULL; + } + + wd->pan_x = 0; + wd->pan_y = 0; + wd->minw = 0; + wd->minh = 0; + evas_object_size_hint_min_set(wd->pan_smart, wd->minw, wd->minh); + evas_object_smart_callback_call(wd->pan_smart, "changed", NULL); +} + +/** + * Get the real evas object of the Gengrid item + * + * This returns the actual evas object used for the specified Gengrid item. + * This may be NULL as it may not be created, and may be deleted at any time + * by Gengrid. Do not modify this object (move, resize, show, hide etc.) as + * Gengrid is controlling it. This function is for querying, emitting custom + * signals or hooking lower level callbacks for events. Do not delete this + * object under any circumstances. + * + * @param item The Gengrid item. + * @return the evas object associated to this item. + * + * @see elm_gengrid_item_data_get() + * + * @ingroup Gengrid + */ +EAPI const Evas_Object * +elm_gengrid_item_object_get(Elm_Gengrid_Item *item) +{ + if (!item) return NULL; + return item->base; +} + +/** + * Returns the data associated to a item + * + * This returns the data value passed on the elm_gengrid_item_append() and + * related item addition calls. + * + * @param item The Gengrid item. + * @return the data associated to this item. + * + * @see elm_gengrid_item_append() + * @see elm_gengrid_item_object_get() + * + * @ingroup Gengrid + */ +EAPI void * +elm_gengrid_item_data_get(Elm_Gengrid_Item *item) +{ + if (!item) return NULL; + return (void *)item->data; +} + +/** + * Get the item's coordinates. + * + * This returns the logical position of the item whithin the Gengrid. + * + * @param item The Gengrid item. + * @param x The x-axis coordinate pointer. + * @param y The y-axis coordinate pointer. + * + * @ingroup Gengrid + */ +EAPI void +elm_gengrid_item_pos_get(const Elm_Gengrid_Item *item, unsigned int *x, unsigned int *y) +{ + if (!item) return; + if (x) *x = item->x; + if (y) *y = item->y; +} + +/** + * Enable or disable multi-select in the Gengrid. + * + * This enables (EINA_TRUE) or disables (EINA_FALSE) multi-select in the Gengrid. + * This allows more than 1 item to be selected. + * + * @param obj The Gengrid object. + * @param multi Multi-select enabled/disabled + * + * @ingroup Gengrid + */ +EAPI void +elm_gengrid_multi_select_set(Evas_Object *obj, Eina_Bool multi) +{ + ELM_CHECK_WIDTYPE(obj, widtype); + Widget_Data *wd = elm_widget_data_get(obj); + if (!wd) return; + wd->multi = multi; +} + +/** + * Get if multi-select in Gengrid is enabled or disabled + * + * @param obj The Gengrid object + * @return Multi-select enable/disable + * (EINA_TRUE = enabled / EINA_FALSE = disabled) + * + * @ingroup Gengrid + */ +EAPI Eina_Bool +elm_gengrid_multi_select_get(const Evas_Object *obj) +{ + ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE; + Widget_Data *wd = elm_widget_data_get(obj); + if (!wd) return EINA_FALSE; + return wd->multi; +} + +/** + * Get the selected item in the Gengrid + * + * This gets the selected item in the Gengrid (if multi-select is enabled only + * the first item in the list is selected - which is not very useful, so see + * elm_gengrid_selected_items_get() for when multi-select is used). + * + * If no item is selected, NULL is returned. + * + * @param obj The Gengrid object. + * @return The selected item, or NULL if none. + * + * @ingroup Gengrid + */ +EAPI Elm_Gengrid_Item * +elm_gengrid_selected_item_get(const Evas_Object *obj) +{ + ELM_CHECK_WIDTYPE(obj, widtype) NULL; + Widget_Data *wd = elm_widget_data_get(obj); + if (!wd) return NULL; + if (wd->selected) return wd->selected->data; + return NULL; +} + +/** + * Get a list of selected items in the Gengrid. + * + * This returns a list of the selected items. This list pointer is only valid + * so long as no items are selected or unselected (or unselected implictly by + * deletion). The list contains Elm_Gengrid_Item pointers. + * + * @param obj The Gengrid object. + * @return The list of selected items, or NULL if none are selected. + * + * @ingroup Gengrid + */ +EAPI const Eina_List * +elm_gengrid_selected_items_get(const Evas_Object *obj) +{ + ELM_CHECK_WIDTYPE(obj, widtype) NULL; + Widget_Data *wd = elm_widget_data_get(obj); + if (!wd) return NULL; + return wd->selected; +} + +/** + * Set the selected state of a item. + * + * This sets the selected state of a item. If multi-select is not enabled and + * selected is EINA_TRUE, previously selected items are unselected. + * + * @param item The item + * @param selected The selected state. + * + * @ingroup Gengrid + */ +EAPI void +elm_gengrid_item_selected_set(Elm_Gengrid_Item *item, Eina_Bool selected) +{ + Widget_Data *wd = elm_widget_data_get(item->wd->self); + if (!wd) return; + if (!item || item->delete_me) return; + selected = !!selected; + if (item->selected == selected) return; + + if (selected) + { + if (!wd->multi) + { + while (wd->selected) + _item_unselect(wd->selected->data); + } + _item_hilight(item); + _item_select(item); + } + else + _item_unselect(item); +} + +/** + * Get the selected state of a item. + * + * This gets the selected state of a item (1 selected, 0 not selected). + * + * @param item The item + * @return The selected state + * + * @ingroup Gengrid + */ +EAPI Eina_Bool +elm_gengrid_item_selected_get(const Elm_Gengrid_Item *item) +{ + if (!item) return EINA_FALSE; + return item->selected; +} + +/** + * Sets the disabled state of a item. + * + * A disabled item cannot be selected or unselected. It will also change + * appearance to disabled. This sets the disabled state (1 disabled, 0 not + * disabled). + * + * @param item The item + * @param disabled The disabled state + * + * @ingroup Gengrid + */ +EAPI void +elm_gengrid_item_disabled_set(Elm_Gengrid_Item *item, Eina_Bool disabled) +{ + if (!item) return; + if (item->disabled == disabled) return; + if (item->delete_me) return; + item->disabled = disabled; + if (item->realized) + { + if (item->disabled) + edje_object_signal_emit(item->base, "elm,state,disabled", "elm"); + else + edje_object_signal_emit(item->base, "elm,state,enabled", "elm"); + } +} + +/** + * Get the disabled state of a item. + * + * This gets the disabled state of the given item. + * + * @param item The item + * @return The disabled state + * + * @ingroup Gengrid + */ +EAPI Eina_Bool +elm_gengrid_item_disabled_get(const Elm_Gengrid_Item *item) +{ + if (!item) return EINA_FALSE; + if (item->delete_me) return EINA_FALSE; + return item->disabled; +} + +/** + * Set the always select mode. + * + * Cells will only call their selection func and callback when first becoming + * selected. Any further clicks will do nothing, unless you enable always select + * with elm_gengrid_always_select_mode_set(). This means even if selected, + * every click will make the selected callbacks be called. + * + * @param obj The Gengrid object + * @param always_select The always select mode (EINA_TRUE = on, EINA_FALSE = off) + * + * @ingroup Gengrid + */ +EAPI void +elm_gengrid_always_select_mode_set(Evas_Object *obj, Eina_Bool always_select) +{ + ELM_CHECK_WIDTYPE(obj, widtype); + Widget_Data *wd = elm_widget_data_get(obj); + if (!wd) return; + wd->always_select = always_select; +} + +/** + * Get the always select mode. + * + * @param obj The Gengrid object. + * @return The always select mode (EINA_TRUE = on, EINA_FALSE = off) + * + * @ingroup Gengrid + */ +EAPI Eina_Bool +elm_gengrid_always_select_mode_get(const Evas_Object *obj) +{ + ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE; + Widget_Data *wd = elm_widget_data_get(obj); + if (!wd) return EINA_FALSE; + return wd->always_select; +} + +/** + * Set no select mode. + * + * This will turn off the ability to select items entirely and they will + * neither appear selected nor call selected callback functions. + * + * @param obj The Gengrid object + * @param no_select The no select mode (EINA_TRUE = on, EINA_FALSE = off) + * + * @ingroup Gengrid + */ +EAPI void +elm_gengrid_no_select_mode_set(Evas_Object *obj, Eina_Bool no_select) +{ + ELM_CHECK_WIDTYPE(obj, widtype); + Widget_Data *wd = elm_widget_data_get(obj); + if (!wd) return; + wd->no_select = no_select; +} + +/** + * Gets no select mode. + * + * @param obj The Gengrid object + * @return The no select mode (EINA_TRUE = on, EINA_FALSE = off) + * + * @ingroup Gengrid + */ +EAPI Eina_Bool +elm_gengrid_no_select_mode_get(const Evas_Object *obj) +{ + ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE; + Widget_Data *wd = elm_widget_data_get(obj); + if (!wd) return EINA_FALSE; + return wd->no_select; +} + +/** + * Set bounce mode. + * + * This will enable or disable the scroller bounce mode for the Gengrid. See + * elm_scroller_bounce_set() for details. + * + * @param obj The Gengrid object + * @param h_bounce Allow bounce horizontally + * @param v_bounce Allow bounce vertically + * + * @ingroup Gengrid + */ +EAPI void +elm_gengrid_bounce_set(Evas_Object *obj, Eina_Bool h_bounce, Eina_Bool v_bounce) +{ + ELM_CHECK_WIDTYPE(obj, widtype); + Widget_Data *wd = elm_widget_data_get(obj); + if (!wd) return; + elm_smart_scroller_bounce_allow_set(wd->scr, h_bounce, v_bounce); +} + +/** + * Get the bounce mode + * + * @param obj The Gengrid object + * @param h_bounce Allow bounce horizontally + * @param v_bounce Allow bounce vertically + * + * @ingroup Gengrid + */ +EAPI void +elm_gengrid_bounce_get(const Evas_Object *obj, Eina_Bool *h_bounce, Eina_Bool *v_bounce) +{ + ELM_CHECK_WIDTYPE(obj, widtype); + Widget_Data *wd = elm_widget_data_get(obj); + if (!wd) return; + elm_smart_scroller_bounce_allow_get(wd->scr, h_bounce, v_bounce); +} diff --git a/legacy/elementary/src/lib/elm_scrolled_grid.c b/legacy/elementary/src/lib/elm_scrolled_grid.c deleted file mode 100644 index 46d7b3e72c..0000000000 --- a/legacy/elementary/src/lib/elm_scrolled_grid.c +++ /dev/null @@ -1,1579 +0,0 @@ -/* - * vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2 - */ -#include -#include "elm_priv.h" - -/** - * @defgroup Grid Scrolled Grid - * - * This widget aims to position objects in a grid layout while actually - * building only the visible ones, using the same idea as genlist: the user - * define a class for each cell, specifying functions that will be called at - * object creation and deletion. - * - * Signals that you can add callbacks for are: - * - * clicked - The user has double-clicked a cell. The event_info parameter is - * the grid cell that was double-clicked. - * - * selected - The user has made an item selected. The event_info parameter is - * the grid cell that was selected. - * - * unselected - The user has made an item unselected. The event_info parameter - * is the grid cell that was unselected. - * - * realized - This is called when the cell in the grid is created as a real - * evas object. event_info is the grid cell that was created. The object may be - * deleted at any time, so it is up to the caller to not use the object pointer - * from elm_scrolled_grid_cell_object_get() in a way where it may point to - * freed objects. - * - * drag,start,up - Called when the cell in the grid has been dragged (not - * scrolled) up. - * - * drag,start,down - Called when the cell in the grid has been dragged (not - * scrolled) down. - * - * drag,start,left - Called when the cell in the grid has been dragged (not - * scrolled) left. - * - * drag,start,right - Called when the cell in the grid has been dragged (not - * scrolled) right. - * - * drag,stop - Called when the cell in the grid has stopped being dragged. - * - * drag - Called when the cell in the grid is being dragged. - * - * scroll - called when the content has been scrolled (moved). - * - * scroll,drag,start - called when dragging the content has started. - * - * scroll,drag,stop - called when dragging the content has stopped. - * - * - * A cell in the grid can have 0 or more text labels (they can be regular text - * or textblock - that's up to the style to determine), 0 or more icons (which - * are simply objects swallowed into the grid cell) and 0 or more boolean states - * that can be used for check, radio or other indicators by the edje theme style. - * A cell may be one of several styles (Elementary provides 1 by default - - * "default", but this can be extended by system or application custom - * themes/overlays/extensions). - * - * In order to implement the ability to add and delete cells on the fly, Grid - * implements a class/callback system where the application provides a structure - * with information about that type of cell (grid may contain multiple different - * cells with different classes, states and styles). Grid will call the functions - * in this struct (methods) when a cell is "realized" (that is created - * dynamically while scrolling). All objects will simply be deleted when no - * longer needed with evas_object_del(). The Elm_Genlist_Item_Class structure - * contains the following members: - * - * cell_style - This is a constant string and simply defines the name of the - * cell style. It must be specified and the default should be "default". - * - * func.label_get - This function is called when an actual cell object is - * created. The data parameter is the one passed to elm_scrolled_grid_cell_add() - * and related cell creation functions. The obj parameter is the grid object and - * the part parameter is the string name of the text part in the edje design that - * is listed as one of the possible labels that can be set. This function must - * return a strdup'()ed string as the caller will free() it when done. - * - * func.icon_get - This function is called when an actual item object is - * created. The data parameter is the one passed to elm_scrolled_grid_cell_add() - * and related cell creation functions. The obj parameter is the grid object and - * the part parameter is the string name of the icon part in the edje design that - * is listed as one of the possible icons that can be set. This must return NULL - * for no object or a valid object. The object will be deleted by grid on - * shutdown or when the cell is unrealized. - * - * func.state_get - This function is called when an actual cell object is - * created. The data parameter is the one passed to elm_scrolled_grid_cell_add() - * and related cell creation functions. The obj parameter is the grid object and - * the part parameter is the string name of th state part in the edje design that - * is listed as one of the possible states that can be set. Return 0 for false - * and 1 for true. Grid will emit a signal to the edje object with - * "elm,state,XXX,active" "elm" when true (the default is false), where XXX is - * the name of the part. - * - * func.del - This is called when elm_scrolled_grid_cell_del() is called on a - * cell or elm_scrolled_grid_clear() is called on the grid. This is intended for - * use when actual grid cells are deleted, so any backing data attached to the - * cell (e.g. its data parameter on creation) can be deleted. - * - * If the application wants multiple cells to be able to be selected, - * elm_scrolled_grid_multi_select_set() can enable this. If the grid is - * single-selection only (the default), then elm_scrolled_grid_select_cell_get() - * will return the selected cell, if any, or NULL if none is selected. If the - * grid is multi-select then elm_scrolled_grid_selected_cells_get() will return a - * list (that is only valid as long as no cells are modified (added, deleted, - * selected or unselected). - * - * If a cell changes (state of boolean changes, label or icons change), then use - * elm_scrolled_grid_cell_update() to have grid update the cell with the new - * state. Grid will re-realize the cell thus call the functions in the - * _Elm_Grid_Cell_Class for that cell. - * - * To programmatically (un)select a cell use elm_scrolled_grid_cell_selected_set(). - * To get its selected state use elm_scrolled_grid_cell_selected_get(). To make a - * cell disabled (unable to be selected and appear differently) use - * elm_scrolled_grid_cell_disable_set() to set this and - * elm_scrolled_grid_cell_disable_get() to get the disabled state. - * - * Cells will only call their selection func and callback when first becoming - * selected. Any further clicks will do nothing, unless you enable always - * select with elm_scrolled_grid_always_select_mode_set(). This means event if - * selected, every click will make the selected callbacks be called. - * elm_scrolled_grid_no_select_mode_set() will turn off the ability to select - * items entirely and they will neither appear selected nor call selected - * callback function. - * - * Remember that you can create new styles and add your own theme augmentation - * per application with elm_theme_extension_add(). If you absolutely must have a - * specific style that overrides any theme the user or system sets up you can use - * elm_theme_overlay_add() to add such a file. - * - * -- - * TODO: - * * Handle non-homogeneous objects too. - */ - -typedef struct _Widget_Data Widget_Data; -typedef struct _Pan Pan; - -#define PRELOAD 1 - -struct _Elm_Grid_Cell -{ - Evas_Object *base, *spacer; - const Elm_Grid_Cell_Class *gcc; - Ecore_Timer *long_timer; - Widget_Data *wd; - Eina_List *labels, *icons, *states, *icon_objs; - const void *data; - struct - { - Evas_Smart_Cb func; - const void *data; - } func; - - Evas_Coord x, y, dx, dy; - int relcount; - - Eina_Bool want_unrealize : 1; - Eina_Bool realized : 1; - Eina_Bool dragging : 1; - Eina_Bool down : 1; - Eina_Bool delete_me : 1; - Eina_Bool display_only : 1; - Eina_Bool disabled : 1; - Eina_Bool selected : 1; - Eina_Bool hilighted : 1; - Eina_Bool walking : 1; -}; - -struct _Widget_Data -{ - Evas_Object *self, *scr; - Evas_Object *pan_smart; - Pan *pan; - Eina_List *cells; - Ecore_Job *calc_job; - Eina_List *selected; - double align_x, align_y; - - Evas_Coord pan_x, pan_y; - Evas_Coord cell_width, cell_height; /* Each cell size */ - Evas_Coord minw, minh; /* Total obj size */ - unsigned int nmax; - - Eina_Bool horizontal : 1; - Eina_Bool on_hold : 1; - Eina_Bool longpressed : 1; - Eina_Bool multi : 1; - Eina_Bool no_select : 1; - Eina_Bool wasselected : 1; - Eina_Bool always_select : 1; -}; - -struct _Pan -{ - Evas_Object_Smart_Clipped_Data __clipped_data; - Widget_Data *wd; -}; - -static const char *widtype = NULL; -static void _cell_hilight(Elm_Grid_Cell *cell); -static void _cell_unrealize(Elm_Grid_Cell *cell); -static void _cell_select(Elm_Grid_Cell *cell); -static void _cell_unselect(Elm_Grid_Cell *cell); - -static Evas_Smart_Class _pan_sc = EVAS_SMART_CLASS_INIT_VERSION; - -static void -_theme_hook(Evas_Object *obj) -{ - Widget_Data *wd = elm_widget_data_get(obj); - if (!wd) return; - elm_smart_scroller_object_theme_set(obj, wd->scr, "grid", "base", - elm_widget_style_get(obj)); -} - -static void -_del_hook(Evas_Object *obj) -{ - Widget_Data *wd = elm_widget_data_get(obj); - if (!wd) return; - if (wd->calc_job) ecore_job_del(wd->calc_job); - evas_object_del(wd->pan_smart); - wd->pan_smart = NULL; - elm_scrolled_grid_clear(obj); - free(wd); -} - -static void -_mouse_move(void *data, Evas *evas __UNUSED__, Evas_Object *obj, void *event_info) -{ - Elm_Grid_Cell *cell = data; - Evas_Event_Mouse_Move *ev = event_info; - Evas_Coord minw = 0, minh = 0, x, y, dx, dy, adx, ady; - - if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) - { - if (!cell->wd->on_hold) { - cell->wd->on_hold = EINA_TRUE; - _cell_unselect(cell); - } - } - if ((cell->dragging) && (cell->down)) - { - if (cell->long_timer) - { - ecore_timer_del(cell->long_timer); - cell->long_timer = NULL; - } - evas_object_smart_callback_call(cell->wd->self, "drag", cell); - return; - } - if ((!cell->down) || (cell->wd->longpressed)) - { - if (cell->long_timer) - { - ecore_timer_del(cell->long_timer); - cell->long_timer = NULL; - } - return; - } - if (!cell->display_only) - elm_coords_finger_size_adjust(1, &minw, 1, &minh); - evas_object_geometry_get(obj, &x, &y, NULL, NULL); - x = ev->cur.canvas.x - x; - y = ev->cur.canvas.y - y; - dx = x - cell->dx; - adx = dx; - if (adx < 0) adx = -dx; - dy = y - cell->dy; - ady = dy; - if (ady < 0) ady = -dy; - minw /= 2; - minh /= 2; - if ((adx > minw) || (ady > minh)) - { - cell->dragging = 1; - if (cell->long_timer) - { - ecore_timer_del(cell->long_timer); - cell->long_timer = NULL; - } - if (cell->wd->wasselected) - _cell_unselect(cell); - cell->wd->wasselected = 0; - if (dy < 0) - { - if (ady > adx) - evas_object_smart_callback_call(cell->wd->self, "drag,start,up", - cell); - else - { - if (dx < 0) - evas_object_smart_callback_call(cell->wd->self, - "drag,start,left", cell); - } - } - else - { - if (ady > adx) - evas_object_smart_callback_call(cell->wd->self, - "drag,start,down", cell); - else - { - if (dx < 0) - evas_object_smart_callback_call(cell->wd->self, - "drag,start,left", cell); - else - evas_object_smart_callback_call(cell->wd->self, - "drag,start,right", cell); - } - } - } -} - -static int -_long_press(void *data) -{ - Elm_Grid_Cell *cell = data; - - cell->long_timer = NULL; - if ((cell->disabled) || (cell->dragging)) return 0; - cell->wd->longpressed = EINA_TRUE; - evas_object_smart_callback_call(cell->wd->self, "longpressed", cell); - return 0; -} - -static void -_mouse_down(void *data, Evas *evas __UNUSED__, Evas_Object *obj, void *event_info) -{ - Elm_Grid_Cell *cell = data; - Evas_Event_Mouse_Down *ev = event_info; - Evas_Coord x, y; - - if (ev->button != 1) return; - cell->down = 1; - cell->dragging = 0; - evas_object_geometry_get(obj, &x, &y, NULL, NULL); - cell->dx = ev->canvas.x - x; - cell->dy = ev->canvas.y - y; - cell->wd->longpressed = EINA_FALSE; - if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) cell->wd->on_hold = EINA_TRUE; - else cell->wd->on_hold = EINA_FALSE; - cell->wd->wasselected = cell->selected; - _cell_hilight(cell); - if (ev->flags & EVAS_BUTTON_DOUBLE_CLICK) - evas_object_smart_callback_call(cell->wd->self, "clicked", cell); - if (cell->long_timer) ecore_timer_del(cell->long_timer); - if (cell->realized) - cell->long_timer = ecore_timer_add(1.0, _long_press, cell); - else - cell->long_timer = NULL; -} - -static void -_mouse_up(void *data, Evas *evas __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info) -{ - Elm_Grid_Cell *cell = data; - Evas_Event_Mouse_Up *ev = event_info; - Eina_Bool dragged = EINA_FALSE; - - if (ev->button != 1) return; - cell->down = EINA_FALSE; - if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) cell->wd->on_hold = EINA_TRUE; - else cell->wd->on_hold = EINA_FALSE; - if (cell->long_timer) - { - ecore_timer_del(cell->long_timer); - cell->long_timer = NULL; - } - if (cell->dragging) - { - cell->dragging = EINA_FALSE; - evas_object_smart_callback_call(cell->wd->self, "drag,stop", cell); - dragged = EINA_TRUE; - } - if (cell->wd->on_hold) - { - cell->wd->longpressed = EINA_FALSE; - cell->wd->on_hold = EINA_FALSE; - return; - } - if (cell->wd->longpressed) - { - cell->wd->longpressed = EINA_FALSE; - if (!cell->wd->wasselected) - _cell_unselect(cell); - cell->wd->wasselected = EINA_FALSE; - return; - } - if (dragged) - { - if (cell->want_unrealize) - _cell_unrealize(cell); - } - if ((cell->disabled) || dragged) return; - if (cell->wd->multi) - { - if (!cell->selected) - { - _cell_hilight(cell); - _cell_select(cell); - } - else _cell_unselect(cell); - } - else - { - if (!cell->selected) - { - while (cell->wd->selected) _cell_unselect(cell->wd->selected->data); - } - else - { - const Eina_List *l, *l_next; - Elm_Grid_Cell *cell2; - - EINA_LIST_FOREACH_SAFE(cell->wd->selected, l, l_next, cell2) - if (cell2 != cell) _cell_unselect(cell2); - } - _cell_hilight(cell); - _cell_select(cell); - } -} - -static void -_cell_hilight(Elm_Grid_Cell *cell) -{ - if ((cell->wd->no_select) || (cell->delete_me) || (cell->hilighted)) return; - edje_object_signal_emit(cell->base, "elm,state,selected", "elm"); - cell->hilighted = EINA_TRUE; -} - -static void -_cell_realize(Elm_Grid_Cell *cell) -{ - char buf[1024]; - - if ((cell->realized) || (cell->delete_me)) return; - cell->base = edje_object_add(evas_object_evas_get(cell->wd->self)); - edje_object_scale_set(cell->base, elm_widget_scale_get(cell->wd->self) * - _elm_config->scale); - evas_object_smart_member_add(cell->base, cell->wd->pan_smart); - elm_widget_sub_object_add(cell->wd->self, cell->base); - _elm_theme_object_set(cell->wd->self, cell->base, "grid", "cell/default", - elm_widget_style_get(cell->wd->self)); - cell->spacer = evas_object_rectangle_add(evas_object_evas_get(cell->wd->self)); - evas_object_color_set(cell->spacer, 0, 0, 0, 0); - elm_widget_sub_object_add(cell->wd->self, cell->spacer); - evas_object_size_hint_min_set(cell->spacer, 2 * _elm_config->scale, 1); - edje_object_part_swallow(cell->base, "elm.swallow.pad", cell->spacer); - - if (cell->gcc->func.label_get) - { - const Eina_List *l; - const char *key; - - cell->labels = _elm_stringlist_get(edje_object_data_get(cell->base, - "labels")); - EINA_LIST_FOREACH(cell->labels, l, key) - { - char *s = cell->gcc->func.label_get(cell->data, cell->wd->self, - l->data); - if (s) - { - edje_object_part_text_set(cell->base, l->data, s); - free(s); - } - } - } - - if (cell->gcc->func.icon_get) - { - const Eina_List *l; - const char *key; - - cell->icons = _elm_stringlist_get(edje_object_data_get(cell->base, - "icons")); - EINA_LIST_FOREACH(cell->icons, l, key) - { - Evas_Object *ic = cell->gcc->func.icon_get(cell->data, - cell->wd->self, - l->data); - if (ic) - { - cell->icon_objs = eina_list_append(cell->icon_objs, ic); - edje_object_part_swallow(cell->base, key, ic); - evas_object_show(ic); - elm_widget_sub_object_add(cell->wd->self, ic); - } - } - } - - if (cell->gcc->func.state_get) - { - const Eina_List *l; - const char *key; - - cell->states = _elm_stringlist_get(edje_object_data_get(cell->base, - "states")); - EINA_LIST_FOREACH(cell->states, l, key) - { - Eina_Bool on = cell->gcc->func.state_get(cell->data, - cell->wd->self, l->data); - if (on) - { - snprintf(buf, sizeof(buf), "elm,state,%s,active", key); - edje_object_signal_emit(cell->base, buf, "elm"); - } - } - } - - if (!cell->wd->cell_width && !cell->wd->cell_height) - { - edje_object_size_min_restricted_calc(cell->base, - &cell->wd->cell_width, &cell->wd->cell_height, - cell->wd->cell_width, cell->wd->cell_height); - elm_coords_finger_size_adjust(1, &cell->wd->cell_width, - 1, &cell->wd->cell_height); - } - - evas_object_event_callback_add(cell->base, EVAS_CALLBACK_MOUSE_DOWN, - _mouse_down, cell); - evas_object_event_callback_add(cell->base, EVAS_CALLBACK_MOUSE_UP, - _mouse_up, cell); - evas_object_event_callback_add(cell->base, EVAS_CALLBACK_MOUSE_MOVE, - _mouse_move, cell); - - if (cell->selected) - edje_object_signal_emit(cell->base, "elm,state,selected", "elm"); - if (cell->disabled) - edje_object_signal_emit(cell->base, "elm,state,disabled", "elm"); - - evas_object_show(cell->base); - cell->realized = EINA_TRUE; - cell->want_unrealize = EINA_FALSE; -} - -static void -_cell_unrealize(Elm_Grid_Cell *cell) -{ - Evas_Object *icon; - - if (!cell->realized) return; - if (cell->long_timer) - { - ecore_timer_del(cell->long_timer); - cell->long_timer = NULL; - } - evas_object_del(cell->base); - cell->base = NULL; - evas_object_del(cell->spacer); - cell->spacer = NULL; - _elm_stringlist_free(cell->labels); - cell->labels = NULL; - _elm_stringlist_free(cell->icons); - cell->icons = NULL; - _elm_stringlist_free(cell->states); - - EINA_LIST_FREE(cell->icon_objs, icon) - evas_object_del(icon); - - cell->states = NULL; - cell->realized = EINA_FALSE; - cell->want_unrealize = EINA_FALSE; -} - -static void -_cell_place(Elm_Grid_Cell *cell, Evas_Coord cx, Evas_Coord cy) -{ - Evas_Coord x, y, ox, oy, cvx, cvy, cvw, cvh; - Evas_Coord tch, tcw, alignw = 0, alignh = 0, vw, vh; - - cell->x = cx; - cell->y = cy; - evas_object_geometry_get(cell->wd->self, &ox, &oy, &vw, &vh); - evas_output_viewport_get(evas_object_evas_get(cell->wd->self), - &cvx, &cvy, &cvw, &cvh); - - /* Preload rows/columns at each side of the Grid */ - cvx -= PRELOAD * cell->wd->cell_width; - cvy -= PRELOAD * cell->wd->cell_height; - cvw += 2 * PRELOAD * cell->wd->cell_width; - cvh += 2 * PRELOAD * cell->wd->cell_height; - - tch = ((vh/cell->wd->cell_height)*cell->wd->cell_height); - alignh = (vh - tch)*cell->wd->align_y; - - tcw = ((vw/cell->wd->cell_width)*cell->wd->cell_width); - alignw = (vw - tcw)*cell->wd->align_x; - - if (cell->wd->horizontal && cell->wd->minw < vw) - { - int columns; - - columns = eina_list_count(cell->wd->cells)/(vh/cell->wd->cell_height); - if (eina_list_count(cell->wd->cells) % (vh/cell->wd->cell_height)) - columns++; - - tcw = cell->wd->cell_width * columns; - alignw = (vw - tcw)*cell->wd->align_x; - } - else if (cell->wd->horizontal && cell->wd->minw > vw) - alignw = 0; - if (!cell->wd->horizontal && cell->wd->minh < vh) - { - int rows; - - rows = eina_list_count(cell->wd->cells)/(vw/cell->wd->cell_width); - if (eina_list_count(cell->wd->cells) % (vw/cell->wd->cell_width)) - rows++; - - tch = cell->wd->cell_height * rows; - alignh = (vh - tch)*cell->wd->align_y; - } - else if (!cell->wd->horizontal && cell->wd->minh > vh) - alignh = 0; - x = cx * cell->wd->cell_width - cell->wd->pan_x + ox + alignw; - y = cy * cell->wd->cell_height - cell->wd->pan_y + oy + alignh; - - if (ELM_RECTS_INTERSECT(x, y, cell->wd->cell_width, cell->wd->cell_height, - cvx, cvy, cvw, cvh)) - { - Eina_Bool was_realized = cell->realized; - _cell_realize(cell); - if (!was_realized) - evas_object_smart_callback_call(cell->wd->self, "realized", cell); - evas_object_move(cell->base, x, y); - evas_object_resize(cell->base, cell->wd->cell_width, - cell->wd->cell_height); - } - else - _cell_unrealize(cell); -} - -static Elm_Grid_Cell * -_cell_create(Widget_Data *wd, const Elm_Grid_Cell_Class *gcc, - const void *data, Evas_Smart_Cb func, const void *func_data) -{ - Elm_Grid_Cell *cell; - - cell = calloc(1, sizeof(*cell)); - if (!cell) return NULL; - cell->wd = wd; - cell->gcc = gcc; - cell->data = data; - cell->func.func = func; - cell->func.data = func_data; - return cell; -} - -static void -_cell_del(Elm_Grid_Cell *cell) -{ - if (cell->selected) - cell->wd->selected = eina_list_remove(cell->wd->selected, cell); - if (cell->realized) _cell_unrealize(cell); - if ((!cell->delete_me) && (cell->gcc->func.del)) - cell->gcc->func.del(cell->data, cell->wd->self); - cell->delete_me = EINA_TRUE; - cell->wd->cells = eina_list_remove(cell->wd->cells, cell); - if (cell->long_timer) ecore_timer_del(cell->long_timer); - free(cell); -} - -static void -_cell_select(Elm_Grid_Cell *cell) -{ - if ((cell->wd->no_select) || (cell->delete_me)) return; - if (cell->selected) - { - if (cell->wd->always_select) goto call; - return; - } - cell->selected = EINA_TRUE; - cell->wd->selected = eina_list_append(cell->wd->selected, cell); -call: - cell->walking++; - if (cell->func.func) cell->func.func((void *)cell->func.data, cell->wd->self, - cell); - if (!cell->delete_me) - evas_object_smart_callback_call(cell->wd->self, "selected", cell); - cell->walking--; - if ((cell->walking == 0) && (cell->delete_me)) - if (cell->relcount == 0) _cell_del(cell); -} - -static void -_cell_unselect(Elm_Grid_Cell *cell) -{ - if ((cell->delete_me) || (!cell->hilighted)) return; - edje_object_signal_emit(cell->base, "elm,state,unselected", "elm"); - cell->hilighted = EINA_FALSE; - if (cell->selected) - { - cell->selected = EINA_FALSE; - cell->wd->selected = eina_list_remove(cell->wd->selected, cell); - evas_object_smart_callback_call(cell->wd->self, "unselected", cell); - } -} - -static void -_calc_job(void *data) -{ - Widget_Data *wd = data; - Evas_Coord minw = 0, minh = 0, nmax = 0, cvw, cvh; - int count; - - evas_object_geometry_get(wd->self, NULL, NULL, &cvw, &cvh); - if (wd->horizontal && wd->cell_height) - nmax = cvh / wd->cell_height; - else if (wd->cell_width) - nmax = cvw / wd->cell_width; - - if (nmax) - { - count = eina_list_count(wd->cells); - if (wd->horizontal) - { - minw = ceil(count / (float)nmax) * wd->cell_width; - minh = nmax * wd->cell_height; - } - else - { - minw = nmax * wd->cell_width; - minh = ceil(count / (float)nmax) * wd->cell_height; - } - } - - if ((minw != wd->minw) || (minh != wd->minh)) - { - wd->minh = minh; - wd->minw = minw; - evas_object_smart_callback_call(wd->pan_smart, "changed", NULL); - } - - wd->nmax = nmax; - wd->calc_job = NULL; - evas_object_smart_changed(wd->pan_smart); -} - -static void -_pan_add(Evas_Object *obj) -{ - Pan *sd; - Evas_Object_Smart_Clipped_Data *cd; - - _pan_sc.add(obj); - cd = evas_object_smart_data_get(obj); - sd = ELM_NEW(Pan); - if (!sd) return; - sd->__clipped_data = *cd; - free(cd); - evas_object_smart_data_set(obj, sd); -} - -static void -_pan_del(Evas_Object *obj) -{ - Pan *sd = evas_object_smart_data_get(obj); - - if (!sd) return; - _pan_sc.del(obj); -} - -static void -_pan_set(Evas_Object *obj, Evas_Coord x, Evas_Coord y) -{ - Pan *sd = evas_object_smart_data_get(obj); - if ((x == sd->wd->pan_x) && (y == sd->wd->pan_y)) return; - sd->wd->pan_x = x; - sd->wd->pan_y = y; - evas_object_smart_changed(obj); -} - -static void -_pan_get(Evas_Object *obj, Evas_Coord *x, Evas_Coord *y) -{ - Pan *sd = evas_object_smart_data_get(obj); - if (x) *x = sd->wd->pan_x; - if (y) *y = sd->wd->pan_y; -} - -static void -_pan_child_size_get(Evas_Object *obj, Evas_Coord *w, Evas_Coord *h) -{ - Pan *sd = evas_object_smart_data_get(obj); - if (w) *w = sd->wd->minw; - if (h) *h = sd->wd->minh; -} - -static void -_pan_max_get(Evas_Object *obj, Evas_Coord *x, Evas_Coord *y) -{ - Pan *sd = evas_object_smart_data_get(obj); - Evas_Coord ow, oh; - - if (!sd) return; - evas_object_geometry_get(obj, NULL, NULL, &ow, &oh); - if (x) - *x = (ow < sd->wd->minw) ? sd->wd->minw - ow : 0; - if (y) - *y = (oh < sd->wd->minh) ? sd->wd->minh - oh : 0; -} - -static void -_pan_resize(Evas_Object *obj, Evas_Coord w, Evas_Coord h) -{ - Pan *sd = evas_object_smart_data_get(obj); - Evas_Coord ow, oh; - - evas_object_geometry_get(obj, NULL, NULL, &ow, &oh); - if ((ow == w) && (oh == h)) return; - if (sd->wd->calc_job) ecore_job_del(sd->wd->calc_job); - sd->wd->calc_job = ecore_job_add(_calc_job, sd->wd); -} - -static void -_pan_calculate(Evas_Object *obj) -{ - Pan *sd = evas_object_smart_data_get(obj); - Evas_Coord cx = 0, cy = 0; - Eina_List *l; - Elm_Grid_Cell *cell; - - if (!sd) return; - if (!sd->wd->nmax) return; - - EINA_LIST_FOREACH(sd->wd->cells, l, cell) - { - _cell_place(cell, cx, cy); - if (sd->wd->horizontal) - { - cy = (cy + 1) % sd->wd->nmax; - if (!cy) cx++; - } - else - { - cx = (cx + 1) % sd->wd->nmax; - if (!cx) cy++; - } - } -} - -static void -_pan_move(Evas_Object *obj, Evas_Coord x __UNUSED__, Evas_Coord y __UNUSED__) -{ - Pan *sd = evas_object_smart_data_get(obj); - if (!sd) return; - if (sd->wd->calc_job) ecore_job_del(sd->wd->calc_job); - sd->wd->calc_job = ecore_job_add(_calc_job, sd->wd); -} - -static void -_hold_on(void *data __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__) -{ - Widget_Data *wd = elm_widget_data_get(obj); - if (!wd) return; - elm_smart_scroller_hold_set(wd->scr, 1); -} - -static void -_hold_off(void *data __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__) -{ - Widget_Data *wd = elm_widget_data_get(obj); - if (!wd) return; - elm_smart_scroller_hold_set(wd->scr, 0); -} - -static void -_freeze_on(void *data __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__) -{ - Widget_Data *wd = elm_widget_data_get(obj); - if (!wd) return; - elm_smart_scroller_freeze_set(wd->scr, 1); -} - -static void -_freeze_off(void *data __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__) -{ - Widget_Data *wd = elm_widget_data_get(obj); - if (!wd) return; - elm_smart_scroller_freeze_set(wd->scr, 0); -} - -static void -_scr_drag_start(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__) -{ - evas_object_smart_callback_call(data, "scroll,drag,start", NULL); -} - -static void -_scr_drag_stop(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__) -{ - evas_object_smart_callback_call(data, "scroll,drag,stop", NULL); -} - -static void -_scr_scroll(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__) -{ - evas_object_smart_callback_call(data, "scroll", NULL); -} - -/** - * Add a new Scrolled Grid object. - * - * @param parent The parent object. - * @return The new object or NULL if it cannot be created. - * - * @see elm_scrolled_grid_cell_size_set() - * @see elm_scrolled_grid_horizontal_set() - * @see elm_scrolled_grid_cell_add() - * @see elm_scrolled_grid_cell_del() - * @see elm_scrolled_grid_clear() - * - * @ingroup Grid - */ -EAPI Evas_Object * -elm_scrolled_grid_add(Evas_Object *parent) -{ - Evas_Object *obj; - Evas *e; - Evas_Coord minw, minh; - Widget_Data *wd; - static Evas_Smart *smart = NULL; - - wd = ELM_NEW(Widget_Data); - e = evas_object_evas_get(parent); - obj = elm_widget_add(e); - ELM_SET_WIDTYPE(widtype, "grid"); - elm_widget_type_set(obj, "grid"); - elm_widget_sub_object_add(parent, obj); - elm_widget_data_set(obj, wd); - elm_widget_del_hook_set(obj, _del_hook); - elm_widget_theme_hook_set(obj, _theme_hook); - - wd->scr = elm_smart_scroller_add(e); - elm_smart_scroller_widget_set(wd->scr, obj); - elm_smart_scroller_object_theme_set(obj, wd->scr, "grid", "base", "default"); - elm_widget_resize_object_set(obj, wd->scr); - - evas_object_smart_callback_add(wd->scr, "drag,start", _scr_drag_start, obj); - evas_object_smart_callback_add(wd->scr, "drag,stop", _scr_drag_stop, obj); - evas_object_smart_callback_add(wd->scr, "scroll", _scr_scroll, obj); - - elm_smart_scroller_bounce_allow_set(wd->scr, 1, 1); - - wd->self = obj; - wd->align_x = 0.5; - wd->align_y = 0.5; - - evas_object_smart_callback_add(obj, "scroll-hold-on", _hold_on, obj); - evas_object_smart_callback_add(obj, "scroll-hold-off", _hold_off, obj); - evas_object_smart_callback_add(obj, "scroll-freeze-on", _freeze_on, obj); - evas_object_smart_callback_add(obj, "scroll-freeze-off", _freeze_off, obj); - - if (!smart) - { - static Evas_Smart_Class sc; - - evas_object_smart_clipped_smart_set(&_pan_sc); - sc = _pan_sc; - sc.name = "elm_scrolled_grid_pan"; - sc.version = EVAS_SMART_CLASS_VERSION; - sc.add = _pan_add; - sc.del = _pan_del; - sc.resize = _pan_resize; - sc.move = _pan_move; - sc.calculate = _pan_calculate; - smart = evas_smart_class_new(&sc); - } - if (smart) - { - wd->pan_smart = evas_object_smart_add(e, smart); - wd->pan = evas_object_smart_data_get(wd->pan_smart); - wd->pan->wd = wd; - } - - elm_smart_scroller_extern_pan_set(wd->scr, wd->pan_smart, - _pan_set, _pan_get, - _pan_max_get, _pan_child_size_get); - - return obj; -} - -/** - * Set the size for the cell of the Grid. - * - * @param obj The Grid object. - * @param w The cell's width. - * @param h The cell's height; - * - * @see elm_scrolled_grid_cell_size_get() - * - * @ingroup Grid - */ -EAPI void -elm_scrolled_grid_cell_size_set(Evas_Object *obj, Evas_Coord w, Evas_Coord h) -{ - ELM_CHECK_WIDTYPE(obj, widtype); - Widget_Data *wd = elm_widget_data_get(obj); - if (!wd) return; - if (wd->cell_width == w && wd->cell_height == h) return; - wd->cell_width = w; - wd->cell_height = h; - if (wd->calc_job) ecore_job_del(wd->calc_job); - wd->calc_job = ecore_job_add(_calc_job, wd); -} - -/** - * Get the size of the cell of the Grid. - * - * @param obj The Grid object. - * @param w Pointer to the cell's width. - * @param h Pointer to the cell's height. - * - * @see elm_scrolled_grid_cell_size_get() - * - * @ingroup Grid - */ -EAPI void -elm_scrolled_grid_cell_size_get(const Evas_Object *obj, Evas_Coord *w, Evas_Coord *h) -{ - ELM_CHECK_WIDTYPE(obj, widtype); - Widget_Data *wd = elm_widget_data_get(obj); - if (!wd) return; - if (w) *w = wd->cell_width; - if (h) *h = wd->cell_height; -} - -/** - * Set cell's alignment within the scroller. - * - * @param obj The grid object. - * @param align_x The x alignment (0 <= x <= 1). - * @param align_y The y alignment (0 <= y <= 1). - * - * @see elm_scrolled_grid_align_get() - * - * @ingroup Grid - */ -EAPI void -elm_scrolled_grid_align_set(Evas_Object *obj, double align_x, double align_y) -{ - ELM_CHECK_WIDTYPE(obj, widtype); - Widget_Data *wd = elm_widget_data_get(obj); - - if (align_x > 1.0) - align_x = 1.0; - else if (align_x < 0.0) - align_x = 0.0; - wd->align_x = align_x; - - if (align_y > 1.0) - align_y = 1.0; - else if (align_y < 0.0) - align_y = 0.0; - wd->align_y = align_y; -} - -/** - * Get the alignenment set for the grid object. - * - * @param obj The grid object. - * @param align_x Pointer to x alignenment. - * @param align_y Pointer to y alignenment. - * - * @see elm_scrolled_grid_align_set() - * - * @ingroup Grid - */ -EAPI void -elm_scrolled_grid_align_get(const Evas_Object *obj, double *align_x, double *align_y) -{ - ELM_CHECK_WIDTYPE(obj, widtype); - Widget_Data *wd = elm_widget_data_get(obj); - if (align_x) *align_x = wd->align_x; - if (align_y) *align_y = wd->align_y; -} - -/** - * Add cell to the end of the Grid. - * - * @param obj The Grid object. - * @param gcc The cell class for the cell. - * @param data The cell data. - * @param func Convenience function called when cell is selected. - * @param func_data Data passed to @p func above. - * @return A handle to the cell added or NULL if not possible. - * - * @see elm_scrolled_grid_cell_del() - * - * @ingroup Grid - */ -EAPI Elm_Grid_Cell * -elm_scrolled_grid_cell_add(Evas_Object *obj, const Elm_Grid_Cell_Class *gcc, - const void *data, Evas_Smart_Cb func, - const void *func_data) -{ - Elm_Grid_Cell *cell; - ELM_CHECK_WIDTYPE(obj, widtype) NULL; - Widget_Data *wd = elm_widget_data_get(obj); - if (!wd) return NULL; - - cell = _cell_create(wd, gcc, data, func, func_data); - if (!cell) return NULL; - - wd->cells = eina_list_append(wd->cells, cell); - wd->no_select = EINA_FALSE; - - if (wd->calc_job) ecore_job_del(wd->calc_job); - wd->calc_job = ecore_job_add(_calc_job, wd); - - return cell; -} - -/** - * Remove a cell from the Grid. - * - * @param cell The cell to be removed. - * @return @c EINA_TRUE on success or @c EINA_FALSE otherwise. - * - * @see elm_scrolled_grid_clear() to remove all cells of the grid. - * - * @ingroup Grid - */ -EAPI void -elm_scrolled_grid_cell_del(Elm_Grid_Cell *cell) -{ - if (!cell) return; - if ((cell->relcount > 0) || (cell->walking > 0)) - { - cell->delete_me = EINA_TRUE; - if (cell->selected) - cell->wd->selected = eina_list_remove(cell->wd->selected, cell); - if (cell->gcc->func.del) cell->gcc->func.del(cell->data, cell->wd->self); - return; - } - - _cell_del(cell); - - if (cell->wd->calc_job) ecore_job_del(cell->wd->calc_job); - cell->wd->calc_job = ecore_job_add(_calc_job, cell->wd); -} - -/** - * Set for what direction the grid will expand. - * - * @param obj The Grid object. - * @param setting If @c EINA_TRUE the grid will expand horizontally or - * vertically if @c EINA_FALSE. - * - * @ingroup Grid - */ -EAPI void -elm_scrolled_grid_horizontal_set(Evas_Object *obj, Eina_Bool setting) -{ - ELM_CHECK_WIDTYPE(obj, widtype); - Widget_Data *wd = elm_widget_data_get(obj); - if (!wd) return; - if (setting == wd->horizontal) return; - wd->horizontal = setting; - - /* Update the cells to conform to the new layout */ - if (wd->calc_job) ecore_job_del(wd->calc_job); - wd->calc_job = ecore_job_add(_calc_job, wd); -} - -/** - * Clear the Grid - * - * This clears all cells in the grid, leaving it empty. - * - * @param obj The Grid object. - * - * @see elm_scrolled_grid_cell_del() to remove just one cell. - * - * @ingroup Grid - */ -EAPI void -elm_scrolled_grid_clear(Evas_Object *obj) -{ - Eina_List *l, *l_next; - Elm_Grid_Cell *cell; - ELM_CHECK_WIDTYPE(obj, widtype); - Widget_Data *wd = elm_widget_data_get(obj); - if (!wd) return; - - if (wd->calc_job) - { - ecore_job_del(wd->calc_job); - wd->calc_job = NULL; - } - - EINA_LIST_FOREACH_SAFE(wd->cells, l, l_next, cell) - { - if (cell->realized) _cell_unrealize(cell); - if (cell->gcc->func.del) cell->gcc->func.del(cell->data, wd->self); - if (cell->long_timer) ecore_timer_del(cell->long_timer); - free(cell); - wd->cells = eina_list_remove_list(wd->cells, l); - } - - if (wd->selected) - { - eina_list_free(wd->selected); - wd->selected = NULL; - } - - wd->pan_x = 0; - wd->pan_y = 0; - wd->minw = 0; - wd->minh = 0; - evas_object_size_hint_min_set(wd->pan_smart, wd->minw, wd->minh); - evas_object_smart_callback_call(wd->pan_smart, "changed", NULL); -} - -/** - * Get the real evas object of the grid cell - * - * This returns the actual evas object used for the specified grid cell. - * This may be NULL as it may not be created, and may be deleted at any time - * by grid. Do not modify this object (move, resize, show, hide etc.) as grid - * is controlling it. This function is for querying, emitting custom signals - * or hooking lower level callbacks for events. Do not delete this object - * under any circumstances. - * - * @param cell The Grid cell. - * @return the evas object associated to this cell. - * - * @see elm_scrolled_grid_cell_data_get() - * - * @ingroup Grid - */ -EAPI const Evas_Object * -elm_scrolled_grid_cell_object_get(Elm_Grid_Cell *cell) -{ - if (!cell) return NULL; - return cell->base; -} - -/** - * Returns the data associated to a cell - * - * This returns the data value passed on the elm_scrolled_grid_cell_add() and - * related cell addition calls. - * - * @param cell The Grid cell. - * @return the data associated to this cell. - * - * @see elm_scrolled_grid_cell_add() - * @see elm_scrolled_grid_cell_object_get() - * - * @ingroup Grid - */ -EAPI void * -elm_scrolled_grid_cell_data_get(Elm_Grid_Cell *cell) -{ - if (!cell) return NULL; - return (void *)cell->data; -} - -/** - * Get the cell's coordinates. - * - * This returns the logical position of the cell whithin the Grid. - * - * @param cell The Grid cell. - * @param x The x-axis coordinate pointer. - * @param y The y-axis coordinate pointer. - * - * @ingroup Grid - */ -EAPI void -elm_scrolled_grid_cell_pos_get(const Elm_Grid_Cell *cell, unsigned int *x, unsigned int *y) -{ - if (!cell) return; - if (x) *x = cell->x; - if (y) *y = cell->y; -} - -/** - * Enable or disable multi-select in the grid. - * - * This enables (EINA_TRUE) or disables (EINA_FALSE) multi-select in the grid. - * This allows more than 1 cell to be selected. - * - * @param obj The grid object. - * @param multi Multi-select enabled/disabled - * - * @ingroup Grid - */ -EAPI void -elm_scrolled_grid_multi_select_set(Evas_Object *obj, Eina_Bool multi) -{ - ELM_CHECK_WIDTYPE(obj, widtype); - Widget_Data *wd = elm_widget_data_get(obj); - if (!wd) return; - wd->multi = multi; -} - -/** - * Get if multi-select in grid is enabled or disabled - * - * @param obj The grid object - * @return Multi-select enable/disable - * (EINA_TRUE = enabled / EINA_FALSE = disabled) - * - * @ingroup Grid - */ -EAPI Eina_Bool -elm_scrolled_grid_multi_select_get(const Evas_Object *obj) -{ - ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE; - Widget_Data *wd = elm_widget_data_get(obj); - if (!wd) return EINA_FALSE; - return wd->multi; -} - -/** - * Get the selected cell in the grid - * - * This gets the selected cell in the grid (if multi-select is enabled only - * the first cell in the list is selected - which is not very useful, so see - * elm_scrolled_grid_selected_cells_get() for when multi-select is used). - * - * If no cell is selected, NULL is returned. - * - * @param obj The grid object. - * @return The selected cell, or NULL if none. - * - * @ingroup Grid - */ -EAPI Elm_Grid_Cell * -elm_scrolled_grid_selected_cell_get(const Evas_Object *obj) -{ - ELM_CHECK_WIDTYPE(obj, widtype) NULL; - Widget_Data *wd = elm_widget_data_get(obj); - if (!wd) return NULL; - if (wd->selected) return wd->selected->data; - return NULL; -} - -/** - * Get a list of selected cells in the grid. - * - * This returns a list of the selected cells. This list pointer is only valid - * so long as no cells are selected or unselected (or unselected implictly by - * deletion). The list contains Elm_Grid_Cell pointers. - * - * @param obj The grid object. - * @return The list of selected cells, or NULL if none are selected. - * - * @ingroup Grid - */ -EAPI const Eina_List * -elm_scrolled_grid_selected_cells_get(const Evas_Object *obj) -{ - ELM_CHECK_WIDTYPE(obj, widtype) NULL; - Widget_Data *wd = elm_widget_data_get(obj); - if (!wd) return NULL; - return wd->selected; -} - -/** - * Set the selected state of a cell. - * - * This sets the selected state of a cell. If multi-select is not enabled and - * selected is EINA_TRUE, previously selected cells are unselected. - * - * @param cell The cell - * @param selected The selected state. - * - * @ingroup Grid - */ -EAPI void -elm_scrolled_grid_cell_selected_set(Elm_Grid_Cell *cell, Eina_Bool selected) -{ - Widget_Data *wd = elm_widget_data_get(cell->wd->self); - if (!wd) return; - if (!cell || cell->delete_me) return; - selected = !!selected; - if (cell->selected == selected) return; - - if (selected) - { - if (!wd->multi) - { - while (wd->selected) - _cell_unselect(wd->selected->data); - } - _cell_hilight(cell); - _cell_select(cell); - } - else - _cell_unselect(cell); -} - -/** - * Get the selected state of a cell. - * - * This gets the selected state of a cell (1 selected, 0 not selected). - * - * @param cell The cell - * @return The selected state - * - * @ingroup Grid - */ -EAPI Eina_Bool -elm_scrolled_grid_cell_selected_get(const Elm_Grid_Cell *cell) -{ - if (!cell) return EINA_FALSE; - return cell->selected; -} - -/** - * Sets the disabled state of a cell. - * - * A disabled cell cannot be selected or unselected. It will also change - * appearance to disabled. This sets the disabled state (1 disabled, 0 not - * disabled). - * - * @param cell The cell - * @param disabled The disabled state - * - * @ingroup Grid - */ -EAPI void -elm_scrolled_grid_cell_disabled_set(Elm_Grid_Cell *cell, Eina_Bool disabled) -{ - if (!cell) return; - if (cell->disabled == disabled) return; - if (cell->delete_me) return; - cell->disabled = disabled; - if (cell->realized) - { - if (cell->disabled) - edje_object_signal_emit(cell->base, "elm,state,disabled", "elm"); - else - edje_object_signal_emit(cell->base, "elm,state,enabled", "elm"); - } -} - -/** - * Get the disabled state of a cell. - * - * This gets the disabled state of the given cell. - * - * @param cell The cell - * @return The disabled state - * - * @ingroup Grid - */ -EAPI Eina_Bool -elm_scrolled_grid_cell_disabled_get(const Elm_Grid_Cell *cell) -{ - if (!cell) return EINA_FALSE; - if (cell->delete_me) return EINA_FALSE; - return cell->disabled; -} - -/** - * Set the always select mode. - * - * Cells will only call their selection func and callback when first becoming - * selected. Any further clicks will do nothing, unless you enable always select - * with elm_scrolled_grid_always_select_mode_set(). This means even if selected, - * every click will make the selected callbacks be called. - * - * @param obj The grid object - * @param always_select The always select mode (EINA_TRUE = on, EINA_FALSE = off) - * - * @ingroup Grid - */ -EAPI void -elm_scrolled_grid_always_select_mode_set(Evas_Object *obj, Eina_Bool always_select) -{ - ELM_CHECK_WIDTYPE(obj, widtype); - Widget_Data *wd = elm_widget_data_get(obj); - if (!wd) return; - wd->always_select = always_select; -} - -/** - * Get the always select mode. - * - * @param obj The grid object. - * @return The always select mode (EINA_TRUE = on, EINA_FALSE = off) - * - * @ingroup Grid - */ -EAPI Eina_Bool -elm_scrolled_grid_always_select_mode_get(const Evas_Object *obj) -{ - ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE; - Widget_Data *wd = elm_widget_data_get(obj); - if (!wd) return EINA_FALSE; - return wd->always_select; -} - -/** - * Set no select mode. - * - * This will turn off the ability to select items entirely and they will - * neither appear selected nor call selected callback functions. - * - * @param obj The grid object - * @param no_select The no select mode (EINA_TRUE = on, EINA_FALSE = off) - * - * @ingroup Grid - */ -EAPI void -elm_scrolled_grid_no_select_mode_set(Evas_Object *obj, Eina_Bool no_select) -{ - ELM_CHECK_WIDTYPE(obj, widtype); - Widget_Data *wd = elm_widget_data_get(obj); - if (!wd) return; - wd->no_select = no_select; -} - -/** - * Gets no select mode. - * - * @param obj The grid object - * @return The no select mode (EINA_TRUE = on, EINA_FALSE = off) - * - * @ingroup Grid - */ -EAPI Eina_Bool -elm_scrolled_grid_no_select_mode_get(const Evas_Object *obj) -{ - ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE; - Widget_Data *wd = elm_widget_data_get(obj); - if (!wd) return EINA_FALSE; - return wd->no_select; -} - -/** - * Set bounce mode. - * - * This will enable or disable the scroller bounce mode for the grid. See - * elm_scroller_bounce_set() for details. - * - * @param obj The grid object - * @param h_bounce Allow bounce horizontally - * @param v_bounce Allow bounce vertically - * - * @ingroup Grid - */ -EAPI void -elm_scrolled_grid_bounce_set(Evas_Object *obj, Eina_Bool h_bounce, Eina_Bool v_bounce) -{ - ELM_CHECK_WIDTYPE(obj, widtype); - Widget_Data *wd = elm_widget_data_get(obj); - if (!wd) return; - elm_smart_scroller_bounce_allow_set(wd->scr, h_bounce, v_bounce); -} - -/** - * Get the bounce mode - * - * @param obj The grid object - * @param h_bounce Allow bounce horizontally - * @param v_bounce Allow bounce vertically - * - * @ingroup Grid - */ -EAPI void -elm_scrolled_grid_bounce_get(const Evas_Object *obj, Eina_Bool *h_bounce, Eina_Bool *v_bounce) -{ - ELM_CHECK_WIDTYPE(obj, widtype); - Widget_Data *wd = elm_widget_data_get(obj); - if (!wd) return; - elm_smart_scroller_bounce_allow_get(wd->scr, h_bounce, v_bounce); -}