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