From: Sumanth Krishna Mannam <sumanth.m@samsung.com>
[framework/uifw/elementary.git] / src / lib / elm_datetime.c
1 #include <locale.h>
2 #include <langinfo.h>
3 #include <Elementary.h>
4 #include "elm_priv.h"
5
6 typedef struct _Widget_Data Widget_Data;
7 typedef struct _Datetime_Field Datetime_Field;
8 typedef struct _Datetime_Mod_Api Datetime_Mod_Api;
9 typedef struct _Format_Map Format_Map;
10
11 #define DATETIME_TYPE_COUNT         6
12 #define MAX_FORMAT_LEN              64
13 #define MAX_SEPARATOR_LEN           6
14 #define MAX_FIELD_FORMAT_LEN        3
15 #define MIN_DAYS_IN_MONTH           28
16 #define BUFFER_SIZE                 1024
17
18 // interface between EDC & C code. Field names & signal names.
19 // values 0 to DATETIME_TYPE_COUNT are valid range, can be substituted for %d.
20 #define EDC_DATETIME_ENABLE_SIG_STR          "elm,state,enabled"
21 #define EDC_DATETIME_DISABLE_SIG_STR         "elm,state,disabled"
22 #define EDC_DATETIME_FOCUSIN_SIG_STR         "elm,action,focus"
23 #define EDC_DATETIME_FOCUSOUT_SIG_STR        "elm,action,unfocus"
24 #define EDC_PART_FIELD_STR                   "field%d"
25 #define EDC_PART_SEPARATOR_STR               "separator%d"
26 #define EDC_PART_FIELD_ENABLE_SIG_STR        "field%d,enable"
27 #define EDC_PART_FIELD_DISABLE_SIG_STR       "field%d,disable"
28
29 // struct tm  does not define the fields in the order from year, month, date, hour, minute.
30 // values are reassigned to an array for easy handling.
31 #define DATETIME_TM_ARRAY(intptr, tmptr) int *intptr[] = {&(tmptr)->tm_year, \
32            &(tmptr)->tm_mon, &(tmptr)->tm_mday, &(tmptr)->tm_hour, &(tmptr)->tm_min}
33
34 struct _Datetime_Field
35 {
36    Evas_Object *item_obj;
37    char fmt[MAX_FIELD_FORMAT_LEN];
38    Elm_Datetime_Field_Type type;
39    const char *separator;
40    int location;          // location of the field as per the current format
41    int min, max;
42    Eina_Bool fmt_exist:1;  // whether field format is present or not
43    Eina_Bool visible:1;    // whether field can be visible or not
44 };
45
46 struct _Datetime_Mod_Api
47 {
48    Elm_Datetime_Module_Data *(*obj_hook) (Evas_Object *obj);
49    void (*obj_unhook) (Elm_Datetime_Module_Data *module_data);
50    Evas_Object *(*field_create) (Elm_Datetime_Module_Data *module_data,
51                                      Elm_Datetime_Field_Type field_type);
52    void (*field_value_display) (Elm_Datetime_Module_Data *module_data,
53                                     Evas_Object *obj);
54 };
55
56 struct _Widget_Data
57 {
58    Evas_Object *base;
59    Datetime_Field field_list[DATETIME_TYPE_COUNT]; // fixed set of fields.
60    struct tm curr_time, min_limit, max_limit;
61    Elm_Datetime_Module_Data *mod_data;
62    char format[MAX_FORMAT_LEN];
63    Eina_Bool user_format:1; // whether user set format or default format.
64 };
65
66 struct _Format_Map
67 {
68    char *fmt_char;
69    int def_min;
70    int def_max;
71 };
72
73 // default limits for individual fields
74 static const Format_Map mapping[DATETIME_TYPE_COUNT] = {
75    [ELM_DATETIME_YEAR]   =  { "Yy",   0, 137 },
76    [ELM_DATETIME_MONTH]  =  { "mbBh", 0,  11  },
77    [ELM_DATETIME_DATE]   =  { "de",   1,  31  },
78    [ELM_DATETIME_HOUR]   =  { "IHkl", 0,  23  },
79    [ELM_DATETIME_MINUTE] =  { "M",    0,  59  },
80    [ELM_DATETIME_AMPM]   =  { "pP",   0,  1   }
81 };
82
83 static const char *multifield_formats = "cxXrRTDF";
84
85 static Datetime_Mod_Api *dt_mod = NULL;
86 static const char *widtype = NULL;
87
88 static void _del_hook(Evas_Object *obj);
89 static void _disable_hook(Evas_Object *obj);
90 static void _translate_hook(Evas_Object *obj);
91 static void _on_focus_hook(void *data __UNUSED__, Evas_Object *obj);
92 static void _mirrored_set(Evas_Object *obj, Eina_Bool rtl);
93 static void _sizing_eval(Evas_Object *obj);
94 static void _theme_hook(Evas_Object *obj);
95 static void _validate_datetime_limits(struct tm *time1, struct tm *time2, Eina_Bool swap);
96 static void _apply_field_limits(Evas_Object *obj);
97 static void _apply_range_restrictions(Evas_Object *obj, struct tm *time);
98 static const char *_field_format_get(Evas_Object * obj, Elm_Datetime_Field_Type field_type);
99 static void _field_limit_get(Evas_Object * obj, Elm_Datetime_Field_Type field_type,
100                              int *range_min, int *range_max);
101 static void _reload_format(Evas_Object *obj);
102 static void _field_list_display(Evas_Object *obj);
103 static void _field_list_arrange(Evas_Object *obj);
104 static void _field_list_init(Evas_Object *obj);
105
106 static const char SIG_CHANGED[] = "changed";
107 static const char SIG_LANGUAGE_CHANGED[] = "language,changed";
108 static const Evas_Smart_Cb_Description _signals[] = {
109        {SIG_CHANGED, ""},
110        {SIG_LANGUAGE_CHANGED, ""},
111        {NULL, NULL}
112 };
113
114 static Datetime_Mod_Api *
115 _dt_mod_init()
116 {
117    Elm_Module *mod = NULL;
118    if (!(mod = _elm_module_find_as("datetime/api"))) return NULL;
119
120    mod->api = malloc(sizeof(Datetime_Mod_Api));
121    if (!mod->api) return NULL;
122
123    ((Datetime_Mod_Api *)(mod->api))->obj_hook            =   _elm_module_symbol_get(mod, "obj_hook");
124    ((Datetime_Mod_Api *)(mod->api))->obj_unhook          =   _elm_module_symbol_get(mod, "obj_unhook");
125    ((Datetime_Mod_Api *)(mod->api))->field_create        =   _elm_module_symbol_get(mod, "field_create");
126    ((Datetime_Mod_Api *)(mod->api))->field_value_display =   _elm_module_symbol_get(mod, "field_value_display");
127
128    return mod->api;
129 }
130
131 static void
132 _del_hook(Evas_Object *obj)
133 {
134    Widget_Data *wd;
135    Datetime_Field *tmp;
136    unsigned int idx;
137
138    wd = elm_widget_data_get(obj);
139    if (!wd) return;
140
141    for (idx = 0; idx < DATETIME_TYPE_COUNT; idx++)
142      {
143         tmp = wd->field_list + idx;
144         evas_object_del(tmp->item_obj);
145         eina_stringshare_del(tmp->separator);
146      }
147
148    if ((dt_mod) && (dt_mod->obj_unhook))
149      dt_mod->obj_unhook(wd->mod_data); // module - unhook
150
151    free(wd);
152 }
153
154 static void
155 _disable_hook(Evas_Object *obj)
156 {
157    Widget_Data *wd;
158
159    wd = elm_widget_data_get(obj);
160    if (!wd || !wd->base) return;
161    if (elm_widget_disabled_get(obj))
162      edje_object_signal_emit(wd->base, EDC_DATETIME_DISABLE_SIG_STR, "elm");
163    else
164      edje_object_signal_emit(wd->base, EDC_DATETIME_ENABLE_SIG_STR, "elm");
165 }
166
167 static void
168 _translate_hook(Evas_Object *obj)
169 {
170    Widget_Data *wd;
171    wd = elm_widget_data_get(obj);
172    if (!wd) return;
173
174    if (!wd->user_format) _reload_format(obj);
175    else _field_list_display(obj);
176    evas_object_smart_callback_call(obj, SIG_LANGUAGE_CHANGED, NULL);
177 }
178
179 static void
180 _on_focus_hook(void *data __UNUSED__, Evas_Object *obj)
181 {
182    Widget_Data *wd;
183
184    wd = elm_widget_data_get(obj);
185    if (!wd) return;
186
187    if (elm_widget_focus_get(obj))
188      edje_object_signal_emit(wd->base, EDC_DATETIME_FOCUSIN_SIG_STR, "elm");
189    else
190      edje_object_signal_emit(wd->base, EDC_DATETIME_FOCUSOUT_SIG_STR, "elm");
191 }
192
193 static Eina_List *
194 _datetime_items_get(const Evas_Object *obj)
195 {
196    Widget_Data *wd;
197    Eina_List *items;
198    Datetime_Field *field;
199    int loc, count = 0;
200    unsigned int idx;
201
202    wd = elm_widget_data_get(obj);
203    if (!wd) return NULL;
204
205    for (idx = 0; idx < DATETIME_TYPE_COUNT; idx++)
206      {
207         field = wd->field_list + idx;
208         if (field->fmt_exist && field->visible) count++;
209      }
210    for (loc = 0; loc < count; loc++)
211      for (idx = 0; idx < DATETIME_TYPE_COUNT; idx++)
212        {
213           field = wd->field_list + idx;
214           if (field->location == loc)
215             items = eina_list_append(items, field->item_obj);
216        }
217
218    return items;
219 }
220
221 static Eina_Bool
222 _elm_datetime_focus_next_hook(const Evas_Object *obj, Elm_Focus_Direction dir, Evas_Object **next)
223 {
224    Widget_Data *wd;
225    const Eina_List *items;
226    void *(*list_data_get) (const Eina_List *list);
227    Eina_List *(*list_free) (Eina_List *list);
228    Eina_Bool ret;
229
230    wd = elm_widget_data_get(obj);
231    if (!wd) return EINA_FALSE;
232
233    if ((items = elm_widget_focus_custom_chain_get(obj)))
234      {
235         list_data_get = eina_list_data_get;
236         list_free = NULL;
237      }
238    else
239      {
240         items = _datetime_items_get(obj);
241         list_data_get = eina_list_data_get;
242         list_free = eina_list_free;
243         if (!items) return EINA_FALSE;
244      }
245
246    ret = elm_widget_focus_list_next_get(obj, items, list_data_get, dir, next);
247    if (list_free) list_free((Eina_List *)items);
248
249    return ret;
250 }
251
252 static void
253 _mirrored_set(Evas_Object *obj, Eina_Bool rtl)
254 {
255    Widget_Data *wd;
256
257    wd = elm_widget_data_get(obj);
258    if (!wd) return;
259
260    edje_object_mirrored_set(wd->base, rtl);
261 }
262
263 static void
264 _sizing_eval(Evas_Object *obj)
265 {
266    Widget_Data *wd;
267    Datetime_Field *field;
268    Evas_Coord minw = -1, minh = -1;
269    unsigned int idx, field_count = 0;
270
271    wd = elm_widget_data_get(obj);
272    if (!wd || !wd->base) return;
273    for (idx = 0; idx < DATETIME_TYPE_COUNT; idx++)
274      {
275         field = wd->field_list + idx;
276         if ((field->visible) && (field->fmt_exist)) field_count ++;
277      }
278    if (field_count)
279      elm_coords_finger_size_adjust(field_count, &minw, 1, &minh);
280    edje_object_size_min_restricted_calc(wd->base, &minw, &minh, minw, minh);
281    evas_object_size_hint_min_set(obj, minw, minh);
282    evas_object_size_hint_max_set(obj, -1, -1);
283 }
284
285 static void
286 _theme_hook(Evas_Object *obj)
287 {
288    Widget_Data *wd;
289    Datetime_Field *field;
290    char buf[BUFFER_SIZE];
291    unsigned int idx;
292
293    wd = elm_widget_data_get(obj);
294    if (!wd || !wd->base) return;
295
296    _elm_theme_object_set(obj, wd->base, "datetime", "base",
297                          elm_widget_style_get(obj));
298    _elm_widget_mirrored_reload(obj);
299    _mirrored_set(obj, elm_widget_mirrored_get(obj));
300
301    edje_object_scale_set(wd->base, elm_widget_scale_get(obj) * _elm_config->scale);
302
303    if (elm_widget_disabled_get(obj))
304      edje_object_signal_emit(wd->base, EDC_DATETIME_DISABLE_SIG_STR,"elm");
305    else
306      edje_object_signal_emit(wd->base, EDC_DATETIME_ENABLE_SIG_STR, "elm");
307
308    if ((!dt_mod) || (!dt_mod->field_value_display)) return;
309
310    for (idx = 0; idx < DATETIME_TYPE_COUNT; idx++)
311      {
312         field = wd->field_list + idx;
313         if (field->fmt_exist && field->visible)
314           {
315              snprintf(buf, sizeof(buf), EDC_PART_FIELD_ENABLE_SIG_STR, field->location);
316              edje_object_signal_emit(wd->base, buf, "elm");
317              snprintf(buf, sizeof(buf), EDC_PART_SEPARATOR_STR, field->location);
318              edje_object_part_text_set(wd->base, buf, field->separator);
319              dt_mod->field_value_display(wd->mod_data, field->item_obj);
320           }
321         else
322           {
323              snprintf(buf, sizeof(buf),EDC_PART_FIELD_DISABLE_SIG_STR, field->location);
324              edje_object_signal_emit(wd->base, buf, "elm");
325           }
326      }
327    edje_object_message_signal_process(wd->base);
328    _sizing_eval(obj);
329 }
330
331 static int
332 _max_days_get(int year, int month)
333 {
334    struct tm time1;
335    time_t t;
336    int day;
337
338    t = time(NULL);
339    localtime_r(&t, &time1);
340    time1.tm_year = year;
341    time1.tm_mon = month;
342    for(day = MIN_DAYS_IN_MONTH; day <= mapping[ELM_DATETIME_DATE].def_max; day++)
343      {
344         time1.tm_mday = day;
345         mktime(&time1);
346         if (time1.tm_mday == 1) break;
347      }
348    day --;
349    return day;
350 }
351
352 static Eina_Bool
353 _date_cmp(struct tm *time1, struct tm *time2)
354 {
355    unsigned int idx;
356    DATETIME_TM_ARRAY(timearr1, time1);
357    DATETIME_TM_ARRAY(timearr2, time2);
358
359    for (idx = 0; idx < DATETIME_TYPE_COUNT - 1; idx++)
360      {
361         if (*timearr1[idx] != *timearr2[idx])
362           return EINA_FALSE;
363      }
364    return EINA_TRUE;
365 }
366
367 // validates curr_time/min_limt/max_limit according to the newly set value
368 static void
369 _validate_datetime_limits(struct tm *time1, struct tm *time2, Eina_Bool swap)
370 {
371    struct tm *t1, *t2;
372    unsigned int idx;
373    if (!time1 || !time2) return;
374
375    t1 = (swap) ? time2 : time1;
376    t2 = (swap) ? time1 : time2;
377
378    DATETIME_TM_ARRAY(timearr1, time1);
379    DATETIME_TM_ARRAY(timearr2, time2);
380    for (idx = 0; idx < DATETIME_TYPE_COUNT - 1; idx++)
381      {
382        if (*timearr1[idx] < *timearr2[idx])
383          {
384             memcpy(t1, t2, sizeof(struct tm));
385             break;
386          }
387        else if (*timearr1[idx] > *timearr2[idx])
388          break;
389      }
390 }
391
392 static void
393 _apply_field_limits(Evas_Object *obj)
394 {
395    Widget_Data *wd;
396    Datetime_Field *field;
397    int val;
398    unsigned int idx = 0;
399
400    wd = elm_widget_data_get(obj);
401    if (!wd) return;
402
403    DATETIME_TM_ARRAY(timearr, &wd->curr_time);
404    for (idx = 0; idx < DATETIME_TYPE_COUNT - 1; idx++)
405      {
406         field = wd->field_list + idx;
407         val = *timearr[idx];
408         if (val < field->min)
409           *timearr[idx] = field->min;
410         else if (val > field->max)
411           *timearr[idx] = field->max;
412      }
413    _field_list_display(obj);
414 }
415
416 static void
417 _apply_range_restrictions(Evas_Object *obj, struct tm *time)
418 {
419    Widget_Data *wd;
420    unsigned int idx;
421    int val, min, max;
422
423    wd = elm_widget_data_get(obj);
424    if (!wd || !time) return;
425
426    DATETIME_TM_ARRAY(timearr, time);
427    for (idx = ELM_DATETIME_MONTH; idx < DATETIME_TYPE_COUNT - 1; idx++)
428      {
429         val = *timearr[idx];
430         min = mapping[idx].def_min;
431         max = mapping[idx].def_max;
432         if (idx == ELM_DATETIME_DATE)
433           max = _max_days_get(time->tm_year, time->tm_mon);
434         if (val < min)
435           *timearr[idx] = min;
436         else if (val > max)
437           *timearr[idx] = max;
438      }
439 }
440
441 static const char *
442 _field_format_get(Evas_Object * obj, Elm_Datetime_Field_Type field_type)
443 {
444    Widget_Data *wd;
445    Datetime_Field *field;
446
447    wd = elm_widget_data_get(obj);
448    if (!wd) return NULL;
449
450    field = wd->field_list + field_type;
451    if (!field) return NULL;
452
453    return field->fmt;
454 }
455
456 static void
457 _field_limit_get(Evas_Object * obj, Elm_Datetime_Field_Type field_type, int *range_min, int *range_max)
458 {
459    Widget_Data *wd;
460    Datetime_Field *field;
461    int min, max, max_days;
462    unsigned int idx;
463
464    wd = elm_widget_data_get(obj);
465    if (!wd) return;
466
467    field = wd->field_list + field_type;
468    if (!field) return;
469
470    min = field->min;
471    max = field->max;
472
473    DATETIME_TM_ARRAY(curr_timearr, &wd->curr_time);
474    DATETIME_TM_ARRAY(min_timearr, &wd->min_limit);
475    DATETIME_TM_ARRAY(max_timearr, &wd->max_limit);
476
477    for (idx = 0; idx < field->type; idx++)
478      if (*curr_timearr[idx] > *min_timearr[idx]) break;
479    if ((idx == field_type) && (min < *min_timearr[field_type]))
480      min = *min_timearr[field_type];
481    if (field_type == ELM_DATETIME_DATE)
482      {
483         max_days = _max_days_get(wd->curr_time.tm_year, wd->curr_time.tm_mon);
484         if (max > max_days) max = max_days;
485      }
486    for (idx = 0; idx < field->type; idx++)
487      if (*curr_timearr[idx] < *max_timearr[idx]) break;
488    if ((idx == field_type) && (max > *max_timearr[field_type]))
489      max = *max_timearr[field_type];
490
491    *range_min = min;
492    *range_max = max;
493 }
494
495 static void
496 _field_list_display(Evas_Object *obj)
497 {
498    Widget_Data *wd;
499    Datetime_Field *field;
500    unsigned int idx= 0;
501
502    wd = elm_widget_data_get(obj);
503    if (!wd) return;
504
505    for (idx = 0; idx < DATETIME_TYPE_COUNT; idx++)
506      {
507         field = wd->field_list + idx;
508         if (field->fmt_exist && field->visible)
509           {
510              if ((dt_mod) && (dt_mod->field_value_display))
511                dt_mod->field_value_display(wd->mod_data, field->item_obj);
512           }
513      }
514 }
515
516 static void
517 _field_list_arrange(Evas_Object *obj)
518 {
519    Widget_Data *wd;
520    Datetime_Field *field;
521    char buf[BUFFER_SIZE];
522    int idx;
523
524    wd = elm_widget_data_get(obj);
525    if (!wd) return;
526
527    for (idx = 0; idx < DATETIME_TYPE_COUNT; idx++)
528      {
529         field = wd->field_list + idx;
530         edje_object_part_unswallow(wd->base, field->item_obj);
531      }
532    for (idx = 0; idx < DATETIME_TYPE_COUNT; idx++)
533      {
534         field = wd->field_list + idx;
535         if (field->visible && field->fmt_exist)
536           {
537              snprintf(buf, sizeof(buf), EDC_PART_FIELD_STR, field->location);
538              edje_object_part_swallow(wd->base, buf, field->item_obj);
539           }
540         else evas_object_hide(field->item_obj);
541      }
542    _sizing_eval(obj);
543    _field_list_display(obj);
544 }
545
546 // returns expanded format string for corresponding multi-field format character
547 static char *
548 _expanded_fmt_str_get(char ch)
549 {
550    char *exp_fmt = "";
551    switch (ch)
552      {
553       case 'c':
554          exp_fmt = nl_langinfo(D_T_FMT);
555          break;
556       case 'x':
557          exp_fmt = nl_langinfo(D_FMT);
558          break;
559       case 'X':
560          exp_fmt = nl_langinfo(T_FMT);
561          break;
562       case 'r':
563          exp_fmt = nl_langinfo(T_FMT_AMPM);
564          break;
565       case 'R':
566          exp_fmt =  "%H:%M";
567          break;
568       case 'T':
569          exp_fmt =  "%H:%M:%S";
570          break;
571       case 'D':
572          exp_fmt =  "%m/%d/%y";
573          break;
574       case 'F':
575          exp_fmt =  "%Y-%m-%d";
576          break;
577       default:
578          exp_fmt =  "";
579          break;
580      }
581    return exp_fmt;
582 }
583
584 static void
585 _expand_format(char * dt_fmt)
586 {
587    char *ptr, *expanded_fmt, ch;
588    char buf[MAX_FORMAT_LEN] = {0,};
589    unsigned int idx = 0, len = 0;
590    Eina_Bool fmt_char = EINA_FALSE;
591
592    ptr = dt_fmt;
593    while ((ch = *ptr))
594      {
595         if ((fmt_char) && (strchr(multifield_formats, ch)))
596           {
597              // replace the multi-field format characters with corresponding expanded format
598              expanded_fmt = _expanded_fmt_str_get(ch);
599              len = strlen(expanded_fmt);
600              buf[--idx] = 0;
601              strncat(buf, expanded_fmt, len);
602              idx += len;
603           }
604         else buf[idx++] = ch;
605         if (ch == '%') fmt_char = EINA_TRUE;
606         else fmt_char = EINA_FALSE;
607         ptr++;
608      }
609    buf[idx] = 0;
610    strncpy(dt_fmt, buf, MAX_FORMAT_LEN);
611 }
612
613 static unsigned int
614 _parse_format(Evas_Object *obj, char *fmt_ptr)
615 {
616    Widget_Data *wd;
617    Datetime_Field *field = NULL;
618    unsigned int len = 0, idx, location = 0;
619    char separator[MAX_SEPARATOR_LEN];
620    char cur;
621    Eina_Bool fmt_parsing = EINA_FALSE, sep_parsing = EINA_FALSE,
622              sep_lookup = EINA_FALSE;
623
624    wd = elm_widget_data_get(obj);
625
626    while ((cur = *fmt_ptr))
627      {
628         if (fmt_parsing)
629           {
630              for (idx = 0; idx < DATETIME_TYPE_COUNT; idx++)
631                {
632                   if (strchr(mapping[idx].fmt_char, cur))
633                     {
634                        field = wd->field_list + idx;
635                        // ignore the fields already have or disabled
636                        // valid formats, means already parsed & repeated, ignore.
637                        if (!field->visible || field->location != -1) break;
638                        field->fmt[1] = cur;
639                        field->fmt_exist = EINA_TRUE;
640                        field->location = location++;
641                        fmt_parsing = EINA_FALSE;
642                        sep_lookup = EINA_TRUE;
643                        len = 0;
644                        break;
645                     }
646                }
647           }
648         if (cur == ' ') separator[len++] = cur;
649         else if (cur == '%') fmt_parsing = EINA_TRUE;
650         if ((cur == ' ') || (cur == '%'))
651           {
652              sep_parsing = EINA_FALSE;
653              // set the separator to previous field
654              separator[len] = 0;
655              if (field) eina_stringshare_replace(&field->separator, separator);
656           }
657         if (sep_parsing && (len < MAX_SEPARATOR_LEN-1) &&
658             (field->type != ELM_DATETIME_AMPM) &&
659             (!((field->type == ELM_DATETIME_MINUTE) && (cur ==':'))))
660            separator[len++] = cur;
661         if (sep_lookup) sep_parsing = EINA_TRUE;
662         sep_lookup = EINA_FALSE;
663         fmt_ptr++;
664    }
665    // return the number of valid fields parsed.
666    return location;
667 }
668
669 static void
670 _reload_format(Evas_Object *obj)
671 {
672    Widget_Data *wd;
673    Datetime_Field *field;
674    char buf[BUFFER_SIZE];
675    unsigned int idx, field_count;
676    char *dt_fmt;
677
678    wd = elm_widget_data_get(obj);
679    if (!wd) return;
680
681    // fetch the default format from Libc.
682    if (!wd->user_format)
683      strncpy(wd->format, nl_langinfo(D_T_FMT), MAX_FORMAT_LEN);
684
685    dt_fmt = (char *)malloc(MAX_FORMAT_LEN);
686    strncpy(dt_fmt, wd->format, MAX_FORMAT_LEN);
687
688    _expand_format(dt_fmt);
689
690    // reset all the fields to disable state
691    for (idx = 0; idx < DATETIME_TYPE_COUNT; idx++)
692      {
693         field = wd->field_list + idx;
694         field->fmt_exist = EINA_FALSE;
695         field->location = -1;
696      }
697
698    field_count = _parse_format(obj, dt_fmt);
699    free(dt_fmt);
700
701    // assign locations to disabled fields for uniform usage
702    for (idx = 0; idx < DATETIME_TYPE_COUNT; idx++)
703      {
704         field = wd->field_list + idx;
705         if (field->location == -1) field->location = field_count++;
706
707         if (field->fmt_exist && field->visible)
708           {
709              snprintf(buf, sizeof(buf), EDC_PART_FIELD_ENABLE_SIG_STR,
710                       field->location);
711              edje_object_signal_emit(wd->base, buf, "elm");
712           }
713         else
714           {
715              snprintf(buf, sizeof(buf),EDC_PART_FIELD_DISABLE_SIG_STR,
716                       field->location);
717              edje_object_signal_emit(wd->base, buf, "elm");
718           }
719         snprintf(buf, sizeof(buf), EDC_PART_SEPARATOR_STR, (field->location + 1));
720         edje_object_part_text_set(wd->base, buf, field->separator);
721      }
722    edje_object_message_signal_process(wd->base);
723    _field_list_arrange(obj);
724 }
725
726 static void
727 _field_list_init(Evas_Object *obj)
728 {
729    Widget_Data *wd;
730    Datetime_Field *field;
731    unsigned int idx;
732    time_t t;
733
734    wd = elm_widget_data_get(obj);
735    if (!wd) return;
736
737    t = time(NULL);
738    localtime_r(&t, &wd->curr_time);
739
740    for (idx = 0; idx < DATETIME_TYPE_COUNT; idx++)
741      {
742         field = wd->field_list + idx;
743         field->type = ELM_DATETIME_YEAR + idx;
744         field->fmt[0] = '%';
745         field->fmt_exist = EINA_FALSE;
746         field->visible  = EINA_TRUE;
747         field->min = mapping[idx].def_min;
748         field->max = mapping[idx].def_max;
749      }
750    DATETIME_TM_ARRAY(min_timearr, &wd->min_limit);
751    DATETIME_TM_ARRAY(max_timearr, &wd->max_limit);
752    for (idx = 0; idx < DATETIME_TYPE_COUNT-1; idx++)
753      {
754         *min_timearr[idx] = mapping[idx].def_min;
755         *max_timearr[idx] = mapping[idx].def_max;
756      }
757 }
758
759 EAPI Evas_Object *
760 elm_datetime_add(Evas_Object *parent)
761 {
762    Evas_Object *obj;
763    Evas *e;
764    Widget_Data *wd;
765    Datetime_Field *field;
766    int idx;
767
768    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
769
770    ELM_SET_WIDTYPE(widtype, "datetime");
771    elm_widget_type_set(obj, widtype);
772    elm_widget_sub_object_add(parent, obj);
773    elm_widget_data_set(obj, wd);
774    elm_widget_del_hook_set(obj, _del_hook);
775    elm_widget_theme_hook_set(obj, _theme_hook);
776    elm_widget_translate_hook_set(obj, _translate_hook);
777    elm_widget_on_focus_hook_set(obj, _on_focus_hook, NULL);
778    elm_widget_disable_hook_set(obj, _disable_hook);
779    elm_widget_can_focus_set(obj, EINA_TRUE);
780    elm_widget_focus_next_hook_set(obj, _elm_datetime_focus_next_hook);
781
782    wd->base = edje_object_add(e);
783    elm_widget_resize_object_set(obj, wd->base);
784    _elm_theme_object_set(obj, wd->base, "datetime", "base", "default");
785    evas_object_smart_callbacks_descriptions_set(obj, _signals);
786
787    // module - initialise module for datetime
788    if (!dt_mod) dt_mod = _dt_mod_init();
789    if ((dt_mod) && (dt_mod->obj_hook))
790      wd->mod_data = dt_mod->obj_hook(obj);
791    // update module data
792    if (wd->mod_data)
793      {
794         wd->mod_data->base = obj;
795         wd->mod_data->field_limit_get = _field_limit_get;
796         wd->mod_data->field_format_get = _field_format_get;
797      }
798
799    _field_list_init(obj);
800    _reload_format(obj);
801
802    if ((dt_mod)&&(dt_mod->field_create))
803      {
804         for (idx = 0; idx < DATETIME_TYPE_COUNT; idx++)
805           {
806              field = wd->field_list + idx;
807              field->item_obj = dt_mod->field_create(wd->mod_data, idx);
808           }
809      }
810    _field_list_arrange(obj);
811    _mirrored_set(obj, elm_widget_mirrored_get(obj));
812
813    return obj;
814 }
815
816 EAPI const char *
817 elm_datetime_format_get(const Evas_Object *obj)
818 {
819    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
820    Widget_Data *wd;
821
822    wd = elm_widget_data_get(obj);
823    if (!wd) return NULL;
824
825    return wd->format;
826 }
827
828 EAPI void
829 elm_datetime_format_set(Evas_Object *obj, const char *fmt)
830 {
831    ELM_CHECK_WIDTYPE(obj, widtype);
832    Widget_Data *wd;
833
834    wd = elm_widget_data_get(obj);
835    if (!wd) return;
836
837    if (fmt)
838      {
839         strncpy(wd->format, fmt, MAX_FORMAT_LEN);
840         wd->user_format = EINA_TRUE;
841      }
842    else
843      wd->user_format = EINA_FALSE;
844
845    _reload_format(obj);
846 }
847
848 EAPI Eina_Bool
849 elm_datetime_field_visible_get(const Evas_Object *obj, Elm_Datetime_Field_Type
850                                fieldtype)
851 {
852    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
853    Widget_Data *wd;
854    Datetime_Field *field;
855
856    wd = elm_widget_data_get(obj);
857    if (!wd || (fieldtype > ELM_DATETIME_AMPM)) return EINA_FALSE;
858
859    field = wd->field_list + fieldtype;
860    return field->visible;
861 }
862
863 EAPI void
864 elm_datetime_field_visible_set(Evas_Object *obj, Elm_Datetime_Field_Type fieldtype,
865                               Eina_Bool visible)
866 {
867    ELM_CHECK_WIDTYPE(obj, widtype);
868    Widget_Data *wd;
869    Datetime_Field *field;
870
871    wd = elm_widget_data_get(obj);
872    if (!wd || (fieldtype > ELM_DATETIME_AMPM)) return;
873
874    field = wd->field_list + fieldtype;
875    if (field->visible == visible) return;
876
877    field->visible = visible;
878    _reload_format(obj);
879 }
880
881 EAPI void
882 elm_datetime_field_limit_get(const Evas_Object *obj, Elm_Datetime_Field_Type fieldtype,
883                              int *min, int *max)
884 {
885    ELM_CHECK_WIDTYPE(obj, widtype);
886    Widget_Data *wd;
887    Datetime_Field *field;
888
889    wd = elm_widget_data_get(obj);
890    if (!wd || (fieldtype >= ELM_DATETIME_AMPM)) return;
891
892    field = wd->field_list + fieldtype;
893    if (min) *min = field->min;
894    if (max) *max = field->max;
895 }
896
897 EAPI void
898 elm_datetime_field_limit_set(Evas_Object *obj, Elm_Datetime_Field_Type fieldtype,
899                              int min, int max)
900 {
901    ELM_CHECK_WIDTYPE(obj, widtype);
902    Widget_Data *wd;
903    Datetime_Field *field;
904
905    wd = elm_widget_data_get(obj);
906    if (!wd || (fieldtype >= ELM_DATETIME_AMPM)) return;
907
908    if (min > max) return;
909
910    field = wd->field_list + fieldtype;
911    if ((min > mapping[fieldtype].def_min && min < mapping[fieldtype].def_max)
912         || (field->type == ELM_DATETIME_YEAR))
913      field->min = min;
914    if ((max > mapping[fieldtype].def_min && max < mapping[fieldtype].def_max)
915         || (field->type == ELM_DATETIME_YEAR))
916      field->max = max;
917
918    _apply_field_limits(obj);
919 }
920
921 EAPI Eina_Bool
922 elm_datetime_value_get(const Evas_Object *obj, struct tm *currtime)
923 {
924    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
925    EINA_SAFETY_ON_NULL_RETURN_VAL(currtime, EINA_FALSE);
926    Widget_Data *wd;
927
928    wd = elm_widget_data_get(obj);
929    if (!wd) return EINA_FALSE;
930
931    *currtime = wd->curr_time;
932    return EINA_TRUE;
933 }
934
935 EAPI Eina_Bool
936 elm_datetime_value_set(Evas_Object *obj, const struct tm *newtime)
937 {
938    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
939    EINA_SAFETY_ON_NULL_RETURN_VAL(newtime, EINA_FALSE);
940    Widget_Data *wd;
941    struct tm old_time;
942
943    wd = elm_widget_data_get(obj);
944    if (!wd) return EINA_FALSE;
945
946    old_time = wd->curr_time;
947    wd->curr_time = *newtime;
948    // apply default field restrictions for curr_time
949    _apply_range_restrictions(obj, &wd->curr_time);
950    // validate the curr_time according to the min_limt and max_limt
951    _validate_datetime_limits(&wd->curr_time, &wd->min_limit, EINA_FALSE);
952    _validate_datetime_limits(&wd->max_limit, &wd->curr_time, EINA_TRUE);
953    _apply_field_limits(obj);
954
955    if (!_date_cmp(&old_time, &wd->curr_time))
956      evas_object_smart_callback_call(obj, SIG_CHANGED, NULL);
957
958    return EINA_TRUE;
959 }
960
961 EAPI Eina_Bool
962 elm_datetime_value_min_get(const Evas_Object *obj, struct tm *mintime)
963 {
964    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
965    EINA_SAFETY_ON_NULL_RETURN_VAL(mintime, EINA_FALSE);
966    Widget_Data *wd;
967
968    wd = elm_widget_data_get(obj);
969    if (!wd) return EINA_FALSE;
970
971    *mintime = wd->min_limit;
972    return EINA_TRUE;
973 }
974
975 EAPI Eina_Bool
976 elm_datetime_value_min_set(Evas_Object *obj, const struct tm *mintime)
977 {
978    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
979    EINA_SAFETY_ON_NULL_RETURN_VAL(mintime, EINA_FALSE);
980    Widget_Data *wd;
981    struct tm old_time;
982
983    wd = elm_widget_data_get(obj);
984    if (!wd) return EINA_FALSE;
985
986    wd->min_limit = *mintime;
987    old_time = wd->curr_time;
988    // apply default field restrictions for min_limit
989    _apply_range_restrictions(obj, &wd->min_limit);
990    // validate curr_time and max_limt according to the min_limit
991    _validate_datetime_limits(&wd->max_limit, &wd->min_limit, EINA_FALSE);
992    _validate_datetime_limits(&wd->curr_time, &wd->min_limit, EINA_FALSE);
993    _apply_field_limits(obj);
994
995    if (!_date_cmp(&old_time, &wd->curr_time))
996      evas_object_smart_callback_call(obj, SIG_CHANGED, NULL);
997
998    return EINA_TRUE;
999 }
1000
1001 EAPI Eina_Bool
1002 elm_datetime_value_max_get(const Evas_Object *obj, struct tm *maxtime)
1003 {
1004    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
1005    EINA_SAFETY_ON_NULL_RETURN_VAL(maxtime, EINA_FALSE);
1006    Widget_Data *wd;
1007
1008    wd = elm_widget_data_get(obj);
1009    if (!wd) return EINA_FALSE;
1010
1011    *maxtime = wd->max_limit;
1012    return EINA_TRUE;
1013 }
1014
1015 EAPI Eina_Bool
1016 elm_datetime_value_max_set(Evas_Object *obj, const struct tm *maxtime)
1017 {
1018    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
1019    EINA_SAFETY_ON_NULL_RETURN_VAL(maxtime, EINA_FALSE);
1020    Widget_Data *wd;
1021    struct tm old_time;
1022
1023    wd = elm_widget_data_get(obj);
1024    if (!wd) return EINA_FALSE;
1025
1026    wd->max_limit = *maxtime;
1027    old_time = wd->curr_time;
1028    // apply default field restrictions for max_limit
1029    _apply_range_restrictions(obj, &wd->max_limit);
1030    // validate curr_time and min_limt according to the max_limit
1031    _validate_datetime_limits(&wd->max_limit, &wd->min_limit, EINA_TRUE);
1032    _validate_datetime_limits(&wd->max_limit, &wd->curr_time, EINA_TRUE);
1033    _apply_field_limits(obj);
1034
1035    if (!_date_cmp(&old_time, &wd->curr_time))
1036      evas_object_smart_callback_call(obj, SIG_CHANGED, NULL);
1037
1038    return EINA_TRUE;
1039 }