Merge "[Password]: New design based changes, a new style removed password mode contro...
[framework/uifw/elementary.git] / src / lib / elm_calendar.c
1 #ifdef HAVE_CONFIG_H
2 # include "elementary_config.h"
3 #endif
4
5 #ifdef HAVE_EVIL
6 # include <Evil.h>
7 #endif
8
9 #include <Elementary.h>
10 #include "elm_priv.h"
11
12 /**
13  * @defgroup Calendar
14  * @ingroup Elementary
15  *
16  * A calendar is a widget that allows the user to select a date. It has
17  * support to adding check marks (holidays and checks by default). The calendar
18  * is displayed one month at a time.
19  *
20  * Weekday names and the function used to format month and year to
21  * be displayed can be set, giving more flexibility to this widget.
22  *
23  * Signals that you can add callbacks for are:
24  *
25  * "changed" - emitted when the user selects a day or changes the displayed
26  *             month, what actually changes the selected day as well.
27  */
28
29 typedef enum _Day_Color // EINA_DEPRECATED
30 {
31    DAY_WEEKDAY = 0,
32    DAY_SATURDAY = 1,
33    DAY_SUNDAY = 2
34 } Day_Color;
35
36 typedef struct _Widget_Data Widget_Data;
37
38 struct _Widget_Data
39 {
40    Evas_Object *calendar;
41    Eina_List *marks;
42    double interval, first_interval;
43    int year_min, year_max, spin_speed;
44    int today_it, selected_it, first_day_it;
45    Ecore_Timer *spin, *update_timer;
46    char * (*format_func) (struct tm *stime);
47    const char *weekdays[7];
48    struct tm current_time, selected_time;
49    Day_Color day_color[42]; // EINA_DEPRECATED
50    Eina_Bool selection_enabled : 1;
51 };
52
53 struct _Elm_Calendar_Mark
54 {
55    Evas_Object *obj;
56    Eina_List *node;
57    struct tm mark_time;
58    const char *mark_type;
59    Elm_Calendar_Mark_Repeat repeat;
60 };
61
62 static const char *widtype = NULL;
63 static void _on_focus_hook(void *data, Evas_Object *obj);
64 static void _mirrored_set(Evas_Object *obj, Eina_Bool rtl);
65
66 /* Should not be translated, it's used if we failed
67  * getting from locale. */
68 static const char *_days_abbrev[] =
69 {
70    "Sun", "Mon", "Tue", "Wed",
71    "Thu", "Fri", "Sat"
72 };
73
74 static int _days_in_month[2][12] =
75 {
76      {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
77      {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
78 };
79
80 static Elm_Calendar_Mark *
81 _mark_new(Evas_Object *obj, const char *mark_type, struct tm *mark_time, Elm_Calendar_Mark_Repeat repeat)
82 {
83    Widget_Data *wd = elm_widget_data_get(obj);
84    Elm_Calendar_Mark *mark;
85
86    if (!wd) return NULL;
87    mark = calloc(1, sizeof(Elm_Calendar_Mark));
88    if (!mark) return NULL;
89    mark->obj = obj;
90    mark->mark_type = eina_stringshare_add(mark_type);
91    mark->mark_time = *mark_time;
92    mark->repeat = repeat;
93    return mark;
94 }
95
96 static inline void
97 _mark_free(Elm_Calendar_Mark *mark)
98 {
99    eina_stringshare_del(mark->mark_type);
100    free(mark);
101 }
102
103 static void
104 _sizing_eval(Evas_Object *obj)
105 {
106    Widget_Data *wd = elm_widget_data_get(obj);
107    Evas_Coord minw = -1, minh = -1;
108    if (!wd) return;
109    elm_coords_finger_size_adjust(8, &minw, 7, &minh);
110    edje_object_size_min_restricted_calc(wd->calendar, &minw, &minh, minw, minh);
111    evas_object_size_hint_min_set(obj, minw, minh);
112    evas_object_size_hint_max_set(obj, -1, -1);
113 }
114
115 static inline int
116 _maxdays_get(struct tm *time)
117 {
118    int month, year;
119
120    month = time->tm_mon;
121    year = time->tm_year + 1900;
122
123    return _days_in_month[((!(year % 4)) &&
124                           ((!(year % 400)) ||
125                            (year % 100)))]
126       [month];
127 }
128
129 static inline void
130 _unselect(Widget_Data *wd, int selected)
131 {
132    char emission[32];
133    snprintf(emission, sizeof(emission), "cit_%i,unselected", selected);
134    edje_object_signal_emit(wd->calendar, emission, "elm");
135 }
136
137 static inline void
138 _select(Widget_Data *wd, int selected)
139 {
140    char emission[32];
141    snprintf(emission, sizeof(emission), "cit_%i,selected", selected);
142    edje_object_signal_emit(wd->calendar, emission, "elm");
143 }
144
145 static inline void
146 _not_today(Widget_Data *wd)
147 {
148    char emission[32];
149    snprintf(emission, sizeof(emission), "cit_%i,not_today", wd->today_it);
150    edje_object_signal_emit(wd->calendar, emission, "elm");
151    wd->today_it = -1;
152 }
153
154 static inline void
155 _today(Widget_Data *wd, int it)
156 {
157    char emission[32];
158    snprintf(emission, sizeof(emission), "cit_%i,today", it);
159    edje_object_signal_emit(wd->calendar, emission, "elm");
160    wd->today_it = it;
161 }
162
163 static char *
164 _format_month_year(struct tm *stime)
165 {
166    char buf[32];
167    if (!strftime(buf, sizeof(buf), "%B %Y", stime)) return NULL;
168    return strdup(buf);
169 }
170
171 static inline void
172 _cit_mark(Evas_Object *cal, int cit, const char *mtype)
173 {
174    char sign[64];
175    snprintf(sign, sizeof(sign), "cit_%i,%s", cit, mtype);
176    edje_object_signal_emit(cal, sign, "elm");
177 }
178
179 static inline int
180 _weekday_get(int first_week_day, int day)
181 {
182    return (day + first_week_day - 1) % 7;
183 }
184
185 // EINA_DEPRECATED
186 static void
187 _text_day_color_update(Widget_Data *wd, int pos)
188 {
189    char emission[32];
190
191    switch (wd->day_color[pos])
192      {
193       case DAY_WEEKDAY:
194          snprintf(emission, sizeof(emission), "cit_%i,weekday", pos);
195          break;
196       case DAY_SATURDAY:
197          snprintf(emission, sizeof(emission), "cit_%i,saturday", pos);
198          break;
199       case DAY_SUNDAY:
200          snprintf(emission, sizeof(emission), "cit_%i,sunday", pos);
201          break;
202       default:
203          return;
204      }
205
206    edje_object_signal_emit(wd->calendar, emission, "elm");
207 }
208
209 // EINA_DEPRECATED
210 static void
211 _text_day_color_set(Widget_Data *wd, Day_Color col, int pos)
212 {
213    if ((pos < 0) || (pos >= 42)) return;
214    if (wd->day_color[pos] == col) return;
215    wd->day_color[pos] = col;
216    _text_day_color_update(wd, pos);
217 }
218
219 static void
220 _populate(Evas_Object *obj)
221 {
222    int maxdays, day, mon, year, i;
223    Elm_Calendar_Mark *mark;
224    char part[12], day_s[3];
225    struct tm first_day;
226    Eina_List *l;
227    char *buf;
228    Eina_Bool last_row = EINA_TRUE;
229    Widget_Data *wd = elm_widget_data_get(obj);
230
231    if (!wd) return;
232
233    if (wd->today_it > 0) _not_today(wd);
234
235    maxdays = _maxdays_get(&wd->selected_time);
236    mon = wd->selected_time.tm_mon;
237    year = wd->selected_time.tm_year;
238
239    /* Set selected month */
240    buf = wd->format_func(&wd->selected_time);
241    if (buf)
242      {
243         edje_object_part_text_set(wd->calendar, "month_text", buf);
244         free(buf);
245      }
246    else
247      edje_object_part_text_set(wd->calendar, "month_text", "");
248
249    /* Set days */
250    day = 0;
251    first_day = wd->selected_time;
252    first_day.tm_mday = 1;
253    mktime(&first_day);
254
255    // Layout of the calendar is changed for removing the unfilled last row.
256    wd->first_day_it = first_day.tm_wday;
257
258    if ((35 - wd->first_day_it) > (maxdays - 1)) last_row = EINA_FALSE;
259
260    if (!last_row)
261      {
262         char emission[32];
263
264         for (i = 0; i < 5; i++)
265           {
266              snprintf(emission, sizeof(emission), "cseph_%i,row_hide", i);
267              edje_object_signal_emit(wd->calendar, emission, "elm");
268           }
269         snprintf(emission, sizeof(emission), "cseph_%i,row_invisible", 5);
270         edje_object_signal_emit(wd->calendar, emission, "elm");
271         for (i = 0; i < 35; i++)
272           {
273              snprintf(emission, sizeof(emission), "cit_%i,cell_expanded", i);
274              edje_object_signal_emit(wd->calendar, emission, "elm");
275           }
276         for (i = 35; i < 42; i++)
277           {
278              snprintf(emission, sizeof(emission), "cit_%i,cell_invisible", i);
279              edje_object_signal_emit(wd->calendar, emission, "elm");
280           }
281      }
282    else
283      {
284         char emission[32];
285
286         for (i = 0; i < 6; i++)
287           {
288              snprintf(emission, sizeof(emission), "cseph_%i,row_show", i);
289              edje_object_signal_emit(wd->calendar, emission, "elm");
290           }
291         for (i = 0; i < 42; i++)
292           {
293              snprintf(emission, sizeof(emission), "cit_%i,cell_default", i);
294              edje_object_signal_emit(wd->calendar, emission, "elm");
295           }
296      }
297
298    for (i = 0; i < 42; i++)
299      {
300         _text_day_color_update(wd, i); // EINA_DEPRECATED
301         if ((!day) && (i == first_day.tm_wday)) day = 1;
302
303         if ((day == wd->current_time.tm_mday)
304             && (mon == wd->current_time.tm_mon)
305             && (year == wd->current_time.tm_year))
306           _today(wd, i);
307
308         if (day == wd->selected_time.tm_mday)
309           {
310              if ((wd->selected_it > -1) && (wd->selected_it != i))
311                _unselect(wd, wd->selected_it);
312
313              if (wd->selection_enabled) _select(wd, i);
314
315              wd->selected_it = i;
316           }
317
318         if ((day) && (day <= maxdays))
319           snprintf(day_s, sizeof(day_s), "%i", day++);
320         else
321           day_s[0] = 0;
322
323         snprintf(part, sizeof(part), "cit_%i.text", i);
324         edje_object_part_text_set(wd->calendar, part, day_s);
325         /* Clear previous marks */
326         _cit_mark(wd->calendar, i, "clear");
327      }
328
329    /* Set marks */
330    EINA_LIST_FOREACH(wd->marks, l, mark)
331      {
332         struct tm *mtime = &mark->mark_time;
333         int mon = wd->selected_time.tm_mon;
334         int year = wd->selected_time.tm_year;
335         int mday_it = mtime->tm_mday + wd->first_day_it - 1;
336
337         switch (mark->repeat)
338           {
339            case ELM_CALENDAR_UNIQUE:
340               if ((mtime->tm_mon == mon) && (mtime->tm_year == year))
341                 _cit_mark(wd->calendar, mday_it, mark->mark_type);
342               break;
343            case ELM_CALENDAR_DAILY:
344               if (((mtime->tm_year == year) && (mtime->tm_mon < mon)) ||
345                   (mtime->tm_year < year))
346                 day = 1;
347               else if ((mtime->tm_year == year) && (mtime->tm_mon == mon))
348                 day = mtime->tm_mday;
349               else
350                 break;
351               for (; day <= maxdays; day++)
352                 _cit_mark(wd->calendar, day + wd->first_day_it - 1,
353                           mark->mark_type);
354               break;
355            case ELM_CALENDAR_WEEKLY:
356               if (((mtime->tm_year == year) && (mtime->tm_mon < mon)) ||
357                   (mtime->tm_year < year))
358                 day = 1;
359               else if ((mtime->tm_year == year) && (mtime->tm_mon == mon))
360                 day = mtime->tm_mday;
361               else
362                 break;
363               for (; day <= maxdays; day++)
364                 if (mtime->tm_wday == _weekday_get(wd->first_day_it, day))
365                   _cit_mark(wd->calendar, day + wd->first_day_it - 1,
366                             mark->mark_type);
367               break;
368            case ELM_CALENDAR_MONTHLY:
369               if (((mtime->tm_year < year) ||
370                    ((mtime->tm_year == year) && (mtime->tm_mon <= mon))) &&
371                   (mtime->tm_mday <= maxdays))
372                 _cit_mark(wd->calendar, mday_it, mark->mark_type);
373               break;
374            case ELM_CALENDAR_ANNUALLY:
375               if ((mtime->tm_year <= year) && (mtime->tm_mon == mon) &&
376                   (mtime->tm_mday <= maxdays))
377                 _cit_mark(wd->calendar, mday_it, mark->mark_type);
378               break;
379           }
380      }
381 }
382
383 static void
384 _set_headers(Evas_Object *obj)
385 {
386    static char part[] = "ch_0.text";
387    int i;
388    Widget_Data *wd = elm_widget_data_get(obj);
389    if (!wd) return;
390
391    for (i = 0; i < 7; i++)
392      {
393         part[3] = i + '0';
394         edje_object_part_text_set(wd->calendar, part, wd->weekdays[i]);
395      }
396 }
397
398 static void
399 _del_hook(Evas_Object *obj)
400 {
401    int i;
402    Elm_Calendar_Mark *mark;
403    Widget_Data *wd = elm_widget_data_get(obj);
404
405    if (!wd) return;
406
407    if (wd->spin) ecore_timer_del(wd->spin);
408    if (wd->update_timer) ecore_timer_del(wd->update_timer);
409
410    if (wd->marks)
411      {
412         EINA_LIST_FREE(wd->marks, mark)
413           {
414              _mark_free(mark);
415           }
416      }
417
418    for (i = 0; i < 7; i++)
419      eina_stringshare_del(wd->weekdays[i]);
420
421    free(wd);
422 }
423
424 static void
425 _on_focus_hook(void *data __UNUSED__, Evas_Object *obj)
426 {
427    Widget_Data *wd = elm_widget_data_get(obj);
428    if (!wd) return;
429    if (elm_widget_focus_get(obj))
430      {
431         edje_object_signal_emit(wd->calendar, "elm,action,focus", "elm");
432         evas_object_focus_set(wd->calendar, EINA_TRUE);
433      }
434    else
435      {
436         edje_object_signal_emit(wd->calendar, "elm,action,unfocus", "elm");
437         evas_object_focus_set(wd->calendar, EINA_FALSE);
438      }
439 }
440
441 static void
442 _mirrored_set(Evas_Object *obj, Eina_Bool rtl)
443 {
444    Widget_Data *wd = elm_widget_data_get(obj);
445    if (!wd) return;
446    edje_object_mirrored_set(wd->calendar, rtl);
447 }
448
449 static void
450 _theme_hook(Evas_Object *obj)
451 {
452    Widget_Data *wd = elm_widget_data_get(obj);
453    if (!wd) return;
454    _elm_widget_mirrored_reload(obj);
455    _elm_theme_object_set(obj, wd->calendar, "calendar", "base",
456                          elm_widget_style_get(obj));
457    _mirrored_set(obj, elm_widget_mirrored_get(obj));
458    _set_headers(obj);
459    _populate(obj);
460    edje_object_message_signal_process(wd->calendar);
461    edje_object_scale_set(wd->calendar,
462                          elm_widget_scale_get(obj) * _elm_config->scale);
463    _sizing_eval(obj);
464 }
465
466 static void
467 _signal_emit_hook(Evas_Object *obj, const char *emission, const char *source)
468 {
469    Widget_Data *wd = elm_widget_data_get(obj);
470    if (!wd) return;
471    edje_object_signal_emit(wd->calendar, emission, source);
472 }
473
474 static void
475 _signal_callback_add_hook(Evas_Object *obj, const char *emission, const char *source, Edje_Signal_Cb func_cb, void *data)
476 {
477    Widget_Data *wd = elm_widget_data_get(obj);
478    if (!wd) return;
479    edje_object_signal_callback_add(wd->calendar, emission,
480                                    source, func_cb, data);
481 }
482
483 static void
484 _signal_callback_del_hook(Evas_Object *obj, const char *emission, const char *source, Edje_Signal_Cb func_cb, void *data)
485 {
486    Widget_Data *wd = elm_widget_data_get(obj);
487    if (!wd) return;
488    edje_object_signal_callback_del_full(wd->calendar, emission, source, func_cb,
489                                         data);
490 }
491
492 /* Set correct tm_wday and tm_yday after other fields changes*/
493 static inline void
494 _fix_selected_time(Widget_Data *wd)
495 {
496    mktime(&wd->selected_time);
497 }
498
499 static Eina_Bool
500 _update_month(Evas_Object *obj, int delta)
501 {
502    struct tm time_check;
503    int maxdays;
504    Widget_Data *wd = elm_widget_data_get(obj);
505    if (!wd) return EINA_FALSE;
506
507    /* check if it's a valid time. for 32 bits, year greater than 2037 is not */
508    time_check = wd->selected_time;
509    time_check.tm_mon += delta;
510    if (mktime(&time_check) == -1)
511      return EINA_FALSE;
512
513    wd->selected_time.tm_mon += delta;
514    if (wd->selected_time.tm_mon < 0)
515      {
516         if (wd->selected_time.tm_year == wd->year_min)
517           {
518              wd->selected_time.tm_mon++;
519              return EINA_FALSE;
520           }
521         wd->selected_time.tm_mon = 11;
522         wd->selected_time.tm_year--;
523      }
524    else if (wd->selected_time.tm_mon > 11)
525      {
526         if (wd->selected_time.tm_year == wd->year_max)
527           {
528              wd->selected_time.tm_mon--;
529              return EINA_FALSE;
530           }
531         wd->selected_time.tm_mon = 0;
532         wd->selected_time.tm_year++;
533      }
534
535    maxdays = _maxdays_get(&wd->selected_time);
536    if (wd->selected_time.tm_mday > maxdays)
537      wd->selected_time.tm_mday = maxdays;
538
539    _fix_selected_time(wd);
540    evas_object_smart_callback_call(obj, "changed", NULL);
541
542    return EINA_TRUE;
543 }
544
545 static Eina_Bool
546 _spin_value(void *data)
547 {
548    Widget_Data *wd = elm_widget_data_get(data);
549    if (!wd) return ECORE_CALLBACK_CANCEL;
550    if (_update_month(data, wd->spin_speed)) _populate(data);
551    wd->interval = wd->interval / 1.05;
552    ecore_timer_interval_set(wd->spin, wd->interval);
553    return ECORE_CALLBACK_RENEW;
554 }
555
556 static void
557 _button_inc_start(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
558 {
559    Widget_Data *wd = elm_widget_data_get(data);
560    if (!wd) return;
561    wd->interval = wd->first_interval;
562    wd->spin_speed = 1;
563    if (wd->spin) ecore_timer_del(wd->spin);
564    wd->spin = ecore_timer_add(wd->interval, _spin_value, data);
565    _spin_value(data);
566 }
567
568 static void
569 _button_dec_start(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
570 {
571    Widget_Data *wd = elm_widget_data_get(data);
572    if (!wd) return;
573    wd->interval = wd->first_interval;
574    wd->spin_speed = -1;
575    if (wd->spin) ecore_timer_del(wd->spin);
576    wd->spin = ecore_timer_add(wd->interval, _spin_value, data);
577    _spin_value(data);
578 }
579
580 static void
581 _button_stop(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
582 {
583    Widget_Data *wd = elm_widget_data_get(data);
584    if (!wd) return;
585    wd->interval = wd->first_interval;
586    if (wd->spin) ecore_timer_del(wd->spin);
587    wd->spin = NULL;
588 }
589
590 static int
591 _get_item_day(Evas_Object *obj, int selected_it)
592 {
593    int day;
594    Widget_Data *wd = elm_widget_data_get(obj);
595    if (!wd) return 0;
596
597    day = selected_it - wd->first_day_it + 1;
598    if ((day < 0) || (day > _maxdays_get(&wd->selected_time)))
599      return 0;
600
601    return day;
602 }
603
604 static void
605 _update_sel_it(Evas_Object *obj, int sel_it)
606 {
607    int day;
608    Widget_Data *wd = elm_widget_data_get(obj);
609    if ((!wd) || (!wd->selection_enabled))
610      return;
611
612    day = _get_item_day(obj, sel_it);
613    if (!day)
614      return;
615
616    _unselect(wd, wd->selected_it);
617
618    wd->selected_it = sel_it;
619    wd->selected_time.tm_mday = day;
620    _select(wd, wd->selected_it);
621    _fix_selected_time(wd);
622    evas_object_smart_callback_call(obj, "changed", NULL);
623 }
624
625 static void
626 _day_selected(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source)
627 {
628    int sel_it;
629    Widget_Data *wd = elm_widget_data_get(data);
630    if ((!wd) || (!wd->selection_enabled))
631      return;
632    sel_it = atoi(source);
633
634    _update_sel_it(data, sel_it);
635 }
636
637 static inline int
638 _time_to_next_day(struct tm *t)
639 {
640    return ((((24 - t->tm_hour) * 60) - t->tm_min) * 60) - t->tm_sec;
641 }
642
643 static Eina_Bool
644 _update_cur_date(void *data)
645 {
646    time_t current_time;
647    int t, day;
648    Widget_Data *wd = elm_widget_data_get(data);
649    if (!wd) return ECORE_CALLBACK_RENEW;
650
651    if (wd->today_it > 0) _not_today(wd);
652
653    current_time = time(NULL);
654    localtime_r(&current_time, &wd->current_time);
655    t = _time_to_next_day(&wd->current_time);
656    ecore_timer_interval_set(wd->update_timer, t);
657
658    if ((wd->current_time.tm_mon != wd->selected_time.tm_mon) ||
659        (wd->current_time.tm_year!= wd->selected_time.tm_year))
660      return ECORE_CALLBACK_RENEW;
661
662    day = wd->current_time.tm_mday + wd->first_day_it - 1;
663    _today(wd, day);
664
665    return ECORE_CALLBACK_RENEW;
666 }
667
668 static Eina_Bool
669 _event_hook(Evas_Object *obj, Evas_Object *src __UNUSED__, Evas_Callback_Type type, void *event_info)
670 {
671    if (type != EVAS_CALLBACK_KEY_DOWN) return EINA_FALSE;
672    Evas_Event_Key_Down *ev = event_info;
673    Widget_Data *wd = elm_widget_data_get(obj);
674
675    if (!wd) return EINA_FALSE;
676    if (elm_widget_disabled_get(obj)) return EINA_FALSE;
677    if (!wd->selection_enabled) return EINA_FALSE;
678
679    if ((!strcmp(ev->keyname, "Left")) ||
680        (!strcmp(ev->keyname, "KP_Left")))
681      {
682         _update_sel_it(obj, wd->selected_it-1);
683      }
684    else if ((!strcmp(ev->keyname, "Right")) ||
685             (!strcmp(ev->keyname, "KP_Right")))
686      {
687         _update_sel_it(obj, wd->selected_it+1);
688      }
689    else if ((!strcmp(ev->keyname, "Up"))  ||
690             (!strcmp(ev->keyname, "KP_Up")))
691      {
692         _update_sel_it(obj, wd->selected_it-7);
693      }
694    else if ((!strcmp(ev->keyname, "Down")) ||
695             (!strcmp(ev->keyname, "KP_Down")))
696      {
697         _update_sel_it(obj, wd->selected_it+7);
698      }
699    else if ((!strcmp(ev->keyname, "Prior")) ||
700             (!strcmp(ev->keyname, "KP_Prior")))
701      {
702         if (_update_month(obj, -1)) _populate(obj);
703      }
704    else if ((!strcmp(ev->keyname, "Next")) ||
705             (!strcmp(ev->keyname, "KP_Next")))
706      {
707         if (_update_month(obj, 1)) _populate(obj);
708      }
709    else return EINA_FALSE;
710
711    return EINA_TRUE;
712 }
713
714 /**
715  * Add a new calendar to the parent
716  *
717  * @param parent The parent object
718  * @return The new object or NULL if it cannot be created
719  *
720  * @ingroup Calendar
721  */
722 EAPI Evas_Object *
723 elm_calendar_add(Evas_Object *parent)
724 {
725    time_t current_time;
726    time_t weekday = 259200; /* Just the first sunday since epoch */
727    Evas_Object *obj;
728    Widget_Data *wd;
729    int i, t;
730    Evas *e;
731
732    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
733
734    ELM_SET_WIDTYPE(widtype, "calendar");
735    elm_widget_type_set(obj, "calendar");
736    elm_widget_sub_object_add(parent, obj);
737    elm_widget_on_focus_hook_set(obj, _on_focus_hook, NULL);
738    elm_widget_data_set(obj, wd);
739    elm_widget_del_hook_set(obj, _del_hook);
740    elm_widget_theme_hook_set(obj, _theme_hook);
741    elm_widget_signal_emit_hook_set(obj, _signal_emit_hook);
742    elm_widget_signal_callback_add_hook_set(obj, _signal_callback_add_hook);
743    elm_widget_signal_callback_del_hook_set(obj, _signal_callback_del_hook);
744    elm_widget_can_focus_set(obj, EINA_TRUE);
745    elm_widget_event_hook_set(obj, _event_hook);
746
747    wd->first_interval = 0.85;
748    wd->year_min = 2;
749    wd->year_max = -1;
750    wd->today_it = -1;
751    wd->selected_it = -1;
752    wd->first_day_it = -1;
753    wd->selection_enabled = EINA_TRUE;
754    wd->format_func = _format_month_year;
755    wd->marks = NULL;
756
757    wd->calendar = edje_object_add(e);
758    _elm_theme_object_set(obj, wd->calendar, "calendar", "base", "default");
759    elm_widget_resize_object_set(obj, wd->calendar);
760
761    edje_object_signal_callback_add(wd->calendar, "elm,action,increment,start",
762                                    "*", _button_inc_start, obj);
763    edje_object_signal_callback_add(wd->calendar, "elm,action,decrement,start",
764                                    "*", _button_dec_start, obj);
765    edje_object_signal_callback_add(wd->calendar, "elm,action,stop",
766                                    "*", _button_stop, obj);
767    edje_object_signal_callback_add(wd->calendar, "elm,action,selected",
768                                    "*", _day_selected, obj);
769
770    for (i = 0; i < 7; i++)
771      {
772         /* FIXME: I'm not aware of a known max, so if it fails,
773          * just make it larger. :| */
774         char buf[20];
775         /* I don't know of a better way of doing it */
776         if (strftime(buf, sizeof(buf), "%a", gmtime(&weekday)))
777           {
778              wd->weekdays[i] = eina_stringshare_add(buf);
779           }
780         else
781           {
782              /* If we failed getting day, get a default value */
783              wd->weekdays[i] = _days_abbrev[i];
784              WRN("Failed getting weekday name for '%s' from locale.",
785                  _days_abbrev[i]);
786           }
787         weekday += 86400; /* Advance by a day */
788      }
789
790    current_time = time(NULL);
791    localtime_r(&current_time, &wd->selected_time);
792    wd->current_time = wd->selected_time;
793    t = _time_to_next_day(&wd->current_time);
794    wd->update_timer = ecore_timer_add(t, _update_cur_date, obj);
795
796    _set_headers(obj);
797    _populate(obj);
798    _mirrored_set(obj, elm_widget_mirrored_get(obj));
799    _sizing_eval(obj);
800    return obj;
801 }
802
803 /**
804  * Set weekdays names to display in the calendar.
805  *
806  * By default, the following abbreviations are displayed:
807  * "Sun, Mon, Tue, Wed, Thu, Fri, Sat"
808  * The first string should be related to Sunday, the second to Monday...
809  *
810  * The usage should be like this:
811  * @code
812  *   const char *weekdays[] =
813  *   {
814  *      "Sunday", "Monday", "Tuesday", "Wednesday",
815  *      "Thursday", "Friday", "Saturday"
816  *   };
817  *   elm_calendar_weekdays_names_set(calendar, weekdays);
818  * @endcode
819  *
820  * @param obj The calendar object
821  * @param weedays Array of seven strings to be used as weekday names.
822  * Warning: it must have 7 elements, or it will access invalid memory.
823  * The strings must be NULL terminated ('@\0').
824  *
825  * @ingroup Calendar
826  */
827 EAPI void
828 elm_calendar_weekdays_names_set(Evas_Object *obj, const char *weekdays[])
829 {
830    int i;
831    ELM_CHECK_WIDTYPE(obj, widtype);
832    Widget_Data *wd = elm_widget_data_get(obj);
833    if (!wd) return;
834
835    EINA_SAFETY_ON_NULL_RETURN(weekdays);
836
837    for (i = 0; i < 7; i++)
838      {
839         eina_stringshare_replace(&wd->weekdays[i], weekdays[i]);
840      }
841    _set_headers(obj);
842 }
843
844 /**
845  * Get weekdays names displayed in the calendar.
846  *
847  * By default, the following abbreviations are displayed:
848  * "Sun, Mon, Tue, Wed, Thu, Fri, Sat"
849  * The first string is related to Sunday, the second to Monday...
850  *
851  * @param obj The calendar object
852  * @return Array of seven strings to used as weekday names.
853  *
854  * @ingroup Calendar
855  */
856 EAPI const char **
857 elm_calendar_weekdays_names_get(const Evas_Object *obj)
858 {
859    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
860    Widget_Data *wd = elm_widget_data_get(obj);
861    if (!wd) return NULL;
862    return wd->weekdays;
863 }
864
865 /**
866  * Set the interval for the calendar
867  *
868  * The interval value is decreased while the user increments or decrements
869  * the calendar value. The next interval value is the previous interval / 1.05,
870  * so it speed up a bit. Default value is 0.85 seconds.
871  *
872  * @param obj The calendar object
873  * @param interval The interval value in seconds
874  *
875  * @ingroup Calendar
876  */
877 EAPI void
878 elm_calendar_interval_set(Evas_Object *obj, double interval)
879 {
880    ELM_CHECK_WIDTYPE(obj, widtype);
881    Widget_Data *wd = elm_widget_data_get(obj);
882    if (!wd) return;
883    wd->first_interval = interval;
884 }
885
886 /**
887  * Get the interval of the calendar
888  *
889  * The interval value is decreased while the user increments or decrements
890  * the calendar value. The next interval value is the previous interval / 1.05,
891  * so it speed up a bit. Default value is 0.85 seconds.
892  *
893  * @param obj The calendar object
894  * @return The value of the first interval in seconds
895  *
896  * @ingroup Calendar
897  */
898 EAPI double
899 elm_calendar_interval_get(const Evas_Object *obj)
900 {
901    ELM_CHECK_WIDTYPE(obj, widtype) 0.0;
902    Widget_Data *wd = elm_widget_data_get(obj);
903    if (!wd) return 0.0;
904    return wd->first_interval;
905 }
906
907 /**
908  * Set the minimum and maximum values for the year
909  *
910  * Maximum must be greater than minimum, except if you don't wan't to set
911  * maximum year.
912  * Default values are 1902 and -1.
913  *
914  * If the maximum year is a negative value, it will be limited depending of the
915  * platform architecture (2037 for 32 bits);
916  *
917  * @param obj The calendar object
918  * @param min The minimum year, greater than 1901;
919  * @param max The maximum year;
920  *
921  * @ingroup Calendar
922  */
923 EAPI void
924 elm_calendar_min_max_year_set(Evas_Object *obj, int min, int max)
925 {
926    ELM_CHECK_WIDTYPE(obj, widtype);
927    Widget_Data *wd = elm_widget_data_get(obj);
928    if (!wd) return;
929    min -= 1900;
930    max -= 1900;
931    if ((wd->year_min == min) && (wd->year_max == max)) return;
932    wd->year_min = min > 2 ? min : 2;
933    if (max > wd->year_min)
934           wd->year_max = max;
935    else
936           wd->year_max = wd->year_min;
937    if (wd->selected_time.tm_year > wd->year_max)
938      wd->selected_time.tm_year = wd->year_max;
939    if (wd->selected_time.tm_year < wd->year_min)
940      wd->selected_time.tm_year = wd->year_min;
941    _fix_selected_time(wd);
942    _populate(obj);
943 }
944
945 /**
946  * Get the minimum and maximum values for the year
947  *
948  * Default values are 1902 and -1.
949  *
950  * If the maximum year is a negative value, it will be limited depending of the
951  * platform architecture (2037 for 32 bits);
952  *
953  * @param obj The calendar object
954  * @param min The minimum year
955  * @param max The maximum year
956  *
957  * @ingroup Calendar
958  */
959 EAPI void
960 elm_calendar_min_max_year_get(const Evas_Object *obj, int *min, int *max)
961 {
962    ELM_CHECK_WIDTYPE(obj, widtype);
963    Widget_Data *wd = elm_widget_data_get(obj);
964    if (!wd) return;
965    if (min) *min = wd->year_min + 1900;
966    if (max) *max = wd->year_max + 1900;
967 }
968
969 /**
970  * Enable or disable day selection
971  *
972  * Enabled by default. If disabled, the user can select months, but not days.
973  * It should be used if you won't need such selection for the widget usage.
974  *
975  * @param obj The calendar object
976  * @param enabled Boolean to enable (true) or disable (false) day selection
977  *
978  * @ingroup Calendar
979  */
980 EAPI void
981 elm_calendar_day_selection_enabled_set(Evas_Object *obj, Eina_Bool enabled)
982 {
983    ELM_CHECK_WIDTYPE(obj, widtype);
984    Widget_Data *wd = elm_widget_data_get(obj);
985    if (!wd) return;
986    wd->selection_enabled = enabled;
987    if (enabled)
988      _select(wd, wd->selected_it);
989    else
990      _unselect(wd, wd->selected_it);
991 }
992
993 /**
994  * Get day selection state
995  *
996  * Enabled by default. If disabled, the user can select months, but not days.
997  * It should be used if you won't need such selection for the widget usage.
998  *
999  * @param obj The calendar object
1000  * @return True if day selection is enabled, or false otherwise. It will
1001  * return false if it can't get widget data.
1002  *
1003  * @ingroup Calendar
1004  */
1005 EAPI Eina_Bool
1006 elm_calendar_day_selection_enabled_get(const Evas_Object *obj)
1007 {
1008    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
1009    Widget_Data *wd = elm_widget_data_get(obj);
1010    if (!wd) return EINA_FALSE;
1011    return wd->selection_enabled;
1012 }
1013
1014 /**
1015  * Set selected time
1016  *
1017  * Set the time selected, changing the displayed month if needed.
1018  * Selected time changes when the user changes the month or select a day.
1019  *
1020  * @param obj The calendar object
1021  * @param selected_time A tm struct to represent the selected date
1022  *
1023  * @ingroup Calendar
1024  */
1025 EAPI void
1026 elm_calendar_selected_time_set(Evas_Object *obj, struct tm *selected_time)
1027 {
1028    ELM_CHECK_WIDTYPE(obj, widtype);
1029    Widget_Data *wd = elm_widget_data_get(obj);
1030    if (!wd) return;
1031
1032    EINA_SAFETY_ON_NULL_RETURN(selected_time);
1033    wd->selected_time = *selected_time;
1034    _populate(obj);
1035    return;
1036 }
1037
1038 /**
1039  * Get selected time
1040  *
1041  * Get the time selected by the user.
1042  * Selected time changes when the user changes the month or select a day.
1043  *
1044  * @param obj The calendar object
1045  * @param selected_time A tm struct to represent the selected date
1046  * @return It will return false if it can't get widget data, or true otherwise
1047  *
1048  * @ingroup Calendar
1049  */
1050 EAPI Eina_Bool
1051 elm_calendar_selected_time_get(const Evas_Object *obj, struct tm *selected_time)
1052 {
1053    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
1054    Widget_Data *wd = elm_widget_data_get(obj);
1055    if (!wd) return EINA_FALSE;
1056    EINA_SAFETY_ON_NULL_RETURN_VAL(selected_time, EINA_FALSE);
1057    *selected_time = wd->selected_time;
1058    return EINA_TRUE;
1059 }
1060
1061 /**
1062  * Set a function to format the string that will be used to display
1063  * month - year
1064  *
1065  * By default it uses strftime with "%B %Y" format string.
1066  * It should allocate the memory that will be used by the string,
1067  * that will be freed by the widget after usage.
1068  * A pointer to the string and a pointer to the time struct will be provided.
1069  *
1070  * Example:
1071  * @code
1072  * static char *
1073  * _format_month_year(struct tm *stime)
1074  * {
1075  *    char buf[32];
1076  *    if (!strftime(buf, sizeof(buf), "%B %Y", stime)) return NULL;
1077  *    return strdup(buf);
1078  * }
1079  * elm_calendar_format_function_set(calendar, _format_month_year);
1080  * @endcode
1081  *
1082  * @param obj The calendar object
1083  * @param format_function Function to set the month-year string given
1084  * the selected date
1085  *
1086  * @ingroup Calendar
1087  */
1088 EAPI void
1089 elm_calendar_format_function_set(Evas_Object *obj, char * (*format_function) (struct tm *stime))
1090 {
1091    ELM_CHECK_WIDTYPE(obj, widtype);
1092    Widget_Data *wd = elm_widget_data_get(obj);
1093    if (!wd) return;
1094    wd->format_func = format_function;
1095 }
1096
1097 /**
1098  * Add a new mark to the calendar
1099  *
1100  * Add a mark that will be drawn in the calendar respecting the insertion time
1101  * and periodicity. It will emit the type as signal to the widget theme.
1102  * By default, it supports "holiday" and "checked", but it can be extended.
1103  *
1104  * It won't immediately update the calendar, drawing the marks. For this, call
1105  * elm_calendar_marks_draw().
1106  *
1107  * Example
1108  * @code
1109  * struct tm selected_time;
1110  * time_t current_time;
1111  *
1112  * current_time = time(NULL) + 5 * 84600;
1113  * localtime_r(&current_time, &selected_time);
1114  * elm_calendar_mark_add(cal, "holiday", selected_time, ELM_CALENDAR_ANNUALLY);
1115  *
1116  * current_time = time(NULL) + 1 * 84600;
1117  * localtime_r(&current_time, &selected_time);
1118  * elm_calendar_mark_add(cal, "checked", selected_time, ELM_CALENDAR_UNIQUE);
1119  *
1120  * elm_calendar_marks_draw(cal);
1121  * @endcode
1122  *
1123  * @param obj The calendar object
1124  * @param mark_type A string used to define the type of mark. It will be
1125  * emitted to the theme, that should display a related modification on these
1126  * days representation.
1127  * @param mark_time A time struct to represent the date of inclusion of the
1128  * mark. For marks that repeats it will just be displayed after the inclusion
1129  * date in the calendar.
1130  * @param repeat Repeat the event following this periodicity. Can be a unique
1131  * mark (that don't repeat), daily, weekly, monthly or annually.
1132  *
1133  * @return The created mark or NULL upon failure
1134  *
1135  * @ingroup Calendar
1136  */
1137 EAPI Elm_Calendar_Mark *
1138 elm_calendar_mark_add(Evas_Object *obj, const char *mark_type, struct tm *mark_time, Elm_Calendar_Mark_Repeat repeat)
1139 {
1140    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
1141    Widget_Data *wd = elm_widget_data_get(obj);
1142    Elm_Calendar_Mark *mark;
1143    if (!wd) return NULL;
1144
1145    mark = _mark_new(obj, mark_type, mark_time, repeat);
1146    wd->marks = eina_list_append(wd->marks, mark);
1147    mark->node = eina_list_last(wd->marks);
1148    return mark;
1149 }
1150
1151 /**
1152  * Delete mark from the calendar.
1153  *
1154  * @param mark The mark to delete
1155  *
1156  * @ingroup Calendar
1157  */
1158 EAPI void
1159 elm_calendar_mark_del(Elm_Calendar_Mark *mark)
1160 {
1161    Evas_Object *obj;
1162    Widget_Data *wd;
1163
1164    EINA_SAFETY_ON_NULL_RETURN(mark);
1165
1166    obj = mark->obj;
1167    wd = elm_widget_data_get(obj);
1168    if (!wd) return;
1169
1170    wd->marks = eina_list_remove_list(wd->marks, mark->node);
1171    _mark_free(mark);
1172 }
1173
1174 /**
1175  * Remove all the marks from the calendar
1176  *
1177  * @param obj The calendar object
1178  *
1179  * @ingroup Calendar
1180  */
1181 EAPI void
1182 elm_calendar_marks_clear(Evas_Object *obj)
1183 {
1184    ELM_CHECK_WIDTYPE(obj, widtype);
1185    Widget_Data *wd = elm_widget_data_get(obj);
1186    Elm_Calendar_Mark *mark;
1187
1188    if (!wd) return;
1189    EINA_LIST_FREE(wd->marks, mark)
1190       _mark_free(mark);
1191 }
1192
1193 /**
1194  * Returns a list of all the calendar marks.
1195  *
1196  * @param obj The calendar object
1197  * @return An Eina_List* of the calendar marks, or NULL on failure
1198  *
1199  * @ingroup Calendar
1200  */
1201 EAPI const Eina_List *
1202 elm_calendar_marks_get(const Evas_Object *obj)
1203 {
1204    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
1205    Widget_Data *wd = elm_widget_data_get(obj);
1206    if (!wd) return NULL;
1207    return wd->marks;
1208 }
1209
1210 /**
1211  * Draw calendar marks.
1212  *
1213  * Should be used after adding, removing or clearing marks.
1214  * It will go through the entire marks list updating the calendar
1215  * (not a cheap function). So if lots of marks will be added,
1216  * add all the marks and then call this function.
1217  *
1218  * When the month is changed marks will be drawed.
1219  *
1220  * @param obj The calendar object
1221  *
1222  * @ingroup Calendar
1223  */
1224 EAPI void
1225 elm_calendar_marks_draw(Evas_Object *obj)
1226 {
1227    ELM_CHECK_WIDTYPE(obj, widtype);
1228    Widget_Data *wd = elm_widget_data_get(obj);
1229    if (!wd) return;
1230    _populate(obj);
1231 }
1232
1233 /**
1234  * Set a text color to the saturday color.
1235  *
1236  * Deprecated. use elm_calendar_mark_add() instead like:
1237  *
1238  * @code
1239  * struct tm t = { 0, 0, 12, 6, 0, 0, 5, 5, -1 };
1240  * elm_calendar_mark_add(obj, "sat", &t, ELM_CALENDAR_WEEKLY);
1241  * @endcode
1242  *
1243  * @param obj The calendar object
1244  * @param pos The text position
1245  *
1246  * @ingroup Calendar
1247  */
1248 EINA_DEPRECATED EAPI void
1249 elm_calendar_text_saturday_color_set(Evas_Object *obj, int pos)
1250 {
1251    ELM_CHECK_WIDTYPE(obj, widtype);
1252    Widget_Data *wd = elm_widget_data_get(obj);
1253    if (!wd) return;
1254    _text_day_color_set(wd, DAY_SATURDAY, pos);
1255 }
1256
1257 /**
1258  * Set a text color to the sunday color.
1259  *
1260  * Deprecated. use elm_calendar_mark_add() instead like:
1261  *
1262  * @code
1263  * struct tm t = { 0, 0, 12, 7, 0, 0, 6, 6, -1 };
1264  * elm_calendar_mark_add(obj, "sun", &t, ELM_CALENDAR_WEEKLY);
1265  * @endcode
1266  *
1267  * @param obj The calendar object
1268  * @param pos The text position
1269  *
1270  * @ingroup Calendar
1271  */
1272 EINA_DEPRECATED EAPI void
1273 elm_calendar_text_sunday_color_set(Evas_Object *obj, int pos)
1274 {
1275    ELM_CHECK_WIDTYPE(obj, widtype);
1276    Widget_Data *wd = elm_widget_data_get(obj);
1277    if (!wd) return;
1278    _text_day_color_set(wd, DAY_SUNDAY, pos);
1279 }
1280
1281 /**
1282  * Set a text color to the weekday color.
1283  *
1284  * Deprecated. use elm_calendar_mark_add() instead like:
1285  *
1286  * @code
1287  * struct tm t = { 0, 0, 12, 1, 0, 0, 0, 0, -1 };
1288  *
1289  * elm_calendar_mark_add(obj, "week", &t, ELM_CALENDAR_WEEKLY); // monday
1290  * t.tm_tm_mday++; t.tm_wday++; t.tm_yday++;
1291  * elm_calendar_mark_add(obj, "week", &t, ELM_CALENDAR_WEEKLY); // tuesday
1292  * t.tm_tm_mday++; t.tm_wday++; t.tm_yday++;
1293  * elm_calendar_mark_add(obj, "week", &t, ELM_CALENDAR_WEEKLY); // wednesday
1294  * t.tm_tm_mday++; t.tm_wday++; t.tm_yday++;
1295  * elm_calendar_mark_add(obj, "week", &t, ELM_CALENDAR_WEEKLY); // thursday
1296  * t.tm_tm_mday++; t.tm_wday++; t.tm_yday++;
1297  * elm_calendar_mark_add(obj, "week", &t, ELM_CALENDAR_WEEKLY); // friday
1298  * @endcode
1299  *
1300  * @param obj The calendar object
1301  * @param pos The text position
1302  *
1303  * @ingroup Calendar
1304  */
1305 EINA_DEPRECATED EAPI void
1306 elm_calendar_text_weekday_color_set(Evas_Object *obj, int pos)
1307 {
1308    ELM_CHECK_WIDTYPE(obj, widtype);
1309    Widget_Data *wd = elm_widget_data_get(obj);
1310    if (!wd) return;
1311    _text_day_color_set(wd, DAY_WEEKDAY, pos);
1312 }