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