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