discomfitor pushed a commit to branch master.

http://git.enlightenment.org/core/enlightenment.git/commit/?id=dad9160ffa748bdf69a45ee7f1f2dfdd23225f4a

commit dad9160ffa748bdf69a45ee7f1f2dfdd23225f4a
Author: Mike Blumenkrantz <zm...@osg.samsung.com>
Date:   Thu Mar 3 09:42:11 2016 -0500

    add new time module with clock gadgets
    
    this is the successor to the clock module/gadget. it uses separate config 
files
    and theme groups from the "clock" module.
---
 configure.ac                       |   2 +
 src/bin/e_module.c                 |   1 +
 src/modules/Makefile.mk            |   1 +
 src/modules/Makefile_time.mk       |  25 ++
 src/modules/time/clock.c           | 507 ++++++++++++++++++++++++++++++
 src/modules/time/clock.h           |  93 ++++++
 src/modules/time/config.c          | 627 +++++++++++++++++++++++++++++++++++++
 src/modules/time/e-module-time.edj | Bin 0 -> 14062 bytes
 src/modules/time/mod.c             | 142 +++++++++
 src/modules/time/module.desktop.in |   7 +
 src/modules/time/time.c            | 354 +++++++++++++++++++++
 11 files changed, 1759 insertions(+)

diff --git a/configure.ac b/configure.ac
index 7b286c6..1ac5831 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 945788d..25ec3a7 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 bb618da..0962f9e 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 0000000..14477c1
--- /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 0000000..1952413
--- /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 0000000..990d42b
--- /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 0000000..ad4c528
--- /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 0000000..7566698
Binary files /dev/null and b/src/modules/time/e-module-time.edj differ
diff --git a/src/modules/time/mod.c b/src/modules/time/mod.c
new file mode 100644
index 0000000..4552f19
--- /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 0000000..e578401
--- /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 0000000..b3f93d6
--- /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);
+}

-- 


Reply via email to