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