aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--configure.ac2
-rw-r--r--src/bin/e_module.c1
-rw-r--r--src/modules/Makefile.mk1
-rw-r--r--src/modules/Makefile_time.mk25
-rw-r--r--src/modules/time/clock.c507
-rw-r--r--src/modules/time/clock.h93
-rw-r--r--src/modules/time/config.c627
-rw-r--r--src/modules/time/e-module-time.edjbin0 -> 14062 bytes
-rw-r--r--src/modules/time/mod.c142
-rw-r--r--src/modules/time/module.desktop.in7
-rw-r--r--src/modules/time/time.c354
11 files changed, 1759 insertions, 0 deletions
diff --git a/configure.ac b/configure.ac
index 7b286c626..1ac583131 100644
--- a/configure.ac
+++ b/configure.ac
@@ -925,6 +925,7 @@ AC_E_OPTIONAL_MODULE([policy_mobile], true)
AC_E_OPTIONAL_MODULE([geolocation], true)
AC_E_OPTIONAL_MODULE([xwayland], $have_wayland, [CHECK_MODULE_XWAYLAND])
AC_E_OPTIONAL_MODULE([wireless], true)
+AC_E_OPTIONAL_MODULE([time], true)
if test "x${HAVE_WL_X11}" != "xyes" && test "x${have_wayland}" = "xyes" && test "x${HAVE_XWAYLAND}" != "xyes"; then
AC_DEFINE_UNQUOTED([HAVE_WAYLAND_ONLY],[1],[enable wayland-only version of enlightenment])
@@ -1127,6 +1128,7 @@ src/modules/music-control/module.desktop
src/modules/packagekit/module.desktop
src/modules/wl_desktop_shell/module.desktop
src/modules/wireless/module.desktop
+src/modules/time/module.desktop
data/xsession/enlightenment.desktop
data/etc/sysactions.conf
data/units/enlightenment.service
diff --git a/src/bin/e_module.c b/src/bin/e_module.c
index 945788de6..25ec3a7b1 100644
--- a/src/bin/e_module.c
+++ b/src/bin/e_module.c
@@ -1024,6 +1024,7 @@ _e_module_whitelist_check(void)
"teamwork",
"temperature",
"tiling",
+ "time",
"winlist",
"wireless",
"wizard",
diff --git a/src/modules/Makefile.mk b/src/modules/Makefile.mk
index bb618da1e..0962f9ee9 100644
--- a/src/modules/Makefile.mk
+++ b/src/modules/Makefile.mk
@@ -129,3 +129,4 @@ include src/modules/Makefile_policy_mobile.mk
include src/modules/Makefile_geolocation.mk
include src/modules/Makefile_wireless.mk
+include src/modules/Makefile_time.mk
diff --git a/src/modules/Makefile_time.mk b/src/modules/Makefile_time.mk
new file mode 100644
index 000000000..14477c1b5
--- /dev/null
+++ b/src/modules/Makefile_time.mk
@@ -0,0 +1,25 @@
+EXTRA_DIST += src/modules/time/module.desktop.in \
+src/modules/time/e-module-time.edj
+if USE_MODULE_TIME
+timedir = $(MDIR)/time
+time_DATA = src/modules/time/e-module-time.edj \
+ src/modules/time/module.desktop
+
+
+timepkgdir = $(MDIR)/time/$(MODULE_ARCH)
+timepkg_LTLIBRARIES = src/modules/time/module.la
+
+src_modules_time_module_la_LIBADD = $(MOD_LIBS)
+src_modules_time_module_la_CPPFLAGS = $(MOD_CPPFLAGS)
+src_modules_time_module_la_LDFLAGS = $(MOD_LDFLAGS)
+src_modules_time_module_la_SOURCES = \
+src/modules/time/clock.c \
+src/modules/time/clock.h \
+src/modules/time/config.c \
+src/modules/time/mod.c \
+src/modules/time/time.c
+
+PHONIES += time install-time
+time: $(timepkg_LTLIBRARIES) $(time_DATA)
+install-time: install-timeDATA install-timepkgLTLIBRARIES
+endif
diff --git a/src/modules/time/clock.c b/src/modules/time/clock.c
new file mode 100644
index 000000000..1952413d1
--- /dev/null
+++ b/src/modules/time/clock.c
@@ -0,0 +1,507 @@
+#include "clock.h"
+
+EINTERN Config *time_config = NULL;
+EINTERN Eina_List *clock_instances = NULL;
+static Ecore_Timer *clock_timer;
+
+static void
+_clock_calendar_month_update(Instance *inst)
+{
+ Evas_Object *od, *oi;
+ int x, y;
+
+ oi = elm_layout_edje_get(inst->o_cal);
+ edje_object_part_text_set(oi, "e.text.month", inst->month);
+ edje_object_part_text_set(oi, "e.text.year", inst->year);
+ for (x = 0; x < 7; x++)
+ {
+ od = edje_object_part_table_child_get(oi, "e.table.daynames", x, 0);
+ edje_object_part_text_set(od, "e.text.label", inst->daynames[x]);
+ edje_object_message_signal_process(od);
+ if (inst->dayweekends[x][0])
+ edje_object_signal_emit(od, "e,state,weekend", "e");
+ else
+ edje_object_signal_emit(od, "e,state,weekday", "e");
+ }
+
+ for (y = 0; y < 6; y++)
+ {
+ for (x = 0; x < 7; x++)
+ {
+ char buf[32];
+
+ od = edje_object_part_table_child_get(oi, "e.table.days", x, y);
+ snprintf(buf, sizeof(buf), "%i", (int)inst->daynums[x][y]);
+ edje_object_part_text_set(od, "e.text.label", buf);
+ if (inst->dayweekends[x][y])
+ edje_object_signal_emit(od, "e,state,weekend", "e");
+ else
+ edje_object_signal_emit(od, "e,state,weekday", "e");
+ if (inst->dayvalids[x][y])
+ edje_object_signal_emit(od, "e,state,visible", "e");
+ else
+ edje_object_signal_emit(od, "e,state,hidden", "e");
+ if (inst->daytoday[x][y])
+ edje_object_signal_emit(od, "e,state,today", "e");
+ else
+ edje_object_signal_emit(od, "e,state,someday", "e");
+ edje_object_message_signal_process(od);
+ }
+ }
+ edje_object_message_signal_process(oi);
+}
+
+static void
+_clock_month_prev_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED)
+{
+ Instance *inst = data;
+ inst->madj--;
+ time_instance_update(inst);
+ _clock_calendar_month_update(inst);
+}
+
+static void
+_clock_month_next_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED)
+{
+ Instance *inst = data;
+ inst->madj++;
+ time_instance_update(inst);
+ _clock_calendar_month_update(inst);
+}
+
+static void
+_clock_popup_dismissed(void *data EINA_UNUSED, Evas_Object *obj, void *info EINA_UNUSED)
+{
+ evas_object_del(obj);
+}
+
+static void
+_eval_instance_size(Instance *inst)
+{
+ Evas_Coord mw, mh;
+ int sw = 0, sh = 0;
+ Evas_Object *ed = elm_layout_edje_get(inst->o_clock);
+
+ edje_object_size_min_get(ed, &mw, &mh);
+
+ if ((mw < 1) || (mh < 1))
+ {
+ if (edje_object_part_exists(ed, "e.sizer"))
+ {
+ edje_object_part_geometry_get(ed, "e.sizer", NULL, NULL, &mw, &mh);
+ }
+ else
+ {
+ Evas_Object *owner;
+
+ owner = e_gadget_site_get(inst->o_clock);
+ switch (e_gadget_site_orient_get(owner))
+ {
+ case E_GADGET_SITE_ORIENT_HORIZONTAL:
+ evas_object_geometry_get(owner, NULL, NULL, NULL, &sh);
+ break;
+
+ case E_GADGET_SITE_ORIENT_VERTICAL:
+ evas_object_geometry_get(owner, NULL, NULL, &sw, NULL);
+ break;
+
+ default: break;
+ }
+
+ evas_object_resize(inst->o_clock, sw, sh);
+ edje_object_message_signal_process(ed);
+
+ edje_object_parts_extends_calc(ed, NULL, NULL, &mw, &mh);
+ }
+ }
+
+ if (mw < 4) mw = 4;
+ if (mh < 4) mh = 4;
+
+ if (mw < sw) mw = sw;
+ if (mh < sh) mh = sh;
+
+ evas_object_size_hint_aspect_set(inst->o_clock, EVAS_ASPECT_CONTROL_BOTH, mw, mh);
+}
+
+static void
+_clock_edje_init(Instance *inst, Evas_Object *o)
+{
+ char datestr[128];
+ const char *digital[] =
+ {
+ "e/gadget/clock/digital",
+ "e/gadget/clock/digital/advanced",
+ };
+
+ time_datestring_format(inst, datestr, sizeof(datestr) - 1);
+ if (inst->cfg->digital_clock)
+ e_theme_edje_object_set(o, NULL, digital[inst->cfg->advanced]);
+ else
+ e_theme_edje_object_set(o, NULL, "e/gadget/clock/analog");
+ if (inst->cfg->show_date)
+ elm_layout_signal_emit(o, "e,state,date,on", "e");
+ else
+ elm_layout_signal_emit(o, "e,state,date,off", "e");
+ if (inst->cfg->digital_24h)
+ elm_layout_signal_emit(o, "e,state,24h,on", "e");
+ else
+ elm_layout_signal_emit(o, "e,state,24h,off", "e");
+ if (inst->cfg->show_seconds)
+ elm_layout_signal_emit(o, "e,state,seconds,on", "e");
+ else
+ elm_layout_signal_emit(o, "e,state,seconds,off", "e");
+
+ elm_object_part_text_set(o, "e.text.sub", datestr);
+ if (inst->cfg->timezone)
+ {
+ Edje_Message_String msg;
+
+ msg.str = (char*)inst->cfg->timezone;
+ edje_object_message_send(elm_layout_edje_get(o), EDJE_MESSAGE_STRING, 1, &msg);
+ }
+ {
+ Edje_Message_String_Int msg;
+ msg.str = (char*)inst->cfg->colorclass[0] ?: "";
+ msg.val = !!inst->cfg->colorclass[0];
+ edje_object_message_send(elm_layout_edje_get(o), EDJE_MESSAGE_STRING_INT, 2, &msg);
+ msg.str = (char*)inst->cfg->colorclass[1] ?: "";
+ msg.val = !!inst->cfg->colorclass[1];
+ edje_object_message_send(elm_layout_edje_get(o), EDJE_MESSAGE_STRING_INT, 3, &msg);
+ }
+ edje_object_message_signal_process(elm_layout_edje_get(o));
+}
+
+static Eina_Bool
+_clock_timer(void *d EINA_UNUSED)
+{
+ Eina_List *l;
+ Instance *inst;
+ Eina_Bool seconds = EINA_FALSE;
+ int sec;
+ char buf[128];
+
+ EINA_LIST_FOREACH(clock_instances, l, inst)
+ {
+ if (!inst->cfg->advanced) continue;
+ seconds |= inst->cfg->show_seconds;
+ sec = time_string_format(inst, buf, sizeof(buf));
+ elm_object_part_text_set(inst->o_clock, "e.text", buf);
+ _eval_instance_size(inst);
+ }
+ sec = seconds ? 1 : (61 - sec);
+ if (clock_timer)
+ ecore_timer_interval_set(clock_timer, sec);
+ else
+ clock_timer = ecore_timer_add(sec, _clock_timer, NULL);
+ return EINA_TRUE;
+}
+
+static void
+_clock_popup_del(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED)
+{
+ Instance *inst = data;
+
+ if (obj != inst->popup) return;
+ inst->popup = inst->o_table = inst->o_cal = NULL;
+}
+
+EINTERN void
+clock_popup_new(Instance *inst)
+{
+ Evas_Object *oi;
+
+ if (inst->popup) return;
+
+ inst->madj = 0;
+
+ time_instance_update(inst);
+
+ inst->popup = elm_ctxpopup_add(inst->o_clock);
+ elm_object_style_set(inst->popup, "noblock");
+ evas_object_smart_callback_add(inst->popup, "dismissed", _clock_popup_dismissed, inst);
+ evas_object_event_callback_add(inst->popup, EVAS_CALLBACK_DEL, _clock_popup_del, inst);
+
+ inst->o_table = elm_table_add(inst->popup);
+
+ oi = elm_layout_add(inst->o_table);
+ inst->o_cal = oi;
+ e_theme_edje_object_set(oi, "base/theme/gadget/clock",
+ "e/gadget/clock/calendar");
+ _clock_calendar_month_update(inst);
+
+ elm_object_signal_callback_add(oi, "e,action,prev", "*",
+ _clock_month_prev_cb, inst);
+ elm_object_signal_callback_add(oi, "e,action,next", "*",
+ _clock_month_next_cb, inst);
+ edje_object_message_signal_process(elm_layout_edje_get(oi));
+ elm_layout_sizing_eval(oi);
+ elm_table_pack(inst->o_table, oi, 0, 1, 1, 1);
+ evas_object_show(oi);
+
+ elm_object_content_set(inst->popup, inst->o_table);
+ e_gadget_util_ctxpopup_place(inst->o_clock, inst->popup, NULL);
+ evas_object_show(inst->popup);
+}
+
+void
+clock_instances_redo(void)
+{
+ Eina_List *l;
+ Instance *inst;
+
+ EINA_LIST_FOREACH(clock_instances, l, inst)
+ {
+ _clock_edje_init(inst, inst->o_clock);
+ _eval_instance_size(inst);
+ }
+}
+
+static void
+_clock_cb_mouse_down(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event)
+{
+ Instance *inst = data;
+ Evas_Event_Mouse_Down *ev = event;
+
+ if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
+ if (ev->button == 1)
+ {
+ if (inst->popup) elm_ctxpopup_dismiss(inst->popup);
+ else clock_popup_new(inst);
+ }
+ else if (ev->button == 3)
+ e_gadget_configure(inst->o_clock);
+}
+
+static void
+_clock_sizing_changed_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED)
+{
+ _eval_instance_size(data);
+}
+
+static void
+clock_del(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
+{
+ Instance *inst = data;
+ Eina_List *l;
+ Eina_Bool advanced = EINA_FALSE, seconds = EINA_FALSE;
+
+ clock_instances = eina_list_remove(clock_instances, inst);
+ evas_object_del(inst->popup);
+ time_daynames_clear(inst);
+ free(inst);
+ EINA_LIST_FOREACH(clock_instances, l, inst)
+ {
+ advanced |= !!inst->cfg->advanced;
+ seconds |= !!inst->cfg->show_seconds;
+ if (seconds) break;
+ }
+ if (seconds) return; //no change possible
+ E_FREE_FUNC(clock_timer, ecore_timer_del);
+ if (advanced)
+ _clock_timer(NULL);
+}
+
+static Config_Item *
+_conf_item_get(int *id, Eina_Bool digital)
+{
+ Config_Item *ci;
+ Eina_List *l;
+
+ if (*id > 0)
+ {
+ EINA_LIST_FOREACH(time_config->items, l, ci)
+ if (*id == ci->id) return ci;
+ }
+
+ ci = E_NEW(Config_Item, 1);
+ if (!*id)
+ ci->id = time_config->items ? eina_list_count(time_config->items) + 1 : 1;
+ else
+ ci->id = -1;
+
+ ci->weekend.start = 6;
+ ci->weekend.len = 2;
+ ci->week.start = 1;
+ ci->digital_clock = digital;
+ ci->digital_24h = 0;
+ ci->show_seconds = 0;
+ ci->show_date = 0;
+ ci->time_str[0] = eina_stringshare_add("%I:%M");
+ ci->time_str[1] = eina_stringshare_add("%F");
+
+ if (ci->id < 1) return ci;
+ time_config->items = eina_list_append(time_config->items, ci);
+ e_config_save_queue();
+
+ return ci;
+}
+
+static Evas_Object *
+_clock_gadget_configure(Evas_Object *g)
+{
+ Instance *inst = evas_object_data_get(g, "clock");
+ return config_clock(inst->cfg);
+}
+
+static void
+_clock_gadget_removed_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info)
+{
+ Instance *inst = data;
+
+ if (inst->o_clock != event_info) return;
+ time_config->items = eina_list_remove(time_config->items, inst->cfg);
+ eina_stringshare_del(inst->cfg->timezone);
+ eina_stringshare_del(inst->cfg->time_str[0]);
+ eina_stringshare_del(inst->cfg->time_str[1]);
+ E_FREE(inst->cfg);
+}
+
+static void
+_clock_gadget_created_cb(void *data, Evas_Object *obj, void *event_info EINA_UNUSED)
+{
+ Instance *inst = data;
+
+ e_gadget_configure_cb_set(inst->o_clock, _clock_gadget_configure);
+ evas_object_smart_callback_del_full(obj, "gadget_created", _clock_gadget_created_cb, data);
+ if (inst->cfg->advanced)
+ {
+ _clock_timer(NULL);
+ ecore_timer_reset(clock_timer);
+ }
+ _eval_instance_size(inst);
+}
+
+static Evas_Object *
+clock_create(Evas_Object *parent, Instance *inst, E_Gadget_Site_Orient orient)
+{
+ Evas_Object *o;
+ const char *sig = NULL;
+
+ inst->o_clock = o = elm_layout_add(parent);
+ elm_layout_signal_callback_add(o, "e,state,sizing,changed", "*",
+ _clock_sizing_changed_cb, inst);
+
+ _clock_edje_init(inst, o);
+
+ switch (orient)
+ {
+ case E_GADGET_SITE_ORIENT_HORIZONTAL:
+ sig = "e,state,horizontal";
+ break;
+
+ case E_GADGET_SITE_ORIENT_VERTICAL:
+ sig = "e,state,vertical";
+ break;
+
+ default:
+ sig = "e,state,float";
+ }
+
+ elm_layout_signal_emit(inst->o_clock, sig, "e");
+
+ evas_object_event_callback_add(o, EVAS_CALLBACK_DEL, clock_del, inst);
+ evas_object_smart_callback_add(parent, "gadget_created", _clock_gadget_created_cb, inst);
+ evas_object_smart_callback_add(parent, "gadget_removed", _clock_gadget_removed_cb, inst);
+ evas_object_data_set(o, "clock", inst);
+
+ evas_object_event_callback_add(inst->o_clock,
+ EVAS_CALLBACK_MOUSE_DOWN,
+ _clock_cb_mouse_down,
+ inst);
+
+ if (inst->cfg->id < 0) return o;
+ clock_instances = eina_list_append(clock_instances, inst);
+
+ return o;
+}
+
+EINTERN Evas_Object *
+digital_clock_create(Evas_Object *parent, int *id, E_Gadget_Site_Orient orient)
+{
+ Instance *inst;
+
+ inst = E_NEW(Instance, 1);
+ inst->cfg = _conf_item_get(id, 1);
+ return clock_create(parent, inst, orient);
+}
+
+EINTERN Evas_Object *
+analog_clock_create(Evas_Object *parent, int *id, E_Gadget_Site_Orient orient)
+{
+ Instance *inst;
+
+ inst = E_NEW(Instance, 1);
+ inst->cfg = _conf_item_get(id, 0);
+ return clock_create(parent, inst, orient);
+}
+
+typedef struct Wizard_Item
+{
+ E_Gadget_Wizard_End_Cb cb;
+ void *data;
+ int id;
+} Wizard_Item;
+
+static void
+_wizard_end(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
+{
+ Wizard_Item *wi = data;
+
+ wi->cb(wi->data, wi->id);
+ free(wi);
+}
+
+static void
+clock_wizard(E_Gadget_Wizard_End_Cb cb, void *data, Eina_Bool digital)
+{
+ int id = 0;
+ Config_Item *ci;
+ Wizard_Item *wi;
+
+ wi = E_NEW(Wizard_Item, 1);
+ wi->cb = cb;
+ wi->data = data;
+
+ ci = _conf_item_get(&id, digital);
+ wi->id = ci->id;
+ evas_object_event_callback_add(config_clock(ci), EVAS_CALLBACK_DEL, _wizard_end, wi);
+}
+
+EINTERN void
+digital_clock_wizard(E_Gadget_Wizard_End_Cb cb, void *data)
+{
+ clock_wizard(cb, data, 1);
+}
+
+EINTERN void
+analog_clock_wizard(E_Gadget_Wizard_End_Cb cb, void *data)
+{
+ clock_wizard(cb, data, 0);
+}
+
+EINTERN void
+time_config_update(Config_Item *ci)
+{
+ Eina_List *l;
+ Instance *inst;
+ Eina_Bool advanced = EINA_FALSE;
+
+ ci->week.start = (ci->weekend.start + ci->weekend.len) % 7;
+ EINA_LIST_FOREACH(clock_instances, l, inst)
+ {
+ if (inst->cfg != ci) continue;
+ _clock_edje_init(inst, inst->o_clock);
+ if (!advanced)
+ {
+ advanced |= inst->cfg->advanced;
+ if (!inst->cfg->advanced) continue;
+ _clock_timer(NULL);
+ ecore_timer_reset(clock_timer);
+ }
+ _eval_instance_size(inst);
+ }
+ if (!advanced)
+ E_FREE_FUNC(clock_timer, ecore_timer_del);
+ e_config_save_queue();
+}
diff --git a/src/modules/time/clock.h b/src/modules/time/clock.h
new file mode 100644
index 000000000..990d42b56
--- /dev/null
+++ b/src/modules/time/clock.h
@@ -0,0 +1,93 @@
+#ifndef CLOCK_H
+#define CLOCK_H
+
+#include "e.h"
+
+E_API extern E_Module_Api e_modapi;
+
+E_API void *e_modapi_init (E_Module *m);
+E_API int e_modapi_shutdown (E_Module *m);
+E_API int e_modapi_save (E_Module *m);
+
+typedef struct _Config Config;
+typedef struct _Config_Item Config_Item;
+typedef struct _Instance Instance;
+
+typedef enum
+{
+ CLOCK_DATE_DISPLAY_NONE,
+ CLOCK_DATE_DISPLAY_FULL,
+ CLOCK_DATE_DISPLAY_NUMERIC,
+ CLOCK_DATE_DISPLAY_DATE_ONLY,
+ CLOCK_DATE_DISPLAY_ISO8601,
+ CLOCK_DATE_DISPLAY_CUSTOM,
+} Clock_Date_Display;
+
+struct _Config
+{
+ Eina_List *items;
+
+ E_Module *module;
+ Evas_Object *config_dialog;
+};
+
+struct _Config_Item
+{
+ int id;
+ struct {
+ int start, len; // 0->6 0 == sun, 6 == sat, number of days
+ } weekend;
+ struct {
+ int start; // 0->6 0 == sun, 6 == sat
+ } week;
+ Eina_Bool digital_clock;
+ Eina_Bool digital_24h;
+ Eina_Bool show_seconds;
+ Clock_Date_Display show_date;
+ Eina_Bool advanced;
+ Eina_Stringshare *timezone;
+ Eina_Stringshare *time_str[2];
+ Eina_Stringshare *colorclass[2];
+};
+
+
+struct _Instance
+{
+ Evas_Object *o_clock, *o_table, *o_cal;
+ Evas_Object *popup;
+
+ int madj;
+
+ char year[8];
+ char month[64];
+ const char *daynames[7];
+ unsigned char daynums[7][6];
+ Eina_Bool dayweekends[7][6];
+ Eina_Bool dayvalids[7][6];
+ Eina_Bool daytoday[7][6];
+ Config_Item *cfg;
+};
+
+EINTERN Evas_Object *config_clock(Config_Item *);
+EINTERN void config_timezone_populate(Evas_Object *obj, const char *name);
+void clock_instances_redo(void);
+
+EINTERN void time_daynames_clear(Instance *inst);
+EINTERN void time_datestring_format(Instance *inst, char *buf, int bufsz);
+EINTERN int time_string_format(Instance *inst, char *buf, int bufsz);
+EINTERN void time_instance_update(Instance *inst);
+EINTERN void time_init(void);
+EINTERN void time_shutdown(void);
+EINTERN void time_zoneinfo_scan(Evas_Object *obj);
+
+EINTERN Evas_Object *digital_clock_create(Evas_Object *parent, int *id, E_Gadget_Site_Orient orient);
+EINTERN Evas_Object *analog_clock_create(Evas_Object *parent, int *id, E_Gadget_Site_Orient orient);
+EINTERN void digital_clock_wizard(E_Gadget_Wizard_End_Cb cb, void *data);
+EINTERN void analog_clock_wizard(E_Gadget_Wizard_End_Cb cb, void *data);
+EINTERN void clock_popup_new(Instance *inst);
+EINTERN void time_config_update(Config_Item *ci);
+
+extern Config *time_config;
+extern Eina_List *clock_instances;
+
+#endif
diff --git a/src/modules/time/config.c b/src/modules/time/config.c
new file mode 100644
index 000000000..ad4c528ef
--- /dev/null
+++ b/src/modules/time/config.c
@@ -0,0 +1,627 @@
+#include "clock.h"
+#include <time.h>
+
+static const char *datecfg[] =
+{
+ N_("None"),
+ N_("Full"),
+ N_("Numeric"),
+ N_("Date-only"),
+ N_("ISO 8601"),
+ N_("Custom"),
+};
+
+
+static void
+_config_rect_click(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info)
+{
+ Evas_Event_Mouse_Up *ev = event_info;
+ ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
+ evas_object_del(data);
+}
+
+static Evas_Object *
+_config_autoclose_rect_add(Evas_Object *obj)
+{
+ Evas_Object *rect;
+
+ rect = evas_object_rectangle_add(e_comp->evas);
+ e_comp_object_util_fullscreen(rect);
+ evas_object_color_set(rect, 0, 0, 0, 0);
+ evas_object_layer_set(rect, E_LAYER_MENU - 1);
+ evas_object_show(rect);
+ evas_object_event_callback_add(rect, EVAS_CALLBACK_MOUSE_UP, _config_rect_click, obj);
+ return rect;
+}
+
+static void
+_config_close(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
+{
+ time_config->config_dialog = NULL;
+}
+
+static void
+_config_changed(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
+{
+ time_config_update(data);
+}
+
+static void
+_clock_color_dismissed(void *data EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED)
+{
+ evas_object_del(obj);
+}
+
+static void
+_config_color_reset(void *data, Evas_Object *obj, void *event_info EINA_UNUSED)
+{
+ Config_Item *ci = data;
+ Evas_Object *cs;
+ int num;
+
+ num = !evas_object_data_get(obj, "bg_color");
+ if (ci->colorclass[num])
+ {
+ elm_config_color_overlay_unset(ci->colorclass[num]);
+ edje_color_class_del(ci->colorclass[num]);
+ }
+ eina_stringshare_replace(&ci->colorclass[num], NULL);
+ cs = evas_object_data_get(obj, "colorselector");
+ elm_colorselector_color_set(cs, 0, 0, 0, 0);
+ elm_colorselector_palette_item_color_set(evas_object_data_get(cs, "colorselector_it"),
+ 0, 0, 0, 0);
+ time_config_update(data);
+}
+
+static void
+_config_color_change(void *data, Evas_Object *obj, void *event_info EINA_UNUSED)
+{
+ Config_Item *ci = data;
+ int r, g, b, a;
+ int num;
+ char buf[1024];
+
+ num = !evas_object_data_get(obj, "bg_color");
+ elm_colorselector_color_get(obj, &r, &g, &b, &a);
+
+ if (!ci->colorclass[num])
+ {
+ snprintf(buf, sizeof(buf), "e.clock_color_%s.%d", num ? "fg" : "bg", ci->id);
+ eina_stringshare_replace(&ci->colorclass[num], buf);
+ }
+ elm_config_color_overlay_set(ci->colorclass[num], r, g, b, a, 0, 0, 0, 0, 0, 0, 0, 0);
+ edje_color_class_set(ci->colorclass[num], r, g, b, a, 0, 0, 0, 0, 0, 0, 0, 0);
+ elm_colorselector_palette_item_color_set(evas_object_data_get(obj, "colorselector_it"), r, g, b, a);
+ time_config_update(data);
+}
+
+static void
+_config_color_setup(void *data, Evas_Object *obj, void *event_info EINA_UNUSED)
+{
+ Config_Item *ci = data;
+ Evas_Object *cs, *ctx, *bx, *bt, *rect;
+ int r, g, b, a, x, y;
+ Eina_Bool bg;
+ const char *ccname, *ccnames[] =
+ {
+ "e.clock_color_bg",
+ "e.clock_color_fg",
+ };
+
+ bg = !!evas_object_data_get(obj, "bg_color");
+ ccname = ci->colorclass[!bg];
+ if (!ccname) ccname = ccnames[!bg];
+ edje_color_class_get(ccname, &r, &g, &b, &a,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+
+ bx = elm_box_add(obj);
+ E_FILL(bx);
+ evas_object_show(bx);
+
+ cs = elm_colorselector_add(obj);
+ evas_object_data_set(cs, "colorselector_bt", evas_object_data_get(obj, "colorselector_tt"));
+ evas_object_data_set(cs, "bg_color", (void*)(long)bg);
+ evas_object_smart_callback_add(cs, "changed,user", _config_color_change, ci);
+ elm_colorselector_mode_set(cs, ELM_COLORSELECTOR_COMPONENTS);
+ elm_colorselector_color_set(cs, r, g, b, a);
+ E_FILL(cs);
+ elm_box_pack_end(bx, cs);
+ evas_object_show(cs);
+
+ bt = elm_button_add(bx);
+ evas_object_data_set(bt, "colorselector", cs);
+ evas_object_data_set(bt, "bg_color", (void*)(long)bg);
+ elm_object_text_set(bt, _("Reset"));
+ evas_object_smart_callback_add(bt, "clicked", _config_color_reset, ci);
+ evas_object_show(bt);
+ elm_box_pack_end(bx, bt);
+
+ /* size hints: the final frontier */
+ rect = evas_object_rectangle_add(e_comp->elm);
+ evas_object_geometry_get(time_config->config_dialog, NULL, NULL, &x, &y);
+ evas_object_size_hint_min_set(rect, x - 10, 1);
+ e_comp_object_util_del_list_append(bx, rect);
+ elm_box_pack_end(bx, rect);
+
+ ctx = elm_ctxpopup_add(obj);
+ elm_ctxpopup_hover_parent_set(ctx, e_comp->elm);
+ evas_object_layer_set(ctx, E_LAYER_MENU);
+ elm_object_style_set(ctx, "noblock");
+ e_comp_object_util_del_list_append(ctx, _config_autoclose_rect_add(ctx));
+ evas_object_smart_callback_add(ctx, "dismissed", _clock_color_dismissed, NULL);
+ elm_object_content_set(ctx, bx);
+ evas_pointer_canvas_xy_get(e_comp->evas, &x, &y);
+ evas_object_move(ctx, x, y);
+ evas_object_show(ctx);
+}
+
+static void
+_config_digital_timestr_update(Config_Item *ci, Evas_Object *obj, int idx)
+{
+ const char *str, *p;
+ char seconds[] =
+ {
+ 'S',
+ 's',
+ 'r',
+ 'T',
+ };
+ unsigned int i;
+
+ str = elm_entry_entry_get(obj);
+ eina_stringshare_replace(&ci->time_str[idx], str);
+ ci->show_seconds = 0;
+ for (p = strchr(str, '%'); p; p = strchr(p + 1, '%'))
+ {
+ for (i = 0; i < EINA_C_ARRAY_LENGTH(seconds); i++)
+ if (p[1] == seconds[i])
+ {
+ ci->show_seconds = 1;
+ time_config_update(ci);
+ return;
+ }
+ }
+
+ time_config_update(ci);
+}
+
+static void
+_config_digital_datestr_changed(void *data, Evas_Object *obj, void *event_info EINA_UNUSED)
+{
+ _config_digital_timestr_update(data, obj, 1);
+}
+
+static Evas_Object *
+_config_date_custom(Config_Item *ci, Evas_Object *bx)
+{
+ Evas_Object *o;
+
+ o = elm_entry_add(bx);
+ elm_entry_single_line_set(o, 1);
+ elm_object_tooltip_text_set(o, _("strftime() format string"));
+ elm_entry_entry_set(o, ci->time_str[1]);
+ evas_object_smart_callback_add(o, "changed,user", _config_digital_datestr_changed, ci);
+ E_FILL(o);
+ E_EXPAND(o);
+ evas_object_show(o);
+ elm_box_pack_end(bx, o);
+ return o;
+}
+
+static void
+_config_date_changed(void *data, Evas_Object *obj, void *event_info)
+{
+ Config_Item *ci = data;
+ Evas_Object *bx = elm_object_parent_widget_get(obj);
+ Eina_Bool custom;
+
+ custom = ci->show_date == CLOCK_DATE_DISPLAY_CUSTOM;
+ ci->show_date = (intptr_t)elm_object_item_data_get(event_info);
+ if (custom)
+ {
+ elm_box_unpack(bx, obj);
+ elm_box_clear(bx);
+ E_FILL(obj);
+ E_EXPAND(obj);
+ elm_box_pack_end(bx, obj);
+ }
+ else if (ci->show_date == CLOCK_DATE_DISPLAY_CUSTOM)
+ {
+ E_WEIGHT(obj, 0, 0);
+ E_ALIGN(obj, 0, 0.5);
+ elm_object_focus_set(_config_date_custom(ci, bx), 1);
+ }
+ time_config_update(ci);
+}
+
+static void
+_config_weekend_changed(void *data, Evas_Object *obj EINA_UNUSED, void *event_info)
+{
+ Config_Item *ci = data;
+
+ ci->weekend.start = (intptr_t)elm_object_item_data_get(event_info);
+ time_config_update(ci);
+}
+
+static void
+_config_weekend_end_changed(void *data, Evas_Object *obj EINA_UNUSED, void *event_info)
+{
+ Config_Item *ci = data;
+ int end;
+
+ end = (intptr_t)elm_object_item_data_get(event_info);
+ if (end < ci->weekend.start) end += 7;
+ ci->weekend.len = end - ci->weekend.start + 1;
+ time_config_update(ci);
+}
+
+static void
+_config_date_populate(void *data, Evas_Object *obj, void *event_info EINA_UNUSED)
+{
+ Config_Item *ci = data;
+ unsigned int i;
+
+ elm_hoversel_clear(obj);
+ for (i = 0; i <= 5; i++)
+ if (ci->show_date != i)
+ elm_hoversel_item_add(obj, datecfg[i], NULL, ELM_ICON_NONE, NULL, (uintptr_t*)(unsigned long)i);
+}
+
+static void
+_config_weekend_populate(void *data, Evas_Object *obj, void *event_info EINA_UNUSED)
+{
+ Config_Item *ci = data;
+ char daynames[7][64];
+ struct tm tm;
+ int i;
+
+ memset(&tm, 0, sizeof(struct tm));
+ for (i = 0; i < 7; i++)
+ {
+ tm.tm_wday = i;
+ strftime(daynames[i], sizeof(daynames[i]), "%A", &tm);
+ }
+
+ elm_hoversel_clear(obj);
+ for (i = ci->weekend.start + 1; i <= 6; i++)
+ if (ci->weekend.start != i)
+ elm_hoversel_item_add(obj, daynames[i], NULL, ELM_ICON_NONE, NULL, (intptr_t*)(long)i);
+ for (i = 0; i < ci->weekend.start; i++)
+ elm_hoversel_item_add(obj, daynames[i], NULL, ELM_ICON_NONE, NULL, (intptr_t*)(long)i);
+}
+
+static void
+_config_weekend_end_populate(void *data, Evas_Object *obj, void *event_info EINA_UNUSED)
+{
+ Config_Item *ci = data;
+ char daynames[7][64];
+ struct tm tm;
+ int i, end;
+
+ memset(&tm, 0, sizeof(struct tm));
+ for (i = 0; i < 7; i++)
+ {
+ tm.tm_wday = i;
+ strftime(daynames[i], sizeof(daynames[i]), "%A", &tm);
+ }
+
+ elm_hoversel_clear(obj);
+ end = (ci->weekend.start + ci->weekend.len - 1) % 7;
+ for (i = end + 1; i <= 6; i++)
+ if (end != i)
+ elm_hoversel_item_add(obj, daynames[i], NULL, ELM_ICON_NONE, NULL, (intptr_t*)(long)i);
+ for (i = 0; i < end; i++)
+ elm_hoversel_item_add(obj, daynames[i], NULL, ELM_ICON_NONE, NULL, (intptr_t*)(long)i);
+}
+
+static void
+_config_timezone_setup(void *data, Evas_Object *obj, void *event_info EINA_UNUSED)
+{
+ Evas_Object *hover, *gl;
+
+ hover = elm_hover_add(e_comp->elm);
+ evas_object_layer_set(hover, E_LAYER_MENU);
+ elm_hover_parent_set(hover, elm_object_parent_widget_get(obj));
+ elm_hover_target_set(hover, elm_object_parent_widget_get(obj));
+
+ gl = elm_genlist_add(hover);
+ evas_object_layer_set(gl, E_LAYER_MENU);
+ evas_object_data_set(gl, "config_item", data);
+ evas_object_data_set(gl, "button", obj);
+ elm_genlist_mode_set(gl, ELM_LIST_COMPRESS);
+ elm_genlist_homogeneous_set(gl, 1);
+ elm_scroller_bounce_set(gl, 0, 0);
+ evas_object_show(gl);
+ elm_object_part_content_set(hover, "middle", gl);
+ time_zoneinfo_scan(gl);
+ e_comp_object_util_del_list_append(gl, hover);
+ e_comp_object_util_del_list_append(gl, _config_autoclose_rect_add(gl));
+ evas_object_show(hover);
+}
+
+static void
+_config_digital_timestr_changed(void *data, Evas_Object *obj, void *event_info EINA_UNUSED)
+{
+ _config_digital_timestr_update(data, obj, 0);
+}
+
+static void
+_config_label_add(Evas_Object *tb, const char *txt, int row)
+{
+ Evas_Object *o;
+
+ o = elm_label_add(tb);
+ E_ALIGN(o, 0, 0.5);
+ elm_object_text_set(o, txt);
+ evas_object_show(o);
+ elm_table_pack(tb, o, 0, row, 1, 1);
+}
+
+static void
+_config_digital_rows_setup(Config_Item *ci, Evas_Object *tb)
+{
+ int row = 1;
+ Evas_Object *o;
+
+ evas_object_del(elm_table_child_get(tb, 0, 1));
+ evas_object_del(elm_table_child_get(tb, 1, 1));
+ evas_object_del(elm_table_child_get(tb, 0, 2));
+ evas_object_del(elm_table_child_get(tb, 1, 2));
+ if (ci->advanced)
+ {
+ _config_label_add(tb, _("Time string:"), row);
+ o = elm_entry_add(tb);
+ E_FILL(o);
+ evas_object_show(o);
+ elm_entry_single_line_set(o, 1);
+ elm_entry_entry_set(o, ci->time_str[0]);
+ elm_object_focus_set(o, 1);
+ evas_object_smart_callback_add(o, "changed,user", _config_digital_timestr_changed, ci);
+ elm_table_pack(tb, o, 1, row++, 1, 1);
+
+ o = elm_separator_add(tb);
+ E_EXPAND(o);
+ E_FILL(o);
+ elm_separator_horizontal_set(o, 1);
+ evas_object_show(o);
+ elm_table_pack(tb, o, 0, row++, 2, 1);
+ return;
+ }
+ if (ci->digital_clock)
+ {
+ _config_label_add(tb, _("24-hour Display:"), row);
+ o = elm_check_add(tb);
+ E_FILL(o);
+ evas_object_show(o);
+ elm_object_style_set(o, "toggle");
+ elm_object_part_text_set(o, "on", "On");
+ elm_object_part_text_set(o, "off", "Off");
+ elm_check_state_pointer_set(o, &ci->digital_24h);
+ evas_object_smart_callback_add(o, "changed", _config_changed, ci);
+ elm_table_pack(tb, o, 1, row++, 1, 1);
+ }
+
+ _config_label_add(tb, _("Show Seconds:"), row);
+ o = elm_check_add(tb);
+ E_FILL(o);
+ evas_object_show(o);
+ elm_object_style_set(o, "toggle");
+ elm_object_part_text_set(o, "on", _("On"));
+ elm_object_part_text_set(o, "off", _("Off"));
+ elm_check_state_pointer_set(o, &ci->show_seconds);
+ evas_object_smart_callback_add(o, "changed", _config_changed, ci);
+ elm_table_pack(tb, o, 1, row++, 1, 1);
+}
+
+static void
+_config_advanced_changed(void *data, Evas_Object *obj, void *event_info EINA_UNUSED)
+{
+ Config_Item *ci = data;
+
+ _config_digital_rows_setup(data, evas_object_data_get(obj, "table"));
+ time_config_update(ci);
+}
+
+EINTERN Evas_Object *
+config_clock(Config_Item *ci)
+{
+ Evas_Object *popup, *tb, *o, *bx;
+ int i, row = 0;
+ char daynames[7][64];
+ struct tm tm;
+
+ memset(&tm, 0, sizeof(struct tm));
+ for (i = 0; i < 7; i++)
+ {
+ tm.tm_wday = i;
+ strftime(daynames[i], sizeof(daynames[i]), "%A", &tm);
+ }
+ popup = elm_popup_add(e_comp->elm);
+ E_EXPAND(popup);
+ evas_object_layer_set(popup, E_LAYER_POPUP);
+ elm_popup_allow_events_set(popup, 1);
+ elm_popup_scrollable_set(popup, 1);
+
+ tb = elm_table_add(popup);
+ E_EXPAND(tb);
+ evas_object_show(tb);
+ elm_object_content_set(popup, tb);
+
+ if (ci->digital_clock)
+ {
+ _config_label_add(tb, _("Mode"), row);
+ o = elm_check_add(tb);
+ E_FILL(o);
+ evas_object_show(o);
+ elm_object_style_set(o, "toggle");
+ elm_object_part_text_set(o, "on", _("Advanced"));
+ elm_object_part_text_set(o, "off", _("Simple"));
+ elm_check_state_pointer_set(o, &ci->advanced);
+ evas_object_smart_callback_add(o, "changed", _config_advanced_changed, ci);
+ evas_object_data_set(o, "table", tb);
+ elm_table_pack(tb, o, 1, row++, 1, 1);
+ }
+ _config_digital_rows_setup(ci, tb);
+ row = 3;
+
+ _config_label_add(tb, _("Date Display:"), row);
+ bx = elm_box_add(tb);
+ elm_box_horizontal_set(bx, 1);
+ evas_object_show(bx);
+ elm_table_pack(tb, bx, 1, row++, 1, 1);
+ E_FILL(bx);
+ E_EXPAND(bx);
+ o = elm_hoversel_add(tb);
+ elm_box_pack_end(bx, o);
+ elm_hoversel_hover_parent_set(o, popup);
+ elm_hoversel_auto_update_set(o, 1);
+ evas_object_show(o);
+ evas_object_smart_callback_add(o, "clicked", _config_date_populate, ci);
+ evas_object_smart_callback_add(o, "selected", _config_date_changed, ci);
+ elm_object_text_set(o, datecfg[ci->show_date]);
+ if (ci->show_date == CLOCK_DATE_DISPLAY_CUSTOM)
+ {
+ E_ALIGN(o, 0, 0.5);
+ E_WEIGHT(o, 0, 0);
+ o = _config_date_custom(ci, bx);
+ }
+ else
+ {
+ E_FILL(o);
+ E_EXPAND(o);
+ }
+
+ _config_label_add(tb, _("Weekend Start:"), row);
+ o = elm_hoversel_add(tb);
+ E_FILL(o);
+ elm_hoversel_hover_parent_set(o, popup);
+ elm_hoversel_auto_update_set(o, 1);
+ evas_object_show(o);
+ elm_table_pack(tb, o, 1, row++, 1, 1);
+ elm_object_text_set(o, daynames[ci->weekend.start]);
+ evas_object_smart_callback_add(o, "clicked", _config_weekend_populate, ci);
+ evas_object_smart_callback_add(o, "selected", _config_weekend_changed, ci);
+
+ _config_label_add(tb, _("Weekend End:"), row);
+ o = elm_hoversel_add(tb);
+ E_FILL(o);
+ elm_hoversel_hover_parent_set(o, popup);
+ elm_hoversel_auto_update_set(o, 1);
+ evas_object_show(o);
+ elm_table_pack(tb, o, 1, row++, 1, 1);
+ elm_object_text_set(o, daynames[(ci->weekend.start + ci->weekend.len - 1) % 7]);
+ evas_object_smart_callback_add(o, "clicked", _config_weekend_end_populate, ci);
+ evas_object_smart_callback_add(o, "selected", _config_weekend_end_changed, ci);
+
+ _config_label_add(tb, _("Timezone:"), row);
+ o = elm_button_add(tb);
+ E_FILL(o);
+ elm_object_text_set(o, ci->timezone ?: _("System"));
+ evas_object_show(o);
+ evas_object_smart_callback_add(o, "clicked", _config_timezone_setup, ci);
+ elm_table_pack(tb, o, 1, row++, 1, 1);
+
+ for (i = 0; i <= 1; i++)
+ {
+ const char *ccname, *names[] =
+ {
+ N_("Background"),
+ N_("Foreground"),
+ };
+ const char *ccnames[] =
+ {
+ "e.clock_color_bg",
+ "e.clock_color_fg",
+ };
+ Evas_Object *cs;
+ Elm_Object_Item *it;
+ int r, g, b, a;
+
+ cs = elm_colorselector_add(tb);
+ elm_colorselector_mode_set(cs, ELM_COLORSELECTOR_PALETTE);
+ ccname = ci->colorclass[i];
+ if (!ccname) ccname = ccnames[i];
+ edje_color_class_get(ccname, &r, &g, &b, &a,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+
+ it = elm_colorselector_palette_color_add(cs, r, g, b, a);
+ o = elm_button_add(tb);
+ elm_object_text_set(o, names[i]);
+ elm_object_content_set(o, cs);
+ E_FILL(o);
+ if (!i)
+ evas_object_data_set(o, "bg_color", (void*)1L);
+ evas_object_data_set(o, "colorselector_it", it);
+ evas_object_smart_callback_add(o, "clicked", _config_color_setup, ci);
+ evas_object_show(o);
+ elm_table_pack(tb, o, i, row, 1, 1);
+ }
+
+ popup = e_comp_object_util_add(popup, E_COMP_OBJECT_TYPE_NONE);
+ evas_object_layer_set(popup, E_LAYER_POPUP);
+ evas_object_resize(popup, e_zone_current_get()->w / 4, e_zone_current_get()->h / 3);
+ e_comp_object_util_center(popup);
+ evas_object_show(popup);
+ e_comp_object_util_autoclose(popup, NULL, e_comp_object_util_autoclose_on_escape, NULL);
+ evas_object_event_callback_add(popup, EVAS_CALLBACK_DEL, _config_close, NULL);
+
+ return time_config->config_dialog = popup;
+}
+
+
+static char *
+_config_timezone_text_get(const char *str, Evas_Object *obj EINA_UNUSED, const char *part EINA_UNUSED)
+{
+ return strdup(str);
+}
+
+static void
+_config_timezone_text_del(void *d, Evas_Object *obj EINA_UNUSED)
+{
+ free(d);
+}
+
+static int
+_config_timezone_sort(void *ita, void *itb)
+{
+ const char *a, *b;
+ a = elm_object_item_data_get(ita);
+ b = elm_object_item_data_get(itb);
+ return strcmp(a, b);
+}
+
+static void
+_config_timezone_set(void *data EINA_UNUSED, Evas_Object *obj, void *event_info)
+{
+ char *tz = elm_object_item_data_get(event_info);
+ Config_Item *ci;
+ Evas_Object *bt;
+
+ ci = evas_object_data_get(obj, "config_item");
+ bt = evas_object_data_get(obj, "button");
+ eina_stringshare_replace(&ci->timezone, tz);
+ elm_object_text_set(bt, tz);
+ time_config_update(ci);
+ evas_object_del(obj);
+}
+
+EINTERN void
+config_timezone_populate(Evas_Object *obj, const char *name)
+{
+ static const Elm_Genlist_Item_Class itc =
+ {
+ .item_style = "default",
+ .func = {
+ .text_get = (Elm_Genlist_Item_Text_Get_Cb)_config_timezone_text_get,
+ .del = _config_timezone_text_del,
+ },
+ .version = ELM_GENLIST_ITEM_CLASS_VERSION
+ };
+ Config_Item *ci;
+ Elm_Object_Item *it;
+
+ it = elm_genlist_item_sorted_insert(obj, &itc, strdup(name), NULL, 0, (Eina_Compare_Cb)_config_timezone_sort, _config_timezone_set, NULL);
+ ci = evas_object_data_get(obj, "config_item");
+ if (eina_streq(name, ci->timezone))
+ elm_genlist_item_bring_in(it, ELM_GENLIST_ITEM_SCROLLTO_MIDDLE);
+}
diff --git a/src/modules/time/e-module-time.edj b/src/modules/time/e-module-time.edj
new file mode 100644
index 000000000..756669888
--- /dev/null
+++ b/src/modules/time/e-module-time.edj
Binary files differ
diff --git a/src/modules/time/mod.c b/src/modules/time/mod.c
new file mode 100644
index 000000000..4552f199a
--- /dev/null
+++ b/src/modules/time/mod.c
@@ -0,0 +1,142 @@
+#include "clock.h"
+static E_Config_DD *conf_edd = NULL;
+static E_Config_DD *conf_item_edd = NULL;
+static E_Action *act = NULL;
+
+static void
+_e_mod_action_cb(E_Object *obj EINA_UNUSED, const char *params, ...)
+{
+ Eina_List *l;
+ Instance *inst;
+
+ if (!eina_streq(params, "show_calendar")) return;
+
+ EINA_LIST_FOREACH(clock_instances, l, inst)
+ if (inst->popup)
+ {
+ elm_ctxpopup_dismiss(inst->popup);
+ inst->popup = NULL;
+ }
+ else
+ clock_popup_new(inst);
+}
+
+EINTERN void
+clock_init(void)
+{
+ conf_item_edd = E_CONFIG_DD_NEW("Config_Item", Config_Item);
+#undef T
+#undef D
+#define T Config_Item
+#define D conf_item_edd
+ E_CONFIG_VAL(D, T, id, INT);
+ E_CONFIG_VAL(D, T, weekend.start, INT);
+ E_CONFIG_VAL(D, T, weekend.len, INT);
+ E_CONFIG_VAL(D, T, week.start, INT);
+ E_CONFIG_VAL(D, T, digital_clock, INT);
+ E_CONFIG_VAL(D, T, digital_24h, INT);
+ E_CONFIG_VAL(D, T, show_seconds, INT);
+ E_CONFIG_VAL(D, T, show_date, INT);
+ E_CONFIG_VAL(D, T, advanced, UCHAR);
+ E_CONFIG_VAL(D, T, timezone, STR);
+ E_CONFIG_VAL(D, T, time_str[0], STR);
+ E_CONFIG_VAL(D, T, time_str[1], STR);
+ E_CONFIG_VAL(D, T, colorclass[0], STR);
+ E_CONFIG_VAL(D, T, colorclass[1], STR);
+
+ conf_edd = E_CONFIG_DD_NEW("Config", Config);
+#undef T
+#undef D
+#define T Config
+#define D conf_edd
+ E_CONFIG_LIST(D, T, items, conf_item_edd);
+
+ time_config = e_config_domain_load("module.time", conf_edd);
+
+ if (!time_config)
+ time_config = E_NEW(Config, 1);
+
+ act = e_action_add("clock");
+ if (act)
+ {
+ act->func.go = (void*)_e_mod_action_cb;
+ act->func.go_key = (void*)_e_mod_action_cb;
+ act->func.go_mouse = (void*)_e_mod_action_cb;
+ act->func.go_edge = (void*)_e_mod_action_cb;
+
+ e_action_predef_name_set(N_("Clock"), N_("Toggle calendar"), "clock", "show_calendar", NULL, 0);
+ }
+
+ e_gadget_type_add("Digital Clock", digital_clock_create, digital_clock_wizard);
+ e_gadget_type_add("Analog Clock", analog_clock_create, analog_clock_wizard);
+ time_init();
+}
+
+EINTERN void
+clock_shutdown(void)
+{
+ if (act)
+ {
+ e_action_predef_name_del("Clock", "Toggle calendar");
+ e_action_del("clock");
+ act = NULL;
+ }
+ if (time_config)
+ {
+ Config_Item *ci;
+
+ if (time_config->config_dialog)
+ {
+ evas_object_hide(time_config->config_dialog);
+ evas_object_del(time_config->config_dialog);
+ }
+
+ EINA_LIST_FREE(time_config->items, ci)
+ {
+ eina_stringshare_del(ci->timezone);
+ eina_stringshare_del(ci->time_str[0]);
+ eina_stringshare_del(ci->time_str[1]);
+ eina_stringshare_del(ci->colorclass[0]);
+ eina_stringshare_del(ci->colorclass[1]);
+ free(ci);
+ }
+
+ E_FREE(time_config);
+ }
+ E_CONFIG_DD_FREE(conf_edd);
+ E_CONFIG_DD_FREE(conf_item_edd);
+
+ e_gadget_type_del("Digital Clock");
+ e_gadget_type_del("Analog Clock");
+ time_shutdown();
+}
+
+/* module setup */
+E_API E_Module_Api e_modapi =
+{
+ E_MODULE_API_VERSION,
+ "Time"
+};
+
+E_API void *
+e_modapi_init(E_Module *m)
+{
+ clock_init();
+
+ time_config->module = m;
+ return m;
+}
+
+E_API int
+e_modapi_shutdown(E_Module *m EINA_UNUSED)
+{
+ clock_shutdown();
+ return 1;
+}
+
+E_API int
+e_modapi_save(E_Module *m EINA_UNUSED)
+{
+ e_config_domain_save("module.time", conf_edd, time_config);
+ return 1;
+}
diff --git a/src/modules/time/module.desktop.in b/src/modules/time/module.desktop.in
new file mode 100644
index 000000000..e5784010d
--- /dev/null
+++ b/src/modules/time/module.desktop.in
@@ -0,0 +1,7 @@
+[Desktop Entry]
+Encoding=UTF-8
+Type=Link
+Name=Time
+Comment=Time-related gadgets and utilities
+Icon=e-module-time
+X-Enlightenment-ModuleType=utils
diff --git a/src/modules/time/time.c b/src/modules/time/time.c
new file mode 100644
index 000000000..b3f93d628
--- /dev/null
+++ b/src/modules/time/time.c
@@ -0,0 +1,354 @@
+#include "clock.h"
+
+#include <sys/time.h>
+#include <time.h>
+
+static Eio_Monitor *clock_te_monitor = NULL;
+static Eio_Monitor *clock_tz2_monitor = NULL;
+static Eio_Monitor *clock_tzetc_monitor = NULL;
+static Eina_List *clock_eio_handlers = NULL;
+
+static Ecore_Timer *update_today = NULL;
+
+#define ZONEINFO_DIR "/usr/share/zoneinfo/posix"
+#define ZONEINFO_DIR_LEN sizeof(ZONEINFO_DIR) - 1
+
+#define TE_DECL \
+ const char *tzenv; \
+ char prevtz[128] = {0}
+
+#define TZSET(inst) \
+ tzenv = getenv("TZ"); \
+ if (tzenv) \
+ strncpy(prevtz, tzenv, sizeof(prevtz) - 1); \
+ if (inst->cfg->timezone) \
+ setenv("TZ", inst->cfg->timezone, 1); \
+ tzset()
+
+#define TZUNSET() \
+ if (prevtz[0]) \
+ setenv("TZ", prevtz, 1); \
+ else \
+ unsetenv("TZ"); \
+ tzset()
+
+static void
+_zoneinfo_del(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
+{
+ eio_file_cancel(data);
+}
+
+static void
+_zoneinfo_done(void *data, Eio_File *handler EINA_UNUSED)
+{
+ evas_object_event_callback_del(data, EVAS_CALLBACK_DEL, _zoneinfo_del);
+}
+
+static void
+_zoneinfo_error(void *data, Eio_File *handler EINA_UNUSED, int error EINA_UNUSED)
+{
+ evas_object_event_callback_del(data, EVAS_CALLBACK_DEL, _zoneinfo_del);
+}
+
+static Eina_Bool
+_zoneinfo_filter(void *data EINA_UNUSED, Eio_File *handler EINA_UNUSED, const Eina_File_Direct_Info *info)
+{
+ return (info->type == EINA_FILE_REG) || (info->type == EINA_FILE_DIR);
+}
+
+static void
+_zoneinfo_main(void *data, Eio_File *handler EINA_UNUSED, const Eina_File_Direct_Info *info)
+{
+ if (info->type == EINA_FILE_REG)
+ config_timezone_populate(data, &info->path[ZONEINFO_DIR_LEN + 1]);
+ else
+ {
+ Eio_File *ls;
+
+ ls = eio_file_direct_ls(info->path, _zoneinfo_filter, _zoneinfo_main, _zoneinfo_done, _zoneinfo_error, data);
+ evas_object_event_callback_add(data, EVAS_CALLBACK_DEL, _zoneinfo_del, ls);
+ }
+}
+
+EINTERN void
+time_zoneinfo_scan(Evas_Object *obj)
+{
+ Eio_File *ls;
+
+ ls = eio_file_direct_ls(ZONEINFO_DIR, _zoneinfo_filter, _zoneinfo_main, _zoneinfo_done, _zoneinfo_error, obj);
+ evas_object_event_callback_add(obj, EVAS_CALLBACK_DEL, _zoneinfo_del, ls);
+}
+
+EINTERN void
+time_daynames_clear(Instance *inst)
+{
+ int x;
+
+ for (x = 0; x < 7; x++)
+ eina_stringshare_replace(&inst->daynames[x], NULL);
+}
+
+EINTERN void
+time_datestring_format(Instance *inst, char *buf, int bufsz)
+{
+ struct timeval timev;
+ struct tm *tm;
+ time_t tt;
+ const char *default_str = "%F";
+ TE_DECL;
+
+ buf[0] = 0;
+ if (!inst->cfg->show_date) return;
+ TZSET(inst);
+ gettimeofday(&timev, NULL);
+ tt = (time_t)(timev.tv_sec);
+ tm = localtime(&tt);
+ TZUNSET();
+ switch (inst->cfg->show_date)
+ {
+ case CLOCK_DATE_DISPLAY_FULL:
+ strftime(buf, bufsz, _("%a, %e %b, %Y"), (const struct tm *)tm);
+ break;
+ case CLOCK_DATE_DISPLAY_NUMERIC:
+ strftime(buf, bufsz, _("%a, %x"), (const struct tm *)tm);
+ break;
+ case CLOCK_DATE_DISPLAY_DATE_ONLY:
+ strftime(buf, bufsz, "%x", (const struct tm *)tm);
+ break;
+ case CLOCK_DATE_DISPLAY_ISO8601:
+ strftime(buf, bufsz, "%F", (const struct tm *)tm);
+ break;
+ case CLOCK_DATE_DISPLAY_CUSTOM:
+ if (!strftime(buf, bufsz, inst->cfg->time_str[1] ?: default_str, (const struct tm *)tm))
+ strncpy(buf, "ERROR", bufsz - 1);
+ break;
+ default: break;
+ }
+}
+
+EINTERN int
+time_string_format(Instance *inst, char *buf, int bufsz)
+{
+ struct timeval timev;
+ struct tm *tm;
+ time_t tt;
+ const char *default_fmt = "%R";
+ TE_DECL;
+
+ buf[0] = 0;
+ TZSET(inst);
+ gettimeofday(&timev, NULL);
+ tt = (time_t)(timev.tv_sec);
+ tm = localtime(&tt);
+ TZUNSET();
+ if (!strftime(buf, bufsz, inst->cfg->time_str[0] ?: default_fmt, (const struct tm *)tm))
+ strncpy(buf, "ERROR", bufsz - 1);
+ return tm->tm_sec;
+}
+
+EINTERN void
+time_instance_update(Instance *inst)
+{
+ struct timeval timev;
+ struct tm *tm, tms, tmm, tm2;
+ time_t tt;
+ int started = 0, num, i;
+ int day;
+
+ tzset();
+ gettimeofday(&timev, NULL);
+ tt = (time_t)(timev.tv_sec);
+ tm = localtime(&tt);
+
+ time_daynames_clear(inst);
+ if (!tm) return;
+
+ // tms == current date time "saved"
+ // tm2 == date to look at adjusting for madj
+ // tm2 == month baseline @ 1st
+ memcpy(&tms, tm, sizeof(struct tm));
+ num = 0;
+ for (day = (0 - 6); day < (31 + 16); day++)
+ {
+ memcpy(&tmm, &tms, sizeof(struct tm));
+ tmm.tm_sec = 0;
+ tmm.tm_min = 0;
+ tmm.tm_hour = 10;
+ tmm.tm_mon += inst->madj;
+ tmm.tm_mday = 1; // start at the 1st of the month
+ tmm.tm_wday = 0; // ignored by mktime
+ tmm.tm_yday = 0; // ignored by mktime
+ tmm.tm_isdst = 0; // ignored by mktime
+ tt = mktime(&tmm);
+ tm = localtime(&tt);
+ memcpy(&tm2, tm, sizeof(struct tm));
+
+ tt = mktime(&tmm);
+ tt += (day * 60 * 60 * 24);
+ tm = localtime(&tt);
+ memcpy(&tmm, tm, sizeof(struct tm));
+ if (!started)
+ {
+ if (tm->tm_wday == inst->cfg->week.start)
+ {
+ char buf[32];
+
+ for (i = 0; i < 7; i++, tm->tm_wday = (tm->tm_wday + 1) % 7)
+ {
+ strftime(buf, sizeof(buf), "%a", tm);
+ inst->daynames[i] = eina_stringshare_add(buf);
+ }
+ started = 1;
+ }
+ }
+ if (started)
+ {
+ int y = num / 7;
+ int x = num % 7;
+
+ if (y < 6)
+ {
+ inst->daynums[x][y] = tmm.tm_mday;
+
+ inst->dayvalids[x][y] = 0;
+ if (tmm.tm_mon == tm2.tm_mon) inst->dayvalids[x][y] = 1;
+
+ inst->daytoday[x][y] = 0;
+ if ((tmm.tm_mon == tms.tm_mon) &&
+ (tmm.tm_year == tms.tm_year) &&
+ (tmm.tm_mday == tms.tm_mday))
+ inst->daytoday[x][y] = 1;
+
+ inst->dayweekends[x][y] = 0;
+ for (i = inst->cfg->weekend.start;
+ i < (inst->cfg->weekend.start + inst->cfg->weekend.len);
+ i++)
+ {
+ if (tmm.tm_wday == (i % 7))
+ {
+ inst->dayweekends[x][y] = 1;
+ break;
+ }
+ }
+ }
+ num++;
+ }
+ }
+
+ memcpy(&tmm, &tms, sizeof(struct tm));
+ tmm.tm_sec = 0;
+ tmm.tm_min = 0;
+ tmm.tm_hour = 10;
+ tmm.tm_mon += inst->madj;
+ tmm.tm_mday = 1; // start at the 1st of the month
+ tmm.tm_wday = 0; // ignored by mktime
+ tmm.tm_yday = 0; // ignored by mktime
+ tmm.tm_isdst = 0; // ignored by mktime
+ tt = mktime(&tmm);
+ tm = localtime(&tt);
+ memcpy(&tm2, tm, sizeof(struct tm));
+ inst->year[sizeof(inst->year) - 1] = 0;
+ strftime(inst->year, sizeof(inst->year) - 1, "%Y", (const struct tm *)&tm2);
+ inst->month[sizeof(inst->month) - 1] = 0;
+ strftime(inst->month, sizeof(inst->month) - 1, "%B", (const struct tm *)&tm2); // %b for short month
+}
+
+
+static Eina_Bool
+_update_today_timer(void *data EINA_UNUSED)
+{
+ time_t t, t_tomorrow;
+ const struct tm *now;
+ struct tm today;
+
+ t = time(NULL);
+ now = localtime(&t);
+ memcpy(&today, now, sizeof(today));
+ today.tm_sec = 1;
+ today.tm_min = 0;
+ today.tm_hour = 0;
+
+ t_tomorrow = mktime(&today) + 24 * 60 * 60;
+ if (update_today) ecore_timer_interval_set(update_today, t_tomorrow - t);
+ else update_today = ecore_timer_add(t_tomorrow - t, _update_today_timer, NULL);
+ return EINA_TRUE;
+}
+
+
+static Eina_Bool
+_clock_eio_update(void *d EINA_UNUSED, int type EINA_UNUSED, void *event)
+{
+ Eio_Monitor_Event *ev = event;
+
+ if ((ev->monitor == clock_te_monitor) ||
+ (ev->monitor == clock_tz2_monitor) ||
+ (ev->monitor == clock_tzetc_monitor))
+ {
+ if (eina_streq(ev->filename, "/etc/localtime") ||
+ eina_streq(ev->filename, "/etc/timezone"))
+ clock_instances_redo();
+ }
+ return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool
+_clock_time_update(void *d EINA_UNUSED, int type EINA_UNUSED, void *event EINA_UNUSED)
+{
+ clock_instances_redo();
+ return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool
+_clock_eio_error(void *d EINA_UNUSED, int type EINA_UNUSED, void *event)
+{
+ Eio_Monitor_Event *ev = event;
+
+ if ((ev->monitor == clock_te_monitor) ||
+ (ev->monitor == clock_tz2_monitor) ||
+ (ev->monitor == clock_tzetc_monitor))
+ {
+ E_FREE_FUNC(clock_te_monitor, eio_monitor_del);
+ if (ecore_file_exists("/etc/localtime"))
+ clock_te_monitor = eio_monitor_add("/etc/localtime");
+
+ E_FREE_FUNC(clock_tz2_monitor, eio_monitor_del);
+ if (ecore_file_exists("/etc/timezone"))
+ clock_tz2_monitor = eio_monitor_add("/etc/timezone");
+
+ E_FREE_FUNC(clock_tzetc_monitor, eio_monitor_del);
+ if (ecore_file_is_dir("/etc"))
+ clock_tzetc_monitor = eio_monitor_add("/etc");
+ }
+
+ return ECORE_CALLBACK_PASS_ON;
+}
+
+EINTERN void
+time_init(void)
+{
+ if (ecore_file_exists("/etc/localtime"))
+ clock_te_monitor = eio_monitor_add("/etc/localtime");
+ if (ecore_file_exists("/etc/timezone"))
+ clock_tz2_monitor = eio_monitor_add("/etc/timezone");
+ if (ecore_file_is_dir("/etc"))
+ clock_tzetc_monitor = eio_monitor_add("/etc");
+
+ E_LIST_HANDLER_APPEND(clock_eio_handlers, EIO_MONITOR_ERROR, _clock_eio_error, NULL);
+ E_LIST_HANDLER_APPEND(clock_eio_handlers, EIO_MONITOR_FILE_CREATED, _clock_eio_update, NULL);
+ E_LIST_HANDLER_APPEND(clock_eio_handlers, EIO_MONITOR_FILE_MODIFIED, _clock_eio_update, NULL);
+ E_LIST_HANDLER_APPEND(clock_eio_handlers, EIO_MONITOR_FILE_DELETED, _clock_eio_update, NULL);
+ E_LIST_HANDLER_APPEND(clock_eio_handlers, EIO_MONITOR_SELF_DELETED, _clock_eio_update, NULL);
+ E_LIST_HANDLER_APPEND(clock_eio_handlers, EIO_MONITOR_SELF_RENAME, _clock_eio_update, NULL);
+ E_LIST_HANDLER_APPEND(clock_eio_handlers, E_EVENT_SYS_RESUME, _clock_time_update, NULL);
+ E_LIST_HANDLER_APPEND(clock_eio_handlers, ECORE_EVENT_SYSTEM_TIMEDATE_CHANGED, _clock_time_update, NULL);
+ _update_today_timer(NULL);
+}
+
+EINTERN void
+time_shutdown(void)
+{
+ E_FREE_FUNC(update_today, ecore_timer_del);
+ E_FREE_FUNC(clock_te_monitor, eio_monitor_del);
+ E_FREE_FUNC(clock_tz2_monitor, eio_monitor_del);
+ E_FREE_FUNC(clock_tzetc_monitor, eio_monitor_del);
+}