[Genlist][Nabi:S1-1013] bug fix, Abnormality is displayed when selecting Type while...
[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    if (!dt_fmt) return;
720    strncpy(dt_fmt, wd->format, MAX_FORMAT_LEN);
721
722    _expand_format(dt_fmt);
723
724    // reset all the fields to disable state
725    for (idx = 0; idx < DATETIME_TYPE_COUNT; idx++)
726      {
727         field = wd->field_list + idx;
728         field->fmt_exist = EINA_FALSE;
729         field->location = -1;
730      }
731
732    field_count = _parse_format(obj, dt_fmt);
733    free(dt_fmt);
734
735    // assign locations to disabled fields for uniform usage
736    for (idx = 0; idx < DATETIME_TYPE_COUNT; idx++)
737      {
738         field = wd->field_list + idx;
739         if (field->location == -1) field->location = field_count++;
740
741         if (field->fmt_exist && field->visible)
742           {
743              snprintf(buf, sizeof(buf), EDC_PART_FIELD_ENABLE_SIG_STR,
744                       field->location);
745              edje_object_signal_emit(wd->base, buf, "elm");
746           }
747         else
748           {
749              snprintf(buf, sizeof(buf),EDC_PART_FIELD_DISABLE_SIG_STR,
750                       field->location);
751              edje_object_signal_emit(wd->base, buf, "elm");
752           }
753         snprintf(buf, sizeof(buf), EDC_PART_SEPARATOR_STR, (field->location + 1));
754         edje_object_part_text_set(wd->base, buf, field->separator);
755      }
756    edje_object_message_signal_process(wd->base);
757    _field_list_arrange(obj);
758 }
759
760 static void
761 _field_list_init(Evas_Object *obj)
762 {
763    Widget_Data *wd;
764    Datetime_Field *field;
765    unsigned int idx;
766    time_t t;
767
768    wd = elm_widget_data_get(obj);
769    if (!wd) return;
770
771    t = time(NULL);
772    localtime_r(&t, &wd->curr_time);
773
774    mapping[ELM_DATETIME_YEAR].def_min = _elm_config->year_min;
775    mapping[ELM_DATETIME_YEAR].def_max = _elm_config->year_max;
776    for (idx = 0; idx < DATETIME_TYPE_COUNT; idx++)
777      {
778         field = wd->field_list + idx;
779         field->type = ELM_DATETIME_YEAR + idx;
780         field->fmt[0] = '%';
781         field->fmt_exist = EINA_FALSE;
782         field->visible  = EINA_TRUE;
783         field->min = mapping[idx].def_min;
784         field->max = mapping[idx].def_max;
785      }
786    DATETIME_TM_ARRAY(min_timearr, &wd->min_limit);
787    DATETIME_TM_ARRAY(max_timearr, &wd->max_limit);
788    for (idx = 0; idx < DATETIME_TYPE_COUNT-1; idx++)
789      {
790         *min_timearr[idx] = mapping[idx].def_min;
791         *max_timearr[idx] = mapping[idx].def_max;
792      }
793 }
794
795 EAPI Evas_Object *
796 elm_datetime_add(Evas_Object *parent)
797 {
798    Evas_Object *obj;
799    Evas *e;
800    Widget_Data *wd;
801    Datetime_Field *field;
802    int idx;
803
804    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
805
806    ELM_SET_WIDTYPE(widtype, "datetime");
807    elm_widget_type_set(obj, widtype);
808    elm_widget_sub_object_add(parent, obj);
809    elm_widget_data_set(obj, wd);
810    elm_widget_del_hook_set(obj, _del_hook);
811    elm_widget_theme_hook_set(obj, _theme_hook);
812    elm_widget_translate_hook_set(obj, _translate_hook);
813    elm_widget_on_focus_hook_set(obj, _on_focus_hook, NULL);
814    elm_widget_disable_hook_set(obj, _disable_hook);
815    elm_widget_can_focus_set(obj, EINA_TRUE);
816    elm_widget_focus_next_hook_set(obj, _elm_datetime_focus_next_hook);
817
818    wd->base = edje_object_add(e);
819    elm_widget_resize_object_set(obj, wd->base);
820    _elm_theme_object_set(obj, wd->base, "datetime", "base", "default");
821    evas_object_smart_callbacks_descriptions_set(obj, _signals);
822
823    // module - initialise module for datetime
824    if (!dt_mod) dt_mod = _dt_mod_init();
825    if ((dt_mod) && (dt_mod->obj_hook))
826      wd->mod_data = dt_mod->obj_hook(obj);
827    // update module data
828    if (wd->mod_data)
829      {
830         wd->mod_data->base = obj;
831         wd->mod_data->field_limit_get = _field_limit_get;
832         wd->mod_data->field_format_get = _field_format_get;
833      }
834
835    _field_list_init(obj);
836    _reload_format(obj);
837
838    if ((dt_mod)&&(dt_mod->field_create))
839      {
840         for (idx = 0; idx < DATETIME_TYPE_COUNT; idx++)
841           {
842              field = wd->field_list + idx;
843              field->item_obj = dt_mod->field_create(wd->mod_data, idx);
844           }
845      }
846    _field_list_arrange(obj);
847    _mirrored_set(obj, elm_widget_mirrored_get(obj));
848
849    return obj;
850 }
851
852 EAPI const char *
853 elm_datetime_format_get(const Evas_Object *obj)
854 {
855    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
856    Widget_Data *wd;
857
858    wd = elm_widget_data_get(obj);
859    if (!wd) return NULL;
860
861    return wd->format;
862 }
863
864 EAPI void
865 elm_datetime_format_set(Evas_Object *obj, const char *fmt)
866 {
867    ELM_CHECK_WIDTYPE(obj, widtype);
868    Widget_Data *wd;
869
870    wd = elm_widget_data_get(obj);
871    if (!wd) return;
872
873    if (fmt)
874      {
875         strncpy(wd->format, fmt, MAX_FORMAT_LEN);
876         wd->user_format = EINA_TRUE;
877      }
878    else
879      wd->user_format = EINA_FALSE;
880
881    _reload_format(obj);
882 }
883
884 EAPI Eina_Bool
885 elm_datetime_field_visible_get(const Evas_Object *obj, Elm_Datetime_Field_Type
886                                fieldtype)
887 {
888    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
889    Widget_Data *wd;
890    Datetime_Field *field;
891
892    wd = elm_widget_data_get(obj);
893    if (!wd || (fieldtype > ELM_DATETIME_AMPM)) return EINA_FALSE;
894
895    field = wd->field_list + fieldtype;
896    return field->visible;
897 }
898
899 EAPI void
900 elm_datetime_field_visible_set(Evas_Object *obj, Elm_Datetime_Field_Type fieldtype,
901                               Eina_Bool visible)
902 {
903    ELM_CHECK_WIDTYPE(obj, widtype);
904    Widget_Data *wd;
905    Datetime_Field *field;
906
907    wd = elm_widget_data_get(obj);
908    if (!wd || (fieldtype > ELM_DATETIME_AMPM)) return;
909
910    field = wd->field_list + fieldtype;
911    if (field->visible == visible) return;
912
913    field->visible = visible;
914    _reload_format(obj);
915 }
916
917 EAPI void
918 elm_datetime_field_limit_get(const Evas_Object *obj, Elm_Datetime_Field_Type fieldtype,
919                              int *min, int *max)
920 {
921    ELM_CHECK_WIDTYPE(obj, widtype);
922    Widget_Data *wd;
923    Datetime_Field *field;
924
925    wd = elm_widget_data_get(obj);
926    if (!wd || (fieldtype >= ELM_DATETIME_AMPM)) return;
927
928    field = wd->field_list + fieldtype;
929    if (min) *min = field->min;
930    if (max) *max = field->max;
931 }
932
933 EAPI void
934 elm_datetime_field_limit_set(Evas_Object *obj, Elm_Datetime_Field_Type fieldtype,
935                              int min, int max)
936 {
937    ELM_CHECK_WIDTYPE(obj, widtype);
938    Widget_Data *wd;
939    Datetime_Field *field;
940
941    wd = elm_widget_data_get(obj);
942    if (!wd || (fieldtype >= ELM_DATETIME_AMPM)) return;
943
944    if (min > max) return;
945
946    field = wd->field_list + fieldtype;
947    if ((min > mapping[fieldtype].def_min && min < mapping[fieldtype].def_max)
948         || (field->type == ELM_DATETIME_YEAR))
949      field->min = min;
950    if ((max > mapping[fieldtype].def_min && max < mapping[fieldtype].def_max)
951         || (field->type == ELM_DATETIME_YEAR))
952      field->max = max;
953
954    _apply_field_limits(obj);
955 }
956
957 EAPI Eina_Bool
958 elm_datetime_value_get(const Evas_Object *obj, struct tm *currtime)
959 {
960    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
961    EINA_SAFETY_ON_NULL_RETURN_VAL(currtime, EINA_FALSE);
962    Widget_Data *wd;
963
964    wd = elm_widget_data_get(obj);
965    if (!wd) return EINA_FALSE;
966
967    *currtime = wd->curr_time;
968    return EINA_TRUE;
969 }
970
971 EAPI Eina_Bool
972 elm_datetime_value_set(Evas_Object *obj, const struct tm *newtime)
973 {
974    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
975    EINA_SAFETY_ON_NULL_RETURN_VAL(newtime, EINA_FALSE);
976    Widget_Data *wd;
977    struct tm old_time;
978
979    wd = elm_widget_data_get(obj);
980    if (!wd) return EINA_FALSE;
981
982    old_time = wd->curr_time;
983    wd->curr_time = *newtime;
984    // apply default field restrictions for curr_time
985    _apply_range_restrictions(obj, &wd->curr_time);
986    // validate the curr_time according to the min_limt and max_limt
987    _validate_datetime_limits(&wd->curr_time, &wd->min_limit, EINA_FALSE);
988    _validate_datetime_limits(&wd->max_limit, &wd->curr_time, EINA_TRUE);
989    _apply_field_limits(obj);
990
991    if (!_date_cmp(&old_time, &wd->curr_time))
992      evas_object_smart_callback_call(obj, SIG_CHANGED, NULL);
993
994    return EINA_TRUE;
995 }
996
997 EAPI Eina_Bool
998 elm_datetime_value_min_get(const Evas_Object *obj, struct tm *mintime)
999 {
1000    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
1001    EINA_SAFETY_ON_NULL_RETURN_VAL(mintime, EINA_FALSE);
1002    Widget_Data *wd;
1003
1004    wd = elm_widget_data_get(obj);
1005    if (!wd) return EINA_FALSE;
1006
1007    *mintime = wd->min_limit;
1008    return EINA_TRUE;
1009 }
1010
1011 EAPI Eina_Bool
1012 elm_datetime_value_min_set(Evas_Object *obj, const struct tm *mintime)
1013 {
1014    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
1015    EINA_SAFETY_ON_NULL_RETURN_VAL(mintime, EINA_FALSE);
1016    Widget_Data *wd;
1017    struct tm old_time;
1018
1019    wd = elm_widget_data_get(obj);
1020    if (!wd) return EINA_FALSE;
1021
1022    wd->min_limit = *mintime;
1023    old_time = wd->curr_time;
1024    // apply default field restrictions for min_limit
1025    _apply_range_restrictions(obj, &wd->min_limit);
1026    // validate curr_time and max_limt according to the min_limit
1027    _validate_datetime_limits(&wd->max_limit, &wd->min_limit, EINA_FALSE);
1028    _validate_datetime_limits(&wd->curr_time, &wd->min_limit, EINA_FALSE);
1029    _apply_field_limits(obj);
1030
1031    if (!_date_cmp(&old_time, &wd->curr_time))
1032      evas_object_smart_callback_call(obj, SIG_CHANGED, NULL);
1033
1034    return EINA_TRUE;
1035 }
1036
1037 EAPI Eina_Bool
1038 elm_datetime_value_max_get(const Evas_Object *obj, struct tm *maxtime)
1039 {
1040    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
1041    EINA_SAFETY_ON_NULL_RETURN_VAL(maxtime, EINA_FALSE);
1042    Widget_Data *wd;
1043
1044    wd = elm_widget_data_get(obj);
1045    if (!wd) return EINA_FALSE;
1046
1047    *maxtime = wd->max_limit;
1048    return EINA_TRUE;
1049 }
1050
1051 EAPI Eina_Bool
1052 elm_datetime_value_max_set(Evas_Object *obj, const struct tm *maxtime)
1053 {
1054    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
1055    EINA_SAFETY_ON_NULL_RETURN_VAL(maxtime, EINA_FALSE);
1056    Widget_Data *wd;
1057    struct tm old_time;
1058
1059    wd = elm_widget_data_get(obj);
1060    if (!wd) return EINA_FALSE;
1061
1062    wd->max_limit = *maxtime;
1063    old_time = wd->curr_time;
1064    // apply default field restrictions for max_limit
1065    _apply_range_restrictions(obj, &wd->max_limit);
1066    // validate curr_time and min_limt according to the max_limit
1067    _validate_datetime_limits(&wd->max_limit, &wd->min_limit, EINA_TRUE);
1068    _validate_datetime_limits(&wd->max_limit, &wd->curr_time, EINA_TRUE);
1069    _apply_field_limits(obj);
1070
1071    if (!_date_cmp(&old_time, &wd->curr_time))
1072      evas_object_smart_callback_call(obj, SIG_CHANGED, NULL);
1073
1074    return EINA_TRUE;
1075 }