1 #ifdef HAVE_CONFIG_H
2 # include "elementary_config.h"
3 #endif
4 
5 #ifdef _WIN32
6 # include <evil_private.h> /* nl_langinfo */
7 #endif
8 
9 #define EFL_ACCESS_OBJECT_PROTECTED
10 #define EFL_UI_L10N_PROTECTED
11 
12 #include <Elementary.h>
13 #include "elm_priv.h"
14 #include <efl_ui_clock.h>
15 #include <efl_ui_clock_private.h>
16 
17 #define MY_CLASS EFL_UI_CLOCK_CLASS
18 
19 #define MY_CLASS_NAME "Efl.Ui.Clock"
20 
21 #ifdef HAVE_LOCALE_H
22 # include <locale.h>
23 #endif
24 
25 #ifdef HAVE_LANGINFO_H
26 # include <langinfo.h>
27 #endif
28 
29 #define MAX_SEPARATOR_LEN              6
30 #define MIN_DAYS_IN_MONTH              28
31 #define BUFFER_SIZE                    1024
32 #define CLOCK_FIELD_COUNT       8
33 
34 /* interface between EDC & C code (field & signal names). values 0 to
35  * EFL_UI_CLOCK_TYPE_COUNT are in the valid range, and must get in the
36  * place of "%d".
37  */
38 #define EDC_PART_FIELD_STR             "efl.field%d"
39 #define EDC_PART_SEPARATOR_STR         "efl.separator%d"
40 #define EDC_PART_FIELD_ENABLE_SIG_STR  "field%d,enable"
41 #define EDC_PART_FIELD_DISABLE_SIG_STR "field%d,disable"
42 
43 /* struct tm does not define the fields in the order year, month,
44  * date, hour, minute. values are reassigned to an array for easy
45  * handling.
46  */
47 #define CLOCK_TM_ARRAY(intptr, tmptr) \
48   int *intptr[] = {                      \
49      &(tmptr)->tm_year,                  \
50      &(tmptr)->tm_mon,                   \
51      &(tmptr)->tm_mday,                  \
52      &(tmptr)->tm_hour,                  \
53      &(tmptr)->tm_min,                  \
54      &(tmptr)->tm_sec,                  \
55      &(tmptr)->tm_wday,                  \
56      &(tmptr)->tm_hour}
57 
58 // default limits for individual fields
59 static Format_Map mapping[EFL_UI_CLOCK_TYPE_COUNT] = {
60    [EFL_UI_CLOCK_TYPE_YEAR] = { "Yy", -1, -1, "" },
61    [EFL_UI_CLOCK_TYPE_MONTH] = { "mbBh", 0, 11, "" },
62    [EFL_UI_CLOCK_TYPE_DATE] = { "de", 1, 31, "" },
63    [EFL_UI_CLOCK_TYPE_HOUR] = { "IHkl", 0, 23, "" },
64    [EFL_UI_CLOCK_TYPE_MINUTE] = { "M", 0, 59, "" },
65    [EFL_UI_CLOCK_TYPE_SECOND] = { "S", 0, 59, "" },
66    [EFL_UI_CLOCK_TYPE_DAY] = { "Aa", 0, 6, "" },
67    [EFL_UI_CLOCK_TYPE_AMPM] = { "pP", 0, 1, "" }
68 };
69 
70 static const char *multifield_formats = "cxXrRTDF";
71 static const char *ignore_separators = "()";
72 static const char *ignore_extensions = "E0_-O^#";
73 
74 static const char SIG_CHANGED[] = "changed";
75 static const Evas_Smart_Cb_Description _smart_callbacks[] = {
76    {SIG_CHANGED, ""},
77    {SIG_WIDGET_LANG_CHANGED, ""}, /**< handled by elm_widget */
78    {SIG_WIDGET_ACCESS_CHANGED, ""}, /**< handled by elm_widget */
79    {SIG_LAYOUT_FOCUSED, ""}, /**< handled by elm_layout */
80    {SIG_LAYOUT_UNFOCUSED, ""}, /**< handled by elm_layout */
81    {NULL, NULL}
82 };
83 
_part_name_snprintf(char * buffer,int buffer_size,const Evas_Object * obj,const char * template,int n)84 static void _part_name_snprintf(char *buffer, int buffer_size,
85    const Evas_Object *obj, const char *template, int n)
86 {
87    snprintf(buffer, buffer_size, template, n);
88    if (edje_object_part_exists (obj, buffer)) return;
89    // Try 'elm' prefix instead of 'efl'
90    buffer[0] = 'e';
91    buffer[1] = 'l';
92    buffer[2] = 'm';
93    if (edje_object_part_exists (obj, buffer)) return;
94    // Skip the namespace prefix "elm." which was not present
95    // in previous versions
96    snprintf(buffer, buffer_size, template + 4, n);
97 }
98 
99 static void
_ampm_clicked_cb(void * data,const Efl_Event * event EINA_UNUSED)100 _ampm_clicked_cb(void *data, const Efl_Event *event EINA_UNUSED)
101 {
102    struct tm curr_time;
103 
104    curr_time = efl_ui_clock_time_get(data);
105    if (curr_time.tm_hour >= 12) curr_time.tm_hour -= 12;
106    else curr_time.tm_hour += 12;
107    efl_ui_clock_time_set(data, curr_time);
108 }
109 
110 static void
_access_set(Evas_Object * obj,Efl_Ui_Clock_Type field_type)111 _access_set(Evas_Object *obj, Efl_Ui_Clock_Type field_type)
112 {
113    const char* type = NULL;
114 
115    switch (field_type)
116      {
117       case EFL_UI_CLOCK_TYPE_YEAR:
118          type = "datetime field, year";
119          break;
120 
121       case EFL_UI_CLOCK_TYPE_MONTH:
122          type = "datetime field, month";
123          break;
124 
125       case EFL_UI_CLOCK_TYPE_DATE:
126          type = "datetime field, date";
127          break;
128 
129       case EFL_UI_CLOCK_TYPE_HOUR:
130          type = "datetime field, hour";
131          break;
132 
133       case EFL_UI_CLOCK_TYPE_MINUTE:
134          type = "datetime field, minute";
135          break;
136 
137       case EFL_UI_CLOCK_TYPE_AMPM:
138          type = "datetime field, AM PM";
139          break;
140 
141       default:
142          break;
143      }
144 
145    _elm_access_text_set
146      (_elm_access_info_get(obj), ELM_ACCESS_TYPE, type);
147    _elm_access_callback_set
148      (_elm_access_info_get(obj), ELM_ACCESS_STATE, NULL, NULL);
149 }
150 
151 static const char *
_field_format_get(Evas_Object * obj,Efl_Ui_Clock_Type field_type)152 _field_format_get(Evas_Object *obj,
153                   Efl_Ui_Clock_Type field_type)
154 {
155    Clock_Field *field;
156 
157    if (field_type > EFL_UI_CLOCK_TYPE_AMPM) return NULL;
158 
159    EFL_UI_CLOCK_DATA_GET(obj, sd);
160 
161    field = sd->field_list + field_type;
162 
163    return field->fmt;
164 }
165 
166 static void
field_value_display(Eo * obj,Evas_Object * item_obj)167 field_value_display(Eo *obj, Evas_Object *item_obj)
168 {
169    Efl_Ui_Clock_Type  field_type;
170    struct tm tim;
171    char buf[BUFFER_SIZE];
172    const char *fmt;
173 
174    tim = efl_ui_clock_time_get(obj);
175    field_type = (Efl_Ui_Clock_Type )evas_object_data_get(item_obj, "_field_type");
176    fmt = _field_format_get(obj, field_type);
177    buf[0] = 0;
178    strftime(buf, sizeof(buf), fmt, &tim);
179    if ((!buf[0]) && ((!strcmp(fmt, "%p")) || (!strcmp(fmt, "%P"))))
180      {
181         // yes BUFFER_SIZE is more than 2 bytes!
182         if (tim.tm_hour < 12) strcpy(buf, "AM");
183         else strcpy(buf, "PM");
184      }
185    efl_text_set(item_obj, buf);
186 }
187 
188 static Evas_Object *
field_create(Eo * obj,Efl_Ui_Clock_Type field_type)189 field_create(Eo *obj, Efl_Ui_Clock_Type  field_type)
190 {
191    Evas_Object *field_obj;
192 
193    if (field_type == EFL_UI_CLOCK_TYPE_AMPM)
194      {
195         field_obj = efl_add(EFL_UI_BUTTON_CLASS, obj,
196           efl_event_callback_add(efl_added, EFL_INPUT_EVENT_CLICKED, _ampm_clicked_cb, obj));
197      }
198    else
199      {
200         field_obj = efl_add(EFL_UI_TEXTBOX_CLASS,obj,
201           efl_text_multiline_set(efl_added, EINA_FALSE),
202           efl_text_interactive_editable_set(efl_added, EINA_FALSE),
203           efl_input_text_input_panel_autoshow_set(efl_added, EINA_FALSE),
204           efl_ui_textbox_context_menu_enabled_set(efl_added, EINA_FALSE));
205      }
206    evas_object_data_set(field_obj, "_field_type", (void *)field_type);
207 
208    // ACCESS
209    _access_set(field_obj, field_type);
210 
211    return field_obj;
212 }
213 
214 static void
_field_list_display(Evas_Object * obj)215 _field_list_display(Evas_Object *obj)
216 {
217    Clock_Field *field;
218    unsigned int idx = 0;
219 
220    EFL_UI_CLOCK_DATA_GET(obj, sd);
221 
222    for (idx = 0; idx < EFL_UI_CLOCK_TYPE_COUNT; idx++)
223      {
224         field = sd->field_list + idx;
225         if (field->fmt_exist && field->visible)
226           field_value_display(obj, field->item_obj);
227      }
228 }
229 
230 // FIXME: provide nl_langinfo on Windows if possible
231 // returns expanded format string for corresponding multi-field format character
232 static char *
_expanded_fmt_str_get(char ch)233 _expanded_fmt_str_get(char ch)
234 {
235    char *exp_fmt = "";
236    switch (ch)
237      {
238       case 'c':
239 #if defined(HAVE_LANGINFO_H) || defined (_WIN32)
240         exp_fmt = nl_langinfo(D_T_FMT);
241 #else
242         exp_fmt = "";
243 #endif
244         break;
245 
246       case 'x':
247 #if defined(HAVE_LANGINFO_H) || defined (_WIN32)
248         exp_fmt = nl_langinfo(D_FMT);
249 #else
250         exp_fmt = "";
251 #endif
252         break;
253 
254       case 'X':
255 #if defined(HAVE_LANGINFO_H) || defined (_WIN32)
256         exp_fmt = nl_langinfo(T_FMT);
257 #else
258         exp_fmt = "";
259 #endif
260         break;
261 
262       case 'r':
263 #if defined(HAVE_LANGINFO_H) || defined (_WIN32)
264         exp_fmt = nl_langinfo(T_FMT_AMPM);
265 #else
266         exp_fmt = "";
267 #endif
268         break;
269 
270       case 'R':
271         exp_fmt = "%H:%M";
272         break;
273 
274       case 'T':
275         exp_fmt = "%H:%M:%S";
276         break;
277 
278       case 'D':
279         exp_fmt = "%m/%d/%y";
280         break;
281 
282       case 'F':
283         exp_fmt = "%Y-%m-%d";
284         break;
285 
286       default:
287         exp_fmt = "";
288         break;
289      }
290 
291    return exp_fmt;
292 }
293 
294 static void
_expand_format(char * dt_fmt)295 _expand_format(char *dt_fmt)
296 {
297    char *ptr, *expanded_fmt, ch;
298    unsigned int idx, len = 0;
299    char buf[EFL_UI_CLOCK_MAX_FORMAT_LEN] = {0, };
300    Eina_Bool fmt_char, fmt_expanded;
301 
302    do {
303      idx = 0;
304      fmt_char = EINA_FALSE;
305      fmt_expanded = EINA_FALSE;
306      ptr = dt_fmt;
307      while ((ch = *ptr))
308        {
309           if ((fmt_char) && (strchr(multifield_formats, ch)))
310             {
311                /* replace the multi-field format characters with
312                 * corresponding expanded format */
313                expanded_fmt = _expanded_fmt_str_get(ch);
314                len = strlen(expanded_fmt);
315                if (len > 0) fmt_expanded = EINA_TRUE;
316                buf[--idx] = 0;
317                strncat(buf, expanded_fmt, len);
318                idx += len;
319             }
320           else buf[idx++] = ch;
321 
322           if (ch == '%') fmt_char = EINA_TRUE;
323           else fmt_char = EINA_FALSE;
324 
325           ptr++;
326        }
327 
328      buf[idx] = 0;
329      strncpy(dt_fmt, buf, EFL_UI_CLOCK_MAX_FORMAT_LEN);
330    } while (fmt_expanded);
331 }
332 
333 static void
_field_list_arrange(Evas_Object * obj)334 _field_list_arrange(Evas_Object *obj)
335 {
336    Clock_Field *field;
337    char buf[BUFFER_SIZE];
338    int idx;
339    Eina_Bool freeze;
340 
341    EFL_UI_CLOCK_DATA_GET(obj, sd);
342 
343    freeze = sd->freeze_sizing;
344    sd->freeze_sizing = EINA_TRUE;
345    for (idx = 0; idx < EFL_UI_CLOCK_TYPE_COUNT; idx++)
346      {
347         field = sd->field_list + idx;
348         _part_name_snprintf(buf, sizeof(buf), obj, EDC_PART_FIELD_STR,
349                             field->location);
350 
351         efl_gfx_entity_visible_set(efl_content_unset(efl_part(obj, buf)), EINA_FALSE);
352         if (field->visible && field->fmt_exist)
353           efl_content_set(efl_part(obj, buf), field->item_obj);
354      }
355    sd->freeze_sizing = freeze;
356 
357    efl_canvas_group_change(obj);
358    _field_list_display(obj);
359 }
360 
361 static unsigned int
_parse_format(Evas_Object * obj,char * fmt_ptr)362 _parse_format(Evas_Object *obj,
363               char *fmt_ptr)
364 {
365    Eina_Bool fmt_parsing = EINA_FALSE, sep_parsing = EINA_FALSE,
366              sep_lookup = EINA_FALSE;
367    unsigned int len = 0, idx = 0, location = 0;
368    char separator[MAX_SEPARATOR_LEN];
369    Clock_Field *field = NULL;
370    char cur;
371 
372    EFL_UI_CLOCK_DATA_GET(obj, sd);
373 
374    while ((cur = *fmt_ptr))
375      {
376         if (fmt_parsing)
377           {
378              if (strchr(ignore_extensions, cur))
379                {
380                   fmt_ptr++;
381                   continue;
382                }
383              fmt_parsing = EINA_FALSE;
384              for (idx = 0; idx < EFL_UI_CLOCK_TYPE_COUNT; idx++)
385                {
386                   if (strchr(mapping[idx].fmt_char, cur))
387                     {
388                        field = sd->field_list + idx;
389                        /* ignore the fields already have or disabled
390                         * valid formats, means already parsed &
391                         * repeated, ignore. */
392                        if (field->location != -1) break;
393                        field->fmt[1] = cur;
394                        field->fmt_exist = EINA_TRUE;
395                        field->location = location++;
396                        sep_lookup = EINA_TRUE;
397                        len = 0;
398                        break;
399                     }
400                }
401           }
402         if (cur == '%')
403           {
404              fmt_parsing = EINA_TRUE;
405              sep_parsing = EINA_FALSE;
406              // set the separator to previous field
407              separator[len] = 0;
408              if (field) eina_stringshare_replace(&field->separator, separator);
409           }
410         // ignore the set of chars (global, field specific) as field separators
411         if (sep_parsing &&
412             (len < MAX_SEPARATOR_LEN - 1) &&
413             (field->type != EFL_UI_CLOCK_TYPE_AMPM) &&
414             (!strchr(ignore_separators, cur)) &&
415             (!strchr(mapping[idx].ignore_sep, cur)))
416           separator[len++] = cur;
417         if (sep_lookup) sep_parsing = EINA_TRUE;
418         sep_lookup = EINA_FALSE;
419         fmt_ptr++;
420      }
421    // return the number of valid fields parsed.
422    return location;
423 }
424 
425 static void
_reload_format(Evas_Object * obj)426 _reload_format(Evas_Object *obj)
427 {
428    unsigned int idx, field_count;
429    Clock_Field *field;
430    char buf[BUFFER_SIZE];
431    char *dt_fmt;
432 
433    EFL_UI_CLOCK_DATA_GET(obj, sd);
434    ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd);
435 
436    // FIXME: provide nl_langinfo on Windows if possible
437    // fetch the default format from Libc.
438    if (!sd->user_format)
439 #if defined(HAVE_LANGINFO_H) || defined (_WIN32)
440      strncpy(sd->format, nl_langinfo(D_T_FMT), EFL_UI_CLOCK_MAX_FORMAT_LEN);
441 #else
442      strncpy(sd->format, "", EFL_UI_CLOCK_MAX_FORMAT_LEN);
443 #endif
444    sd->format[EFL_UI_CLOCK_MAX_FORMAT_LEN - 1] = '\0';
445 
446    dt_fmt = (char *)malloc(EFL_UI_CLOCK_MAX_FORMAT_LEN);
447    if (!dt_fmt) return;
448 
449    strncpy(dt_fmt, sd->format, EFL_UI_CLOCK_MAX_FORMAT_LEN);
450 
451    _expand_format(dt_fmt);
452 
453    // reset all the fields to disable state
454    sd->enabled_field_count = 0;
455    for (idx = 0; idx < EFL_UI_CLOCK_TYPE_COUNT; idx++)
456      {
457         field = sd->field_list + idx;
458         field->fmt_exist = EINA_FALSE;
459         field->location = -1;
460      }
461 
462    field_count = _parse_format(obj, dt_fmt);
463    free(dt_fmt);
464 
465    for (idx = 0; idx < EFL_UI_CLOCK_TYPE_COUNT; idx++)
466      {
467         field = sd->field_list + idx;
468         if (field->fmt_exist && field->visible)
469           sd->enabled_field_count++;
470      }
471    efl_ui_layout_finger_size_multiplier_set(obj, sd->enabled_field_count, 1);
472 
473    // assign locations to disabled fields for uniform usage
474    for (idx = 0; idx < EFL_UI_CLOCK_TYPE_COUNT; idx++)
475      {
476         field = sd->field_list + idx;
477         if (field->location == -1) field->location = field_count++;
478 
479         if (field->fmt_exist && field->visible)
480           {
481              snprintf(buf, sizeof(buf), EDC_PART_FIELD_ENABLE_SIG_STR,
482                       field->location);
483              efl_layout_signal_emit(obj, buf, "efl");
484           }
485         else
486           {
487              snprintf(buf, sizeof(buf), EDC_PART_FIELD_DISABLE_SIG_STR,
488                       field->location);
489              efl_layout_signal_emit(obj, buf, "efl");
490           }
491         if (field->location + 1)
492           {
493              _part_name_snprintf(buf, sizeof(buf), obj, EDC_PART_SEPARATOR_STR,
494                                  field->location + 1);
495              efl_text_set(efl_part(obj, buf), field->separator);
496           }
497      }
498 
499    edje_object_message_signal_process(wd->resize_obj);
500    _field_list_arrange(obj);
501 }
502 
503 EOLIAN static void
_efl_ui_clock_efl_ui_l10n_translation_update(Eo * obj,Efl_Ui_Clock_Data * sd)504 _efl_ui_clock_efl_ui_l10n_translation_update(Eo *obj, Efl_Ui_Clock_Data *sd)
505 {
506    if (!sd->user_format) _reload_format(obj);
507    else _field_list_display(obj);
508 
509    efl_ui_l10n_translation_update(efl_super(obj, MY_CLASS));
510 }
511 
512 EOLIAN static void
_efl_ui_clock_pause_set(Eo * obj EINA_UNUSED,Efl_Ui_Clock_Data * sd,Eina_Bool paused)513 _efl_ui_clock_pause_set(Eo *obj EINA_UNUSED, Efl_Ui_Clock_Data *sd, Eina_Bool paused)
514 {
515    paused = !!paused;
516    if (sd->paused == paused)
517      return;
518    sd->paused = paused;
519    if (paused)
520      ecore_timer_freeze(sd->ticker);
521    else
522      ecore_timer_thaw(sd->ticker);
523 }
524 
525 EOLIAN static Eina_Bool
_efl_ui_clock_pause_get(const Eo * obj EINA_UNUSED,Efl_Ui_Clock_Data * sd)526 _efl_ui_clock_pause_get(const Eo *obj EINA_UNUSED, Efl_Ui_Clock_Data *sd)
527 {
528    return sd->paused;
529 }
530 
531 EOLIAN static void
_efl_ui_clock_edit_mode_set(Eo * obj EINA_UNUSED,Efl_Ui_Clock_Data * sd,Eina_Bool edit_mode)532 _efl_ui_clock_edit_mode_set(Eo *obj EINA_UNUSED, Efl_Ui_Clock_Data *sd, Eina_Bool edit_mode)
533 {
534    sd->edit_mode = edit_mode;
535 }
536 
537 EOLIAN static Eina_Bool
_efl_ui_clock_edit_mode_get(const Eo * obj EINA_UNUSED,Efl_Ui_Clock_Data * sd)538 _efl_ui_clock_edit_mode_get(const Eo *obj EINA_UNUSED, Efl_Ui_Clock_Data *sd)
539 {
540    return sd->edit_mode;
541 }
542 
543 EOLIAN static void
_efl_ui_clock_efl_canvas_group_group_calculate(Eo * obj,Efl_Ui_Clock_Data * sd)544 _efl_ui_clock_efl_canvas_group_group_calculate(Eo *obj, Efl_Ui_Clock_Data *sd)
545 {
546    /* FIXME: this seems dumb */
547    if (!sd->freeze_sizing)
548      efl_canvas_group_calculate(efl_super(obj, MY_CLASS));
549 }
550 
551 EOLIAN static Eina_Error
_efl_ui_clock_efl_ui_widget_theme_apply(Eo * obj,Efl_Ui_Clock_Data * sd)552 _efl_ui_clock_efl_ui_widget_theme_apply(Eo *obj, Efl_Ui_Clock_Data *sd)
553 {
554    Eina_Error int_ret = EFL_UI_THEME_APPLY_ERROR_GENERIC;
555 
556    Clock_Field *field;
557    char buf[BUFFER_SIZE];
558    unsigned int idx;
559 
560    ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, EINA_FALSE);
561 
562    int_ret = efl_ui_widget_theme_apply(efl_super(obj, MY_CLASS));
563    if (int_ret == EFL_UI_THEME_APPLY_ERROR_GENERIC) return int_ret;
564 
565    for (idx = 0; idx < EFL_UI_CLOCK_TYPE_COUNT; idx++)
566      {
567         field = sd->field_list + idx;
568         // TODO: Different group name for each field_obj may be needed.
569         elm_widget_element_update(obj, field->item_obj, PART_NAME_ARRAY[idx]);
570         if (field->fmt_exist && field->visible)
571           {
572              snprintf(buf, sizeof(buf), EDC_PART_FIELD_ENABLE_SIG_STR,
573                       field->location);
574              efl_layout_signal_emit(obj, buf, "efl");
575 
576              if (field->location)
577                {
578                   _part_name_snprintf(buf, sizeof(buf), obj, EDC_PART_SEPARATOR_STR,
579                                       field->location);
580                   efl_text_set(efl_part(obj, buf), field->separator);
581                }
582 
583              field_value_display(obj, field->item_obj);
584           }
585         else
586           {
587              snprintf(buf, sizeof(buf), EDC_PART_FIELD_DISABLE_SIG_STR,
588                       field->location);
589              efl_layout_signal_emit(obj, buf, "efl");
590           }
591      }
592 
593    edje_object_message_signal_process(wd->resize_obj);
594 
595    return int_ret;
596 }
597 
598 static int
_max_days_get(int year,int month)599 _max_days_get(int year,
600               int month)
601 {
602    struct tm time1;
603    time_t t;
604    int day;
605 
606    t = time(NULL);
607    localtime_r(&t, &time1);
608    time1.tm_year = year;
609    time1.tm_mon = month;
610    for (day = MIN_DAYS_IN_MONTH; day <= mapping[EFL_UI_CLOCK_TYPE_DATE].def_max;
611         day++)
612      {
613         time1.tm_mday = day;
614         mktime(&time1);
615         /* To restrict month wrapping because of summer time in some locales,
616         * ignore day light saving mode in mktime(). */
617         time1.tm_isdst = -1;
618         if (time1.tm_mday == 1) break;
619      }
620    day--;
621 
622    return day;
623 }
624 
625 static Eina_Bool
_date_cmp(const struct tm * time1,const struct tm * time2)626 _date_cmp(const struct tm *time1,
627           const struct tm *time2)
628 {
629    unsigned int idx;
630 
631    const CLOCK_TM_ARRAY(timearr1, time1);
632    const CLOCK_TM_ARRAY(timearr2, time2);
633 
634    for (idx = 0; idx < EFL_UI_CLOCK_TYPE_COUNT; idx++)
635      {
636         if (*timearr1[idx] != *timearr2[idx])
637           return EINA_FALSE;
638      }
639 
640    return EINA_TRUE;
641 }
642 
643 static Eina_Bool
_field_cmp(Efl_Ui_Clock_Type field_type,struct tm * time1,struct tm * time2)644 _field_cmp(Efl_Ui_Clock_Type field_type,
645           struct tm *time1,
646           struct tm *time2)
647 {
648    CLOCK_TM_ARRAY(timearr1, time1);
649    CLOCK_TM_ARRAY(timearr2, time2);
650 
651    if (*timearr1[field_type] != *timearr2[field_type])
652      return EINA_FALSE;
653    else
654      return EINA_TRUE;
655 }
656 
657 // validates curr_time/min_limt/max_limit according to the newly set value
658 static void
_validate_clock_limits(struct tm * time1,struct tm * time2,Eina_Bool swap)659 _validate_clock_limits(struct tm *time1,
660                           struct tm *time2,
661                           Eina_Bool swap)
662 {
663    struct tm *t1, *t2;
664    unsigned int idx;
665 
666    if (!time1 || !time2) return;
667 
668    t1 = (swap) ? time2 : time1;
669    t2 = (swap) ? time1 : time2;
670 
671    CLOCK_TM_ARRAY(timearr1, time1);
672    CLOCK_TM_ARRAY(timearr2, time2);
673    for (idx = 0; idx < EFL_UI_CLOCK_TYPE_COUNT - 1; idx++)
674      {
675         if (*timearr1[idx] < *timearr2[idx])
676           {
677              memcpy(t1, t2, sizeof(struct tm));
678              break;
679           }
680         else if (*timearr1[idx] > *timearr2[idx])
681           break;
682      }
683 }
684 
685 static void
_apply_field_limits(Evas_Object * obj)686 _apply_field_limits(Evas_Object *obj)
687 {
688    Clock_Field *field;
689    unsigned int idx = 0;
690    int val;
691 
692    EFL_UI_CLOCK_DATA_GET(obj, sd);
693 
694    CLOCK_TM_ARRAY(timearr, &sd->curr_time);
695    for (idx = 0; idx < EFL_UI_CLOCK_TYPE_COUNT - 1; idx++)
696      {
697         field = sd->field_list + idx;
698         val = *timearr[idx];
699         if (val < field->min)
700           *timearr[idx] = field->min;
701         else if (val > field->max)
702           *timearr[idx] = field->max;
703      }
704 
705    _field_list_display(obj);
706 }
707 
708 static void
_apply_range_restrictions(struct tm * tim)709 _apply_range_restrictions(struct tm *tim)
710 {
711    unsigned int idx;
712    int val, min, max;
713 
714    if (!tim) return;
715 
716    CLOCK_TM_ARRAY(timearr, tim);
717    for (idx = EFL_UI_CLOCK_TYPE_MONTH; idx < EFL_UI_CLOCK_TYPE_COUNT - 1; idx++)
718      {
719         val = *timearr[idx];
720         min = mapping[idx].def_min;
721         max = mapping[idx].def_max;
722         if (idx == EFL_UI_CLOCK_TYPE_DATE)
723           max = _max_days_get(tim->tm_year, tim->tm_mon);
724         if (val < min)
725           *timearr[idx] = min;
726         else if (val > max)
727           *timearr[idx] = max;
728      }
729 }
730 
731 static void
_field_list_init(Evas_Object * obj)732 _field_list_init(Evas_Object *obj)
733 {
734    Clock_Field *field;
735    unsigned int idx;
736    time_t t;
737 
738    EFL_UI_CLOCK_DATA_GET(obj, sd);
739 
740    t = time(NULL);
741    localtime_r(&t, &sd->curr_time);
742 
743    mapping[EFL_UI_CLOCK_TYPE_YEAR].def_min = _elm_config->year_min;
744    mapping[EFL_UI_CLOCK_TYPE_YEAR].def_max = _elm_config->year_max;
745    for (idx = 0; idx < EFL_UI_CLOCK_TYPE_COUNT; idx++)
746      {
747         field = sd->field_list + idx;
748         field->type = EFL_UI_CLOCK_TYPE_YEAR + idx;
749         field->fmt[0] = '%';
750         field->fmt_exist = EINA_FALSE;
751         field->visible = EINA_TRUE;
752         field->min = mapping[idx].def_min;
753         field->max = mapping[idx].def_max;
754      }
755    CLOCK_TM_ARRAY(min_timearr, &sd->min_limit);
756    CLOCK_TM_ARRAY(max_timearr, &sd->max_limit);
757    for (idx = 0; idx < EFL_UI_CLOCK_TYPE_COUNT - 1; idx++)
758      {
759         *min_timearr[idx] = mapping[idx].def_min;
760         *max_timearr[idx] = mapping[idx].def_max;
761      }
762 }
763 
764 static char *
_access_info_cb(void * data,Evas_Object * obj EINA_UNUSED)765 _access_info_cb(void *data, Evas_Object *obj EINA_UNUSED)
766 {
767    char *ret;
768    Eina_Strbuf *buf;
769    buf = eina_strbuf_new();
770 
771    EFL_UI_CLOCK_DATA_GET(data, sd);
772    eina_strbuf_append_printf(buf,
773                              "%d year, %d month, %d date, %d hour, %d minute",
774                              sd->curr_time.tm_year, sd->curr_time.tm_mon + 1,
775                              sd->curr_time.tm_mday, sd->curr_time.tm_hour,
776                              sd->curr_time.tm_min);
777 
778    ret = eina_strbuf_string_steal(buf);
779    eina_strbuf_free(buf);
780    return ret;
781 }
782 
783 static Eina_Bool
_ticker(void * data)784 _ticker(void *data)
785 {
786    double t;
787    time_t tt;
788    struct timeval timev;
789    Clock_Field *field;
790 
791    EFL_UI_CLOCK_DATA_GET(data, sd);
792 
793    tt = time(NULL);
794    localtime_r(&tt, &sd->curr_time);
795 
796    if (sd->curr_time.tm_sec > 0)
797      {
798         field = sd->field_list + EFL_UI_CLOCK_TYPE_SECOND;
799         if (field->fmt_exist && field->visible)
800           field_value_display(data, field->item_obj);
801      }
802    else
803      _field_list_display(data);
804 
805    gettimeofday(&timev, NULL);
806    t = ((double)(1000000 - timev.tv_usec)) / 1000000.0;
807    sd->ticker = ecore_timer_add(t, _ticker, data);
808 
809    return ECORE_CALLBACK_CANCEL;
810 }
811 
812 EOLIAN static void
_efl_ui_clock_efl_canvas_group_group_add(Eo * obj,Efl_Ui_Clock_Data * priv)813 _efl_ui_clock_efl_canvas_group_group_add(Eo *obj, Efl_Ui_Clock_Data *priv)
814 {
815    Clock_Field *field;
816    int idx;
817    ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd);
818 
819    if (!elm_widget_theme_klass_get(obj))
820      elm_widget_theme_klass_set(obj, "uiclock");
821    efl_canvas_group_add(efl_super(obj, MY_CLASS));
822 
823    if (elm_widget_theme_object_set(obj, wd->resize_obj,
824                                        elm_widget_theme_klass_get(obj),
825                                        elm_widget_theme_element_get(obj),
826                                        elm_widget_theme_style_get(obj)) == EFL_UI_THEME_APPLY_ERROR_GENERIC)
827      CRI("Failed to set layout!");
828 
829    for (idx = 0; idx < EFL_UI_CLOCK_TYPE_COUNT; idx++)
830      {
831         field = priv->field_list + idx;
832         field->item_obj = field_create(obj, idx);
833      }
834 
835    priv->freeze_sizing = EINA_TRUE;
836 
837    _field_list_init(obj);
838    _reload_format(obj);
839    _ticker(obj);
840 
841    elm_widget_can_focus_set(obj, EINA_TRUE);
842 
843    priv->freeze_sizing = EINA_FALSE;
844 
845    // ACCESS
846    if (_elm_config->access_mode == ELM_ACCESS_MODE_ON)
847      {
848         Elm_Access_Info *ai;
849 
850         priv->access_obj = _elm_access_edje_object_part_object_register
851           (obj, elm_layout_edje_get(obj), "efl.access");
852         if (!priv->access_obj)
853           priv->access_obj = _elm_access_edje_object_part_object_register
854           (obj, elm_layout_edje_get(obj), "access");
855 
856         ai = _elm_access_info_get(priv->access_obj);
857         _elm_access_text_set(ai, ELM_ACCESS_TYPE, "date time");
858         _elm_access_callback_set(ai, ELM_ACCESS_INFO, _access_info_cb, obj);
859      }
860 }
861 
862 EOLIAN static void
_efl_ui_clock_efl_canvas_group_group_del(Eo * obj,Efl_Ui_Clock_Data * sd)863 _efl_ui_clock_efl_canvas_group_group_del(Eo *obj, Efl_Ui_Clock_Data *sd)
864 {
865    Clock_Field *tmp;
866    unsigned int idx;
867 
868    ecore_timer_del(sd->ticker);
869    for (idx = 0; idx < EFL_UI_CLOCK_TYPE_COUNT; idx++)
870      {
871         tmp = sd->field_list + idx;
872         evas_object_del(tmp->item_obj);
873         eina_stringshare_del(tmp->separator);
874      }
875 
876    efl_canvas_group_del(efl_super(obj, MY_CLASS));
877 }
878 
879 EOLIAN static Eo *
_efl_ui_clock_efl_object_constructor(Eo * obj,Efl_Ui_Clock_Data * _pd EINA_UNUSED)880 _efl_ui_clock_efl_object_constructor(Eo *obj, Efl_Ui_Clock_Data *_pd EINA_UNUSED)
881 {
882    obj = efl_constructor(efl_super(obj, MY_CLASS));
883    evas_object_smart_callbacks_descriptions_set(obj, _smart_callbacks);
884    efl_access_object_role_set(obj, EFL_ACCESS_ROLE_DATE_EDITOR);
885 
886    return obj;
887 }
888 
889 EOLIAN static const char*
_efl_ui_clock_format_get(const Eo * obj EINA_UNUSED,Efl_Ui_Clock_Data * sd)890 _efl_ui_clock_format_get(const Eo *obj EINA_UNUSED, Efl_Ui_Clock_Data *sd)
891 {
892    return sd->format;
893 }
894 
895 EOLIAN static void
_efl_ui_clock_format_set(Eo * obj,Efl_Ui_Clock_Data * sd,const char * fmt)896 _efl_ui_clock_format_set(Eo *obj, Efl_Ui_Clock_Data *sd, const char *fmt)
897 {
898    if (fmt)
899      {
900         strncpy(sd->format, fmt, EFL_UI_CLOCK_MAX_FORMAT_LEN);
901         sd->format[EFL_UI_CLOCK_MAX_FORMAT_LEN - 1] = '\0';
902         sd->user_format = EINA_TRUE;
903      }
904    else sd->user_format = EINA_FALSE;
905 
906    _reload_format(obj);
907 }
908 
909 EOLIAN static Eina_Bool
_efl_ui_clock_field_visible_get(const Eo * obj EINA_UNUSED,Efl_Ui_Clock_Data * sd,Efl_Ui_Clock_Type fieldtype)910 _efl_ui_clock_field_visible_get(const Eo *obj EINA_UNUSED, Efl_Ui_Clock_Data *sd, Efl_Ui_Clock_Type fieldtype)
911 {
912    Clock_Field *field;
913 
914    if (fieldtype > EFL_UI_CLOCK_TYPE_AMPM) return EINA_FALSE;
915 
916    field = sd->field_list + fieldtype;
917 
918    return field->visible;
919 }
920 
921 EOLIAN static void
_efl_ui_clock_field_visible_set(Eo * obj,Efl_Ui_Clock_Data * sd,Efl_Ui_Clock_Type fieldtype,Eina_Bool visible)922 _efl_ui_clock_field_visible_set(Eo *obj, Efl_Ui_Clock_Data *sd, Efl_Ui_Clock_Type fieldtype, Eina_Bool visible)
923 {
924    char buf[BUFFER_SIZE];
925    Clock_Field *field;
926 
927    if (fieldtype > EFL_UI_CLOCK_TYPE_AMPM) return;
928 
929    field = sd->field_list + fieldtype;
930    visible = !!visible;
931    if (field->visible == visible) return;
932 
933    field->visible = visible;
934 
935    sd->freeze_sizing = EINA_TRUE;
936    if (visible)
937      {
938         sd->enabled_field_count++;
939 
940         if (!field->fmt_exist) return;
941 
942         snprintf(buf, sizeof(buf), EDC_PART_FIELD_ENABLE_SIG_STR,
943                  field->location);
944         efl_layout_signal_emit(obj, buf, "efl");
945 
946         ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd);
947         edje_object_message_signal_process(wd->resize_obj);
948 
949         _part_name_snprintf(buf, sizeof(buf), obj, EDC_PART_FIELD_STR,
950                             field->location);
951         efl_content_unset(efl_part(obj, buf));
952         efl_content_set(efl_part(obj, buf), field->item_obj);
953      }
954    else
955      {
956         sd->enabled_field_count--;
957 
958         if (!field->fmt_exist) return;
959 
960         snprintf(buf, sizeof(buf), EDC_PART_FIELD_DISABLE_SIG_STR,
961                  field->location);
962         efl_layout_signal_emit(obj, buf, "efl");
963 
964         ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd);
965         edje_object_message_signal_process(wd->resize_obj);
966 
967         _part_name_snprintf(buf, sizeof(buf), obj, EDC_PART_FIELD_STR,
968                             field->location);
969         efl_gfx_entity_visible_set(efl_content_unset(efl_part(obj, buf)), EINA_FALSE);
970      }
971    sd->freeze_sizing = EINA_FALSE;
972    efl_ui_layout_finger_size_multiplier_set(obj, sd->enabled_field_count, 1);
973 
974    efl_canvas_group_change(obj);
975 
976    if (!visible) return;
977    field_value_display(obj, field->item_obj);
978 }
979 
980 EOLIAN static void
_efl_ui_clock_field_limit_get(const Eo * obj EINA_UNUSED,Efl_Ui_Clock_Data * sd,Efl_Ui_Clock_Type fieldtype,int * min,int * max)981 _efl_ui_clock_field_limit_get(const Eo *obj EINA_UNUSED, Efl_Ui_Clock_Data *sd, Efl_Ui_Clock_Type fieldtype, int *min, int *max)
982 {
983    Clock_Field *field;
984 
985    if (fieldtype >= EFL_UI_CLOCK_TYPE_AMPM) return;
986 
987    field = sd->field_list + fieldtype;
988    if (min) *min = field->min;
989    if (max) *max = field->max;
990 }
991 
992 EOLIAN static void
_efl_ui_clock_field_limit_set(Eo * obj,Efl_Ui_Clock_Data * sd,Efl_Ui_Clock_Type fieldtype,int min,int max)993 _efl_ui_clock_field_limit_set(Eo *obj, Efl_Ui_Clock_Data *sd, Efl_Ui_Clock_Type fieldtype, int min, int max)
994 {
995    Clock_Field *field;
996    struct tm old_time;
997 
998    if (fieldtype >= EFL_UI_CLOCK_TYPE_AMPM) return;
999 
1000    if (min > max) return;
1001 
1002    old_time = sd->curr_time;
1003    field = sd->field_list + fieldtype;
1004    if (((min >= mapping[fieldtype].def_min) &&
1005         (min <= mapping[fieldtype].def_max)) ||
1006        (field->type == EFL_UI_CLOCK_TYPE_YEAR))
1007      field->min = min;
1008    if (((max >= mapping[fieldtype].def_min) &&
1009         (max <= mapping[fieldtype].def_max)) ||
1010        (field->type == EFL_UI_CLOCK_TYPE_YEAR))
1011      field->max = max;
1012 
1013    _apply_field_limits(obj);
1014 
1015    if (!_field_cmp(fieldtype, &old_time, &sd->curr_time))
1016      efl_event_callback_legacy_call(obj, EFL_UI_CLOCK_EVENT_CHANGED, NULL);
1017 }
1018 
1019 EOLIAN static Efl_Time
_efl_ui_clock_time_get(const Eo * obj EINA_UNUSED,Efl_Ui_Clock_Data * sd)1020 _efl_ui_clock_time_get(const Eo *obj EINA_UNUSED, Efl_Ui_Clock_Data *sd)
1021 {
1022    return sd->curr_time;
1023 }
1024 
1025 EOLIAN static void
_efl_ui_clock_time_set(Eo * obj,Efl_Ui_Clock_Data * sd,Efl_Time newtime)1026 _efl_ui_clock_time_set(Eo *obj, Efl_Ui_Clock_Data *sd, Efl_Time newtime)
1027 {
1028    if (_date_cmp(&sd->curr_time, &newtime)) return;
1029    sd->curr_time = newtime;
1030    // apply default field restrictions for curr_time
1031    _apply_range_restrictions(&sd->curr_time);
1032    // validate the curr_time according to the min_limt and max_limt
1033    _validate_clock_limits(&sd->curr_time, &sd->min_limit, EINA_FALSE);
1034    _validate_clock_limits(&sd->max_limit, &sd->curr_time, EINA_TRUE);
1035    _apply_field_limits(obj);
1036 
1037    efl_event_callback_legacy_call(obj, EFL_UI_CLOCK_EVENT_CHANGED, NULL);
1038 }
1039 
1040 EOLIAN static Efl_Time
_efl_ui_clock_time_min_get(const Eo * obj EINA_UNUSED,Efl_Ui_Clock_Data * sd)1041 _efl_ui_clock_time_min_get(const Eo *obj EINA_UNUSED, Efl_Ui_Clock_Data *sd)
1042 {
1043    return sd->min_limit;
1044 }
1045 
1046 EOLIAN static void
_efl_ui_clock_time_min_set(Eo * obj,Efl_Ui_Clock_Data * sd,Efl_Time mintime)1047 _efl_ui_clock_time_min_set(Eo *obj, Efl_Ui_Clock_Data *sd, Efl_Time mintime)
1048 {
1049    struct tm old_time;
1050 
1051    if (_date_cmp(&sd->min_limit, &mintime)) return;
1052    sd->min_limit = mintime;
1053    old_time = sd->curr_time;
1054    // apply default field restrictions for min_limit
1055    _apply_range_restrictions(&sd->min_limit);
1056    // validate curr_time and max_limt according to the min_limit
1057    _validate_clock_limits(&sd->max_limit, &sd->min_limit, EINA_FALSE);
1058    _validate_clock_limits(&sd->curr_time, &sd->min_limit, EINA_FALSE);
1059    _apply_field_limits(obj);
1060 
1061    if (!_date_cmp(&old_time, &sd->curr_time))
1062      efl_event_callback_legacy_call(obj, EFL_UI_CLOCK_EVENT_CHANGED, NULL);
1063 }
1064 
1065 EOLIAN static Efl_Time
_efl_ui_clock_time_max_get(const Eo * obj EINA_UNUSED,Efl_Ui_Clock_Data * sd)1066 _efl_ui_clock_time_max_get(const Eo *obj EINA_UNUSED, Efl_Ui_Clock_Data *sd)
1067 {
1068    return sd->max_limit;
1069 }
1070 
1071 EOLIAN static void
_efl_ui_clock_time_max_set(Eo * obj,Efl_Ui_Clock_Data * sd,Efl_Time maxtime)1072 _efl_ui_clock_time_max_set(Eo *obj, Efl_Ui_Clock_Data *sd, Efl_Time maxtime)
1073 {
1074    struct tm old_time;
1075 
1076    if (_date_cmp(&sd->max_limit, &maxtime)) return;
1077    sd->max_limit = maxtime;
1078    old_time = sd->curr_time;
1079    // apply default field restrictions for max_limit
1080    _apply_range_restrictions(&sd->max_limit);
1081    // validate curr_time and min_limt according to the max_limit
1082    _validate_clock_limits(&sd->max_limit, &sd->min_limit, EINA_TRUE);
1083    _validate_clock_limits(&sd->max_limit, &sd->curr_time, EINA_TRUE);
1084    _apply_field_limits(obj);
1085 
1086    if (!_date_cmp(&old_time, &sd->curr_time))
1087      efl_event_callback_legacy_call(obj, EFL_UI_CLOCK_EVENT_CHANGED, NULL);
1088 }
1089 
1090 /* Internal EO APIs and hidden overrides */
1091 
1092 #define EFL_UI_CLOCK_EXTRA_OPS \
1093    EFL_CANVAS_GROUP_ADD_DEL_OPS(efl_ui_clock)
1094 
1095 #include "efl_ui_clock.eo.c"
1096