ami pushed a commit to branch master. http://git.enlightenment.org/core/efl.git/commit/?id=0bcb0302fb647b43cef4e633cf0c0b16e5521e04
commit 0bcb0302fb647b43cef4e633cf0c0b16e5521e04 Author: Amitesh Singh <[email protected]> Date: Thu Nov 3 12:00:17 2016 +0530 efl_ui_clock: Merge datetime/dayselector/clock widgets into efl_ui_clock. Summary: Datetime widget is module based, so datetime widget is used as base for efl_ui_clock and merged dayselector/clock features into efl_ui_clock. Added day selection and seconds support in efl_ui_clock. Added clock features like auto updation of time, stop timer etc in efl_ui_clock. Added API to enable/disable edit_mode. efl_ui_clock can be configurable to display either only day/date/time or display any two of them or display all three. Added efl_ui_clock.c and test_ui_clock.c. Theme and Module is added in another patch by Amitesh. Original author is Yeshwanth <[email protected]>. I have polished this patch a bit and make it compatible with current EFL code. Test Plan: test_ui_clock Reviewers: bu5hm4n, tasn, yashu21985, jpeg, cedric, raster Subscribers: CHAN, woohyun Differential Revision: https://phab.enlightenment.org/D3938 --- config/default/base.src.in | 4 +- config/mobile/base.src.in | 4 +- config/standard/base.src.in | 4 +- src/Makefile_Elementary.am | 3 + src/bin/elementary/Makefile.am | 1 + src/bin/elementary/test.c | 2 + src/bin/elementary/test_ui_clock.c | 117 +++ src/lib/elementary/Elementary.h | 1 + src/lib/elementary/efl_ui_clock.c | 1189 +++++++++++++++++++++++++++++ src/lib/elementary/efl_ui_clock.eo | 260 +++++++ src/lib/elementary/efl_ui_clock.h | 204 +++++ src/lib/elementary/efl_ui_clock_private.h | 130 ++++ src/lib/elementary/elm_config.c | 4 + 13 files changed, 1917 insertions(+), 6 deletions(-) diff --git a/config/default/base.src.in b/config/default/base.src.in index 90188f3..9f27fd4 100644 --- a/config/default/base.src.in +++ b/config/default/base.src.in @@ -1,5 +1,5 @@ group "Elm_Config" struct { - value "config_version" int: 131084; + value "config_version" int: 131085; value "engine" string: ""; value "vsync" uchar: 0; value "thumbscroll_enable" uchar: 1; @@ -39,7 +39,7 @@ group "Elm_Config" struct { value "finger_size" int: 10; value "fps" double: 60.0; value "theme" string: "default"; - value "modules" string: "prefs>prefs_iface:access_output>access/api:datetime_input_ctxpopup>datetime/api"; + value "modules" string: "prefs>prefs_iface:access_output>access/api:datetime_input_ctxpopup>datetime/api:clock_input_ctxpopup>clock/api"; value "tooltip_delay" double: 1.0; value "cursor_engine_only" uchar: 0; value "focus_highlight_enable" uchar: 0; diff --git a/config/mobile/base.src.in b/config/mobile/base.src.in index 5619970..fedba06 100644 --- a/config/mobile/base.src.in +++ b/config/mobile/base.src.in @@ -1,5 +1,5 @@ group "Elm_Config" struct { - value "config_version" int: 131084; + value "config_version" int: 131085; value "engine" string: ""; value "vsync" uchar: 0; value "thumbscroll_enable" uchar: 1; @@ -39,7 +39,7 @@ group "Elm_Config" struct { value "finger_size" int: 40; value "fps" double: 60.0; value "theme" string: "default"; - value "modules" string: "prefs>prefs_iface:access_output>access/api:datetime_input_ctxpopup>datetime/api"; + value "modules" string: "prefs>prefs_iface:access_output>access/api:datetime_input_ctxpopup>datetime/api:clock_input_ctxpopup>clock/api"; value "tooltip_delay" double: 1.0; value "cursor_engine_only" uchar: 0; value "focus_highlight_enable" uchar: 0; diff --git a/config/standard/base.src.in b/config/standard/base.src.in index 849185f..b201a3e 100644 --- a/config/standard/base.src.in +++ b/config/standard/base.src.in @@ -1,5 +1,5 @@ group "Elm_Config" struct { - value "config_version" int: 131084; + value "config_version" int: 131085; value "engine" string: ""; value "vsync" uchar: 0; value "thumbscroll_enable" uchar: 0; @@ -39,7 +39,7 @@ group "Elm_Config" struct { value "finger_size" int: 10; value "fps" double: 60.0; value "theme" string: "default"; - value "modules" string: "prefs>prefs_iface:access_output>access/api:datetime_input_ctxpopup>datetime/api"; + value "modules" string: "prefs>prefs_iface:access_output>access/api:datetime_input_ctxpopup>datetime/api:clock_input_ctxpopup>clock/api"; value "tooltip_delay" double: 1.0; value "cursor_engine_only" uchar: 0; value "focus_highlight_enable" uchar: 0; diff --git a/src/Makefile_Elementary.am b/src/Makefile_Elementary.am index dd78134..d2beac4 100644 --- a/src/Makefile_Elementary.am +++ b/src/Makefile_Elementary.am @@ -121,6 +121,7 @@ elm_public_eolian_files = \ # Private classes (not exposed or shipped) elm_private_eolian_files = \ lib/elementary/efl_ui_internal_text_interactive.eo \ + lib/elementary/efl_ui_clock.eo \ $(NULL) # Legacy classes - not part of public EO API @@ -662,6 +663,7 @@ lib_elementary_libelementary_la_SOURCES = \ lib/elementary/efl_ui_grid_static.c \ lib/elementary/efl_ui_grid_private.h \ lib/elementary/efl_ui_text.c \ + lib/elementary/efl_ui_clock.c \ $(NULL) @@ -823,6 +825,7 @@ bin/elementary/test_tooltip.c \ bin/elementary/test_transit.c \ bin/elementary/test_transit_bezier.c \ bin/elementary/test_ui_box.c \ +bin/elementary/test_ui_clock.c \ bin/elementary/test_ui_grid.c \ bin/elementary/test_video.c \ bin/elementary/test_weather.c \ diff --git a/src/bin/elementary/Makefile.am b/src/bin/elementary/Makefile.am index 8f64a68..3bb7ea9 100644 --- a/src/bin/elementary/Makefile.am +++ b/src/bin/elementary/Makefile.am @@ -125,6 +125,7 @@ test_toolbar.c \ test_tooltip.c \ test_transit.c \ test_transit_bezier.c \ +test_ui_clock.c \ test_video.c \ test_weather.c \ test_web.c \ diff --git a/src/bin/elementary/test.c b/src/bin/elementary/test.c index 0c3fa6c..36f2e14 100644 --- a/src/bin/elementary/test.c +++ b/src/bin/elementary/test.c @@ -254,6 +254,7 @@ void test_naviframe3(void *data, Evas_Object *obj, void *event_info); void test_naviframe_complex(void *data, Evas_Object *obj, void *event_info); //void test_factory(void *data, Evas_Object *obj, void *event_info); void test_datetime(void *data, Evas_Object *obj, void *event_info); +void test_ui_clock(void *data, Evas_Object *obj, void *event_info); void test_popup(void *data, Evas_Object *obj, void *event_info); void test_dayselector(void *data, Evas_Object *obj, void *event_info); void test_image(void *data, Evas_Object *obj, void *event_info); @@ -917,6 +918,7 @@ add_tests: ADD_TEST(NULL, "Times & Dates", "Clock Edit 2", test_clock_edit2); ADD_TEST(NULL, "Times & Dates", "Clock Pause", test_clock_pause); ADD_TEST(NULL, "Times & Dates", "Datetime", test_datetime); + ADD_TEST(NULL, "Times & Dates", "Ui.Clock", test_ui_clock); //------------------------------// ADD_TEST(NULL, "Text", "Label", test_label); diff --git a/src/bin/elementary/test_ui_clock.c b/src/bin/elementary/test_ui_clock.c new file mode 100644 index 0000000..47f7f6c --- /dev/null +++ b/src/bin/elementary/test_ui_clock.c @@ -0,0 +1,117 @@ +#ifdef HAVE_CONFIG_H +#include "elementary_config.h" +#endif +#include <Elementary.h> + +/* A simple test, just displaying clock in its default format */ + +Evas_Object *dt1, *dt2, *dt3, *dt4; + +static void +_changed_cb(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + printf("Clock value is changed\n"); +} + +static void +_bt_clicked(void *data EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED) +{ + time_t t; + struct tm new_time; + + t = time(NULL); + localtime_r(&t, &new_time); + + new_time.tm_year = 85; + new_time.tm_mon = 9; + new_time.tm_mday = 26; + new_time.tm_hour = 9; + new_time.tm_min = 0; + efl_ui_clock_field_visible_set(dt1, EFL_UI_CLOCK_TYPE_HOUR, EINA_TRUE); + efl_ui_clock_field_visible_set(dt1, EFL_UI_CLOCK_TYPE_MINUTE, EINA_TRUE); + efl_ui_clock_field_visible_set(dt1, EFL_UI_CLOCK_TYPE_AMPM, EINA_TRUE); + efl_ui_clock_field_visible_set(dt1, EFL_UI_CLOCK_TYPE_SECOND, EINA_TRUE); + efl_ui_clock_field_visible_set(dt1, EFL_UI_CLOCK_TYPE_DAY, EINA_TRUE); + + efl_ui_clock_value_set(dt1, &new_time); + elm_object_disabled_set(dt1, EINA_TRUE); + + elm_object_disabled_set(obj, EINA_TRUE); + evas_object_del(dt2); + evas_object_del(dt3); + dt2 = dt3 = NULL; +} + +void +test_ui_clock(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + Evas_Object *win, *bx, *bt, *lb; + + win = elm_win_util_standard_add("ui_clock", "ui_clock"); + elm_win_autodel_set(win, EINA_TRUE); + + bx = elm_box_add(win); + evas_object_size_hint_weight_set(bx, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + elm_win_resize_object_add(win, bx); + elm_box_horizontal_set(bx, EINA_FALSE); + evas_object_show(bx); + evas_object_size_hint_min_set(bx, 360, 240); + + dt1 = efl_add(EFL_UI_CLOCK_CLASS, bx); + evas_object_size_hint_weight_set(dt1, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(dt1, EVAS_HINT_FILL, 0.5); + efl_ui_clock_field_visible_set(dt1, EFL_UI_CLOCK_TYPE_HOUR, EINA_FALSE); + efl_ui_clock_field_visible_set(dt1, EFL_UI_CLOCK_TYPE_MINUTE, EINA_FALSE); + efl_ui_clock_field_visible_set(dt1, EFL_UI_CLOCK_TYPE_AMPM, EINA_FALSE); + efl_ui_clock_field_visible_set(dt1, EFL_UI_CLOCK_TYPE_SECOND, EINA_FALSE); + efl_ui_clock_field_visible_set(dt1, EFL_UI_CLOCK_TYPE_DAY, EINA_FALSE); + efl_ui_clock_pause_set(dt1, EINA_TRUE); + elm_box_pack_end(bx, dt1); + evas_object_smart_callback_add(dt1, "changed", _changed_cb, NULL); + evas_object_show(dt1); + + dt2 = efl_add(EFL_UI_CLOCK_CLASS, bx); + evas_object_size_hint_weight_set(dt2, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(dt2, EVAS_HINT_FILL, 0.5); + efl_ui_clock_field_visible_set(dt2, EFL_UI_CLOCK_TYPE_YEAR, EINA_FALSE); + efl_ui_clock_field_visible_set(dt2, EFL_UI_CLOCK_TYPE_MONTH, EINA_FALSE); + efl_ui_clock_field_visible_set(dt2, EFL_UI_CLOCK_TYPE_DATE, EINA_FALSE); + efl_ui_clock_field_visible_set(dt1, EFL_UI_CLOCK_TYPE_SECOND, EINA_FALSE); + elm_box_pack_end(bx, dt2); + efl_ui_clock_pause_set(dt2, EINA_TRUE); + elm_object_disabled_set(dt2, EINA_TRUE); + evas_object_show(dt2); + + dt3 = efl_add(EFL_UI_CLOCK_CLASS, bx); + evas_object_size_hint_weight_set(dt3, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(dt3, EVAS_HINT_FILL, 0.5); + elm_box_pack_end(bx, dt3); + evas_object_show(dt3); + + //editable + lb = efl_add(ELM_LABEL_CLASS, bx); + elm_object_text_set(lb, + "<b>Editable Clock:</b>" + ); + evas_object_size_hint_weight_set(lb, 0.0, 0.0); + evas_object_size_hint_align_set(lb, 0, EVAS_HINT_FILL); + evas_object_size_hint_min_set(lb, 100, 25); + elm_box_pack_end(bx, lb); + evas_object_show(lb); + + dt4 = efl_add(EFL_UI_CLOCK_CLASS, bx); + evas_object_size_hint_weight_set(dt4, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(dt4, EVAS_HINT_FILL, 0.5); + efl_ui_clock_edit_mode_set(dt4, EINA_TRUE); + efl_ui_clock_pause_set(dt4, EINA_TRUE); + elm_box_pack_end(bx, dt4); + evas_object_show(dt4); + + bt = elm_button_add(win); + elm_object_text_set(bt, "Back to the future..."); + evas_object_smart_callback_add(bt, "clicked", _bt_clicked, NULL); + elm_box_pack_end(bx, bt); + evas_object_show(bt); + + evas_object_show(win); +} diff --git a/src/lib/elementary/Elementary.h b/src/lib/elementary/Elementary.h index 9de8202..5b956a3 100644 --- a/src/lib/elementary/Elementary.h +++ b/src/lib/elementary/Elementary.h @@ -263,6 +263,7 @@ EAPI extern Elm_Version *elm_version; # include <efl_ui_text_interactive.eo.h> # include <efl_ui_text.eo.h> # include <efl_ui_text_editable.eo.h> +# include <efl_ui_clock.eo.h> #endif /* include deprecated calls last of all */ diff --git a/src/lib/elementary/efl_ui_clock.c b/src/lib/elementary/efl_ui_clock.c new file mode 100644 index 0000000..6c2d864 --- /dev/null +++ b/src/lib/elementary/efl_ui_clock.c @@ -0,0 +1,1189 @@ +#ifdef HAVE_CONFIG_H +# include "elementary_config.h" +#endif + +#define ELM_INTERFACE_ATSPI_ACCESSIBLE_PROTECTED + +#include <Elementary.h> +#include "elm_priv.h" +#include <efl_ui_clock.h> +#include <efl_ui_clock_private.h> + +#define MY_CLASS EFL_UI_CLOCK_CLASS + +#define MY_CLASS_NAME "Efl_Ui_Clock" +#define MY_CLASS_NAME_LEGACY "efl_ui_clock" + +#ifdef HAVE_LOCALE_H +# include <locale.h> +#endif + +#ifdef HAVE_LANGINFO_H +# include <langinfo.h> +#endif + +#define MAX_SEPARATOR_LEN 6 +#define MIN_DAYS_IN_MONTH 28 +#define BUFFER_SIZE 1024 + +/* interface between EDC & C code (field & signal names). values 0 to + * EFL_UI_CLOCK_TYPE_COUNT are in the valid range, and must get in the + * place of "%d". + */ +#define EDC_CLOCK_FOCUSIN_SIG_STR "elm,action,focus" +#define EDC_CLOCK_FOCUSOUT_SIG_STR "elm,action,unfocus" +#define EDC_PART_FIELD_STR "field%d" +#define EDC_PART_SEPARATOR_STR "separator%d" +#define EDC_PART_FIELD_ENABLE_SIG_STR "field%d,enable" +#define EDC_PART_FIELD_DISABLE_SIG_STR "field%d,disable" + +/* struct tm does not define the fields in the order year, month, + * date, hour, minute. values are reassigned to an array for easy + * handling. + */ +#define CLOCK_TM_ARRAY(intptr, tmptr) \ + int *intptr[] = { \ + &(tmptr)->tm_year, \ + &(tmptr)->tm_mon, \ + &(tmptr)->tm_mday, \ + &(tmptr)->tm_hour, \ + &(tmptr)->tm_min, \ + &(tmptr)->tm_sec, \ + &(tmptr)->tm_wday, \ + &(tmptr)->tm_hour} + +// default limits for individual fields +static Format_Map mapping[EFL_UI_CLOCK_TYPE_COUNT] = { + [EFL_UI_CLOCK_TYPE_YEAR] = { "Yy", -1, -1, "" }, + [EFL_UI_CLOCK_TYPE_MONTH] = { "mbBh", 0, 11, "" }, + [EFL_UI_CLOCK_TYPE_DATE] = { "de", 1, 31, "" }, + [EFL_UI_CLOCK_TYPE_HOUR] = { "IHkl", 0, 23, "" }, + [EFL_UI_CLOCK_TYPE_MINUTE] = { "M", 0, 59, "" }, + [EFL_UI_CLOCK_TYPE_SECOND] = { "S", 0, 59, "" }, + [EFL_UI_CLOCK_TYPE_DAY] = { "Aa", 0, 6, "" }, + [EFL_UI_CLOCK_TYPE_AMPM] = { "pP", 0, 1, "" } +}; + +static const char *multifield_formats = "cxXrRTDF"; +static const char *ignore_separators = "()"; +static Clock_Mod_Api *dt_mod = NULL; + +static const char SIG_CHANGED[] = "changed"; +static const Evas_Smart_Cb_Description _smart_callbacks[] = { + {SIG_CHANGED, ""}, + {SIG_WIDGET_LANG_CHANGED, ""}, /**< handled by elm_widget */ + {SIG_WIDGET_ACCESS_CHANGED, ""}, /**< handled by elm_widget */ + {SIG_LAYOUT_FOCUSED, ""}, /**< handled by elm_layout */ + {SIG_LAYOUT_UNFOCUSED, ""}, /**< handled by elm_layout */ + {NULL, NULL} +}; + +static Clock_Mod_Api * +_dt_mod_init() +{ + Elm_Module *mod = NULL; + + if (!(mod = _elm_module_find_as("clock/api"))) return NULL; + + mod->api = malloc(sizeof(Clock_Mod_Api)); + if (!mod->api) return NULL; + + ((Clock_Mod_Api *)(mod->api))->obj_hook = + _elm_module_symbol_get(mod, "obj_hook"); + ((Clock_Mod_Api *)(mod->api))->obj_unhook = + _elm_module_symbol_get(mod, "obj_unhook"); + ((Clock_Mod_Api *)(mod->api))->obj_hide = + _elm_module_symbol_get(mod, "obj_hide"); + ((Clock_Mod_Api *)(mod->api))->field_create = + _elm_module_symbol_get(mod, "field_create"); + ((Clock_Mod_Api *)(mod->api))->field_value_display = + _elm_module_symbol_get(mod, "field_value_display"); + + return mod->api; +} + +static void +_field_list_display(Evas_Object *obj) +{ + Clock_Field *field; + unsigned int idx = 0; + + EFL_UI_CLOCK_DATA_GET(obj, sd); + + if (!dt_mod || !dt_mod->field_value_display) return; + + for (idx = 0; idx < EFL_UI_CLOCK_TYPE_COUNT; idx++) + { + field = sd->field_list + idx; + if (field->fmt_exist && field->visible) + dt_mod->field_value_display(sd->mod_data, field->item_obj); + } +} + +// FIXME: provide nl_langinfo on Windows if possible +// returns expanded format string for corresponding multi-field format character +static char * +_expanded_fmt_str_get(char ch) +{ + char *exp_fmt = ""; + switch (ch) + { + case 'c': +#if defined(HAVE_LANGINFO_H) || defined (HAVE_EVIL) + exp_fmt = nl_langinfo(D_T_FMT); +#else + exp_fmt = ""; +#endif + break; + + case 'x': +#if defined(HAVE_LANGINFO_H) || defined (HAVE_EVIL) + exp_fmt = nl_langinfo(D_FMT); +#else + exp_fmt = ""; +#endif + break; + + case 'X': +#if defined(HAVE_LANGINFO_H) || defined (HAVE_EVIL) + exp_fmt = nl_langinfo(T_FMT); +#else + exp_fmt = ""; +#endif + break; + + case 'r': +#if defined(HAVE_LANGINFO_H) || defined (HAVE_EVIL) + exp_fmt = nl_langinfo(T_FMT_AMPM); +#else + exp_fmt = ""; +#endif + break; + + case 'R': + exp_fmt = "%H:%M"; + break; + + case 'T': + exp_fmt = "%H:%M:%S"; + break; + + case 'D': + exp_fmt = "%m/%d/%y"; + break; + + case 'F': + exp_fmt = "%Y-%m-%d"; + break; + + default: + exp_fmt = ""; + break; + } + + return exp_fmt; +} + +static void +_expand_format(char *dt_fmt) +{ + char *ptr, *expanded_fmt, ch; + unsigned int idx = 0, len = 0; + char buf[EFL_UI_CLOCK_MAX_FORMAT_LEN] = {0, }; + Eina_Bool fmt_char = EINA_FALSE; + + ptr = dt_fmt; + while ((ch = *ptr)) + { + if ((fmt_char) && (strchr(multifield_formats, ch))) + { + /* replace the multi-field format characters with + * corresponding expanded format */ + expanded_fmt = _expanded_fmt_str_get(ch); + len = strlen(expanded_fmt); + buf[--idx] = 0; + strncat(buf, expanded_fmt, len); + idx += len; + } + else buf[idx++] = ch; + + if (ch == '%') fmt_char = EINA_TRUE; + else fmt_char = EINA_FALSE; + + ptr++; + } + + buf[idx] = 0; + strncpy(dt_fmt, buf, EFL_UI_CLOCK_MAX_FORMAT_LEN); +} + +static void +_field_list_arrange(Evas_Object *obj) +{ + Clock_Field *field; + char buf[BUFFER_SIZE]; + int idx; + Eina_Bool freeze; + + EFL_UI_CLOCK_DATA_GET(obj, sd); + + freeze = sd->freeze_sizing; + sd->freeze_sizing = EINA_TRUE; + for (idx = 0; idx < EFL_UI_CLOCK_TYPE_COUNT; idx++) + { + field = sd->field_list + idx; + snprintf(buf, sizeof(buf), EDC_PART_FIELD_STR, field->location); + + if (field->visible && field->fmt_exist) + { + evas_object_hide(elm_layout_content_unset(obj, buf)); + elm_layout_content_set(obj, buf, field->item_obj); + } + else + evas_object_hide(elm_layout_content_unset(obj, buf)); + } + sd->freeze_sizing = freeze; + + elm_layout_sizing_eval(obj); + _field_list_display(obj); +} + +static unsigned int +_parse_format(Evas_Object *obj, + char *fmt_ptr) +{ + Eina_Bool fmt_parsing = EINA_FALSE, sep_parsing = EINA_FALSE, + sep_lookup = EINA_FALSE; + unsigned int len = 0, idx = 0, location = 0; + char separator[MAX_SEPARATOR_LEN]; + Clock_Field *field = NULL; + char cur; + + EFL_UI_CLOCK_DATA_GET(obj, sd); + + while ((cur = *fmt_ptr)) + { + if (fmt_parsing) + { + if (cur == '_' || cur == '-' || cur == '0' || cur == '^' || cur == '#') + { + fmt_ptr++; + continue; + } + fmt_parsing = EINA_FALSE; + for (idx = 0; idx < EFL_UI_CLOCK_TYPE_COUNT; idx++) + { + if (strchr(mapping[idx].fmt_char, cur)) + { + field = sd->field_list + idx; + /* ignore the fields already have or disabled + * valid formats, means already parsed & + * repeated, ignore. */ + if (field->location != -1) break; + field->fmt[1] = cur; + field->fmt_exist = EINA_TRUE; + field->location = location++; + sep_lookup = EINA_TRUE; + len = 0; + break; + } + } + } + if (cur == '%') + { + fmt_parsing = EINA_TRUE; + sep_parsing = EINA_FALSE; + // set the separator to previous field + separator[len] = 0; + if (field) eina_stringshare_replace(&field->separator, separator); + } + // ignore the set of chars (global, field specific) as field separators + if (sep_parsing && + (len < MAX_SEPARATOR_LEN - 1) && + (field->type != EFL_UI_CLOCK_TYPE_AMPM) && + (!strchr(ignore_separators, cur)) && + (!strchr(mapping[idx].ignore_sep, cur))) + separator[len++] = cur; + if (sep_lookup) sep_parsing = EINA_TRUE; + sep_lookup = EINA_FALSE; + fmt_ptr++; + } + // return the number of valid fields parsed. + return location; +} + +static void +_reload_format(Evas_Object *obj) +{ + unsigned int idx, field_count; + Clock_Field *field; + char buf[BUFFER_SIZE]; + char *dt_fmt; + + EFL_UI_CLOCK_DATA_GET(obj, sd); + ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd); + + // FIXME: provide nl_langinfo on Windows if possible + // fetch the default format from Libc. + if (!sd->user_format) +#if defined(HAVE_LANGINFO_H) || defined (HAVE_EVIL) + strncpy(sd->format, nl_langinfo(D_T_FMT), EFL_UI_CLOCK_MAX_FORMAT_LEN); +#else + strncpy(sd->format, "", EFL_UI_CLOCK_MAX_FORMAT_LEN); +#endif + sd->format[EFL_UI_CLOCK_MAX_FORMAT_LEN - 1] = '\0'; + + dt_fmt = (char *)malloc(EFL_UI_CLOCK_MAX_FORMAT_LEN); + if (!dt_fmt) return; + + strncpy(dt_fmt, sd->format, EFL_UI_CLOCK_MAX_FORMAT_LEN); + + _expand_format(dt_fmt); + + // reset all the fields to disable state + sd->enabled_field_count = 0; + for (idx = 0; idx < EFL_UI_CLOCK_TYPE_COUNT; idx++) + { + field = sd->field_list + idx; + field->fmt_exist = EINA_FALSE; + field->location = -1; + } + + field_count = _parse_format(obj, dt_fmt); + free(dt_fmt); + + for (idx = 0; idx < EFL_UI_CLOCK_TYPE_COUNT; idx++) + { + field = sd->field_list + idx; + if (field->fmt_exist && field->visible) + sd->enabled_field_count++; + } + + // assign locations to disabled fields for uniform usage + for (idx = 0; idx < EFL_UI_CLOCK_TYPE_COUNT; idx++) + { + field = sd->field_list + idx; + if (field->location == -1) field->location = field_count++; + + if (field->fmt_exist && field->visible) + { + snprintf(buf, sizeof(buf), EDC_PART_FIELD_ENABLE_SIG_STR, + field->location); + elm_layout_signal_emit(obj, buf, "elm"); + } + else + { + snprintf(buf, sizeof(buf), EDC_PART_FIELD_DISABLE_SIG_STR, + field->location); + elm_layout_signal_emit(obj, buf, "elm"); + } + snprintf + (buf, sizeof(buf), EDC_PART_SEPARATOR_STR, (field->location + 1)); + elm_layout_text_set(obj, buf, field->separator); + } + + edje_object_message_signal_process(wd->resize_obj); + _field_list_arrange(obj); +} + +EOLIAN static Eina_Bool +_efl_ui_clock_elm_widget_translate(Eo *obj, Efl_Ui_Clock_Data *sd) +{ + if (!sd->user_format) _reload_format(obj); + else _field_list_display(obj); + + elm_obj_widget_translate(efl_super(obj, MY_CLASS)); + + return EINA_TRUE; +} + +static Eina_List * +_clock_items_get(const Evas_Object *obj) +{ + Eina_List *items = NULL; + Clock_Field *field; + unsigned int idx; + Clock_Field *sorted_fields[EFL_UI_CLOCK_TYPE_COUNT]; + + EFL_UI_CLOCK_DATA_GET(obj, sd); + + for (idx = 0; idx < EFL_UI_CLOCK_TYPE_COUNT; idx++) + { + field = sd->field_list + idx; + sorted_fields[field->location] = field; + } + + for (idx = 0; idx < EFL_UI_CLOCK_TYPE_COUNT; idx++) + { + field = sorted_fields[idx]; + if (field->fmt_exist && field->visible) + items = eina_list_append(items, field->item_obj); + } + + // ACCESS + if (_elm_config->access_mode == ELM_ACCESS_MODE_ON) + items = eina_list_append(items, sd->access_obj); + + return items; +} + +EOLIAN static Eina_Bool +_efl_ui_clock_elm_widget_focus_next_manager_is(Eo *obj EINA_UNUSED, Efl_Ui_Clock_Data *_pd EINA_UNUSED) +{ + return EINA_TRUE; +} + +EOLIAN static void +_efl_ui_clock_pause_set(Eo *obj EINA_UNUSED, Efl_Ui_Clock_Data *sd, Eina_Bool paused) +{ + paused = !!paused; + if (sd->paused == paused) + return; + sd->paused = paused; + if (paused) + ecore_timer_freeze(sd->ticker); + else + ecore_timer_thaw(sd->ticker); +} + +EOLIAN static Eina_Bool +_efl_ui_clock_pause_get(Eo *obj EINA_UNUSED, Efl_Ui_Clock_Data *sd) +{ + return sd->paused; +} + +EOLIAN static void +_efl_ui_clock_edit_mode_set(Eo *obj EINA_UNUSED, Efl_Ui_Clock_Data *sd, Eina_Bool edit_mode) +{ + sd->edit_mode = edit_mode; +} + +EOLIAN static Eina_Bool +_efl_ui_clock_edit_mode_get(Eo *obj EINA_UNUSED, Efl_Ui_Clock_Data *sd) +{ + return sd->edit_mode; +} + +EOLIAN static Eina_Bool +_efl_ui_clock_elm_widget_focus_next(Eo *obj, Efl_Ui_Clock_Data *_pd EINA_UNUSED, Elm_Focus_Direction dir, Evas_Object **next, Elm_Object_Item **next_item) +{ + const Eina_List *items; + Eina_List *(*list_free)(Eina_List *list); + void *(*list_data_get)(const Eina_List *list); + + Eina_Bool int_ret; + + if ((items = elm_widget_focus_custom_chain_get(obj))) + { + list_data_get = eina_list_data_get; + list_free = NULL; + } + else + { + items = _clock_items_get(obj); + list_data_get = eina_list_data_get; + list_free = eina_list_free; + if (!items) return EINA_FALSE; + } + + int_ret = elm_widget_focus_list_next_get(obj, items, list_data_get, dir, next, next_item); + if (list_free) list_free((Eina_List *)items); + + return int_ret; +} + +EOLIAN static Eina_Bool +_efl_ui_clock_elm_widget_on_focus(Eo *obj, Efl_Ui_Clock_Data *sd, Elm_Object_Item *item EINA_UNUSED) +{ + Eina_Bool int_ret = EINA_FALSE; + + int_ret = elm_obj_widget_on_focus(efl_super(obj, MY_CLASS), NULL); + if (!int_ret) return EINA_FALSE; + + if (!elm_widget_focus_get(obj)) + { + if ((dt_mod) && (dt_mod->obj_hide)) + dt_mod->obj_hide(sd->mod_data); + } + + return EINA_TRUE; +} + +EOLIAN static Eina_Bool +_efl_ui_clock_elm_widget_disable(Eo *obj, Efl_Ui_Clock_Data *sd) +{ + Clock_Field *field; + unsigned int idx = 0; + Eina_Bool int_ret = EINA_FALSE; + + int_ret = elm_obj_widget_disable(efl_super(obj, MY_CLASS)); + if (!int_ret) return EINA_FALSE; + + for (idx = 0; idx < EFL_UI_CLOCK_TYPE_COUNT; idx++) + { + field = sd->field_list + idx; + elm_object_disabled_set(field->item_obj, elm_object_disabled_get(obj)); + } + return EINA_TRUE; +} + +EOLIAN static void +_efl_ui_clock_elm_layout_sizing_eval(Eo *obj, Efl_Ui_Clock_Data *sd) +{ + Evas_Coord minw = -1, minh = -1; + + if (sd->freeze_sizing) return; + + ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd); + + if (sd->enabled_field_count) + elm_coords_finger_size_adjust(sd->enabled_field_count, &minw, 1, &minh); + + edje_object_size_min_restricted_calc + (wd->resize_obj, &minw, &minh, minw, minh); + evas_object_size_hint_min_set(obj, minw, minh); + evas_object_size_hint_max_set(obj, -1, -1); +} + +EOLIAN static Elm_Theme_Apply +_efl_ui_clock_elm_widget_theme_apply(Eo *obj, Efl_Ui_Clock_Data *sd) +{ + Elm_Theme_Apply int_ret = ELM_THEME_APPLY_FAILED; + + Clock_Field *field; + char buf[BUFFER_SIZE]; + unsigned int idx; + + ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, EINA_FALSE); + + int_ret = elm_obj_widget_theme_apply(efl_super(obj, MY_CLASS)); + if (!int_ret) return ELM_THEME_APPLY_FAILED; + + if ((!dt_mod) || (!dt_mod->field_value_display)) return EINA_TRUE; + + for (idx = 0; idx < EFL_UI_CLOCK_TYPE_COUNT; idx++) + { + field = sd->field_list + idx; + if (field->fmt_exist && field->visible) + { + snprintf(buf, sizeof(buf), EDC_PART_FIELD_ENABLE_SIG_STR, + field->location); + elm_layout_signal_emit(obj, buf, "elm"); + + snprintf + (buf, sizeof(buf), EDC_PART_SEPARATOR_STR, field->location); + elm_layout_text_set(obj, buf, field->separator); + + dt_mod->field_value_display(sd->mod_data, field->item_obj); + } + else + { + snprintf(buf, sizeof(buf), EDC_PART_FIELD_DISABLE_SIG_STR, + field->location); + elm_layout_signal_emit(obj, buf, "elm"); + } + } + + edje_object_message_signal_process(wd->resize_obj); + elm_layout_sizing_eval(obj); + + return int_ret; +} + +static int +_max_days_get(int year, + int month) +{ + struct tm time1; + time_t t; + int day; + + t = time(NULL); + localtime_r(&t, &time1); + time1.tm_year = year; + time1.tm_mon = month; + for (day = MIN_DAYS_IN_MONTH; day <= mapping[EFL_UI_CLOCK_TYPE_DATE].def_max; + day++) + { + time1.tm_mday = day; + mktime(&time1); + /* To restrict month wrapping because of summer time in some locales, + * ignore day light saving mode in mktime(). */ + time1.tm_isdst = -1; + if (time1.tm_mday == 1) break; + } + day--; + + return day; +} + +static Eina_Bool +_date_cmp(const struct tm *time1, + const struct tm *time2) +{ + unsigned int idx; + + const CLOCK_TM_ARRAY(timearr1, time1); + const CLOCK_TM_ARRAY(timearr2, time2); + + for (idx = 0; idx < EFL_UI_CLOCK_TYPE_COUNT; idx++) + { + if (*timearr1[idx] != *timearr2[idx]) + return EINA_FALSE; + } + + return EINA_TRUE; +} + +static Eina_Bool +_field_cmp(Efl_Ui_Clock_Type field_type, + struct tm *time1, + struct tm *time2) +{ + CLOCK_TM_ARRAY(timearr1, time1); + CLOCK_TM_ARRAY(timearr2, time2); + + if (*timearr1[field_type] != *timearr2[field_type]) + return EINA_FALSE; + else + return EINA_TRUE; +} + +// validates curr_time/min_limt/max_limit according to the newly set value +static void +_validate_clock_limits(struct tm *time1, + struct tm *time2, + Eina_Bool swap) +{ + struct tm *t1, *t2; + unsigned int idx; + + if (!time1 || !time2) return; + + t1 = (swap) ? time2 : time1; + t2 = (swap) ? time1 : time2; + + CLOCK_TM_ARRAY(timearr1, time1); + CLOCK_TM_ARRAY(timearr2, time2); + for (idx = 0; idx < EFL_UI_CLOCK_TYPE_COUNT - 1; idx++) + { + if (*timearr1[idx] < *timearr2[idx]) + { + memcpy(t1, t2, sizeof(struct tm)); + break; + } + else if (*timearr1[idx] > *timearr2[idx]) + break; + } +} + +static void +_apply_field_limits(Evas_Object *obj) +{ + Clock_Field *field; + unsigned int idx = 0; + int val; + + EFL_UI_CLOCK_DATA_GET(obj, sd); + + CLOCK_TM_ARRAY(timearr, &sd->curr_time); + for (idx = 0; idx < EFL_UI_CLOCK_TYPE_COUNT - 1; idx++) + { + field = sd->field_list + idx; + val = *timearr[idx]; + if (val < field->min) + *timearr[idx] = field->min; + else if (val > field->max) + *timearr[idx] = field->max; + } + + _field_list_display(obj); +} + +static void +_apply_range_restrictions(struct tm *tim) +{ + unsigned int idx; + int val, min, max; + + if (!tim) return; + + CLOCK_TM_ARRAY(timearr, tim); + for (idx = EFL_UI_CLOCK_TYPE_MONTH; idx < EFL_UI_CLOCK_TYPE_COUNT - 1; idx++) + { + val = *timearr[idx]; + min = mapping[idx].def_min; + max = mapping[idx].def_max; + if (idx == EFL_UI_CLOCK_TYPE_DATE) + max = _max_days_get(tim->tm_year, tim->tm_mon); + if (val < min) + *timearr[idx] = min; + else if (val > max) + *timearr[idx] = max; + } +} + +static const char * +_field_format_get(Evas_Object *obj, + Efl_Ui_Clock_Type field_type) +{ + Clock_Field *field; + + if (field_type > EFL_UI_CLOCK_TYPE_DAY) return NULL; + + EFL_UI_CLOCK_DATA_GET(obj, sd); + + field = sd->field_list + field_type; + + return field->fmt; +} + +static void +_field_limit_get(Evas_Object *obj, + Efl_Ui_Clock_Type field_type, + int *range_min, + int *range_max) +{ + int min, max, max_days; + Clock_Field *field; + unsigned int idx; + + if (field_type > EFL_UI_CLOCK_TYPE_DAY) return; + + EFL_UI_CLOCK_DATA_GET(obj, sd); + + field = sd->field_list + field_type; + + min = field->min; + max = field->max; + + CLOCK_TM_ARRAY(curr_timearr, &sd->curr_time); + CLOCK_TM_ARRAY(min_timearr, &sd->min_limit); + CLOCK_TM_ARRAY(max_timearr, &sd->max_limit); + + for (idx = 0; idx < field->type; idx++) + if (*curr_timearr[idx] > *min_timearr[idx]) break; + if ((idx == field_type) && (min < *min_timearr[field_type])) + min = *min_timearr[field_type]; + if (field_type == EFL_UI_CLOCK_TYPE_DATE) + { + max_days = _max_days_get(sd->curr_time.tm_year, sd->curr_time.tm_mon); + if (max > max_days) max = max_days; + } + for (idx = 0; idx < field->type; idx++) + if (*curr_timearr[idx] < *max_timearr[idx]) break; + if ((idx == field_type) && (max > *max_timearr[field_type])) + max = *max_timearr[field_type]; + + *range_min = min; + *range_max = max; +} + +static void +_field_list_init(Evas_Object *obj) +{ + Clock_Field *field; + unsigned int idx; + time_t t; + + EFL_UI_CLOCK_DATA_GET(obj, sd); + + t = time(NULL); + localtime_r(&t, &sd->curr_time); + + mapping[EFL_UI_CLOCK_TYPE_YEAR].def_min = _elm_config->year_min; + mapping[EFL_UI_CLOCK_TYPE_YEAR].def_max = _elm_config->year_max; + for (idx = 0; idx < EFL_UI_CLOCK_TYPE_COUNT; idx++) + { + field = sd->field_list + idx; + field->type = EFL_UI_CLOCK_TYPE_YEAR + idx; + field->fmt[0] = '%'; + field->fmt_exist = EINA_FALSE; + field->visible = EINA_TRUE; + field->min = mapping[idx].def_min; + field->max = mapping[idx].def_max; + } + CLOCK_TM_ARRAY(min_timearr, &sd->min_limit); + CLOCK_TM_ARRAY(max_timearr, &sd->max_limit); + for (idx = 0; idx < EFL_UI_CLOCK_TYPE_COUNT - 1; idx++) + { + *min_timearr[idx] = mapping[idx].def_min; + *max_timearr[idx] = mapping[idx].def_max; + } +} + +static char * +_access_info_cb(void *data, Evas_Object *obj EINA_UNUSED) +{ + char *ret; + Eina_Strbuf *buf; + buf = eina_strbuf_new(); + + EFL_UI_CLOCK_DATA_GET(data, sd); + eina_strbuf_append_printf(buf, + "%d year, %d month, %d date, %d hour, %d minute", + sd->curr_time.tm_year, sd->curr_time.tm_mon + 1, + sd->curr_time.tm_mday, sd->curr_time.tm_hour, + sd->curr_time.tm_min); + + ret = eina_strbuf_string_steal(buf); + eina_strbuf_free(buf); + return ret; +} + +static Eina_Bool +_ticker(void *data) +{ + double t; + time_t tt; + struct timeval timev; + Clock_Field *field; + + EFL_UI_CLOCK_DATA_GET(data, sd); + + tt = time(NULL); + localtime_r(&tt, &sd->curr_time); + + if (sd->curr_time.tm_sec > 0) + { + field = sd->field_list + EFL_UI_CLOCK_TYPE_SECOND; + if (field->fmt_exist && field->visible) + dt_mod->field_value_display(sd->mod_data, field->item_obj); + } + else + _field_list_display(data); + + gettimeofday(&timev, NULL); + t = ((double)(1000000 - timev.tv_usec)) / 1000000.0; + sd->ticker = ecore_timer_add(t, _ticker, data); + + return ECORE_CALLBACK_CANCEL; +} + +EOLIAN static void +_efl_ui_clock_efl_canvas_group_group_add(Eo *obj, Efl_Ui_Clock_Data *priv) +{ + Clock_Field *field; + int idx; + + efl_canvas_group_add(efl_super(obj, MY_CLASS)); + elm_widget_sub_object_parent_add(obj); + + // module - initialise module for clock + if (!dt_mod) dt_mod = _dt_mod_init(); + if (dt_mod) + { + if (dt_mod->obj_hook) + { + priv->mod_data = dt_mod->obj_hook(obj); + + // update module data + if (priv->mod_data) + { + priv->mod_data->base = obj; + priv->mod_data->field_limit_get = _field_limit_get; + priv->mod_data->field_format_get = _field_format_get; + } + } + + if (dt_mod->field_create) + { + for (idx = 0; idx < EFL_UI_CLOCK_TYPE_COUNT; idx++) + { + field = priv->field_list + idx; + field->item_obj = dt_mod->field_create(priv->mod_data, idx); + } + } + } + + priv->freeze_sizing = EINA_TRUE; + if (!elm_layout_theme_set(obj, "uiclock", "base", + elm_widget_style_get(obj))) + CRI("Failed to set layout!"); + + _field_list_init(obj); + _reload_format(obj); + _ticker(obj); + + elm_widget_can_focus_set(obj, EINA_TRUE); + + priv->freeze_sizing = EINA_FALSE; + elm_layout_sizing_eval(obj); + + // ACCESS + if (_elm_config->access_mode == ELM_ACCESS_MODE_ON) + { + Elm_Access_Info *ai; + + priv->access_obj = _elm_access_edje_object_part_object_register + (obj, elm_layout_edje_get(obj), "elm.access"); + if (!priv->access_obj) + priv->access_obj = _elm_access_edje_object_part_object_register + (obj, elm_layout_edje_get(obj), "access"); + + ai = _elm_access_info_get(priv->access_obj); + _elm_access_text_set(ai, ELM_ACCESS_TYPE, "date time"); + _elm_access_callback_set(ai, ELM_ACCESS_INFO, _access_info_cb, obj); + } +} + +EOLIAN static void +_efl_ui_clock_efl_canvas_group_group_del(Eo *obj, Efl_Ui_Clock_Data *sd) +{ + Clock_Field *tmp; + unsigned int idx; + + ecore_timer_del(sd->ticker); + for (idx = 0; idx < EFL_UI_CLOCK_TYPE_COUNT; idx++) + { + tmp = sd->field_list + idx; + evas_object_del(tmp->item_obj); + eina_stringshare_del(tmp->separator); + } + + if ((dt_mod) && (dt_mod->obj_unhook)) + dt_mod->obj_unhook(sd->mod_data); // module - unhook + + efl_canvas_group_del(efl_super(obj, MY_CLASS)); +} + +EOLIAN static Eo * +_efl_ui_clock_efl_object_constructor(Eo *obj, Efl_Ui_Clock_Data *_pd EINA_UNUSED) +{ + obj = efl_constructor(efl_super(obj, MY_CLASS)); + efl_canvas_object_type_set(obj, MY_CLASS_NAME_LEGACY); + evas_object_smart_callbacks_descriptions_set(obj, _smart_callbacks); + elm_interface_atspi_accessible_role_set(obj, ELM_ATSPI_ROLE_DATE_EDITOR); + + return obj; +} + +EOLIAN static const char* +_efl_ui_clock_format_get(Eo *obj EINA_UNUSED, Efl_Ui_Clock_Data *sd) +{ + return sd->format; +} + +EOLIAN static void +_efl_ui_clock_format_set(Eo *obj, Efl_Ui_Clock_Data *sd, const char *fmt) +{ + if (fmt) + { + strncpy(sd->format, fmt, EFL_UI_CLOCK_MAX_FORMAT_LEN); + sd->format[EFL_UI_CLOCK_MAX_FORMAT_LEN - 1] = '\0'; + sd->user_format = EINA_TRUE; + } + else sd->user_format = EINA_FALSE; + + _reload_format(obj); +} + +EOLIAN static Eina_Bool +_efl_ui_clock_field_visible_get(Eo *obj EINA_UNUSED, Efl_Ui_Clock_Data *sd, Efl_Ui_Clock_Type fieldtype) +{ + Clock_Field *field; + + if (fieldtype > EFL_UI_CLOCK_TYPE_DAY) return EINA_FALSE; + + field = sd->field_list + fieldtype; + + return field->visible; +} + +EOLIAN static void +_efl_ui_clock_field_visible_set(Eo *obj, Efl_Ui_Clock_Data *sd, Efl_Ui_Clock_Type fieldtype, Eina_Bool visible) +{ + char buf[BUFFER_SIZE]; + Clock_Field *field; + + if (fieldtype > EFL_UI_CLOCK_TYPE_DAY) return; + + field = sd->field_list + fieldtype; + visible = !!visible; + if (field->visible == visible) return; + + field->visible = visible; + + sd->freeze_sizing = EINA_TRUE; + if (visible) + { + sd->enabled_field_count++; + + if (!field->fmt_exist) return; + + snprintf(buf, sizeof(buf), EDC_PART_FIELD_ENABLE_SIG_STR, + field->location); + elm_layout_signal_emit(obj, buf, "elm"); + + ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd); + edje_object_message_signal_process(wd->resize_obj); + + snprintf(buf, sizeof(buf), EDC_PART_FIELD_STR, field->location); + elm_layout_content_unset(obj, buf); + elm_layout_content_set(obj, buf, field->item_obj); + } + else + { + sd->enabled_field_count--; + + if (!field->fmt_exist) return; + + snprintf(buf, sizeof(buf), EDC_PART_FIELD_DISABLE_SIG_STR, + field->location); + elm_layout_signal_emit(obj, buf, "elm"); + + ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd); + edje_object_message_signal_process(wd->resize_obj); + + snprintf(buf, sizeof(buf), EDC_PART_FIELD_STR, field->location); + evas_object_hide(elm_layout_content_unset(obj, buf)); + } + sd->freeze_sizing = EINA_FALSE; + + elm_layout_sizing_eval(obj); + + if (!visible) return; + if (!dt_mod || !dt_mod->field_value_display) return; + + dt_mod->field_value_display(sd->mod_data, field->item_obj); +} + +EOLIAN static void +_efl_ui_clock_field_limit_get(Eo *obj EINA_UNUSED, Efl_Ui_Clock_Data *sd, Efl_Ui_Clock_Type fieldtype, int *min, int *max) +{ + Clock_Field *field; + + if (fieldtype >= EFL_UI_CLOCK_TYPE_DAY) return; + + field = sd->field_list + fieldtype; + if (min) *min = field->min; + if (max) *max = field->max; +} + +EOLIAN static void +_efl_ui_clock_field_limit_set(Eo *obj, Efl_Ui_Clock_Data *sd, Efl_Ui_Clock_Type fieldtype, int min, int max) +{ + Clock_Field *field; + struct tm old_time; + + if (fieldtype >= EFL_UI_CLOCK_TYPE_DAY) return; + + if (min > max) return; + + old_time = sd->curr_time; + field = sd->field_list + fieldtype; + if (((min >= mapping[fieldtype].def_min) && + (min <= mapping[fieldtype].def_max)) || + (field->type == EFL_UI_CLOCK_TYPE_YEAR)) + field->min = min; + if (((max >= mapping[fieldtype].def_min) && + (max <= mapping[fieldtype].def_max)) || + (field->type == EFL_UI_CLOCK_TYPE_YEAR)) + field->max = max; + + _apply_field_limits(obj); + + if (!_field_cmp(fieldtype, &old_time, &sd->curr_time)) + efl_event_callback_legacy_call(obj, EFL_UI_CLOCK_EVENT_CHANGED, NULL); + +} + +EOLIAN static Eina_Bool +_efl_ui_clock_value_get(Eo *obj EINA_UNUSED, Efl_Ui_Clock_Data *sd, struct tm *currtime) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(currtime, EINA_FALSE); + + *currtime = sd->curr_time; + return EINA_TRUE; +} + +EOLIAN static Eina_Bool +_efl_ui_clock_value_set(Eo *obj, Efl_Ui_Clock_Data *sd, struct tm *newtime) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(newtime, EINA_FALSE); + + if (_date_cmp(&sd->curr_time, newtime)) return EINA_TRUE; + sd->curr_time = *newtime; + // apply default field restrictions for curr_time + _apply_range_restrictions(&sd->curr_time); + // validate the curr_time according to the min_limt and max_limt + _validate_clock_limits(&sd->curr_time, &sd->min_limit, EINA_FALSE); + _validate_clock_limits(&sd->max_limit, &sd->curr_time, EINA_TRUE); + _apply_field_limits(obj); + + efl_event_callback_legacy_call(obj, EFL_UI_CLOCK_EVENT_CHANGED, NULL); + + return EINA_TRUE; +} + +EOLIAN static Eina_Bool +_efl_ui_clock_value_min_get(Eo *obj EINA_UNUSED, Efl_Ui_Clock_Data *sd, Efl_Time *mintime) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(mintime, EINA_FALSE); + + *mintime = sd->min_limit; + return EINA_TRUE; +} + +EOLIAN static Eina_Bool +_efl_ui_clock_value_min_set(Eo *obj, Efl_Ui_Clock_Data *sd, Efl_Time *mintime) +{ + struct tm old_time; + + EINA_SAFETY_ON_NULL_RETURN_VAL(mintime, EINA_FALSE); + + if (_date_cmp(&sd->min_limit, mintime)) return EINA_TRUE; + sd->min_limit = *mintime; + old_time = sd->curr_time; + // apply default field restrictions for min_limit + _apply_range_restrictions(&sd->min_limit); + // validate curr_time and max_limt according to the min_limit + _validate_clock_limits(&sd->max_limit, &sd->min_limit, EINA_FALSE); + _validate_clock_limits(&sd->curr_time, &sd->min_limit, EINA_FALSE); + _apply_field_limits(obj); + + if (!_date_cmp(&old_time, &sd->curr_time)) + efl_event_callback_legacy_call(obj, EFL_UI_CLOCK_EVENT_CHANGED, NULL); + + return EINA_TRUE; +} + +EOLIAN static Eina_Bool +_efl_ui_clock_value_max_get(Eo *obj EINA_UNUSED, Efl_Ui_Clock_Data *sd, struct tm *maxtime) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(maxtime, EINA_FALSE); + + *maxtime = sd->max_limit; + return EINA_TRUE; +} + +EOLIAN static Eina_Bool +_efl_ui_clock_value_max_set(Eo *obj, Efl_Ui_Clock_Data *sd, struct tm *maxtime) +{ + struct tm old_time; + + EINA_SAFETY_ON_NULL_RETURN_VAL(maxtime, EINA_FALSE); + + if (_date_cmp(&sd->max_limit, maxtime)) return EINA_TRUE; + sd->max_limit = *maxtime; + old_time = sd->curr_time; + // apply default field restrictions for max_limit + _apply_range_restrictions(&sd->max_limit); + // validate curr_time and min_limt according to the max_limit + _validate_clock_limits(&sd->max_limit, &sd->min_limit, EINA_TRUE); + _validate_clock_limits(&sd->max_limit, &sd->curr_time, EINA_TRUE); + _apply_field_limits(obj); + + if (!_date_cmp(&old_time, &sd->curr_time)) + efl_event_callback_legacy_call(obj, EFL_UI_CLOCK_EVENT_CHANGED, NULL); + + return EINA_TRUE; +} + +EOLIAN static void +_efl_ui_clock_class_constructor(Efl_Class *klass) +{ + evas_smart_legacy_type_register(MY_CLASS_NAME_LEGACY, klass); +} + +#include "efl_ui_clock.eo.c" diff --git a/src/lib/elementary/efl_ui_clock.eo b/src/lib/elementary/efl_ui_clock.eo new file mode 100644 index 0000000..1261cec --- /dev/null +++ b/src/lib/elementary/efl_ui_clock.eo @@ -0,0 +1,260 @@ +import efl_types; + +enum Efl.Ui.Clock.Type +{ + [[Identifies a clock field, The widget supports 6 fields : Year, month, + Date, Hour, Minute, AM/PM + ]] + + year = 0, [[Indicates Year field.]] + month = 1, [[Indicates Month field.]] + date = 2, [[Indicates Date field.]] + hour = 3, [[Indicates Hour field.]] + minute = 4, [[Indicates Minute field.]] + second = 5, [[Indicates Second field.]] + day = 6, [[Indicated Day field.]] + ampm = 7, [[Indicates AM/PM field .]] +} + +class Efl.Ui.Clock (Elm.Layout) +{ + methods { + @property format { + [[The current clock format. + Format is a combination of allowed + Libc date format specifiers like: "%b %d, %Y %I : %M %p". + + Maximum allowed format length is 64 chars. + + Format can include separators for each individual clock + field except for AM/PM field. + + Each separator can be a maximum of 6 UTF-8 bytes. + Space is also taken as a separator. + + These specifiers can be arranged in any order and the widget + will display the fields accordingly. + + Default format is taken as per the system locale settings. + ]] + /* FIXME-doc + Following are the allowed set of format specifiers for each clock field. + + @b %%Y : The year as a decimal number including the century. + + @b %%y : The year as a decimal number without a century (range 00 to 99). + + @b %%m : The month as a decimal number (range 01 to 12). + + @b %%b : The abbreviated month name according to the current locale. + + @b %%B : The full month name according to the current locale. + + @b %%h : The abbreviated month name according to the current locale(same as %%b). + + @b %%d : The day of the month as a decimal number (range 01 to 31). + + @b %%e : The day of the month as a decimal number (range 1 to 31). single + digits are preceded by a blank. + + @b %%I : The hour as a decimal number using a 12-hour clock (range 01 to 12). + + @b %%H : The hour as a decimal number using a 24-hour clock (range 00 to 23). + + @b %%k : The hour (24-hour clock) as a decimal number (range 0 to 23). single + digits are preceded by a blank. + + @b %%l : The hour (12-hour clock) as a decimal number (range 1 to 12); single + digits are preceded by a blank. + + @b %%M : The minute as a decimal number (range 00 to 59). + + @b %%p : Either 'AM' or 'PM' according to the given time value, or the + corresponding strings for the current locale. Noon is treated as 'PM' + and midnight as 'AM'. + + @b %%P : Like %p but in lower case: 'am' or 'pm' or a corresponding string for + the current locale. + + @b %%c : The preferred date and time representation for the current locale. + + @b %%x : The preferred date representation for the current locale without the time. + + @b %%X : The preferred time representation for the current locale without the date. + + @b %%r : The complete calendar time using the AM/PM format of the current locale. + + @b %%R : The hour and minute in decimal numbers using the format %H:%M. + + @b %%T : The time of day in decimal numbers using the format %H:%M:%S. + + @b %%D : The date using the format %%m/%%d/%%y. + + @b %%F : The date using the format %%Y-%%m-%%d. + */ + set {} get {} + values { + fmt: const(char)* @nullable; [[The clock format.]] + } + } + @property pause { + [[Whether the given clock widget should be paused or not. + + This function pauses or starts the clock widget. + ]] + set {} get {} + values { + paused: bool; [[$true to pause clock, $false otherwise]] + } + } + @property edit_mode { + [[Digits of the given clock widget should be editable when in editing mode.]] + set {} get {} + values { + value: bool; [[$true to set edit mode, $false otherwise]] + } + } + @property value_min { + [[The lower boundary of a field. + + Year: years since 1900. Negative value represents year below 1900 + (year value -30 represents 1870). Year default range is from 70 + to 137. + + Month: default value range is from 0 to 11. + + Date: default value range is from 1 to 31 according to the month + value. + + Hour: default value will be in terms of 24 hr format (0~23) + + Minute: default value range is from 0 to 59. + ]] + set { + return: bool; + } + get { + return: bool; + } + + keys { + mintime: Efl.Time*; [[Time structure containing the minimum time value.]] + } + } + @property value_max { + [[The upper boundary of a field. + + Year: years since 1900. Negative value represents year below 1900 + (year value -30 represents 1870). Year default range is from 70 + to 137. + + Month: default value range is from 0 to 11. + + Date: default value range is from 1 to 31 according to the month + value. + + Hour: default value will be in terms of 24 hr format (0~23) + + Minute: default value range is from 0 to 59. + ]] + set { + return: bool; + } + get { + return: bool; + } + + keys { + maxtime: Efl.Time*; [[Time structure containing the minimum time value.]] + } + } + @property value { + [[The current value of a clock object. + + Year: years since 1900. Negative value represents year below 1900 + (year value -30 represents 1870). Year default range is from 70 + to 137. + + Month: default value range is from 0 to 11. + + Date: default value range is from 1 to 31 according to the month + value. + + Hour: default value will be in terms of 24 hr format (0~23) + + Minute: default value range is from 0 to 59. + ]] + set { + return: bool; + } + get { + return: bool; + } + keys { + curtime: Efl.Time*; [[Time structure containing the minimum time value.]] + } + } + @property field_visible { + [[ The field to be visible/not.]] + set{} get{} + keys { + fieldtype: Efl.Ui.Clock.Type; [[Type of the field. #EFL_UI_CLOCK_TYPE_YEAR etc.]] + } + values { + visible: bool; [[$true field can be visible, $false otherwise.]] + } + } + @property field_limit { + set { + [[Set a field to be visible or not. + + Setting this API to $true does not ensure that the field is + visible, apart from this, the field's format must be present + in clock overall format. If a field's visibility is set + to $false then it won't appear even though its format is + present in overall format. So if and only if this API is + set true and the corresponding field's format is present + in clock format, the field is visible. + + By default the field visibility is set to $true. + ]] + } + get { + [[ Get the field limits of a field. + + Limits can be set to individual fields, independently, except + for AM/PM field. Any field can display the values only in between + these minimum and maximum limits unless the corresponding time + value is restricted from MinTime to MaxTime. That is, min/max + field limits always works under the limitations of mintime/maxtime. + + There is no provision to set the limits of AM/PM field. + ]] + } + keys { + fieldtype: Efl.Ui.Clock.Type; [[Type of the field. #EFL_UI_CLOCK_TYPE_YEAR etc.]] + } + values { + min: int; [[Reference to field's minimum value.]] + max: int; [[Reference to field's maximum value.]] + } + } + } + implements { + class.constructor; + Efl.Object.constructor; + Efl.Canvas.Group.group_add; + Efl.Canvas.Group.group_del; + Elm.Widget.theme_apply; + Elm.Widget.focus_next_manager_is; + Elm.Widget.focus_next; + Elm.Widget.disable; + Elm.Widget.on_focus; + Elm.Widget.translate; + Elm.Layout.sizing_eval; + } + events { + changed; + } + +} diff --git a/src/lib/elementary/efl_ui_clock.h b/src/lib/elementary/efl_ui_clock.h new file mode 100644 index 0000000..4607d39 --- /dev/null +++ b/src/lib/elementary/efl_ui_clock.h @@ -0,0 +1,204 @@ +/** + * @defgroup efl_ui_clock + * @ingroup Elementary + * + * @image html clock_inheritance_tree.png + * @image latex clock_inheritance_tree.eps + * + * @image html img/widget/clock/preview-00.png + * @image latex img/widget/clock/preview-00.eps + * + * @image html img/widget/clock/preview-01.png + * @image latex img/widget/clock/preview-01.eps + * + * @image html img/widget/clock/preview-02.png + * @image latex img/widget/clock/preview-02.eps + * + * Clock widget is used to display and input date & time values. + * This widget displays date and time as per the <b>system's locale</b> settings (Date + * includes Day, Month & Year along with the defined separators and + * Time includes Hour, Minute & AM/PM fields. Separator for AM/PM field is ignored. + * + * The corresponding Month, AM/PM strings are displayed according to the + * system’s language settings. + * + * Clock format is a combination of LIBC standard characters like + * “%%d %%b %%Y %%I : %%M %%p” which, as a whole represents both Date as well as Time + * format. + * + * efl_ui_clock supports only the following sub set of libc date format specifiers: + * + * @b %%Y : The year as a decimal number including the century (example: 2011). + * + * @b %%y : The year as a decimal number without a century (range 00 to 99) + * + * @b %%m : The month as a decimal number (range 01 to 12). + * + * @b %%b : The abbreviated month name according to the current locale. + * + * @b %%B : The full month name according to the current locale. + * + * @b %%h : The abbreviated month name according to the current locale(same as %%b). + * + * @b %%d : The day of the month as a decimal number (range 01 to 31). + * + * @b %%e : The day of the month as a decimal number (range 1 to 31). single + * digits are preceded by a blank. + * + * @b %%I : The hour as a decimal number using a 12-hour clock (range 01 to 12). + * + * @b %%H : The hour as a decimal number using a 24-hour clock (range 00 to 23). + * + * @b %%k : The hour (24-hour clock) as a decimal number (range 0 to 23). single + * digits are preceded by a blank. + * + * @b %%l : The hour (12-hour clock) as a decimal number (range 1 to 12); single + * digits are preceded by a blank. + * + * @b %%M : The minute as a decimal number (range 00 to 59). + * + * @b %%p : Either 'AM' or 'PM' according to the given time value, or the + * corresponding strings for the current locale. Noon is treated as 'PM' + * and midnight as 'AM' + * + * @b %%P : Like %p but in lower case: 'am' or 'pm' or a corresponding string for + * the current locale. + * + * @b %%c : The preferred date and time representation for the current locale. + * + * @b %%x : The preferred date representation for the current locale without the time. + * + * @b %%X : The preferred time representation for the current locale without the date. + * + * @b %%r : The complete calendar time using the AM/PM format of the current locale. + * + * @b %%R : The hour and minute in decimal numbers using the format %H:%M. + * + * @b %%T : The time of day in decimal numbers using the format %H:%M:%S. + * + * @b %%D : The date using the format %%m/%%d/%%y. + * + * @b %%F : The date using the format %%Y-%%m-%%d. + * + * (For more reference on the available <b>LIBC date format specifiers</b>, please + * visit the link: + * http://www.gnu.org/s/hello/manual/libc.html#Formatting-Calendar-Time ) + * + * Clock widget can provide Unicode @b separators in between its fields + * except for AM/PM field. + * A separator can be any <b>Unicode character</b> other than the LIBC standard + * date format specifiers.( Example: In the format %%b %%d @b , %%y %%H @b : %%M + * comma(,) is separator for date field %%d and colon(:) is separator for + * hour field %%H ). + * + * The default format is a predefined one, based on the system Locale. + * + * Hour format 12hr(1-12) or 24hr(0-23) display can be selected by setting + * the corresponding user format. + * + * Clock supports six fields: Year, Month, Date, Hour, Minute, AM/PM. + * Depending on the Clock module that is loaded, the user can see + * different UI to select the individual field values. + * + * The individual fields of Clock can be arranged in any order according to the format + * set by application. + * + * There is a provision to set the visibility of a particular field as TRUE/ FALSE + * so that <b>only time/ only date / only required fields</b> will be displayed. + * + * Each field is having a default minimum and maximum values just like the daily + * calendar information. These min/max values can be modified as per the application usage. + * + * User can enter the values only in between the range of maximum and minimum. + * Apart from these APIs, there is a provision to display only a limited set of + * values out of the possible values. APIs to select the individual field limits + * are intended for this purpose. + * + * The whole widget is left aligned and its size grows horizontally depending + * on the current format and each field's visible/disabled state. + * + * Clock individual field selection is implemented in a modular style. + * Module can be implemented as a Ctxpopup based selection or an ISE based + * selection or even a spinner like selection etc. + * + * <b>Clock Module design:</b> + * + * The following functions are expected to be implemented in a Clock module: + * + * <b>Field creation:</b> + * <pre> + * + * __________ __________ + * | |----- obj_hook() ---------------------->>>| | + * | |<<<----------------returns Mod_data ------| | + * | Clock |_______ | | + * | widget | |Assign module call backs | Module | + * | base |<<<____| | | + * | | | | + * | |----- field_create() ------------------>>>| | + * |__________|<<<----------------returns field_obj -----|__________| + * + * </pre> + * + * <b>Field value setting:</b> + * <pre> + * + * __________ __________ + * | | | | + * | Clock |<<<----------efl_ui_clock_value_set()---| | + * | widget | | Module | + * | base |----display_field_value()------------>>>| | + * |__________| |__________| + * + * </pre> + * + * <b>del_hook:</b> + * <pre> + * __________ __________ + * | | | | + * | Clock |----obj_unhook()-------------------->>>>| | + * | widget | | Module | + * | base | <<<-----frees mod_data---------| | + * |__________| |__________| + * + * </pre> + * + * + * Any module can use the following shared functions that are implemented in efl_ui_clock.c : + * + * <b>field_format_get()</b> - gives the field format. + * + * <b>field_limit_get()</b> - gives the field minimum, maximum limits. + * + * To enable a module, set the ELM_MODULES environment variable as shown: + * + * <b>export ELM_MODULES="clock_input_ctxpopup>clock/api"</b> + * + * This widget inherits from the @ref Layout one, so that all the + * functions acting on it also work for Clock objects. + * + * This widget emits the following signals, besides the ones sent from + * @ref Layout: + * @li @b "changed" - whenever Clock field value is changed, this + * signal is sent. + * @li @b "language,changed" - whenever system locale changes, this + * signal is sent. + * @li @c "focused" - When the Clock has received focus. (since 1.8) + * @li @c "unfocused" - When the Clock has lost focus. (since 1.8) + * + * Here is an example on its usage: + * @li @ref clock_example + * + */ + +/** + * @addtogroup efl_ui_clock + * @{ + */ + +#ifdef EFL_EO_API_SUPPORT +#include "efl_ui_clock.eo.h" +#endif +/** + * @} + */ diff --git a/src/lib/elementary/efl_ui_clock_private.h b/src/lib/elementary/efl_ui_clock_private.h new file mode 100644 index 0000000..976fbbd --- /dev/null +++ b/src/lib/elementary/efl_ui_clock_private.h @@ -0,0 +1,130 @@ +#ifndef EFL_UI_CLOCK_PRIVATE_H +#define EFL_UI_CLOCK_PRIVATE_H + +#include "Elementary.h" + +/* DO NOT USE THIS HEADER UNLESS YOU ARE PREPARED FOR BREAKING OF YOUR + * CODE. THIS IS ELEMENTARY'S INTERNAL WIDGET API (for now) AND IS NOT + * FINAL. CALL elm_widget_api_check(ELM_INTERNAL_API_VERSION) TO CHECK + * IT AT RUNTIME. + */ + +/** + * @addtogroup Widget + * @{ + * + * @section efl-ui-clock-class The Elementary Clock Class + * + * Elementary, besides having the @ref Clock widget, exposes its + * foundation -- the Elementary Clock Class -- in order to create other + * widgets which are a Clock with some more logic on top. + */ + +/** + * Base layout smart data extended with Clock instance data. + */ +typedef struct _Efl_Ui_Clock_Module_Data Efl_Ui_Clock_Module_Data; +typedef struct _Efl_Ui_Clock_Data Efl_Ui_Clock_Data; +typedef struct _Clock_Field Clock_Field; +typedef struct _Clock_Mod_Api Clock_Mod_Api; +typedef struct _Format_Map Format_Map; + +#define EFL_UI_CLOCK_TYPE_COUNT 8 +#define EFL_UI_CLOCK_MAX_FORMAT_LEN 64 +#define EFL_UI_CLOCK_MAX_FIELD_FORMAT_LEN 3 + +struct _Efl_Ui_Clock_Module_Data +{ + Evas_Object *base; + void (*field_limit_get)(Evas_Object *obj, + Efl_Ui_Clock_Type field_type, + int *range_min, + int *range_max); + const char *(*field_format_get)(Evas_Object * obj, + Efl_Ui_Clock_Type field_type); +}; + +struct _Clock_Field +{ + Evas_Object *item_obj; + char fmt[EFL_UI_CLOCK_MAX_FIELD_FORMAT_LEN]; + Efl_Ui_Clock_Type type; + const char *separator; + int location; /* location of the field as per + * current format */ + int min, max; + Eina_Bool fmt_exist : 1; /* whether field format is + * present or not */ + Eina_Bool visible : 1; /* whether field can be + * visible or not */ +}; + +struct _Clock_Mod_Api +{ + Efl_Ui_Clock_Module_Data *(*obj_hook)(Evas_Object * obj); + void (*obj_unhook)(Efl_Ui_Clock_Module_Data *mdata); + void (*obj_hide)(Efl_Ui_Clock_Module_Data *mdata); + Evas_Object *(*field_create)(Efl_Ui_Clock_Module_Data * mdata, + Efl_Ui_Clock_Type ftype); + void (*field_value_display)(Efl_Ui_Clock_Module_Data + *mdata, + Evas_Object *obj); +}; + +struct _Efl_Ui_Clock_Data +{ + /* fixed set of fields. */ + Clock_Field field_list[EFL_UI_CLOCK_TYPE_COUNT]; + struct tm curr_time, min_limit, max_limit; + Efl_Ui_Clock_Module_Data *mod_data; + char format[EFL_UI_CLOCK_MAX_FORMAT_LEN]; + Evas_Object *access_obj; + int enabled_field_count; + Ecore_Timer *ticker; + Eina_Bool paused : 1; + Eina_Bool edit_mode : 1; + Eina_Bool user_format : 1; /* whether user set + * format or default + * format. */ + Eina_Bool freeze_sizing : 1; /* freeze sizing_eval to + * reduce unnecessary sizing */ +}; + +struct _Format_Map +{ + char *fmt_char; + int def_min; + int def_max; + char *ignore_sep; +}; + +/** + * @} + */ + +#define EFL_UI_CLOCK_DATA_GET(o, sd) \ + Efl_Ui_Clock_Data * sd = efl_data_scope_get(o, EFL_UI_CLOCK_CLASS) + +#define EFL_UI_CLOCK_DATA_GET_OR_RETURN(o, ptr) \ + EFL_UI_CLOCK_DATA_GET(o, ptr); \ + if (EINA_UNLIKELY(!ptr)) \ + { \ + CRI("No widget data for object %p (%s)", \ + o, evas_object_type_get(o)); \ + return; \ + } + +#define EFL_UI_CLOCK_DATA_GET_OR_RETURN_VAL(o, ptr, val) \ + EFL_UI_CLOCK_DATA_GET(o, ptr); \ + if (EINA_UNLIKELY(!ptr)) \ + { \ + CRI("No widget data for object %p (%s)", \ + o, evas_object_type_get(o)); \ + return val; \ + } + +#define EFL_UI_CLOCK_CHECK(obj) \ + if (EINA_UNLIKELY(!eo_isa((obj), EFL_UI_CLOCK_CLASS))) \ + return + +#endif diff --git a/src/lib/elementary/elm_config.c b/src/lib/elementary/elm_config.c index 4661611..513a154 100644 --- a/src/lib/elementary/elm_config.c +++ b/src/lib/elementary/elm_config.c @@ -2194,6 +2194,10 @@ _config_update(void) IFCFG(0x000a) _elm_config->icon_theme = eina_stringshare_add(ELM_CONFIG_ICON_THEME_ELEMENTARY); IFCFGEND + + IFCFG(0x000b) + eina_stringshare_refplace(&_elm_config->modules, tcfg->modules); + IFCFGEND /** * Fix user config for current ELM_CONFIG_EPOCH here. **/ --
