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