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