shadow warn--
[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 = NULL;
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      {
212         for (idx = 0; idx < DATETIME_TYPE_COUNT; idx++)
213           {
214              field = wd->field_list + idx;
215              if (field->location == loc)
216                items = eina_list_append(items, field->item_obj);
217           }
218      }
219
220    return items;
221 }
222
223 static Eina_Bool
224 _elm_datetime_focus_next_hook(const Evas_Object *obj, Elm_Focus_Direction dir, Evas_Object **next)
225 {
226    Widget_Data *wd;
227    const Eina_List *items;
228    void *(*list_data_get) (const Eina_List *list);
229    Eina_List *(*list_free) (Eina_List *list);
230    Eina_Bool ret;
231
232    wd = elm_widget_data_get(obj);
233    if (!wd) return EINA_FALSE;
234
235    if ((items = elm_widget_focus_custom_chain_get(obj)))
236      {
237         list_data_get = eina_list_data_get;
238         list_free = NULL;
239      }
240    else
241      {
242         items = _datetime_items_get(obj);
243         list_data_get = eina_list_data_get;
244         list_free = eina_list_free;
245         if (!items) return EINA_FALSE;
246      }
247
248    ret = elm_widget_focus_list_next_get(obj, items, list_data_get, dir, next);
249    if (list_free) list_free((Eina_List *)items);
250
251    return ret;
252 }
253
254 static void
255 _mirrored_set(Evas_Object *obj, Eina_Bool rtl)
256 {
257    Widget_Data *wd;
258
259    wd = elm_widget_data_get(obj);
260    if (!wd) return;
261
262    edje_object_mirrored_set(wd->base, rtl);
263 }
264
265 static void
266 _sizing_eval(Evas_Object *obj)
267 {
268    Widget_Data *wd;
269    Datetime_Field *field;
270    Evas_Coord minw = -1, minh = -1;
271    unsigned int idx, field_count = 0;
272
273    wd = elm_widget_data_get(obj);
274    if (!wd || !wd->base) return;
275    for (idx = 0; idx < DATETIME_TYPE_COUNT; idx++)
276      {
277         field = wd->field_list + idx;
278         if ((field->visible) && (field->fmt_exist)) field_count ++;
279      }
280    if (field_count)
281      elm_coords_finger_size_adjust(field_count, &minw, 1, &minh);
282    edje_object_size_min_restricted_calc(wd->base, &minw, &minh, minw, minh);
283    evas_object_size_hint_min_set(obj, minw, minh);
284    evas_object_size_hint_max_set(obj, -1, -1);
285 }
286
287 static void
288 _theme_hook(Evas_Object *obj)
289 {
290    Widget_Data *wd;
291    Datetime_Field *field;
292    char buf[BUFFER_SIZE];
293    unsigned int idx;
294
295    wd = elm_widget_data_get(obj);
296    if (!wd || !wd->base) return;
297
298    _elm_theme_object_set(obj, wd->base, "datetime", "base",
299                          elm_widget_style_get(obj));
300    _elm_widget_mirrored_reload(obj);
301    _mirrored_set(obj, elm_widget_mirrored_get(obj));
302
303    edje_object_scale_set(wd->base, elm_widget_scale_get(obj) * _elm_config->scale);
304
305    if (elm_widget_disabled_get(obj))
306      edje_object_signal_emit(wd->base, EDC_DATETIME_DISABLE_SIG_STR,"elm");
307    else
308      edje_object_signal_emit(wd->base, EDC_DATETIME_ENABLE_SIG_STR, "elm");
309
310    if ((!dt_mod) || (!dt_mod->field_value_display)) return;
311
312    for (idx = 0; idx < DATETIME_TYPE_COUNT; idx++)
313      {
314         field = wd->field_list + idx;
315         if (field->fmt_exist && field->visible)
316           {
317              snprintf(buf, sizeof(buf), EDC_PART_FIELD_ENABLE_SIG_STR, field->location);
318              edje_object_signal_emit(wd->base, buf, "elm");
319              snprintf(buf, sizeof(buf), EDC_PART_SEPARATOR_STR, field->location);
320              edje_object_part_text_set(wd->base, buf, field->separator);
321              dt_mod->field_value_display(wd->mod_data, field->item_obj);
322           }
323         else
324           {
325              snprintf(buf, sizeof(buf),EDC_PART_FIELD_DISABLE_SIG_STR, field->location);
326              edje_object_signal_emit(wd->base, buf, "elm");
327           }
328      }
329    edje_object_message_signal_process(wd->base);
330    _sizing_eval(obj);
331 }
332
333 static int
334 _max_days_get(int year, int month)
335 {
336    struct tm time1;
337    time_t t;
338    int day;
339
340    t = time(NULL);
341    localtime_r(&t, &time1);
342    time1.tm_year = year;
343    time1.tm_mon = month;
344    for(day = MIN_DAYS_IN_MONTH; day <= mapping[ELM_DATETIME_DATE].def_max; day++)
345      {
346         time1.tm_mday = day;
347         mktime(&time1);
348         if (time1.tm_mday == 1) break;
349      }
350    day --;
351    return day;
352 }
353
354 static Eina_Bool
355 _date_cmp(struct tm *time1, struct tm *time2)
356 {
357    unsigned int idx;
358    DATETIME_TM_ARRAY(timearr1, time1);
359    DATETIME_TM_ARRAY(timearr2, time2);
360
361    for (idx = 0; idx < DATETIME_TYPE_COUNT - 1; idx++)
362      {
363         if (*timearr1[idx] != *timearr2[idx])
364           return EINA_FALSE;
365      }
366    return EINA_TRUE;
367 }
368
369 // validates curr_time/min_limt/max_limit according to the newly set value
370 static void
371 _validate_datetime_limits(struct tm *time1, struct tm *time2, Eina_Bool swap)
372 {
373    struct tm *t1, *t2;
374    unsigned int idx;
375    if (!time1 || !time2) return;
376
377    t1 = (swap) ? time2 : time1;
378    t2 = (swap) ? time1 : time2;
379
380    DATETIME_TM_ARRAY(timearr1, time1);
381    DATETIME_TM_ARRAY(timearr2, time2);
382    for (idx = 0; idx < DATETIME_TYPE_COUNT - 1; idx++)
383      {
384        if (*timearr1[idx] < *timearr2[idx])
385          {
386             memcpy(t1, t2, sizeof(struct tm));
387             break;
388          }
389        else if (*timearr1[idx] > *timearr2[idx])
390          break;
391      }
392 }
393
394 static void
395 _apply_field_limits(Evas_Object *obj)
396 {
397    Widget_Data *wd;
398    Datetime_Field *field;
399    int val;
400    unsigned int idx = 0;
401
402    wd = elm_widget_data_get(obj);
403    if (!wd) return;
404
405    DATETIME_TM_ARRAY(timearr, &wd->curr_time);
406    for (idx = 0; idx < DATETIME_TYPE_COUNT - 1; idx++)
407      {
408         field = wd->field_list + idx;
409         val = *timearr[idx];
410         if (val < field->min)
411           *timearr[idx] = field->min;
412         else if (val > field->max)
413           *timearr[idx] = field->max;
414      }
415    _field_list_display(obj);
416 }
417
418 static void
419 _apply_range_restrictions(Evas_Object *obj, struct tm *tim)
420 {
421    Widget_Data *wd;
422    unsigned int idx;
423    int val, min, max;
424
425    wd = elm_widget_data_get(obj);
426    if (!wd || !tim) return;
427
428    DATETIME_TM_ARRAY(timearr, tim);
429    for (idx = ELM_DATETIME_MONTH; idx < DATETIME_TYPE_COUNT - 1; idx++)
430      {
431         val = *timearr[idx];
432         min = mapping[idx].def_min;
433         max = mapping[idx].def_max;
434         if (idx == ELM_DATETIME_DATE)
435           max = _max_days_get(tim->tm_year, tim->tm_mon);
436         if (val < min)
437           *timearr[idx] = min;
438         else if (val > max)
439           *timearr[idx] = max;
440      }
441 }
442
443 static const char *
444 _field_format_get(Evas_Object * obj, Elm_Datetime_Field_Type field_type)
445 {
446    Widget_Data *wd;
447    Datetime_Field *field;
448
449    wd = elm_widget_data_get(obj);
450    if (!wd) return NULL;
451
452    field = wd->field_list + field_type;
453    if (!field) return NULL;
454
455    return field->fmt;
456 }
457
458 static void
459 _field_limit_get(Evas_Object * obj, Elm_Datetime_Field_Type field_type, int *range_min, int *range_max)
460 {
461    Widget_Data *wd;
462    Datetime_Field *field;
463    int min, max, max_days;
464    unsigned int idx;
465
466    wd = elm_widget_data_get(obj);
467    if (!wd) return;
468
469    field = wd->field_list + field_type;
470    if (!field) return;
471
472    min = field->min;
473    max = field->max;
474
475    DATETIME_TM_ARRAY(curr_timearr, &wd->curr_time);
476    DATETIME_TM_ARRAY(min_timearr, &wd->min_limit);
477    DATETIME_TM_ARRAY(max_timearr, &wd->max_limit);
478
479    for (idx = 0; idx < field->type; idx++)
480      if (*curr_timearr[idx] > *min_timearr[idx]) break;
481    if ((idx == field_type) && (min < *min_timearr[field_type]))
482      min = *min_timearr[field_type];
483    if (field_type == ELM_DATETIME_DATE)
484      {
485         max_days = _max_days_get(wd->curr_time.tm_year, wd->curr_time.tm_mon);
486         if (max > max_days) max = max_days;
487      }
488    for (idx = 0; idx < field->type; idx++)
489      if (*curr_timearr[idx] < *max_timearr[idx]) break;
490    if ((idx == field_type) && (max > *max_timearr[field_type]))
491      max = *max_timearr[field_type];
492
493    *range_min = min;
494    *range_max = max;
495 }
496
497 static void
498 _field_list_display(Evas_Object *obj)
499 {
500    Widget_Data *wd;
501    Datetime_Field *field;
502    unsigned int idx= 0;
503
504    wd = elm_widget_data_get(obj);
505    if (!wd) return;
506
507    for (idx = 0; idx < DATETIME_TYPE_COUNT; idx++)
508      {
509         field = wd->field_list + idx;
510         if (field->fmt_exist && field->visible)
511           {
512              if ((dt_mod) && (dt_mod->field_value_display))
513                dt_mod->field_value_display(wd->mod_data, field->item_obj);
514           }
515      }
516 }
517
518 static void
519 _field_list_arrange(Evas_Object *obj)
520 {
521    Widget_Data *wd;
522    Datetime_Field *field;
523    char buf[BUFFER_SIZE];
524    int idx;
525
526    wd = elm_widget_data_get(obj);
527    if (!wd) return;
528
529    for (idx = 0; idx < DATETIME_TYPE_COUNT; idx++)
530      {
531         field = wd->field_list + idx;
532         edje_object_part_unswallow(wd->base, field->item_obj);
533      }
534    for (idx = 0; idx < DATETIME_TYPE_COUNT; idx++)
535      {
536         field = wd->field_list + idx;
537         if (field->visible && field->fmt_exist)
538           {
539              snprintf(buf, sizeof(buf), EDC_PART_FIELD_STR, field->location);
540              edje_object_part_swallow(wd->base, buf, field->item_obj);
541           }
542         else evas_object_hide(field->item_obj);
543      }
544    _sizing_eval(obj);
545    _field_list_display(obj);
546 }
547
548 // returns expanded format string for corresponding multi-field format character
549 static char *
550 _expanded_fmt_str_get(char ch)
551 {
552    char *exp_fmt = "";
553    switch (ch)
554      {
555       case 'c':
556          exp_fmt = nl_langinfo(D_T_FMT);
557          break;
558       case 'x':
559          exp_fmt = nl_langinfo(D_FMT);
560          break;
561       case 'X':
562          exp_fmt = nl_langinfo(T_FMT);
563          break;
564       case 'r':
565          exp_fmt = nl_langinfo(T_FMT_AMPM);
566          break;
567       case 'R':
568          exp_fmt =  "%H:%M";
569          break;
570       case 'T':
571          exp_fmt =  "%H:%M:%S";
572          break;
573       case 'D':
574          exp_fmt =  "%m/%d/%y";
575          break;
576       case 'F':
577          exp_fmt =  "%Y-%m-%d";
578          break;
579       default:
580          exp_fmt =  "";
581          break;
582      }
583    return exp_fmt;
584 }
585
586 static void
587 _expand_format(char * dt_fmt)
588 {
589    char *ptr, *expanded_fmt, ch;
590    char buf[MAX_FORMAT_LEN] = {0,};
591    unsigned int idx = 0, len = 0;
592    Eina_Bool fmt_char = EINA_FALSE;
593
594    ptr = dt_fmt;
595    while ((ch = *ptr))
596      {
597         if ((fmt_char) && (strchr(multifield_formats, ch)))
598           {
599              // replace the multi-field format characters with corresponding expanded format
600              expanded_fmt = _expanded_fmt_str_get(ch);
601              len = strlen(expanded_fmt);
602              buf[--idx] = 0;
603              strncat(buf, expanded_fmt, len);
604              idx += len;
605           }
606         else buf[idx++] = ch;
607         if (ch == '%') fmt_char = EINA_TRUE;
608         else fmt_char = EINA_FALSE;
609         ptr++;
610      }
611    buf[idx] = 0;
612    strncpy(dt_fmt, buf, MAX_FORMAT_LEN);
613 }
614
615 static unsigned int
616 _parse_format(Evas_Object *obj, char *fmt_ptr)
617 {
618    Widget_Data *wd;
619    Datetime_Field *field = NULL;
620    unsigned int len = 0, idx, location = 0;
621    char separator[MAX_SEPARATOR_LEN];
622    char cur;
623    Eina_Bool fmt_parsing = EINA_FALSE, sep_parsing = EINA_FALSE,
624              sep_lookup = EINA_FALSE;
625
626    wd = elm_widget_data_get(obj);
627
628    while ((cur = *fmt_ptr))
629      {
630         if (fmt_parsing)
631           {
632              for (idx = 0; idx < DATETIME_TYPE_COUNT; idx++)
633                {
634                   if (strchr(mapping[idx].fmt_char, cur))
635                     {
636                        field = wd->field_list + idx;
637                        // ignore the fields already have or disabled
638                        // valid formats, means already parsed & repeated, ignore.
639                        if (!field->visible || field->location != -1) break;
640                        field->fmt[1] = cur;
641                        field->fmt_exist = EINA_TRUE;
642                        field->location = location++;
643                        fmt_parsing = EINA_FALSE;
644                        sep_lookup = EINA_TRUE;
645                        len = 0;
646                        break;
647                     }
648                }
649           }
650         if (cur == ' ') separator[len++] = cur;
651         else if (cur == '%') fmt_parsing = EINA_TRUE;
652         if ((cur == ' ') || (cur == '%'))
653           {
654              sep_parsing = EINA_FALSE;
655              // set the separator to previous field
656              separator[len] = 0;
657              if (field) eina_stringshare_replace(&field->separator, separator);
658           }
659         if (sep_parsing && (len < MAX_SEPARATOR_LEN-1) &&
660             (field->type != ELM_DATETIME_AMPM) &&
661             (!((field->type == ELM_DATETIME_MINUTE) && (cur ==':'))))
662            separator[len++] = cur;
663         if (sep_lookup) sep_parsing = EINA_TRUE;
664         sep_lookup = EINA_FALSE;
665         fmt_ptr++;
666    }
667    // return the number of valid fields parsed.
668    return location;
669 }
670
671 static void
672 _reload_format(Evas_Object *obj)
673 {
674    Widget_Data *wd;
675    Datetime_Field *field;
676    char buf[BUFFER_SIZE];
677    unsigned int idx, field_count;
678    char *dt_fmt;
679
680    wd = elm_widget_data_get(obj);
681    if (!wd) return;
682
683    // fetch the default format from Libc.
684    if (!wd->user_format)
685      strncpy(wd->format, nl_langinfo(D_T_FMT), MAX_FORMAT_LEN);
686
687    dt_fmt = (char *)malloc(MAX_FORMAT_LEN);
688    strncpy(dt_fmt, wd->format, MAX_FORMAT_LEN);
689
690    _expand_format(dt_fmt);
691
692    // reset all the fields to disable state
693    for (idx = 0; idx < DATETIME_TYPE_COUNT; idx++)
694      {
695         field = wd->field_list + idx;
696         field->fmt_exist = EINA_FALSE;
697         field->location = -1;
698      }
699
700    field_count = _parse_format(obj, dt_fmt);
701    free(dt_fmt);
702
703    // assign locations to disabled fields for uniform usage
704    for (idx = 0; idx < DATETIME_TYPE_COUNT; idx++)
705      {
706         field = wd->field_list + idx;
707         if (field->location == -1) field->location = field_count++;
708
709         if (field->fmt_exist && field->visible)
710           {
711              snprintf(buf, sizeof(buf), EDC_PART_FIELD_ENABLE_SIG_STR,
712                       field->location);
713              edje_object_signal_emit(wd->base, buf, "elm");
714           }
715         else
716           {
717              snprintf(buf, sizeof(buf),EDC_PART_FIELD_DISABLE_SIG_STR,
718                       field->location);
719              edje_object_signal_emit(wd->base, buf, "elm");
720           }
721         snprintf(buf, sizeof(buf), EDC_PART_SEPARATOR_STR, (field->location + 1));
722         edje_object_part_text_set(wd->base, buf, field->separator);
723      }
724    edje_object_message_signal_process(wd->base);
725    _field_list_arrange(obj);
726 }
727
728 static void
729 _field_list_init(Evas_Object *obj)
730 {
731    Widget_Data *wd;
732    Datetime_Field *field;
733    unsigned int idx;
734    time_t t;
735
736    wd = elm_widget_data_get(obj);
737    if (!wd) return;
738
739    t = time(NULL);
740    localtime_r(&t, &wd->curr_time);
741
742    for (idx = 0; idx < DATETIME_TYPE_COUNT; idx++)
743      {
744         field = wd->field_list + idx;
745         field->type = ELM_DATETIME_YEAR + idx;
746         field->fmt[0] = '%';
747         field->fmt_exist = EINA_FALSE;
748         field->visible  = EINA_TRUE;
749         field->min = mapping[idx].def_min;
750         field->max = mapping[idx].def_max;
751      }
752    DATETIME_TM_ARRAY(min_timearr, &wd->min_limit);
753    DATETIME_TM_ARRAY(max_timearr, &wd->max_limit);
754    for (idx = 0; idx < DATETIME_TYPE_COUNT-1; idx++)
755      {
756         *min_timearr[idx] = mapping[idx].def_min;
757         *max_timearr[idx] = mapping[idx].def_max;
758      }
759 }
760
761 EAPI Evas_Object *
762 elm_datetime_add(Evas_Object *parent)
763 {
764    Evas_Object *obj;
765    Evas *e;
766    Widget_Data *wd;
767    Datetime_Field *field;
768    int idx;
769
770    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
771
772    ELM_SET_WIDTYPE(widtype, "datetime");
773    elm_widget_type_set(obj, widtype);
774    elm_widget_sub_object_add(parent, obj);
775    elm_widget_data_set(obj, wd);
776    elm_widget_del_hook_set(obj, _del_hook);
777    elm_widget_theme_hook_set(obj, _theme_hook);
778    elm_widget_translate_hook_set(obj, _translate_hook);
779    elm_widget_on_focus_hook_set(obj, _on_focus_hook, NULL);
780    elm_widget_disable_hook_set(obj, _disable_hook);
781    elm_widget_can_focus_set(obj, EINA_TRUE);
782    elm_widget_focus_next_hook_set(obj, _elm_datetime_focus_next_hook);
783
784    wd->base = edje_object_add(e);
785    elm_widget_resize_object_set(obj, wd->base);
786    _elm_theme_object_set(obj, wd->base, "datetime", "base", "default");
787    evas_object_smart_callbacks_descriptions_set(obj, _signals);
788
789    // module - initialise module for datetime
790    if (!dt_mod) dt_mod = _dt_mod_init();
791    if ((dt_mod) && (dt_mod->obj_hook))
792      wd->mod_data = dt_mod->obj_hook(obj);
793    // update module data
794    if (wd->mod_data)
795      {
796         wd->mod_data->base = obj;
797         wd->mod_data->field_limit_get = _field_limit_get;
798         wd->mod_data->field_format_get = _field_format_get;
799      }
800
801    _field_list_init(obj);
802    _reload_format(obj);
803
804    if ((dt_mod)&&(dt_mod->field_create))
805      {
806         for (idx = 0; idx < DATETIME_TYPE_COUNT; idx++)
807           {
808              field = wd->field_list + idx;
809              field->item_obj = dt_mod->field_create(wd->mod_data, idx);
810           }
811      }
812    _field_list_arrange(obj);
813    _mirrored_set(obj, elm_widget_mirrored_get(obj));
814
815    return obj;
816 }
817
818 EAPI const char *
819 elm_datetime_format_get(const Evas_Object *obj)
820 {
821    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
822    Widget_Data *wd;
823
824    wd = elm_widget_data_get(obj);
825    if (!wd) return NULL;
826
827    return wd->format;
828 }
829
830 EAPI void
831 elm_datetime_format_set(Evas_Object *obj, const char *fmt)
832 {
833    ELM_CHECK_WIDTYPE(obj, widtype);
834    Widget_Data *wd;
835
836    wd = elm_widget_data_get(obj);
837    if (!wd) return;
838
839    if (fmt)
840      {
841         strncpy(wd->format, fmt, MAX_FORMAT_LEN);
842         wd->user_format = EINA_TRUE;
843      }
844    else
845      wd->user_format = EINA_FALSE;
846
847    _reload_format(obj);
848 }
849
850 EAPI Eina_Bool
851 elm_datetime_field_visible_get(const Evas_Object *obj, Elm_Datetime_Field_Type
852                                fieldtype)
853 {
854    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
855    Widget_Data *wd;
856    Datetime_Field *field;
857
858    wd = elm_widget_data_get(obj);
859    if (!wd || (fieldtype > ELM_DATETIME_AMPM)) return EINA_FALSE;
860
861    field = wd->field_list + fieldtype;
862    return field->visible;
863 }
864
865 EAPI void
866 elm_datetime_field_visible_set(Evas_Object *obj, Elm_Datetime_Field_Type fieldtype,
867                               Eina_Bool visible)
868 {
869    ELM_CHECK_WIDTYPE(obj, widtype);
870    Widget_Data *wd;
871    Datetime_Field *field;
872
873    wd = elm_widget_data_get(obj);
874    if (!wd || (fieldtype > ELM_DATETIME_AMPM)) return;
875
876    field = wd->field_list + fieldtype;
877    if (field->visible == visible) return;
878
879    field->visible = visible;
880    _reload_format(obj);
881 }
882
883 EAPI void
884 elm_datetime_field_limit_get(const Evas_Object *obj, Elm_Datetime_Field_Type fieldtype,
885                              int *min, int *max)
886 {
887    ELM_CHECK_WIDTYPE(obj, widtype);
888    Widget_Data *wd;
889    Datetime_Field *field;
890
891    wd = elm_widget_data_get(obj);
892    if (!wd || (fieldtype >= ELM_DATETIME_AMPM)) return;
893
894    field = wd->field_list + fieldtype;
895    if (min) *min = field->min;
896    if (max) *max = field->max;
897 }
898
899 EAPI void
900 elm_datetime_field_limit_set(Evas_Object *obj, Elm_Datetime_Field_Type fieldtype,
901                              int min, int max)
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    if (min > max) return;
911
912    field = wd->field_list + fieldtype;
913    if ((min > mapping[fieldtype].def_min && min < mapping[fieldtype].def_max)
914         || (field->type == ELM_DATETIME_YEAR))
915      field->min = min;
916    if ((max > mapping[fieldtype].def_min && max < mapping[fieldtype].def_max)
917         || (field->type == ELM_DATETIME_YEAR))
918      field->max = max;
919
920    _apply_field_limits(obj);
921 }
922
923 EAPI Eina_Bool
924 elm_datetime_value_get(const Evas_Object *obj, struct tm *currtime)
925 {
926    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
927    EINA_SAFETY_ON_NULL_RETURN_VAL(currtime, EINA_FALSE);
928    Widget_Data *wd;
929
930    wd = elm_widget_data_get(obj);
931    if (!wd) return EINA_FALSE;
932
933    *currtime = wd->curr_time;
934    return EINA_TRUE;
935 }
936
937 EAPI Eina_Bool
938 elm_datetime_value_set(Evas_Object *obj, const struct tm *newtime)
939 {
940    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
941    EINA_SAFETY_ON_NULL_RETURN_VAL(newtime, EINA_FALSE);
942    Widget_Data *wd;
943    struct tm old_time;
944
945    wd = elm_widget_data_get(obj);
946    if (!wd) return EINA_FALSE;
947
948    old_time = wd->curr_time;
949    wd->curr_time = *newtime;
950    // apply default field restrictions for curr_time
951    _apply_range_restrictions(obj, &wd->curr_time);
952    // validate the curr_time according to the min_limt and max_limt
953    _validate_datetime_limits(&wd->curr_time, &wd->min_limit, EINA_FALSE);
954    _validate_datetime_limits(&wd->max_limit, &wd->curr_time, EINA_TRUE);
955    _apply_field_limits(obj);
956
957    if (!_date_cmp(&old_time, &wd->curr_time))
958      evas_object_smart_callback_call(obj, SIG_CHANGED, NULL);
959
960    return EINA_TRUE;
961 }
962
963 EAPI Eina_Bool
964 elm_datetime_value_min_get(const Evas_Object *obj, struct tm *mintime)
965 {
966    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
967    EINA_SAFETY_ON_NULL_RETURN_VAL(mintime, EINA_FALSE);
968    Widget_Data *wd;
969
970    wd = elm_widget_data_get(obj);
971    if (!wd) return EINA_FALSE;
972
973    *mintime = wd->min_limit;
974    return EINA_TRUE;
975 }
976
977 EAPI Eina_Bool
978 elm_datetime_value_min_set(Evas_Object *obj, const struct tm *mintime)
979 {
980    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
981    EINA_SAFETY_ON_NULL_RETURN_VAL(mintime, EINA_FALSE);
982    Widget_Data *wd;
983    struct tm old_time;
984
985    wd = elm_widget_data_get(obj);
986    if (!wd) return EINA_FALSE;
987
988    wd->min_limit = *mintime;
989    old_time = wd->curr_time;
990    // apply default field restrictions for min_limit
991    _apply_range_restrictions(obj, &wd->min_limit);
992    // validate curr_time and max_limt according to the min_limit
993    _validate_datetime_limits(&wd->max_limit, &wd->min_limit, EINA_FALSE);
994    _validate_datetime_limits(&wd->curr_time, &wd->min_limit, EINA_FALSE);
995    _apply_field_limits(obj);
996
997    if (!_date_cmp(&old_time, &wd->curr_time))
998      evas_object_smart_callback_call(obj, SIG_CHANGED, NULL);
999
1000    return EINA_TRUE;
1001 }
1002
1003 EAPI Eina_Bool
1004 elm_datetime_value_max_get(const Evas_Object *obj, struct tm *maxtime)
1005 {
1006    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
1007    EINA_SAFETY_ON_NULL_RETURN_VAL(maxtime, EINA_FALSE);
1008    Widget_Data *wd;
1009
1010    wd = elm_widget_data_get(obj);
1011    if (!wd) return EINA_FALSE;
1012
1013    *maxtime = wd->max_limit;
1014    return EINA_TRUE;
1015 }
1016
1017 EAPI Eina_Bool
1018 elm_datetime_value_max_set(Evas_Object *obj, const struct tm *maxtime)
1019 {
1020    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
1021    EINA_SAFETY_ON_NULL_RETURN_VAL(maxtime, EINA_FALSE);
1022    Widget_Data *wd;
1023    struct tm old_time;
1024
1025    wd = elm_widget_data_get(obj);
1026    if (!wd) return EINA_FALSE;
1027
1028    wd->max_limit = *maxtime;
1029    old_time = wd->curr_time;
1030    // apply default field restrictions for max_limit
1031    _apply_range_restrictions(obj, &wd->max_limit);
1032    // validate curr_time and min_limt according to the max_limit
1033    _validate_datetime_limits(&wd->max_limit, &wd->min_limit, EINA_TRUE);
1034    _validate_datetime_limits(&wd->max_limit, &wd->curr_time, EINA_TRUE);
1035    _apply_field_limits(obj);
1036
1037    if (!_date_cmp(&old_time, &wd->curr_time))
1038      evas_object_smart_callback_call(obj, SIG_CHANGED, NULL);
1039
1040    return EINA_TRUE;
1041 }