78f972a5f89cb845f2c0469c7f9eed949754219d
[platform/upstream/elementary.git] / src / lib / elm_datetime.c
1 #ifdef HAVE_CONFIG_H
2 # include "elementary_config.h"
3 #endif
4
5 #define ELM_INTERFACE_ATSPI_ACCESSIBLE_PROTECTED
6
7 #include <Elementary.h>
8 #include "elm_priv.h"
9 #include "elm_widget_datetime.h"
10
11 #define MY_CLASS ELM_DATETIME_CLASS
12
13 #define MY_CLASS_NAME "Elm_Datetime"
14 #define MY_CLASS_NAME_LEGACY "elm_datetime"
15
16 #ifdef HAVE_LOCALE_H
17 # include <locale.h>
18 #endif
19
20 #ifdef HAVE_LANGINFO_H
21 # include <langinfo.h>
22 #endif
23
24 #define MAX_SEPARATOR_LEN              6
25 #define MIN_DAYS_IN_MONTH              28
26 #define BUFFER_SIZE                    1024
27
28 /* interface between EDC & C code (field & signal names). values 0 to
29  * ELM_DATETIME_TYPE_COUNT are in the valid range, and must get in the
30  * place of "%d".
31  */
32 #define EDC_DATETIME_FOCUSIN_SIG_STR   "elm,action,focus"
33 #define EDC_DATETIME_FOCUSOUT_SIG_STR  "elm,action,unfocus"
34 #define EDC_PART_FIELD_STR             "field%d"
35 #define EDC_PART_SEPARATOR_STR         "separator%d"
36 #define EDC_PART_FIELD_ENABLE_SIG_STR  "field%d,enable"
37 #define EDC_PART_FIELD_DISABLE_SIG_STR "field%d,disable"
38
39 /* struct tm does not define the fields in the order year, month,
40  * date, hour, minute. values are reassigned to an array for easy
41  * handling.
42  */
43 #define DATETIME_TM_ARRAY(intptr, tmptr) \
44   int *intptr[] = {                      \
45      &(tmptr)->tm_year,                  \
46      &(tmptr)->tm_mon,                   \
47      &(tmptr)->tm_mday,                  \
48      &(tmptr)->tm_hour,                  \
49      &(tmptr)->tm_min,                  \
50      &(tmptr)->tm_sec}
51
52 // default limits for individual fields
53 static Format_Map mapping[ELM_DATETIME_TYPE_COUNT] = {
54    [ELM_DATETIME_YEAR] = { "Yy", -1, -1, "" },
55    [ELM_DATETIME_MONTH] = { "mbBh", 0, 11, "" },
56    [ELM_DATETIME_DATE] = { "de", 1, 31, "" },
57    [ELM_DATETIME_HOUR] = { "IHkl", 0, 23, "" },
58    [ELM_DATETIME_MINUTE] = { "M", 0, 59, ":" },
59    [ELM_DATETIME_AMPM] = { "pP", 0, 1, "" }
60 };
61
62 static const char *multifield_formats = "cxXrRTDF";
63 static const char *ignore_separators = "()";
64 static const char *ignore_extensions = "E0_-O^#";
65 static Datetime_Mod_Api *dt_mod = NULL;
66
67 static const char SIG_CHANGED[] = "changed";
68 static const Evas_Smart_Cb_Description _smart_callbacks[] = {
69    {SIG_CHANGED, ""},
70    {SIG_WIDGET_LANG_CHANGED, ""}, /**< handled by elm_widget */
71    {SIG_WIDGET_ACCESS_CHANGED, ""}, /**< handled by elm_widget */
72    {SIG_LAYOUT_FOCUSED, ""}, /**< handled by elm_layout */
73    {SIG_LAYOUT_UNFOCUSED, ""}, /**< handled by elm_layout */
74    {NULL, NULL}
75 };
76
77 static Datetime_Mod_Api *
78 _dt_mod_init()
79 {
80    Elm_Module *mod = NULL;
81
82    if (!(mod = _elm_module_find_as("datetime/api"))) return NULL;
83
84    mod->api = malloc(sizeof(Datetime_Mod_Api));
85    if (!mod->api) return NULL;
86
87    ((Datetime_Mod_Api *)(mod->api))->obj_hook =
88      _elm_module_symbol_get(mod, "obj_hook");
89    ((Datetime_Mod_Api *)(mod->api))->obj_unhook =
90      _elm_module_symbol_get(mod, "obj_unhook");
91    ((Datetime_Mod_Api *)(mod->api))->obj_hide =
92      _elm_module_symbol_get(mod, "obj_hide");
93    ((Datetime_Mod_Api *)(mod->api))->field_create =
94      _elm_module_symbol_get(mod, "field_create");
95    ((Datetime_Mod_Api *)(mod->api))->field_value_display =
96      _elm_module_symbol_get(mod, "field_value_display");
97    //TIZEN_ONLY(20151218): Support Mobile UX
98    ((Datetime_Mod_Api *)(mod->api))->field_format_changed =
99      _elm_module_symbol_get(mod, "field_format_changed");
100    //
101
102    return mod->api;
103 }
104
105 static void
106 _field_list_display(Evas_Object *obj)
107 {
108    Datetime_Field *field;
109    unsigned int idx = 0;
110
111    ELM_DATETIME_DATA_GET(obj, sd);
112
113    if (!dt_mod || !dt_mod->field_value_display) return;
114
115    for (idx = 0; idx < ELM_DATETIME_TYPE_COUNT; idx++)
116      {
117         field = sd->field_list + idx;
118         if (field->fmt_exist && field->visible)
119           dt_mod->field_value_display(sd->mod_data, field->item_obj);
120      }
121 }
122
123 // FIXME: provide nl_langinfo on Windows if possible
124 // returns expanded format string for corresponding multi-field format character
125 static char *
126 _expanded_fmt_str_get(char ch)
127 {
128    char *exp_fmt = "";
129    switch (ch)
130      {
131       case 'c':
132 #if defined(HAVE_LANGINFO_H) || defined (HAVE_EVIL)
133         exp_fmt = nl_langinfo(D_T_FMT);
134 #else
135         exp_fmt = "";
136 #endif
137         break;
138
139       case 'x':
140 #if defined(HAVE_LANGINFO_H) || defined (HAVE_EVIL)
141         exp_fmt = nl_langinfo(D_FMT);
142 #else
143         exp_fmt = "";
144 #endif
145         break;
146
147       case 'X':
148 #if defined(HAVE_LANGINFO_H) || defined (HAVE_EVIL)
149         exp_fmt = nl_langinfo(T_FMT);
150 #else
151         exp_fmt = "";
152 #endif
153         break;
154
155       case 'r':
156 #if defined(HAVE_LANGINFO_H) || defined (HAVE_EVIL)
157         exp_fmt = nl_langinfo(T_FMT_AMPM);
158 #else
159         exp_fmt = "";
160 #endif
161         break;
162
163       case 'R':
164         exp_fmt = "%H:%M";
165         break;
166
167       case 'T':
168         exp_fmt = "%H:%M:%S";
169         break;
170
171       case 'D':
172         exp_fmt = "%m/%d/%y";
173         break;
174
175       case 'F':
176         exp_fmt = "%Y-%m-%d";
177         break;
178
179       default:
180         exp_fmt = "";
181         break;
182      }
183
184    return exp_fmt;
185 }
186
187 static void
188 _expand_format(char *dt_fmt)
189 {
190    char *ptr, *expanded_fmt, ch;
191    unsigned int idx, len = 0;
192    char buf[ELM_DATETIME_MAX_FORMAT_LEN] = {0, };
193    Eina_Bool fmt_char, fmt_expanded;
194
195    do {
196      idx = 0;
197      fmt_char = EINA_FALSE;
198      fmt_expanded = EINA_FALSE;
199      ptr = dt_fmt;
200      while ((ch = *ptr))
201        {
202           if ((fmt_char) && (strchr(multifield_formats, ch)))
203             {
204                /* replace the multi-field format characters with
205                 * corresponding expanded format */
206                expanded_fmt = _expanded_fmt_str_get(ch);
207                len = strlen(expanded_fmt);
208                if (len > 0) fmt_expanded = EINA_TRUE;
209                buf[--idx] = 0;
210                strncat(buf, expanded_fmt, len);
211                idx += len;
212             }
213           else buf[idx++] = ch;
214
215           if (ch == '%') fmt_char = EINA_TRUE;
216           else fmt_char = EINA_FALSE;
217
218           ptr++;
219        }
220
221      buf[idx] = 0;
222      strncpy(dt_fmt, buf, ELM_DATETIME_MAX_FORMAT_LEN);
223    } while (fmt_expanded);
224 }
225
226 static void
227 _field_list_arrange(Evas_Object *obj)
228 {
229    Datetime_Field *field;
230    char buf[BUFFER_SIZE];
231    int idx;
232    Eina_Bool freeze;
233
234    ELM_DATETIME_DATA_GET(obj, sd);
235
236    freeze = sd->freeze_sizing;
237    sd->freeze_sizing = EINA_TRUE;
238
239    //////////////////////////////////////////////////////////////////////////////
240    //TIZEN_ONLY(20150225): Open source datetime code doesn't consider dynamically
241    //                      field sequence changing case.
242    /*
243    for (idx = 0; idx < ELM_DATETIME_TYPE_COUNT; idx++)
244      {
245         field = sd->field_list + idx;
246         snprintf(buf, sizeof(buf), EDC_PART_FIELD_STR, field->location);
247
248         if (field->visible && field->fmt_exist)
249           {
250              elm_layout_content_unset(obj, buf);
251              elm_layout_content_set(obj, buf, field->item_obj);
252           }
253         else
254           evas_object_hide(elm_layout_content_unset(obj, buf));
255      }
256    */
257    // remove all the content widget from the layout
258    for (idx = 0; idx < ELM_DATETIME_TYPE_COUNT; idx++)
259      {
260         snprintf(buf, sizeof(buf), EDC_PART_FIELD_STR, idx);
261         elm_layout_content_unset(obj, buf);
262      }
263    // set as content widget or hide it depending on location
264    for (idx = 0; idx < ELM_DATETIME_TYPE_COUNT; idx++)
265      {
266         field = sd->field_list + idx;
267         snprintf(buf, sizeof(buf), EDC_PART_FIELD_STR, field->location);
268
269         if (field->visible && field->fmt_exist)
270           elm_layout_content_set(obj, buf, field->item_obj);
271         else
272           evas_object_hide(field->item_obj);
273      }
274    //////////////////////////////////////////////////////////////////////////////
275
276    sd->freeze_sizing = freeze;
277
278    elm_layout_sizing_eval(obj);
279    _field_list_display(obj);
280 }
281
282 static unsigned int
283 _parse_format(Evas_Object *obj,
284               char *fmt_ptr)
285 {
286    Eina_Bool fmt_parsing = EINA_FALSE, sep_parsing = EINA_FALSE,
287              sep_lookup = EINA_FALSE;
288    //TIZEN_ONLY(20151216) - swap format locations to support some locale.
289    Eina_Bool location_swap = EINA_FALSE;
290    //
291    unsigned int len = 0, idx = 0, location = 0;
292    char separator[MAX_SEPARATOR_LEN];
293    Datetime_Field *field = NULL;
294    char cur;
295
296    ELM_DATETIME_DATA_GET(obj, sd);
297
298    while ((cur = *fmt_ptr))
299      {
300         if (fmt_parsing)
301           {
302              if (strchr(ignore_extensions, cur))
303                {
304                   fmt_ptr++;
305                   continue;
306                }
307              fmt_parsing = EINA_FALSE;
308
309              //TIZEN_ONLY(20151216) - swap format locations to support some locale.
310              if (location == 0 &&
311                  (strchr(mapping[ELM_DATETIME_HOUR].fmt_char, cur) ||
312                   strchr(mapping[ELM_DATETIME_MINUTE].fmt_char, cur) ||
313                   strchr(mapping[ELM_DATETIME_AMPM].fmt_char, cur)))
314                location_swap = EINA_TRUE;
315              //
316              for (idx = 0; idx < ELM_DATETIME_TYPE_COUNT; idx++)
317                {
318                   if (strchr(mapping[idx].fmt_char, cur))
319                     {
320                        field = sd->field_list + idx;
321                        /* ignore the fields already have or disabled
322                         * valid formats, means already parsed &
323                         * repeated, ignore. */
324                        if (field->location != -1) break;
325                        field->fmt[1] = cur;
326                        field->fmt_exist = EINA_TRUE;
327                        field->location = location++;
328                        sep_lookup = EINA_TRUE;
329                        len = 0;
330                        break;
331                     }
332                }
333           }
334         if (cur == '%')
335           {
336              fmt_parsing = EINA_TRUE;
337              sep_parsing = EINA_FALSE;
338              // set the separator to previous field
339              separator[len] = 0;
340              if (field) eina_stringshare_replace(&field->separator, separator);
341           }
342         // ignore the set of chars (global, field specific) as field separators
343         if (sep_parsing &&
344             (len < MAX_SEPARATOR_LEN - 1) &&
345             (field->type != ELM_DATETIME_AMPM) &&
346             (!strchr(ignore_separators, cur)) &&
347             (!strchr(mapping[idx].ignore_sep, cur)))
348           separator[len++] = cur;
349         if (sep_lookup) sep_parsing = EINA_TRUE;
350         sep_lookup = EINA_FALSE;
351         fmt_ptr++;
352      }
353
354    //TIZEN_ONLY(20151216) - swap format locations to support some locale.
355    if (location_swap)
356      {
357         int time_fmt_count;
358
359         time_fmt_count = location - ELM_DATETIME_HOUR;
360         for (idx = 0; idx < ELM_DATETIME_TYPE_COUNT; idx++)
361           {
362              field = sd->field_list + idx;
363              /* ignore the fields already disabled
364               * valid formats, means already ignore. */
365              if (field->location == -1) continue;
366              if (idx < ELM_DATETIME_HOUR)
367                field->location -= time_fmt_count;
368              else
369                field->location += ELM_DATETIME_HOUR;
370           }
371      }
372    //
373
374    // return the number of valid fields parsed.
375    return location;
376 }
377
378 // TIZEN_ONLY(20151218): Support Mobile UX
379 static void
380 _notify_format_change(Evas_Object *obj)
381 {
382    Datetime_Field *field;
383    unsigned int idx = 0;
384
385    ELM_DATETIME_DATA_GET(obj, sd);
386
387    if (!dt_mod || !dt_mod->field_format_changed) return;
388
389    for (idx = 0; idx < ELM_DATETIME_TYPE_COUNT; idx++)
390      {
391         field = sd->field_list + idx;
392         if (field->item_obj)
393           dt_mod->field_format_changed(sd->mod_data, field->item_obj);
394      }
395 }
396 //
397
398 static void
399 _reload_format(Evas_Object *obj)
400 {
401    unsigned int idx, field_count;
402    Datetime_Field *field;
403    char buf[BUFFER_SIZE];
404    char *dt_fmt;
405
406    ELM_DATETIME_DATA_GET(obj, sd);
407    ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd);
408
409    // FIXME: provide nl_langinfo on Windows if possible
410    // fetch the default format from Libc.
411    if (!sd->user_format)
412 #if defined(HAVE_LANGINFO_H) || defined (HAVE_EVIL)
413      strncpy(sd->format, nl_langinfo(D_T_FMT), ELM_DATETIME_MAX_FORMAT_LEN);
414 #else
415      strncpy(sd->format, "", ELM_DATETIME_MAX_FORMAT_LEN);
416 #endif
417    sd->format[ELM_DATETIME_MAX_FORMAT_LEN - 1] = '\0';
418
419    dt_fmt = (char *)malloc(ELM_DATETIME_MAX_FORMAT_LEN);
420    if (!dt_fmt) return;
421
422    strncpy(dt_fmt, sd->format, ELM_DATETIME_MAX_FORMAT_LEN);
423
424    _expand_format(dt_fmt);
425
426    // reset all the fields to disable state
427    sd->enabled_field_count = 0;
428    for (idx = 0; idx < ELM_DATETIME_TYPE_COUNT; idx++)
429      {
430         field = sd->field_list + idx;
431         field->fmt_exist = EINA_FALSE;
432         field->location = -1;
433      }
434
435    field_count = _parse_format(obj, dt_fmt);
436    free(dt_fmt);
437
438    for (idx = 0; idx < ELM_DATETIME_TYPE_COUNT; idx++)
439      {
440         field = sd->field_list + idx;
441         if (field->fmt_exist && field->visible)
442           sd->enabled_field_count++;
443      }
444
445    // assign locations to disabled fields for uniform usage
446    for (idx = 0; idx < ELM_DATETIME_TYPE_COUNT; idx++)
447      {
448         field = sd->field_list + idx;
449         if (field->location == -1) field->location = field_count++;
450
451         if (field->fmt_exist && field->visible)
452           {
453              snprintf(buf, sizeof(buf), EDC_PART_FIELD_ENABLE_SIG_STR,
454                       field->location);
455              elm_layout_signal_emit(obj, buf, "elm");
456           }
457         else
458           {
459              snprintf(buf, sizeof(buf), EDC_PART_FIELD_DISABLE_SIG_STR,
460                       field->location);
461              elm_layout_signal_emit(obj, buf, "elm");
462           }
463         snprintf
464           (buf, sizeof(buf), EDC_PART_SEPARATOR_STR, (field->location + 1));
465         elm_layout_text_set(obj, buf, field->separator);
466      }
467
468    edje_object_message_signal_process(wd->resize_obj);
469    _field_list_arrange(obj);
470
471    //TIZEN_ONLY(20151218): Support Mobile UX
472    _notify_format_change(obj);
473    //
474 }
475
476 EOLIAN static Eina_Bool
477 _elm_datetime_elm_widget_translate(Eo *obj, Elm_Datetime_Data *sd)
478 {
479    if (!sd->user_format) _reload_format(obj);
480    else _field_list_display(obj);
481
482    eo_do_super(obj, MY_CLASS, elm_obj_widget_translate());
483
484    return EINA_TRUE;
485 }
486
487 static Eina_List *
488 _datetime_items_get(const Evas_Object *obj)
489 {
490    Eina_List *items = NULL;
491    Datetime_Field *field;
492    unsigned int idx;
493    //TIZEN_ONLY(20170818): Prevent Null pointer ref.
494    Datetime_Field *sorted_fields[ELM_DATETIME_TYPE_COUNT] = {0};
495    //Datetime_Field *sorted_fields[ELM_DATETIME_TYPE_COUNT];
496    //
497
498    ELM_DATETIME_DATA_GET(obj, sd);
499
500    for (idx = 0; idx < ELM_DATETIME_TYPE_COUNT; idx++)
501      {
502         field = sd->field_list + idx;
503         sorted_fields[field->location] = field;
504      }
505
506    for (idx = 0; idx < ELM_DATETIME_TYPE_COUNT; idx++)
507      {
508         field = sorted_fields[idx];
509         //TIZEN_ONLY(20170818): Prevent Null pointer ref.
510         if (!field) continue;
511         //
512         if (field->fmt_exist && field->visible)
513           items = eina_list_append(items, field->item_obj);
514      }
515
516    // ACCESS
517    if (_elm_config->access_mode == ELM_ACCESS_MODE_ON)
518      items = eina_list_append(items, sd->access_obj);
519
520    return items;
521 }
522
523 EOLIAN static Eina_Bool
524 _elm_datetime_elm_widget_focus_next_manager_is(Eo *obj EINA_UNUSED, Elm_Datetime_Data *_pd EINA_UNUSED)
525 {
526    return EINA_TRUE;
527 }
528
529 EOLIAN static Eina_Bool
530 _elm_datetime_elm_widget_focus_next(Eo *obj, Elm_Datetime_Data *_pd EINA_UNUSED, Elm_Focus_Direction dir, Evas_Object **next, Elm_Object_Item **next_item)
531 {
532    const Eina_List *items;
533    Eina_List *(*list_free)(Eina_List *list);
534    void *(*list_data_get)(const Eina_List *list);
535
536    Eina_Bool int_ret;
537
538    if ((items = elm_widget_focus_custom_chain_get(obj)))
539      {
540         list_data_get = eina_list_data_get;
541         list_free = NULL;
542      }
543    else
544      {
545         items = _datetime_items_get(obj);
546         list_data_get = eina_list_data_get;
547         list_free = eina_list_free;
548         if (!items) return EINA_FALSE;
549      }
550
551    int_ret = elm_widget_focus_list_next_get(obj, items, list_data_get, dir, next, next_item);
552    if (list_free) list_free((Eina_List *)items);
553
554    return int_ret;
555 }
556
557 EOLIAN static Eina_Bool
558 _elm_datetime_elm_widget_on_focus(Eo *obj, Elm_Datetime_Data *sd, Elm_Object_Item *item EINA_UNUSED)
559 {
560    Eina_Bool int_ret = EINA_FALSE;
561
562    eo_do_super(obj, MY_CLASS, int_ret = elm_obj_widget_on_focus(NULL));
563    if (!int_ret) return EINA_FALSE;
564
565    if (!elm_widget_focus_get(obj))
566      {
567         if ((dt_mod) && (dt_mod->obj_hide))
568           dt_mod->obj_hide(sd->mod_data);
569      }
570
571    return EINA_TRUE;
572 }
573
574 EOLIAN static Eina_Bool
575 _elm_datetime_elm_widget_disable(Eo *obj, Elm_Datetime_Data *sd)
576 {
577    Datetime_Field *field;
578    unsigned int idx = 0;
579    Eina_Bool int_ret = EINA_FALSE;
580
581    eo_do_super(obj, MY_CLASS, int_ret = elm_obj_widget_disable());
582    if (!int_ret) return EINA_FALSE;
583
584    for (idx = 0; idx < ELM_DATETIME_TYPE_COUNT; idx++)
585      {
586         field = sd->field_list + idx;
587         elm_object_disabled_set(field->item_obj, elm_object_disabled_get(obj));
588      }
589    return EINA_TRUE;
590 }
591
592 EOLIAN static void
593 _elm_datetime_elm_layout_sizing_eval(Eo *obj, Elm_Datetime_Data *sd)
594 {
595    Evas_Coord minw = -1, minh = -1;
596
597    if (sd->freeze_sizing) return;
598
599    ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd);
600
601    if (sd->enabled_field_count)
602      elm_coords_finger_size_adjust(sd->enabled_field_count, &minw, 1, &minh);
603
604    edje_object_size_min_restricted_calc
605      (wd->resize_obj, &minw, &minh, minw, minh);
606    evas_object_size_hint_min_set(obj, minw, minh);
607    evas_object_size_hint_max_set(obj, -1, -1);
608 }
609
610 EOLIAN static Elm_Theme_Apply
611 _elm_datetime_elm_widget_theme_apply(Eo *obj, Elm_Datetime_Data *sd)
612 {
613    Elm_Theme_Apply int_ret = ELM_THEME_APPLY_FAILED;
614
615    Datetime_Field *field;
616    char buf[BUFFER_SIZE];
617    unsigned int idx;
618
619    ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, ELM_THEME_APPLY_FAILED);
620
621    eo_do_super(obj, MY_CLASS, int_ret = elm_obj_widget_theme_apply());
622    if (!int_ret) return ELM_THEME_APPLY_FAILED;
623
624    if ((!dt_mod) || (!dt_mod->field_value_display)) return int_ret;
625
626    for (idx = 0; idx < ELM_DATETIME_TYPE_COUNT; idx++)
627      {
628         field = sd->field_list + idx;
629         if (field->fmt_exist && field->visible)
630           {
631              snprintf(buf, sizeof(buf), EDC_PART_FIELD_ENABLE_SIG_STR,
632                       field->location);
633              elm_layout_signal_emit(obj, buf, "elm");
634
635              snprintf
636                (buf, sizeof(buf), EDC_PART_SEPARATOR_STR, field->location);
637              elm_layout_text_set(obj, buf, field->separator);
638
639              dt_mod->field_value_display(sd->mod_data, field->item_obj);
640           }
641         else
642           {
643              snprintf(buf, sizeof(buf), EDC_PART_FIELD_DISABLE_SIG_STR,
644                       field->location);
645              elm_layout_signal_emit(obj, buf, "elm");
646           }
647      }
648
649    edje_object_message_signal_process(wd->resize_obj);
650    elm_layout_sizing_eval(obj);
651
652    return int_ret;
653 }
654
655 //TIZEN_ONLY(20170807): Apply UI Mirroring for Tizen 4.0 UX
656 EOLIAN static Eina_Bool
657 _elm_datetime_elm_widget_mirrored_get(Eo *obj EINA_UNUSED, Elm_Datetime_Data *sd EINA_UNUSED)
658 {
659    return EINA_FALSE;
660 }
661
662 EOLIAN static void
663 _elm_datetime_elm_widget_mirrored_set(Eo *obj EINA_UNUSED, Elm_Datetime_Data *sd EINA_UNUSED, Eina_Bool mirrored EINA_UNUSED)
664 {
665    return;
666 }
667 //
668
669 static int
670 _max_days_get(int year,
671               int month)
672 {
673    struct tm time1;
674    time_t t;
675    int day;
676
677    t = time(NULL);
678    localtime_r(&t, &time1);
679    time1.tm_year = year;
680    time1.tm_mon = month;
681    for (day = MIN_DAYS_IN_MONTH; day <= mapping[ELM_DATETIME_DATE].def_max;
682         day++)
683      {
684         time1.tm_mday = day;
685         mktime(&time1);
686         /* To restrict month wrapping because of summer time in some locales,
687         * ignore day light saving mode in mktime(). */
688         time1.tm_isdst = -1;
689         if (time1.tm_mday == 1) break;
690      }
691    day--;
692
693    return day;
694 }
695
696 static Eina_Bool
697 _date_cmp(const struct tm *time1,
698           const struct tm *time2)
699 {
700    unsigned int idx;
701
702    const DATETIME_TM_ARRAY(timearr1, time1);
703    const DATETIME_TM_ARRAY(timearr2, time2);
704
705    for (idx = 0; idx < ELM_DATETIME_TYPE_COUNT; idx++)
706      {
707         if (*timearr1[idx] != *timearr2[idx])
708           return EINA_FALSE;
709      }
710
711    return EINA_TRUE;
712 }
713
714 static Eina_Bool
715 _field_cmp(Elm_Datetime_Field_Type field_type,
716           struct tm *time1,
717           struct tm *time2)
718 {
719    DATETIME_TM_ARRAY(timearr1, time1);
720    DATETIME_TM_ARRAY(timearr2, time2);
721
722    if (*timearr1[field_type] != *timearr2[field_type])
723      return EINA_FALSE;
724    else
725      return EINA_TRUE;
726 }
727
728 // validates curr_time/min_limt/max_limit according to the newly set value
729 static void
730 _validate_datetime_limits(struct tm *time1,
731                           struct tm *time2,
732                           Eina_Bool swap)
733 {
734    struct tm *t1, *t2;
735    unsigned int idx;
736
737    if (!time1 || !time2) return;
738
739    t1 = (swap) ? time2 : time1;
740    t2 = (swap) ? time1 : time2;
741
742    DATETIME_TM_ARRAY(timearr1, time1);
743    DATETIME_TM_ARRAY(timearr2, time2);
744    for (idx = 0; idx < ELM_DATETIME_TYPE_COUNT - 1; idx++)
745      {
746         if (*timearr1[idx] < *timearr2[idx])
747           {
748              memcpy(t1, t2, sizeof(struct tm));
749              break;
750           }
751         else if (*timearr1[idx] > *timearr2[idx])
752           break;
753      }
754 }
755
756 static void
757 _apply_field_limits(Evas_Object *obj)
758 {
759    Datetime_Field *field;
760    unsigned int idx = 0;
761    int val;
762
763    ELM_DATETIME_DATA_GET(obj, sd);
764
765    DATETIME_TM_ARRAY(timearr, &sd->curr_time);
766    for (idx = 0; idx < ELM_DATETIME_TYPE_COUNT - 1; idx++)
767      {
768         field = sd->field_list + idx;
769         val = *timearr[idx];
770         if (val < field->min)
771           *timearr[idx] = field->min;
772         else if (val > field->max)
773           *timearr[idx] = field->max;
774      }
775
776    _field_list_display(obj);
777 }
778
779 static void
780 _apply_range_restrictions(struct tm *tim)
781 {
782    unsigned int idx;
783    int val, min, max;
784
785    if (!tim) return;
786
787    DATETIME_TM_ARRAY(timearr, tim);
788    for (idx = ELM_DATETIME_MONTH; idx < ELM_DATETIME_TYPE_COUNT - 1; idx++)
789      {
790         val = *timearr[idx];
791         min = mapping[idx].def_min;
792         max = mapping[idx].def_max;
793         if (idx == ELM_DATETIME_DATE)
794           max = _max_days_get(tim->tm_year, tim->tm_mon);
795         if (val < min)
796           *timearr[idx] = min;
797         else if (val > max)
798           *timearr[idx] = max;
799      }
800 }
801
802 static const char *
803 _field_format_get(Evas_Object *obj,
804                   Elm_Datetime_Field_Type field_type)
805 {
806    Datetime_Field *field;
807
808    if (field_type > ELM_DATETIME_AMPM) return NULL;
809
810    ELM_DATETIME_DATA_GET(obj, sd);
811
812    field = sd->field_list + field_type;
813
814    return field->fmt;
815 }
816
817 static void
818 _field_limit_get(Evas_Object *obj,
819                  Elm_Datetime_Field_Type field_type,
820                  int *range_min,
821                  int *range_max)
822 {
823    int min, max, max_days;
824    Datetime_Field *field;
825    unsigned int idx;
826
827    if (field_type > ELM_DATETIME_MINUTE) return;
828
829    ELM_DATETIME_DATA_GET(obj, sd);
830
831    field = sd->field_list + field_type;
832
833    min = field->min;
834    max = field->max;
835
836    DATETIME_TM_ARRAY(curr_timearr, &sd->curr_time);
837    DATETIME_TM_ARRAY(min_timearr, &sd->min_limit);
838    DATETIME_TM_ARRAY(max_timearr, &sd->max_limit);
839
840    for (idx = 0; idx < field->type; idx++)
841      if (*curr_timearr[idx] > *min_timearr[idx]) break;
842    if ((idx == field_type) && (min < *min_timearr[field_type]))
843      min = *min_timearr[field_type];
844    if (field_type == ELM_DATETIME_DATE)
845      {
846         max_days = _max_days_get(sd->curr_time.tm_year, sd->curr_time.tm_mon);
847         if (max > max_days) max = max_days;
848      }
849    for (idx = 0; idx < field->type; idx++)
850      if (*curr_timearr[idx] < *max_timearr[idx]) break;
851    if ((idx == field_type) && (max > *max_timearr[field_type]))
852      max = *max_timearr[field_type];
853
854    *range_min = min;
855    *range_max = max;
856 }
857
858 //TIZEN_ONLY(20151218): Support Mobile UX
859 static Eina_Bool
860 _field_location_get(Evas_Object *obj, Elm_Datetime_Field_Type field_type,
861                     int *loc)
862 {
863    Datetime_Field *field;
864
865    ELM_DATETIME_DATA_GET(obj, sd);
866
867    field = sd->field_list + field_type;
868    if (!field) return EINA_FALSE;
869
870    if (loc) *loc = field->location;
871
872    return (field->fmt_exist && field->visible);
873 }
874
875 static Eina_List *
876 _fields_sorted_get(Evas_Object *obj)
877 {
878    Eina_List *items = NULL;
879    Datetime_Field *field;
880    unsigned int idx;
881    Datetime_Field *sorted_fields[ELM_DATETIME_TYPE_COUNT];
882
883    ELM_DATETIME_DATA_GET(obj, sd);
884
885    for (idx = 0; idx < ELM_DATETIME_TYPE_COUNT; idx++)
886      {
887         field = sd->field_list + idx;
888         sorted_fields[field->location] = field;
889      }
890
891    for (idx = 0; idx < ELM_DATETIME_TYPE_COUNT; idx++)
892      {
893         field = sorted_fields[idx];
894         if (field->fmt_exist && field->visible)
895           items = eina_list_append(items, field->item_obj);
896      }
897
898    return items;
899 }
900 //
901
902 static void
903 _field_list_init(Evas_Object *obj)
904 {
905    Datetime_Field *field;
906    unsigned int idx;
907    time_t t;
908
909    ELM_DATETIME_DATA_GET(obj, sd);
910
911    t = time(NULL);
912    localtime_r(&t, &sd->curr_time);
913
914    mapping[ELM_DATETIME_YEAR].def_min = _elm_config->year_min;
915    mapping[ELM_DATETIME_YEAR].def_max = _elm_config->year_max;
916    for (idx = 0; idx < ELM_DATETIME_TYPE_COUNT; idx++)
917      {
918         field = sd->field_list + idx;
919         field->type = ELM_DATETIME_YEAR + idx;
920         field->fmt[0] = '%';
921         field->fmt_exist = EINA_FALSE;
922         field->visible = EINA_TRUE;
923         field->min = mapping[idx].def_min;
924         field->max = mapping[idx].def_max;
925      }
926    DATETIME_TM_ARRAY(min_timearr, &sd->min_limit);
927    DATETIME_TM_ARRAY(max_timearr, &sd->max_limit);
928    for (idx = 0; idx < ELM_DATETIME_TYPE_COUNT - 1; idx++)
929      {
930         *min_timearr[idx] = mapping[idx].def_min;
931         *max_timearr[idx] = mapping[idx].def_max;
932      }
933 }
934
935 static char *
936 _access_info_cb(void *data, Evas_Object *obj EINA_UNUSED)
937 {
938    char *ret;
939    Eina_Strbuf *buf;
940    buf = eina_strbuf_new();
941
942    ELM_DATETIME_DATA_GET(data, sd);
943    eina_strbuf_append_printf(buf,
944                              "%d year, %d month, %d date, %d hour, %d minute",
945                              sd->curr_time.tm_year, sd->curr_time.tm_mon + 1,
946                              sd->curr_time.tm_mday, sd->curr_time.tm_hour,
947                              sd->curr_time.tm_min);
948
949    ret = eina_strbuf_string_steal(buf);
950    eina_strbuf_free(buf);
951    return ret;
952 }
953
954 EOLIAN static void
955 _elm_datetime_evas_object_smart_add(Eo *obj, Elm_Datetime_Data *priv)
956 {
957    Datetime_Field *field;
958    int idx;
959
960    eo_do_super(obj, MY_CLASS, evas_obj_smart_add());
961    elm_widget_sub_object_parent_add(obj);
962
963    // module - initialise module for datetime
964    if (!dt_mod) dt_mod = _dt_mod_init();
965    if (dt_mod)
966      {
967         if (dt_mod->obj_hook)
968           {
969              priv->mod_data = dt_mod->obj_hook(obj);
970
971              // update module data
972              if (priv->mod_data)
973                {
974                   priv->mod_data->base = obj;
975                   priv->mod_data->field_limit_get = _field_limit_get;
976                   priv->mod_data->field_format_get = _field_format_get;
977                   //TIZEN_ONLY(20151218): Support Mobile UX
978                   priv->mod_data->field_location_get = _field_location_get;
979                   priv->mod_data->fields_sorted_get = _fields_sorted_get;
980                   //
981                }
982           }
983
984         if (dt_mod->field_create)
985           {
986              for (idx = 0; idx < ELM_DATETIME_TYPE_COUNT; idx++)
987                {
988                   field = priv->field_list + idx;
989                   field->item_obj = dt_mod->field_create(priv->mod_data, idx);
990                   /* TIZEN_ONLY(20161031): apply color_class parent-child relationship to all widgets */
991                   _elm_widget_color_class_parent_set(field->item_obj, obj);
992                   /* END */
993                }
994           }
995      }
996
997    priv->freeze_sizing = EINA_TRUE;
998    if (!elm_layout_theme_set(obj, "datetime", "base",
999                              elm_widget_style_get(obj)))
1000      CRI("Failed to set layout!");
1001
1002    _field_list_init(obj);
1003    _reload_format(obj);
1004
1005    elm_widget_can_focus_set(obj, EINA_TRUE);
1006
1007    priv->freeze_sizing = EINA_FALSE;
1008    elm_layout_sizing_eval(obj);
1009
1010    // ACCESS
1011    if (_elm_config->access_mode == ELM_ACCESS_MODE_ON)
1012      {
1013         Elm_Access_Info *ai;
1014
1015         priv->access_obj = _elm_access_edje_object_part_object_register
1016           (obj, elm_layout_edje_get(obj), "elm.access");
1017         if (!priv->access_obj)
1018           priv->access_obj = _elm_access_edje_object_part_object_register
1019           (obj, elm_layout_edje_get(obj), "access");
1020
1021         ai = _elm_access_info_get(priv->access_obj);
1022         _elm_access_text_set(ai, ELM_ACCESS_TYPE, "date time");
1023         _elm_access_callback_set(ai, ELM_ACCESS_INFO, _access_info_cb, obj);
1024      }
1025 }
1026
1027 EOLIAN static void
1028 _elm_datetime_evas_object_smart_del(Eo *obj, Elm_Datetime_Data *sd)
1029 {
1030    Datetime_Field *tmp;
1031    unsigned int idx;
1032
1033    for (idx = 0; idx < ELM_DATETIME_TYPE_COUNT; idx++)
1034      {
1035         tmp = sd->field_list + idx;
1036         evas_object_del(tmp->item_obj);
1037         eina_stringshare_del(tmp->separator);
1038      }
1039
1040    if ((dt_mod) && (dt_mod->obj_unhook))
1041      dt_mod->obj_unhook(sd->mod_data);  // module - unhook
1042
1043    eo_do_super(obj, MY_CLASS, evas_obj_smart_del());
1044 }
1045
1046 EAPI Evas_Object *
1047 elm_datetime_add(Evas_Object *parent)
1048 {
1049    EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL);
1050    Evas_Object *obj = eo_add(MY_CLASS, parent);
1051    return obj;
1052 }
1053
1054 EOLIAN static Eo *
1055 _elm_datetime_eo_base_constructor(Eo *obj, Elm_Datetime_Data *_pd EINA_UNUSED)
1056 {
1057    obj = eo_do_super_ret(obj, MY_CLASS, obj, eo_constructor());
1058    eo_do(obj,
1059          evas_obj_type_set(MY_CLASS_NAME_LEGACY),
1060          evas_obj_smart_callbacks_descriptions_set(_smart_callbacks),
1061          elm_interface_atspi_accessible_role_set(ELM_ATSPI_ROLE_DATE_EDITOR));
1062
1063    return obj;
1064 }
1065
1066 EOLIAN static const char*
1067 _elm_datetime_format_get(Eo *obj EINA_UNUSED, Elm_Datetime_Data *sd)
1068 {
1069    return sd->format;
1070 }
1071
1072 EOLIAN static void
1073 _elm_datetime_format_set(Eo *obj, Elm_Datetime_Data *sd, const char *fmt)
1074 {
1075    if (fmt)
1076      {
1077         strncpy(sd->format, fmt, ELM_DATETIME_MAX_FORMAT_LEN);
1078         sd->format[ELM_DATETIME_MAX_FORMAT_LEN - 1] = '\0';
1079         sd->user_format = EINA_TRUE;
1080      }
1081    else sd->user_format = EINA_FALSE;
1082
1083    _reload_format(obj);
1084 }
1085
1086 EOLIAN static Eina_Bool
1087 _elm_datetime_field_visible_get(const Eo *obj EINA_UNUSED, Elm_Datetime_Data *sd, Elm_Datetime_Field_Type fieldtype)
1088 {
1089    Datetime_Field *field;
1090
1091    if (fieldtype > ELM_DATETIME_AMPM) return EINA_FALSE;
1092
1093    field = sd->field_list + fieldtype;
1094
1095    return field->visible;
1096 }
1097
1098 EOLIAN static void
1099 _elm_datetime_field_visible_set(Eo *obj, Elm_Datetime_Data *sd, Elm_Datetime_Field_Type fieldtype, Eina_Bool visible)
1100 {
1101    char buf[BUFFER_SIZE];
1102    Datetime_Field *field;
1103
1104    if (fieldtype > ELM_DATETIME_AMPM) return;
1105
1106    field = sd->field_list + fieldtype;
1107    visible = !!visible;
1108    if (field->visible == visible) return;
1109
1110    field->visible = visible;
1111
1112    sd->freeze_sizing = EINA_TRUE;
1113    if (visible)
1114      {
1115         sd->enabled_field_count++;
1116
1117         if (!field->fmt_exist) return;
1118
1119         snprintf(buf, sizeof(buf), EDC_PART_FIELD_ENABLE_SIG_STR,
1120                  field->location);
1121         elm_layout_signal_emit(obj, buf, "elm");
1122
1123         ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd);
1124         edje_object_message_signal_process(wd->resize_obj);
1125
1126         snprintf(buf, sizeof(buf), EDC_PART_FIELD_STR, field->location);
1127         elm_layout_content_unset(obj, buf);
1128         elm_layout_content_set(obj, buf, field->item_obj);
1129      }
1130    else
1131      {
1132         sd->enabled_field_count--;
1133
1134         if (!field->fmt_exist) return;
1135
1136         snprintf(buf, sizeof(buf), EDC_PART_FIELD_DISABLE_SIG_STR,
1137                  field->location);
1138         elm_layout_signal_emit(obj, buf, "elm");
1139
1140         ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd);
1141         edje_object_message_signal_process(wd->resize_obj);
1142
1143         snprintf(buf, sizeof(buf), EDC_PART_FIELD_STR, field->location);
1144         evas_object_hide(elm_layout_content_unset(obj, buf));
1145      }
1146    sd->freeze_sizing = EINA_FALSE;
1147
1148    elm_layout_sizing_eval(obj);
1149
1150    if (!visible) return;
1151    if (!dt_mod || !dt_mod->field_value_display) return;
1152
1153    dt_mod->field_value_display(sd->mod_data, field->item_obj);
1154 }
1155
1156 EOLIAN static void
1157 _elm_datetime_field_limit_get(const Eo *obj EINA_UNUSED, Elm_Datetime_Data *sd, Elm_Datetime_Field_Type fieldtype, int *min, int *max)
1158 {
1159    Datetime_Field *field;
1160
1161    if (fieldtype >= ELM_DATETIME_AMPM) return;
1162
1163    field = sd->field_list + fieldtype;
1164    if (min) *min = field->min;
1165    if (max) *max = field->max;
1166 }
1167
1168 EOLIAN static void
1169 _elm_datetime_field_limit_set(Eo *obj, Elm_Datetime_Data *sd, Elm_Datetime_Field_Type fieldtype, int min, int max)
1170 {
1171    Datetime_Field *field;
1172    struct tm old_time;
1173
1174    if (fieldtype >= ELM_DATETIME_AMPM) return;
1175
1176    if (min > max) return;
1177
1178    old_time = sd->curr_time;
1179    field = sd->field_list + fieldtype;
1180    if (((min >= mapping[fieldtype].def_min) &&
1181         (min <= mapping[fieldtype].def_max)) ||
1182        (field->type == ELM_DATETIME_YEAR))
1183      field->min = min;
1184    if (((max >= mapping[fieldtype].def_min) &&
1185         (max <= mapping[fieldtype].def_max)) ||
1186        (field->type == ELM_DATETIME_YEAR))
1187      field->max = max;
1188
1189    _apply_field_limits(obj);
1190
1191    if (!_field_cmp(fieldtype, &old_time, &sd->curr_time))
1192      eo_do(obj, eo_event_callback_call(ELM_DATETIME_EVENT_CHANGED, NULL));
1193
1194 }
1195
1196 EOLIAN static Eina_Bool
1197 _elm_datetime_value_get(const Eo *obj EINA_UNUSED, Elm_Datetime_Data *sd, struct tm *currtime)
1198 {
1199    EINA_SAFETY_ON_NULL_RETURN_VAL(currtime, EINA_FALSE);
1200
1201    *currtime = sd->curr_time;
1202    return EINA_TRUE;
1203 }
1204
1205 EOLIAN static Eina_Bool
1206 _elm_datetime_value_set(Eo *obj, Elm_Datetime_Data *sd, const struct tm *newtime)
1207 {
1208    EINA_SAFETY_ON_NULL_RETURN_VAL(newtime, EINA_FALSE);
1209
1210    if (_date_cmp(&sd->curr_time, newtime)) return EINA_TRUE;
1211    sd->curr_time = *newtime;
1212    // apply default field restrictions for curr_time
1213    _apply_range_restrictions(&sd->curr_time);
1214    // validate the curr_time according to the min_limt and max_limt
1215    _validate_datetime_limits(&sd->curr_time, &sd->min_limit, EINA_FALSE);
1216    _validate_datetime_limits(&sd->max_limit, &sd->curr_time, EINA_TRUE);
1217    _apply_field_limits(obj);
1218
1219    eo_do(obj, eo_event_callback_call(ELM_DATETIME_EVENT_CHANGED, NULL));
1220
1221    return EINA_TRUE;
1222 }
1223
1224 EOLIAN static Eina_Bool
1225 _elm_datetime_value_min_get(const Eo *obj EINA_UNUSED, Elm_Datetime_Data *sd, struct tm *mintime)
1226 {
1227    EINA_SAFETY_ON_NULL_RETURN_VAL(mintime, EINA_FALSE);
1228
1229    *mintime = sd->min_limit;
1230    return EINA_TRUE;
1231 }
1232
1233 EOLIAN static Eina_Bool
1234 _elm_datetime_value_min_set(Eo *obj, Elm_Datetime_Data *sd, const struct tm *mintime)
1235 {
1236    struct tm old_time;
1237
1238    EINA_SAFETY_ON_NULL_RETURN_VAL(mintime, EINA_FALSE);
1239
1240    if (_date_cmp(&sd->min_limit, mintime)) return EINA_TRUE;
1241    sd->min_limit = *mintime;
1242    old_time = sd->curr_time;
1243    // apply default field restrictions for min_limit
1244    _apply_range_restrictions(&sd->min_limit);
1245    // validate curr_time and max_limt according to the min_limit
1246    _validate_datetime_limits(&sd->max_limit, &sd->min_limit, EINA_FALSE);
1247    _validate_datetime_limits(&sd->curr_time, &sd->min_limit, EINA_FALSE);
1248    _apply_field_limits(obj);
1249
1250    if (!_date_cmp(&old_time, &sd->curr_time))
1251      eo_do(obj, eo_event_callback_call(ELM_DATETIME_EVENT_CHANGED, NULL));
1252
1253    return EINA_TRUE;
1254 }
1255
1256 EOLIAN static Eina_Bool
1257 _elm_datetime_value_max_get(const Eo *obj EINA_UNUSED, Elm_Datetime_Data *sd, struct tm *maxtime)
1258 {
1259    EINA_SAFETY_ON_NULL_RETURN_VAL(maxtime, EINA_FALSE);
1260
1261    *maxtime = sd->max_limit;
1262    return EINA_TRUE;
1263 }
1264
1265 EOLIAN static Eina_Bool
1266 _elm_datetime_value_max_set(Eo *obj, Elm_Datetime_Data *sd, const struct tm *maxtime)
1267 {
1268    struct tm old_time;
1269
1270    EINA_SAFETY_ON_NULL_RETURN_VAL(maxtime, EINA_FALSE);
1271
1272    if (_date_cmp(&sd->max_limit, maxtime)) return EINA_TRUE;
1273    sd->max_limit = *maxtime;
1274    old_time = sd->curr_time;
1275    // apply default field restrictions for max_limit
1276    _apply_range_restrictions(&sd->max_limit);
1277    // validate curr_time and min_limt according to the max_limit
1278    _validate_datetime_limits(&sd->max_limit, &sd->min_limit, EINA_TRUE);
1279    _validate_datetime_limits(&sd->max_limit, &sd->curr_time, EINA_TRUE);
1280    _apply_field_limits(obj);
1281
1282    if (!_date_cmp(&old_time, &sd->curr_time))
1283      eo_do(obj, eo_event_callback_call(ELM_DATETIME_EVENT_CHANGED, NULL));
1284
1285    return EINA_TRUE;
1286 }
1287
1288 EOLIAN static void
1289 _elm_datetime_class_constructor(Eo_Class *klass)
1290 {
1291    evas_smart_legacy_type_register(MY_CLASS_NAME_LEGACY, klass);
1292 }
1293
1294 #include "elm_datetime.eo.c"