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