[focus] When an object has focus_chain manager, it should return its own next object...
[framework/uifw/elementary.git] / src / lib / elm_calendar.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3 #include "elm_widget_calendar.h"
4
5 EAPI const char ELM_CALENDAR_SMART_NAME[] = "elm_calendar";
6
7 static const char SIG_CHANGED[] = "changed";
8 static const char SIG_DISPLAY_CHANGED[] = "display,changed";
9
10 static const Evas_Smart_Cb_Description _smart_callbacks[] = {
11    {SIG_CHANGED, ""},
12    {SIG_DISPLAY_CHANGED, ""},
13    {NULL, NULL}
14 };
15
16 /* Should not be translated, it's used if we failed
17  * getting from locale. */
18 static const char *_days_abbrev[] =
19 {
20    "Sun", "Mon", "Tue", "Wed",
21    "Thu", "Fri", "Sat"
22 };
23
24 static int _days_in_month[2][12] =
25 {
26    {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
27    {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
28 };
29
30 EVAS_SMART_SUBCLASS_NEW
31   (ELM_CALENDAR_SMART_NAME, _elm_calendar, Elm_Calendar_Smart_Class,
32   Elm_Layout_Smart_Class, elm_layout_smart_class_get, _smart_callbacks);
33
34 static Elm_Calendar_Mark *
35 _mark_new(Evas_Object *obj,
36           const char *mark_type,
37           struct tm *mark_time,
38           Elm_Calendar_Mark_Repeat_Type repeat)
39 {
40    Elm_Calendar_Mark *mark;
41
42    mark = calloc(1, sizeof(Elm_Calendar_Mark));
43    if (!mark) return NULL;
44    mark->obj = obj;
45    mark->mark_type = eina_stringshare_add(mark_type);
46    mark->mark_time = *mark_time;
47    mark->repeat = repeat;
48
49    return mark;
50 }
51
52 static inline void
53 _mark_free(Elm_Calendar_Mark *mark)
54 {
55    eina_stringshare_del(mark->mark_type);
56    free(mark);
57 }
58
59 static void
60 _elm_calendar_smart_sizing_eval(Evas_Object *obj)
61 {
62    Evas_Coord minw = -1, minh = -1;
63
64    ELM_CALENDAR_DATA_GET(obj, sd);
65
66    elm_coords_finger_size_adjust(8, &minw, ELM_DAY_LAST, &minh);
67    edje_object_size_min_restricted_calc
68      (ELM_WIDGET_DATA(sd)->resize_obj, &minw, &minh, minw, minh);
69    evas_object_size_hint_min_set(obj, minw, minh);
70    evas_object_size_hint_max_set(obj, -1, -1);
71 }
72
73 static inline int
74 _maxdays_get(struct tm *selected_time)
75 {
76    int month, year;
77
78    month = selected_time->tm_mon;
79    year = selected_time->tm_year + 1900;
80
81    return _days_in_month
82           [((!(year % 4)) && ((!(year % 400)) || (year % 100)))][month];
83 }
84
85 static inline void
86 _unselect(Evas_Object *obj,
87           int selected)
88 {
89    char emission[32];
90
91    snprintf(emission, sizeof(emission), "cit_%i,unselected", selected);
92    elm_layout_signal_emit(obj, emission, "elm");
93 }
94
95 static inline void
96 _select(Evas_Object *obj,
97         int selected)
98 {
99    char emission[32];
100
101    ELM_CALENDAR_DATA_GET(obj, sd);
102
103    sd->selected_it = selected;
104    snprintf(emission, sizeof(emission), "cit_%i,selected", selected);
105    elm_layout_signal_emit(obj, emission, "elm");
106 }
107
108 static inline void
109 _not_today(Elm_Calendar_Smart_Data *sd)
110 {
111    char emission[32];
112
113    snprintf(emission, sizeof(emission), "cit_%i,not_today", sd->today_it);
114    elm_layout_signal_emit(ELM_WIDGET_DATA(sd)->obj, emission, "elm");
115    sd->today_it = -1;
116 }
117
118 static inline void
119 _today(Elm_Calendar_Smart_Data *sd,
120        int it)
121 {
122    char emission[32];
123
124    snprintf(emission, sizeof(emission), "cit_%i,today", it);
125    elm_layout_signal_emit(ELM_WIDGET_DATA(sd)->obj, emission, "elm");
126    sd->today_it = it;
127 }
128
129 static char *
130 _format_month_year(struct tm *selected_time)
131 {
132    char buf[32];
133
134    if (!strftime(buf, sizeof(buf), E_("%B %Y"), selected_time)) return NULL;
135    return strdup(buf);
136 }
137
138 static inline void
139 _cit_mark(Evas_Object *cal,
140           int cit,
141           const char *mtype)
142 {
143    char sign[64];
144
145    snprintf(sign, sizeof(sign), "cit_%i,%s", cit, mtype);
146    elm_layout_signal_emit(cal, sign, "elm");
147 }
148
149 static inline int
150 _weekday_get(int first_week_day,
151              int day)
152 {
153    return (day + first_week_day - 1) % ELM_DAY_LAST;
154 }
155
156 // EINA_DEPRECATED
157 static void
158 _text_day_color_update(Elm_Calendar_Smart_Data *sd,
159                        int pos)
160 {
161    char emission[32];
162
163    switch (sd->day_color[pos])
164      {
165       case DAY_WEEKDAY:
166         snprintf(emission, sizeof(emission), "cit_%i,weekday", pos);
167         break;
168
169       case DAY_SATURDAY:
170         snprintf(emission, sizeof(emission), "cit_%i,saturday", pos);
171         break;
172
173       case DAY_SUNDAY:
174         snprintf(emission, sizeof(emission), "cit_%i,sunday", pos);
175         break;
176
177       default:
178         return;
179      }
180
181    elm_layout_signal_emit(ELM_WIDGET_DATA(sd)->obj, emission, "elm");
182 }
183
184 static void
185 _set_month_year(Elm_Calendar_Smart_Data *sd)
186 {
187    char *buf;
188
189    /* Set selected month */
190    buf = sd->format_func(&sd->shown_time);
191    if (buf)
192      {
193         elm_layout_text_set(ELM_WIDGET_DATA(sd)->obj, "month_text", buf);
194         free(buf);
195      }
196    else elm_layout_text_set(ELM_WIDGET_DATA(sd)->obj, "month_text", "");
197 }
198
199 static char *
200 _access_info_cb(void *data __UNUSED__, Evas_Object *obj)
201 {
202    char *ret;
203    Eina_Strbuf *buf;
204    buf = eina_strbuf_new();
205
206    eina_strbuf_append_printf(buf, "day %s", elm_widget_access_info_get(obj));
207
208    ret = eina_strbuf_string_steal(buf);
209    eina_strbuf_free(buf);
210    return ret;
211 }
212
213 static void
214 _access_calendar_item_register(Evas_Object *obj)
215 {
216    int maxdays, day, i;
217    char day_s[3], pname[14];
218    Evas_Object *ao;
219
220    ELM_CALENDAR_DATA_GET(obj, sd);
221
222    day = 0;
223    maxdays = _maxdays_get(&sd->shown_time);
224    for (i = 0; i < 42; i++)
225      {
226         if ((!day) && (i == sd->first_day_it)) day = 1;
227         if ((day) && (day <= maxdays))
228           {
229              snprintf(pname, sizeof(pname), "cit_%i.access", i);
230
231              ao = _elm_access_edje_object_part_object_register
232                         (obj, elm_layout_edje_get(obj), pname);
233              _elm_access_text_set(_elm_access_object_get(ao),
234                          ELM_ACCESS_TYPE, E_("calendar item"));
235              _elm_access_callback_set(_elm_access_object_get(ao),
236                            ELM_ACCESS_INFO, _access_info_cb, NULL);
237
238              snprintf(day_s, sizeof(day_s), "%i", day++);
239              elm_widget_access_info_set(ao, (const char*)day_s);
240           }
241         else
242           {
243              snprintf(pname, sizeof(pname), "cit_%i.access", i);
244              _elm_access_edje_object_part_object_unregister
245                      (obj, elm_layout_edje_get(obj), pname);
246           }
247      }
248 }
249
250 static void
251 _access_calendar_spinner_register(Evas_Object *obj)
252 {
253    Evas_Object *po;
254    Elm_Access_Info *ai;
255    ELM_CALENDAR_DATA_GET(obj, sd);
256
257    // decrement button
258    sd->dec_btn_access = _elm_access_edje_object_part_object_register
259                             (obj, elm_layout_edje_get(obj), "left_bt");
260    ai = _elm_access_object_get(sd->dec_btn_access);
261    _elm_access_text_set(ai, ELM_ACCESS_TYPE, E_("calendar decrement button"));
262
263    // increment button
264    sd->inc_btn_access = _elm_access_edje_object_part_object_register
265                             (obj, elm_layout_edje_get(obj), "right_bt");
266    ai = _elm_access_object_get(sd->inc_btn_access);
267    _elm_access_text_set(ai, ELM_ACCESS_TYPE, E_("calendar increment button"));
268
269    // month text
270    sd->month_access = _elm_access_edje_object_part_object_register
271                           (obj, elm_layout_edje_get(obj), "month_text");
272    ai = _elm_access_object_get(sd->month_access);
273    _elm_access_text_set(ai, ELM_ACCESS_TYPE, E_("calendar month"));
274
275    po = (Evas_Object *)edje_object_part_object_get
276           (elm_layout_edje_get(obj), "month_text");
277    evas_object_pass_events_set(po, EINA_FALSE);
278
279 }
280
281 static void
282 _access_calendar_register(Evas_Object *obj)
283 {
284    _access_calendar_spinner_register(obj);
285    _access_calendar_item_register(obj);
286 }
287
288 static void
289 _populate(Evas_Object *obj)
290 {
291    int maxdays, day, mon, yr, i;
292    Elm_Calendar_Mark *mark;
293    char part[12], day_s[3];
294    struct tm first_day;
295    Eina_List *l;
296    Eina_Bool last_row = EINA_TRUE;
297
298    ELM_CALENDAR_DATA_GET(obj, sd);
299
300    elm_layout_freeze(obj);
301
302    if (sd->today_it > 0) _not_today(sd);
303
304    maxdays = _maxdays_get(&sd->shown_time);
305    mon = sd->shown_time.tm_mon;
306    yr = sd->shown_time.tm_year;
307
308    _set_month_year(sd);
309
310    /* Set days */
311    day = 0;
312    first_day = sd->shown_time;
313    first_day.tm_mday = 1;
314    mktime(&first_day);
315
316    // Layout of the calendar is changed for removing the unfilled last row.
317    if (first_day.tm_wday < (int)sd->first_week_day)
318      sd->first_day_it = first_day.tm_wday + ELM_DAY_LAST - sd->first_week_day;
319    else
320      sd->first_day_it = first_day.tm_wday - sd->first_week_day;
321
322    if ((35 - sd->first_day_it) > (maxdays - 1)) last_row = EINA_FALSE;
323
324    if (!last_row)
325      {
326         char emission[32];
327
328         for (i = 0; i < 5; i++)
329           {
330              snprintf(emission, sizeof(emission), "cseph_%i,row_hide", i);
331              elm_layout_signal_emit(obj, emission, "elm");
332           }
333         snprintf(emission, sizeof(emission), "cseph_%i,row_invisible", 5);
334         elm_layout_signal_emit(obj, emission, "elm");
335         for (i = 0; i < 35; i++)
336           {
337              snprintf(emission, sizeof(emission), "cit_%i,cell_expanded", i);
338              elm_layout_signal_emit(obj, emission, "elm");
339           }
340         for (i = 35; i < 42; i++)
341           {
342              snprintf(emission, sizeof(emission), "cit_%i,cell_invisible", i);
343              elm_layout_signal_emit(obj, emission, "elm");
344           }
345      }
346    else
347      {
348         char emission[32];
349
350         for (i = 0; i < 6; i++)
351           {
352              snprintf(emission, sizeof(emission), "cseph_%i,row_show", i);
353              elm_layout_signal_emit(obj, emission, "elm");
354           }
355         for (i = 0; i < 42; i++)
356           {
357              snprintf(emission, sizeof(emission), "cit_%i,cell_default", i);
358              elm_layout_signal_emit(obj, emission, "elm");
359           }
360      }
361
362    for (i = 0; i < 42; i++)
363      {
364         _text_day_color_update(sd, i); // EINA_DEPRECATED
365         if ((!day) && (i == sd->first_day_it)) day = 1;
366
367         if ((day == sd->current_time.tm_mday)
368             && (mon == sd->current_time.tm_mon)
369             && (yr == sd->current_time.tm_year))
370           _today(sd, i);
371
372         if (day == sd->selected_time.tm_mday)
373           {
374              if ((sd->selected_it > -1) && (sd->selected_it != i))
375                _unselect(obj, sd->selected_it);
376
377              if (sd->select_mode == ELM_CALENDAR_SELECT_MODE_ONDEMAND)
378                {
379                   if ((mon == sd->selected_time.tm_mon)
380                       && (yr == sd->selected_time.tm_year)
381                       && (sd->selected))
382                     {
383                        _select(obj, i);
384                     }
385                }
386              else if (sd->select_mode != ELM_CALENDAR_SELECT_MODE_NONE)
387                {
388                   _select(obj, i);
389                }
390           }
391
392         if ((day) && (day <= maxdays))
393           snprintf(day_s, sizeof(day_s), "%i", day++);
394         else
395           day_s[0] = 0;
396
397         snprintf(part, sizeof(part), "cit_%i.text", i);
398         elm_layout_text_set(obj, part, day_s);
399
400         /* Clear previous marks */
401         _cit_mark(obj, i, "clear");
402      }
403
404    // ACCESS
405    if ((_elm_config->access_mode != ELM_ACCESS_MODE_OFF))
406      _access_calendar_item_register(obj);
407
408    /* Set marks */
409    EINA_LIST_FOREACH(sd->marks, l, mark)
410      {
411         struct tm *mtime = &mark->mark_time;
412         int month = sd->shown_time.tm_mon;
413         int year = sd->shown_time.tm_year;
414         int mday_it = mtime->tm_mday + sd->first_day_it - 1;
415
416         switch (mark->repeat)
417           {
418            case ELM_CALENDAR_UNIQUE:
419              if ((mtime->tm_mon == month) && (mtime->tm_year == year))
420                _cit_mark(obj, mday_it, mark->mark_type);
421              break;
422
423            case ELM_CALENDAR_DAILY:
424              if (((mtime->tm_year == year) && (mtime->tm_mon < month)) ||
425                  (mtime->tm_year < year))
426                day = 1;
427              else if ((mtime->tm_year == year) && (mtime->tm_mon == month))
428                day = mtime->tm_mday;
429              else
430                break;
431              for (; day <= maxdays; day++)
432                _cit_mark(obj, day + sd->first_day_it - 1,
433                          mark->mark_type);
434              break;
435
436            case ELM_CALENDAR_WEEKLY:
437              if (((mtime->tm_year == year) && (mtime->tm_mon < month)) ||
438                  (mtime->tm_year < year))
439                day = 1;
440              else if ((mtime->tm_year == year) && (mtime->tm_mon == month))
441                day = mtime->tm_mday;
442              else
443                break;
444              for (; day <= maxdays; day++)
445                if (mtime->tm_wday == _weekday_get(sd->first_day_it, day))
446                  _cit_mark(obj, day + sd->first_day_it - 1,
447                            mark->mark_type);
448              break;
449
450            case ELM_CALENDAR_MONTHLY:
451              if (((mtime->tm_year < year) ||
452                   ((mtime->tm_year == year) && (mtime->tm_mon <= month))) &&
453                  (mtime->tm_mday <= maxdays))
454                _cit_mark(obj, mday_it, mark->mark_type);
455              break;
456
457            case ELM_CALENDAR_ANNUALLY:
458              if ((mtime->tm_year <= year) && (mtime->tm_mon == month) &&
459                  (mtime->tm_mday <= maxdays))
460                _cit_mark(obj, mday_it, mark->mark_type);
461              break;
462
463            case ELM_CALENDAR_LAST_DAY_OF_MONTH:
464              if (((mtime->tm_year < year) ||
465                   ((mtime->tm_year == year) && (mtime->tm_mon <= month))))
466                _cit_mark(obj, maxdays + sd->first_day_it - 1, mark->mark_type);
467              break;
468           }
469      }
470
471    elm_layout_thaw(obj);
472 }
473
474 static void
475 _set_headers(Evas_Object *obj)
476 {
477    static char part[] = "ch_0.text";
478    int i;
479    ELM_CALENDAR_DATA_GET(obj, sd);
480
481    elm_layout_freeze(obj);
482
483    for (i = 0; i < ELM_DAY_LAST; i++)
484      {
485         part[3] = i + '0';
486         elm_layout_text_set
487           (obj, part, sd->weekdays[(i + sd->first_week_day) % ELM_DAY_LAST]);
488      }
489
490    elm_layout_thaw(obj);
491 }
492
493 static Eina_Bool
494 _elm_calendar_smart_theme(Evas_Object *obj)
495 {
496    if (!ELM_WIDGET_CLASS(_elm_calendar_parent_sc)->theme(obj))
497      return EINA_FALSE;
498
499    evas_object_smart_changed(obj);
500
501    return EINA_TRUE;
502 }
503
504 /* Set correct tm_wday and tm_yday after other fields changes*/
505 static inline void
506 _fix_selected_time(Elm_Calendar_Smart_Data *sd)
507 {
508    if (sd->selected_time.tm_mon != sd->shown_time.tm_mon)
509      sd->selected_time.tm_mon = sd->shown_time.tm_mon;
510    if (sd->selected_time.tm_year != sd->shown_time.tm_year)
511      sd->selected_time.tm_year = sd->shown_time.tm_year;
512    mktime(&sd->selected_time);
513 }
514
515 static Eina_Bool
516 _update_month(Evas_Object *obj,
517               int delta)
518 {
519    struct tm time_check;
520    int maxdays;
521
522    ELM_CALENDAR_DATA_GET(obj, sd);
523
524    /* check if it's a valid time. for 32 bits, year greater than 2037 is not */
525    time_check = sd->shown_time;
526    time_check.tm_mon += delta;
527    if (mktime(&time_check) == -1)
528      return EINA_FALSE;
529
530    sd->shown_time.tm_mon += delta;
531    if (sd->shown_time.tm_mon < 0)
532      {
533         if (sd->shown_time.tm_year == sd->year_min)
534           {
535              sd->shown_time.tm_mon++;
536              return EINA_FALSE;
537           }
538         sd->shown_time.tm_mon = 11;
539         sd->shown_time.tm_year--;
540      }
541    else if (sd->shown_time.tm_mon > 11)
542      {
543         if (sd->shown_time.tm_year == sd->year_max)
544           {
545              sd->shown_time.tm_mon--;
546              return EINA_FALSE;
547           }
548         sd->shown_time.tm_mon = 0;
549         sd->shown_time.tm_year++;
550      }
551
552    if ((sd->select_mode != ELM_CALENDAR_SELECT_MODE_ONDEMAND)
553        && (sd->select_mode != ELM_CALENDAR_SELECT_MODE_NONE))
554      {
555         maxdays = _maxdays_get(&sd->shown_time);
556         if (sd->selected_time.tm_mday > maxdays)
557           sd->selected_time.tm_mday = maxdays;
558
559         _fix_selected_time(sd);
560         evas_object_smart_callback_call(obj, SIG_CHANGED, NULL);
561      }
562    evas_object_smart_callback_call(obj, SIG_DISPLAY_CHANGED, NULL);
563
564    return EINA_TRUE;
565 }
566
567 static Eina_Bool
568 _spin_value(void *data)
569 {
570    ELM_CALENDAR_DATA_GET(data, sd);
571
572    if (_update_month(data, sd->spin_speed))
573      evas_object_smart_changed(data);
574
575    sd->interval = sd->interval / 1.05;
576    ecore_timer_interval_set(sd->spin, sd->interval);
577
578    return ECORE_CALLBACK_RENEW;
579 }
580
581 static void
582 _button_inc_start(void *data,
583                   Evas_Object *obj __UNUSED__,
584                   const char *emission __UNUSED__,
585                   const char *source __UNUSED__)
586 {
587    ELM_CALENDAR_DATA_GET(data, sd);
588
589    sd->interval = sd->first_interval;
590    sd->spin_speed = 1;
591    if (sd->spin) ecore_timer_del(sd->spin);
592    sd->spin = ecore_timer_add(sd->interval, _spin_value, data);
593
594    _spin_value(data);
595 }
596
597 static void
598 _button_dec_start(void *data,
599                   Evas_Object *obj __UNUSED__,
600                   const char *emission __UNUSED__,
601                   const char *source __UNUSED__)
602 {
603    ELM_CALENDAR_DATA_GET(data, sd);
604
605    sd->interval = sd->first_interval;
606    sd->spin_speed = -1;
607    if (sd->spin) ecore_timer_del(sd->spin);
608    sd->spin = ecore_timer_add(sd->interval, _spin_value, data);
609
610    _spin_value(data);
611 }
612
613 static void
614 _button_stop(void *data,
615              Evas_Object *obj __UNUSED__,
616              const char *emission __UNUSED__,
617              const char *source __UNUSED__)
618 {
619    ELM_CALENDAR_DATA_GET(data, sd);
620
621    sd->interval = sd->first_interval;
622    if (sd->spin) ecore_timer_del(sd->spin);
623    sd->spin = NULL;
624 }
625
626 static int
627 _get_item_day(Evas_Object *obj,
628               int selected_it)
629 {
630    int day;
631
632    ELM_CALENDAR_DATA_GET(obj, sd);
633
634    day = selected_it - sd->first_day_it + 1;
635    if ((day < 0) || (day > _maxdays_get(&sd->shown_time)))
636      return 0;
637
638    return day;
639 }
640
641 static void
642 _update_sel_it(Evas_Object *obj,
643                int sel_it)
644 {
645    int day;
646
647    ELM_CALENDAR_DATA_GET(obj, sd);
648
649    if (sd->select_mode == ELM_CALENDAR_SELECT_MODE_NONE)
650      return;
651
652    day = _get_item_day(obj, sel_it);
653    if (!day)
654      return;
655
656    _unselect(obj, sd->selected_it);
657    if (!sd->selected)
658      sd->selected = EINA_TRUE;
659
660    sd->selected_time.tm_mday = day;
661    _fix_selected_time(sd);
662    _select(obj, sel_it);
663    evas_object_smart_callback_call(obj, SIG_CHANGED, NULL);
664 }
665
666 static void
667 _day_selected(void *data,
668               Evas_Object *obj __UNUSED__,
669               const char *emission __UNUSED__,
670               const char *source)
671 {
672    int sel_it;
673
674    ELM_CALENDAR_DATA_GET(data, sd);
675
676    if (sd->select_mode == ELM_CALENDAR_SELECT_MODE_NONE)
677      return;
678
679    sel_it = atoi(source);
680
681    _update_sel_it(data, sel_it);
682 }
683
684 static inline int
685 _time_to_next_day(struct tm *t)
686 {
687    return ((((24 - t->tm_hour) * 60) - t->tm_min) * 60) - t->tm_sec;
688 }
689
690 static Eina_Bool
691 _update_cur_date(void *data)
692 {
693    time_t current_time;
694    int t, day;
695    ELM_CALENDAR_DATA_GET(data, sd);
696
697    if (sd->today_it > 0) _not_today(sd);
698
699    current_time = time(NULL);
700    localtime_r(&current_time, &sd->current_time);
701    t = _time_to_next_day(&sd->current_time);
702    ecore_timer_interval_set(sd->update_timer, t);
703
704    if ((sd->current_time.tm_mon != sd->shown_time.tm_mon) ||
705        (sd->current_time.tm_year != sd->shown_time.tm_year))
706      return ECORE_CALLBACK_RENEW;
707
708    day = sd->current_time.tm_mday + sd->first_day_it - 1;
709    _today(sd, day);
710
711    return ECORE_CALLBACK_RENEW;
712 }
713
714 static Eina_Bool
715 _elm_calendar_smart_event(Evas_Object *obj,
716                           Evas_Object *src __UNUSED__,
717                           Evas_Callback_Type type,
718                           void *event_info)
719 {
720    Evas_Event_Key_Down *ev = event_info;
721
722    ELM_CALENDAR_DATA_GET(obj, sd);
723
724    if (type != EVAS_CALLBACK_KEY_DOWN) return EINA_FALSE;
725    if (elm_widget_disabled_get(obj)) return EINA_FALSE;
726
727    if ((!strcmp(ev->keyname, "Prior")) ||
728        ((!strcmp(ev->keyname, "KP_Prior")) && (!ev->string)))
729      {
730         if (_update_month(obj, -1)) _populate(obj);
731      }
732    else if ((!strcmp(ev->keyname, "Next")) ||
733             ((!strcmp(ev->keyname, "KP_Next")) && (!ev->string)))
734      {
735         if (_update_month(obj, 1)) _populate(obj);
736      }
737    else if ((sd->select_mode != ELM_CALENDAR_SELECT_MODE_NONE)
738             && ((sd->select_mode != ELM_CALENDAR_SELECT_MODE_ONDEMAND)
739                 || (sd->selected)))
740      {
741         if ((!strcmp(ev->keyname, "Left")) ||
742             ((!strcmp(ev->keyname, "KP_Left")) && (!ev->string)))
743           {
744              if ((sd->select_mode != ELM_CALENDAR_SELECT_MODE_ONDEMAND)
745                  || ((sd->shown_time.tm_year == sd->selected_time.tm_year)
746                      && (sd->shown_time.tm_mon == sd->selected_time.tm_mon)))
747                _update_sel_it(obj, sd->selected_it - 1);
748           }
749         else if ((!strcmp(ev->keyname, "Right")) ||
750                  ((!strcmp(ev->keyname, "KP_Right")) && (!ev->string)))
751           {
752              if ((sd->select_mode != ELM_CALENDAR_SELECT_MODE_ONDEMAND)
753                  || ((sd->shown_time.tm_year == sd->selected_time.tm_year)
754                      && (sd->shown_time.tm_mon == sd->selected_time.tm_mon)))
755                _update_sel_it(obj, sd->selected_it + 1);
756           }
757         else if ((!strcmp(ev->keyname, "Up")) ||
758                  ((!strcmp(ev->keyname, "KP_Up")) && (!ev->string)))
759           {
760              if ((sd->select_mode != ELM_CALENDAR_SELECT_MODE_ONDEMAND)
761                  || ((sd->shown_time.tm_year == sd->selected_time.tm_year)
762                      && (sd->shown_time.tm_mon == sd->selected_time.tm_mon)))
763                _update_sel_it(obj, sd->selected_it - ELM_DAY_LAST);
764           }
765         else if ((!strcmp(ev->keyname, "Down")) ||
766                  ((!strcmp(ev->keyname, "KP_Down")) && (!ev->string)))
767           {
768              if ((sd->select_mode != ELM_CALENDAR_SELECT_MODE_ONDEMAND)
769                  || ((sd->shown_time.tm_year == sd->selected_time.tm_year)
770                      && (sd->shown_time.tm_mon == sd->selected_time.tm_mon)))
771                _update_sel_it(obj, sd->selected_it + ELM_DAY_LAST);
772           }
773         else return EINA_FALSE;
774      }
775    else return EINA_FALSE;
776
777    return EINA_TRUE;
778 }
779
780 static void
781 _elm_calendar_smart_calculate(Evas_Object *obj)
782 {
783    elm_layout_freeze(obj);
784
785    _set_headers(obj);
786    _populate(obj);
787
788    elm_layout_thaw(obj);   
789 }
790
791 static void
792 _elm_calendar_smart_add(Evas_Object *obj)
793 {
794    time_t weekday = 259200; /* Just the first sunday since epoch */
795    time_t current_time;
796    int i, t;
797
798    EVAS_SMART_DATA_ALLOC(obj, Elm_Calendar_Smart_Data);
799
800    ELM_WIDGET_CLASS(_elm_calendar_parent_sc)->base.add(obj);
801
802    priv->first_interval = 0.85;
803    priv->year_min = 2;
804    priv->year_max = -1;
805    priv->today_it = -1;
806    priv->selected_it = -1;
807    priv->first_day_it = -1;
808    priv->format_func = _format_month_year;
809    priv->marks = NULL;
810    priv->selectable = (~(ELM_CALENDAR_SELECTABLE_NONE));
811
812    edje_object_signal_callback_add
813      (ELM_WIDGET_DATA(priv)->resize_obj, "elm,action,increment,start", "*",
814      _button_inc_start, obj);
815    edje_object_signal_callback_add
816      (ELM_WIDGET_DATA(priv)->resize_obj, "elm,action,decrement,start", "*",
817      _button_dec_start, obj);
818    edje_object_signal_callback_add
819      (ELM_WIDGET_DATA(priv)->resize_obj, "elm,action,stop", "*",
820      _button_stop, obj);
821    edje_object_signal_callback_add
822      (ELM_WIDGET_DATA(priv)->resize_obj, "elm,action,selected", "*",
823      _day_selected, obj);
824
825    for (i = 0; i < ELM_DAY_LAST; i++)
826      {
827         /* FIXME: I'm not aware of a known max, so if it fails,
828          * just make it larger. :| */
829         char buf[20];
830         /* I don't know of a better way of doing it */
831         if (strftime(buf, sizeof(buf), "%a", gmtime(&weekday)))
832           {
833              priv->weekdays[i] = eina_stringshare_add(buf);
834           }
835         else
836           {
837              /* If we failed getting day, get a default value */
838              priv->weekdays[i] = _days_abbrev[i];
839              WRN("Failed getting weekday name for '%s' from locale.",
840                  _days_abbrev[i]);
841           }
842         weekday += 86400; /* Advance by a day */
843      }
844
845    current_time = time(NULL);
846    localtime_r(&current_time, &priv->shown_time);
847    priv->current_time = priv->shown_time;
848    priv->selected_time = priv->shown_time;
849    t = _time_to_next_day(&priv->current_time);
850    priv->update_timer = ecore_timer_add(t, _update_cur_date, obj);
851
852    elm_widget_can_focus_set(obj, EINA_TRUE);
853
854    elm_layout_theme_set(obj, "calendar", "base", elm_object_style_get(obj));
855    evas_object_smart_changed(obj);
856
857    // ACCESS
858    if ((_elm_config->access_mode != ELM_ACCESS_MODE_OFF))
859       _access_calendar_spinner_register(obj);
860 }
861
862 static void
863 _elm_calendar_smart_del(Evas_Object *obj)
864 {
865    int i;
866    Elm_Calendar_Mark *mark;
867    ELM_CALENDAR_DATA_GET(obj, sd);
868
869    if (sd->spin) ecore_timer_del(sd->spin);
870    if (sd->update_timer) ecore_timer_del(sd->update_timer);
871
872    if (sd->marks)
873      {
874         EINA_LIST_FREE (sd->marks, mark)
875           {
876              _mark_free(mark);
877           }
878      }
879
880    for (i = 0; i < ELM_DAY_LAST; i++)
881      eina_stringshare_del(sd->weekdays[i]);
882
883    ELM_WIDGET_CLASS(_elm_calendar_parent_sc)->base.del(obj);
884 }
885
886 static Eina_Bool
887 _elm_calendar_smart_focus_next(const Evas_Object *obj,
888                                Elm_Focus_Direction dir,
889                                Evas_Object **next)
890 {
891    int maxdays, day, i;
892    Eina_List *items = NULL;
893    Evas_Object *ao;
894    Evas_Object *po;
895
896    ELM_CALENDAR_CHECK(obj) EINA_FALSE;
897    ELM_CALENDAR_DATA_GET(obj, sd);
898
899    items = eina_list_append(items, sd->month_access);
900    items = eina_list_append(items, sd->dec_btn_access);
901    items = eina_list_append(items, sd->inc_btn_access);
902
903    day = 0;
904    maxdays = _maxdays_get(&sd->shown_time);
905    for (i = 0; i < 42; i++)
906      {
907         if ((!day) && (i == sd->first_day_it)) day = 1;
908         if ((day) && (day <= maxdays))
909           {
910              char pname[14];
911              snprintf(pname, sizeof(pname), "cit_%i.access", i);
912
913              po = (Evas_Object *)edje_object_part_object_get
914                            (elm_layout_edje_get(obj), pname);
915              ao = evas_object_data_get(po, "_part_access_obj");
916              items = eina_list_append(items, ao);
917           }
918      }
919
920    return elm_widget_focus_list_next_get
921             (obj, items, eina_list_data_get, dir, next);
922 }
923
924 static void
925 _access_obj_process(Evas_Object *obj, Eina_Bool is_access)
926 {
927    int maxdays, day, i;
928
929    ELM_CALENDAR_DATA_GET(obj, sd);
930
931    if (is_access)
932      _access_calendar_register(obj);
933    else
934      {
935         day = 0;
936         maxdays = _maxdays_get(&sd->shown_time);
937         for (i = 0; i < 42; i++)
938           {
939              if ((!day) && (i == sd->first_day_it)) day = 1;
940              if ((day) && (day <= maxdays))
941                {
942                   char pname[14];
943                   snprintf(pname, sizeof(pname), "cit_%i.access", i);
944
945                   _elm_access_edje_object_part_object_unregister
946                           (obj, elm_layout_edje_get(obj), pname);
947                }
948           }
949
950         if (sd->dec_btn_access)
951           _elm_access_edje_object_part_object_unregister
952             (obj, elm_layout_edje_get(obj), "left_bt");
953         if (sd->inc_btn_access)
954           _elm_access_edje_object_part_object_unregister
955             (obj, elm_layout_edje_get(obj), "right_bt");
956         if (sd->month_access)
957           _elm_access_edje_object_part_object_unregister
958             (obj, elm_layout_edje_get(obj), "month_text");
959      }
960 }
961
962 static void
963 _access_hook(Evas_Object *obj, Eina_Bool is_access)
964 {
965    ELM_CALENDAR_CHECK(obj);
966    ELM_CALENDAR_DATA_GET(obj, sd);
967
968    if (is_access)
969      ELM_WIDGET_CLASS(ELM_WIDGET_DATA(sd)->api)->focus_next =
970      _elm_calendar_smart_focus_next;
971    else
972      ELM_WIDGET_CLASS(ELM_WIDGET_DATA(sd)->api)->focus_next = NULL;
973    _access_obj_process(obj, is_access);
974 }
975
976 static void
977 _elm_calendar_smart_set_user(Elm_Calendar_Smart_Class *sc)
978 {
979    ELM_WIDGET_CLASS(sc)->base.add = _elm_calendar_smart_add;
980    ELM_WIDGET_CLASS(sc)->base.del = _elm_calendar_smart_del;
981    ELM_WIDGET_CLASS(sc)->base.calculate = _elm_calendar_smart_calculate;
982
983    ELM_WIDGET_CLASS(sc)->theme = _elm_calendar_smart_theme;
984    ELM_WIDGET_CLASS(sc)->event = _elm_calendar_smart_event;
985
986    /* not a 'focus chain manager' */
987    ELM_WIDGET_CLASS(sc)->focus_next = NULL;
988    ELM_WIDGET_CLASS(sc)->focus_direction = NULL;
989
990    ELM_LAYOUT_CLASS(sc)->sizing_eval = _elm_calendar_smart_sizing_eval;
991
992    // ACCESS
993    if (_elm_config->access_mode != ELM_ACCESS_MODE_OFF)
994      ELM_WIDGET_CLASS(sc)->focus_next = _elm_calendar_smart_focus_next;
995
996    ELM_WIDGET_CLASS(sc)->access = _access_hook;
997 }
998
999 EAPI const Elm_Calendar_Smart_Class *
1000 elm_calendar_smart_class_get(void)
1001 {
1002    static Elm_Calendar_Smart_Class _sc =
1003      ELM_CALENDAR_SMART_CLASS_INIT_NAME_VERSION(ELM_CALENDAR_SMART_NAME);
1004    static const Elm_Calendar_Smart_Class *class = NULL;
1005    Evas_Smart_Class *esc = (Evas_Smart_Class *)&_sc;
1006
1007    if (class)
1008      return class;
1009
1010    _elm_calendar_smart_set(&_sc);
1011    esc->callbacks = _smart_callbacks;
1012    class = &_sc;
1013
1014    return class;
1015 }
1016
1017 EAPI Evas_Object *
1018 elm_calendar_add(Evas_Object *parent)
1019 {
1020    Evas_Object *obj;
1021
1022    EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL);
1023
1024    obj = elm_widget_add(_elm_calendar_smart_class_new(), parent);
1025    if (!obj) return NULL;
1026
1027    if (!elm_widget_sub_object_add(parent, obj))
1028      ERR("could not add %p as sub object of %p", obj, parent);
1029
1030    return obj;
1031 }
1032
1033 EAPI void
1034 elm_calendar_weekdays_names_set(Evas_Object *obj,
1035                                 const char *weekdays[])
1036 {
1037    int i;
1038
1039    ELM_CALENDAR_CHECK(obj);
1040    ELM_CALENDAR_DATA_GET(obj, sd);
1041    EINA_SAFETY_ON_NULL_RETURN(weekdays);
1042
1043    for (i = 0; i < ELM_DAY_LAST; i++)
1044      {
1045         eina_stringshare_replace(&sd->weekdays[i], weekdays[i]);
1046      }
1047
1048    evas_object_smart_changed(obj);
1049 }
1050
1051 EAPI const char **
1052 elm_calendar_weekdays_names_get(const Evas_Object *obj)
1053 {
1054    ELM_CALENDAR_CHECK(obj) NULL;
1055    ELM_CALENDAR_DATA_GET_OR_RETURN_VAL(obj, sd, NULL);
1056
1057    return sd->weekdays;
1058 }
1059
1060 EAPI void
1061 elm_calendar_interval_set(Evas_Object *obj,
1062                           double interval)
1063 {
1064    ELM_CALENDAR_CHECK(obj);
1065    ELM_CALENDAR_DATA_GET(obj, sd);
1066
1067    sd->first_interval = interval;
1068 }
1069
1070 EAPI double
1071 elm_calendar_interval_get(const Evas_Object *obj)
1072 {
1073    ELM_CALENDAR_CHECK(obj) 0.0;
1074    ELM_CALENDAR_DATA_GET_OR_RETURN_VAL(obj, sd, 0.0);
1075
1076    return sd->first_interval;
1077 }
1078
1079 EAPI void
1080 elm_calendar_min_max_year_set(Evas_Object *obj,
1081                               int min,
1082                               int max)
1083 {
1084    ELM_CALENDAR_CHECK(obj);
1085    ELM_CALENDAR_DATA_GET(obj, sd);
1086
1087    min -= 1900;
1088    max -= 1900;
1089    if ((sd->year_min == min) && (sd->year_max == max)) return;
1090    sd->year_min = min > 2 ? min : 2;
1091    if (max > sd->year_min)
1092      sd->year_max = max;
1093    else
1094      sd->year_max = sd->year_min;
1095    if (sd->shown_time.tm_year > sd->year_max)
1096      sd->shown_time.tm_year = sd->year_max;
1097    if (sd->shown_time.tm_year < sd->year_min)
1098      sd->shown_time.tm_year = sd->year_min;
1099    evas_object_smart_changed(obj);
1100 }
1101
1102 EAPI void
1103 elm_calendar_min_max_year_get(const Evas_Object *obj,
1104                               int *min,
1105                               int *max)
1106 {
1107    ELM_CALENDAR_CHECK(obj);
1108    ELM_CALENDAR_DATA_GET(obj, sd);
1109
1110    if (min) *min = sd->year_min + 1900;
1111    if (max) *max = sd->year_max + 1900;
1112 }
1113
1114 EINA_DEPRECATED EAPI void
1115 elm_calendar_day_selection_disabled_set(Evas_Object *obj,
1116                                         Eina_Bool disabled)
1117 {
1118    ELM_CALENDAR_CHECK(obj);
1119
1120    if (disabled)
1121      elm_calendar_select_mode_set(obj, ELM_CALENDAR_SELECT_MODE_NONE);
1122    else
1123      elm_calendar_select_mode_set(obj, ELM_CALENDAR_SELECT_MODE_DEFAULT);
1124 }
1125
1126 EINA_DEPRECATED EAPI Eina_Bool
1127 elm_calendar_day_selection_disabled_get(const Evas_Object *obj)
1128 {
1129    ELM_CALENDAR_CHECK(obj) EINA_FALSE;
1130    ELM_CALENDAR_DATA_GET_OR_RETURN_VAL(obj, sd, EINA_FALSE);
1131
1132    return !!(sd->select_mode == ELM_CALENDAR_SELECT_MODE_NONE);
1133 }
1134
1135 EAPI void
1136 elm_calendar_selected_time_set(Evas_Object *obj,
1137                                struct tm *selected_time)
1138 {
1139    ELM_CALENDAR_CHECK(obj);
1140    ELM_CALENDAR_DATA_GET(obj, sd);
1141    EINA_SAFETY_ON_NULL_RETURN(selected_time);
1142
1143    if (sd->selectable & ELM_CALENDAR_SELECTABLE_YEAR)
1144      sd->selected_time.tm_year = selected_time->tm_year;
1145    if (sd->selectable & ELM_CALENDAR_SELECTABLE_MONTH)
1146      sd->selected_time.tm_mon = selected_time->tm_mon;
1147    if (sd->selectable & ELM_CALENDAR_SELECTABLE_DAY)
1148        {
1149           sd->selected_time.tm_mday = selected_time->tm_mday;
1150           if (!sd->selected)
1151             sd->selected = EINA_TRUE;
1152        }
1153    else if (sd->select_mode != ELM_CALENDAR_SELECT_MODE_ONDEMAND)
1154      {
1155         if (!sd->selected)
1156           sd->selected = EINA_TRUE;
1157      }
1158    if (sd->selected_time.tm_year != sd->shown_time.tm_year)
1159      sd->shown_time.tm_year = sd->selected_time.tm_year;
1160    if (sd->selected_time.tm_mon != sd->shown_time.tm_mon)
1161      sd->shown_time.tm_mon = sd->selected_time.tm_mon;
1162
1163    _fix_selected_time(sd);
1164
1165    evas_object_smart_changed(obj);
1166 }
1167
1168 EAPI Eina_Bool
1169 elm_calendar_selected_time_get(const Evas_Object *obj,
1170                                struct tm *selected_time)
1171 {
1172    ELM_CALENDAR_CHECK(obj) EINA_FALSE;
1173    ELM_CALENDAR_DATA_GET_OR_RETURN_VAL(obj, sd, EINA_FALSE);
1174    EINA_SAFETY_ON_NULL_RETURN_VAL(selected_time, EINA_FALSE);
1175
1176    if ((sd->select_mode == ELM_CALENDAR_SELECT_MODE_ONDEMAND)
1177        && (!sd->selected))
1178      return EINA_FALSE;
1179    *selected_time = sd->selected_time;
1180
1181    return EINA_TRUE;
1182 }
1183
1184 EAPI void
1185 elm_calendar_format_function_set(Evas_Object *obj,
1186                                  Elm_Calendar_Format_Cb format_function)
1187 {
1188    ELM_CALENDAR_CHECK(obj);
1189    ELM_CALENDAR_DATA_GET(obj, sd);
1190
1191    sd->format_func = format_function;
1192    _set_month_year(sd);
1193 }
1194
1195 EAPI Elm_Calendar_Mark *
1196 elm_calendar_mark_add(Evas_Object *obj,
1197                       const char *mark_type,
1198                       struct tm *mark_time,
1199                       Elm_Calendar_Mark_Repeat_Type repeat)
1200 {
1201    ELM_CALENDAR_CHECK(obj) NULL;
1202    ELM_CALENDAR_DATA_GET_OR_RETURN_VAL(obj, sd, NULL);
1203
1204    Elm_Calendar_Mark *mark;
1205
1206    mark = _mark_new(obj, mark_type, mark_time, repeat);
1207    sd->marks = eina_list_append(sd->marks, mark);
1208    mark->node = eina_list_last(sd->marks);
1209
1210    return mark;
1211 }
1212
1213 EAPI void
1214 elm_calendar_mark_del(Elm_Calendar_Mark *mark)
1215 {
1216    EINA_SAFETY_ON_NULL_RETURN(mark);
1217    ELM_CALENDAR_CHECK(mark->obj);
1218    ELM_CALENDAR_DATA_GET(mark->obj, sd);
1219
1220    sd->marks = eina_list_remove_list(sd->marks, mark->node);
1221    _mark_free(mark);
1222 }
1223
1224 EAPI void
1225 elm_calendar_marks_clear(Evas_Object *obj)
1226 {
1227    ELM_CALENDAR_CHECK(obj);
1228    ELM_CALENDAR_DATA_GET(obj, sd);
1229
1230    Elm_Calendar_Mark *mark;
1231
1232    EINA_LIST_FREE (sd->marks, mark)
1233      _mark_free(mark);
1234 }
1235
1236 EAPI const Eina_List *
1237 elm_calendar_marks_get(const Evas_Object *obj)
1238 {
1239    ELM_CALENDAR_CHECK(obj) NULL;
1240    ELM_CALENDAR_DATA_GET(obj, sd);
1241
1242    return sd->marks;
1243 }
1244
1245 EAPI void
1246 elm_calendar_marks_draw(Evas_Object *obj)
1247 {
1248    ELM_CALENDAR_CHECK(obj);
1249
1250    evas_object_smart_changed(obj);
1251 }
1252
1253 EAPI void
1254 elm_calendar_first_day_of_week_set(Evas_Object *obj,
1255                                    Elm_Calendar_Weekday day)
1256 {
1257    ELM_CALENDAR_CHECK(obj);
1258    ELM_CALENDAR_DATA_GET(obj, sd);
1259
1260    if (day >= ELM_DAY_LAST) return;
1261    if (sd->first_week_day != day)
1262      {
1263         sd->first_week_day = day;
1264         evas_object_smart_changed(obj);
1265      }
1266 }
1267
1268 EAPI Elm_Calendar_Weekday
1269 elm_calendar_first_day_of_week_get(const Evas_Object *obj)
1270 {
1271    ELM_CALENDAR_CHECK(obj) - 1;
1272    ELM_CALENDAR_DATA_GET(obj, sd);
1273
1274    return sd->first_week_day;
1275 }
1276
1277 EAPI void
1278 elm_calendar_select_mode_set(Evas_Object *obj,
1279                              Elm_Calendar_Select_Mode mode)
1280 {
1281    ELM_CALENDAR_CHECK(obj);
1282    ELM_CALENDAR_DATA_GET(obj, sd);
1283
1284    if ((mode <= ELM_CALENDAR_SELECT_MODE_ONDEMAND)
1285        && (sd->select_mode != mode))
1286      {
1287         sd->select_mode = mode;
1288         if (sd->select_mode == ELM_CALENDAR_SELECT_MODE_ONDEMAND)
1289           sd->selected = EINA_FALSE;
1290         if ((sd->select_mode == ELM_CALENDAR_SELECT_MODE_ALWAYS)
1291             || (sd->select_mode == ELM_CALENDAR_SELECT_MODE_DEFAULT))
1292           _select(obj, sd->selected_it);
1293         else
1294           _unselect(obj, sd->selected_it);
1295      }
1296 }
1297
1298 EAPI Elm_Calendar_Select_Mode
1299 elm_calendar_select_mode_get(const Evas_Object *obj)
1300 {
1301    ELM_CALENDAR_CHECK(obj) - 1;
1302    ELM_CALENDAR_DATA_GET(obj, sd);
1303
1304    return sd->select_mode;
1305 }
1306
1307 EAPI void
1308 elm_calendar_selectable_set(Evas_Object *obj, Elm_Calendar_Selectable selectable)
1309 {
1310    ELM_CALENDAR_CHECK(obj);
1311    ELM_CALENDAR_DATA_GET(obj, sd);
1312
1313    sd->selectable = selectable;
1314 }
1315
1316 EAPI Elm_Calendar_Selectable
1317 elm_calendar_selectable_get(const Evas_Object *obj)
1318 {
1319    ELM_CALENDAR_CHECK(obj) -1;
1320    ELM_CALENDAR_DATA_GET_OR_RETURN_VAL(obj, sd, -1);
1321
1322    return sd->selectable;
1323 }
1324
1325 EAPI Eina_Bool
1326 elm_calendar_displayed_time_get(const Evas_Object *obj, struct tm *displayed_time)
1327 {
1328    EINA_SAFETY_ON_NULL_RETURN_VAL(displayed_time, EINA_FALSE);
1329    ELM_CALENDAR_CHECK(obj) EINA_FALSE;
1330    ELM_CALENDAR_DATA_GET_OR_RETURN_VAL(obj, sd, EINA_FALSE);
1331
1332    *displayed_time = sd->shown_time;
1333    return EINA_TRUE;
1334 }
1335