diff options
author | WooHyun Jung <wh0705.jung@samsung.com> | 2017-10-25 16:42:39 +0900 |
---|---|---|
committer | Jean-Philippe Andre <jp.andre@samsung.com> | 2017-10-25 16:50:01 +0900 |
commit | 9442f4687cd2eaa202c41254c12f553c463386f5 (patch) | |
tree | 4027a64fb882d607958bc15f0f7620ef495cb361 /src/lib/elementary | |
parent | 40589a92a012e67003b116cd6c559a264b42bc87 (diff) |
efl_ui_calendar: create new efl_ui_calendar
Summary:
This calendar widget will support basic functionality of calendar.
I've separated this widget from elm_calendar since elm_calendar had
lots of unuseful things inside.
Reviewers: jpeg, singh.amitesh, cedric, CHAN, Jaehyun_Cho
Subscribers: cedric, jpeg
Differential Revision: https://phab.enlightenment.org/D5346
Diffstat (limited to 'src/lib/elementary')
-rw-r--r-- | src/lib/elementary/Elementary.h | 1 | ||||
-rw-r--r-- | src/lib/elementary/efl_ui_calendar.c | 1214 | ||||
-rw-r--r-- | src/lib/elementary/efl_ui_calendar.eo | 149 | ||||
-rw-r--r-- | src/lib/elementary/efl_ui_calendar.h | 57 | ||||
-rw-r--r-- | src/lib/elementary/efl_ui_calendar_common.h | 21 | ||||
-rw-r--r-- | src/lib/elementary/efl_ui_calendar_private.h | 65 |
6 files changed, 1507 insertions, 0 deletions
diff --git a/src/lib/elementary/Elementary.h b/src/lib/elementary/Elementary.h index c1065ec754..4d22b69b83 100644 --- a/src/lib/elementary/Elementary.h +++ b/src/lib/elementary/Elementary.h | |||
@@ -197,6 +197,7 @@ EAPI extern Elm_Version *elm_version; | |||
197 | #include <elm_button.h> | 197 | #include <elm_button.h> |
198 | #include <elm_cache.h> | 198 | #include <elm_cache.h> |
199 | #include <elm_calendar.h> | 199 | #include <elm_calendar.h> |
200 | #include <efl_ui_calendar.h> | ||
200 | #include <elm_check.h> | 201 | #include <elm_check.h> |
201 | #include <elm_clock.h> | 202 | #include <elm_clock.h> |
202 | #include <elm_cnp.h> | 203 | #include <elm_cnp.h> |
diff --git a/src/lib/elementary/efl_ui_calendar.c b/src/lib/elementary/efl_ui_calendar.c new file mode 100644 index 0000000000..66ca500bd4 --- /dev/null +++ b/src/lib/elementary/efl_ui_calendar.c | |||
@@ -0,0 +1,1214 @@ | |||
1 | #ifdef HAVE_CONFIG_H | ||
2 | # include "elementary_config.h" | ||
3 | #endif | ||
4 | |||
5 | #define ELM_INTERFACE_ATSPI_ACCESSIBLE_PROTECTED | ||
6 | #define ELM_INTERFACE_ATSPI_WIDGET_ACTION_PROTECTED | ||
7 | |||
8 | #include <Elementary.h> | ||
9 | #include "elm_priv.h" | ||
10 | #include "efl_ui_calendar_private.h" | ||
11 | |||
12 | #define MY_CLASS EFL_UI_CALENDAR_CLASS | ||
13 | |||
14 | #define MY_CLASS_NAME "Efl.Ui.Calendar" | ||
15 | #define MY_CLASS_PFX efl_ui_calendar | ||
16 | |||
17 | #define EFL_UI_CALENDAR_BUTTON_LEFT "elm,calendar,button,left" | ||
18 | #define EFL_UI_CALENDAR_BUTTON_RIGHT "elm,calendar,button,right" | ||
19 | #define EFL_UI_CALENDAR_BUTTON_YEAR_LEFT "elm,calendar,button_year,left" | ||
20 | #define EFL_UI_CALENDAR_BUTTON_YEAR_RIGHT "elm,calendar,button_year,right" | ||
21 | |||
22 | static const char SIG_CHANGED[] = "changed"; | ||
23 | |||
24 | static const Evas_Smart_Cb_Description _smart_callbacks[] = { | ||
25 | {SIG_CHANGED, ""}, | ||
26 | {SIG_WIDGET_LANG_CHANGED, ""}, /**< handled by elm_widget */ | ||
27 | {SIG_WIDGET_ACCESS_CHANGED, ""}, /**< handled by elm_widget */ | ||
28 | {SIG_LAYOUT_FOCUSED, ""}, /**< handled by elm_layout */ | ||
29 | {SIG_LAYOUT_UNFOCUSED, ""}, /**< handled by elm_layout */ | ||
30 | {NULL, NULL} | ||
31 | }; | ||
32 | |||
33 | static void | ||
34 | _button_widget_month_inc_start_click(void *data, | ||
35 | Evas_Object *obj EINA_UNUSED, | ||
36 | void *event_info EINA_UNUSED); | ||
37 | static void | ||
38 | _button_widget_month_inc_start(void *data, | ||
39 | Evas_Object *obj EINA_UNUSED, | ||
40 | void *event_info EINA_UNUSED); | ||
41 | static void | ||
42 | _button_widget_month_dec_start_click(void *data, | ||
43 | Evas_Object *obj EINA_UNUSED, | ||
44 | void *event_info EINA_UNUSED); | ||
45 | static void | ||
46 | _button_widget_month_dec_start(void *data, | ||
47 | Evas_Object *obj EINA_UNUSED, | ||
48 | void *event_info EINA_UNUSED); | ||
49 | |||
50 | static Eina_Bool _key_action_activate(Evas_Object *obj, const char *params); | ||
51 | |||
52 | static const Elm_Action key_actions[] = { | ||
53 | {"activate", _key_action_activate}, | ||
54 | {NULL, NULL} | ||
55 | }; | ||
56 | |||
57 | /* Should not be translated, it's used if we failed | ||
58 | * getting from locale. */ | ||
59 | static const char *_days_abbrev[] = | ||
60 | { | ||
61 | "Sun", "Mon", "Tue", "Wed", | ||
62 | "Thu", "Fri", "Sat" | ||
63 | }; | ||
64 | |||
65 | static int _days_in_month[2][12] = | ||
66 | { | ||
67 | {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, | ||
68 | {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} | ||
69 | }; | ||
70 | |||
71 | static Eina_Bool _efl_ui_calendar_smart_focus_next_enable = EINA_FALSE; | ||
72 | |||
73 | EOLIAN static void | ||
74 | _efl_ui_calendar_elm_layout_sizing_eval(Eo *obj, Efl_Ui_Calendar_Data *_pd EINA_UNUSED) | ||
75 | { | ||
76 | Evas_Coord minw = -1, minh = -1; | ||
77 | EFL_UI_CALENDAR_DATA_GET(obj, sd); | ||
78 | ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd); | ||
79 | |||
80 | if (sd->filling) return; | ||
81 | // 7x8 (1 month+year, days, 6 dates.) | ||
82 | elm_coords_finger_size_adjust(7, &minw, 8, &minh); | ||
83 | edje_object_size_min_restricted_calc | ||
84 | (wd->resize_obj, &minw, &minh, minw, minh); | ||
85 | evas_object_size_hint_min_set(obj, minw, minh); | ||
86 | evas_object_size_hint_max_set(obj, -1, -1); | ||
87 | } | ||
88 | |||
89 | // Get the max day number for each month | ||
90 | static inline int | ||
91 | _maxdays_get(struct tm *date, int month_offset) | ||
92 | { | ||
93 | int month, year; | ||
94 | |||
95 | month = (date->tm_mon + month_offset) % 12; | ||
96 | year = date->tm_year + 1900; | ||
97 | |||
98 | if (month < 0) month += 12; | ||
99 | |||
100 | return _days_in_month | ||
101 | [((!(year % 4)) && ((!(year % 400)) || (year % 100)))][month]; | ||
102 | } | ||
103 | |||
104 | static inline void | ||
105 | _unselect(Evas_Object *obj, | ||
106 | int selected) | ||
107 | { | ||
108 | char emission[32]; | ||
109 | |||
110 | snprintf(emission, sizeof(emission), "cit_%d,unselected", selected); | ||
111 | elm_layout_signal_emit(obj, emission, "elm"); | ||
112 | } | ||
113 | |||
114 | static inline void | ||
115 | _select(Evas_Object *obj, | ||
116 | int selected) | ||
117 | { | ||
118 | char emission[32]; | ||
119 | |||
120 | EFL_UI_CALENDAR_DATA_GET(obj, sd); | ||
121 | |||
122 | sd->focused_it = sd->selected_it = selected; | ||
123 | snprintf(emission, sizeof(emission), "cit_%d,selected", selected); | ||
124 | elm_layout_signal_emit(obj, emission, "elm"); | ||
125 | } | ||
126 | |||
127 | static inline void | ||
128 | _not_today(Efl_Ui_Calendar_Data *sd) | ||
129 | { | ||
130 | char emission[32]; | ||
131 | |||
132 | snprintf(emission, sizeof(emission), "cit_%d,not_today", sd->today_it); | ||
133 | elm_layout_signal_emit(sd->obj, emission, "elm"); | ||
134 | sd->today_it = -1; | ||
135 | } | ||
136 | |||
137 | static inline void | ||
138 | _today(Efl_Ui_Calendar_Data *sd, | ||
139 | int it) | ||
140 | { | ||
141 | char emission[32]; | ||
142 | |||
143 | snprintf(emission, sizeof(emission), "cit_%d,today", it); | ||
144 | elm_layout_signal_emit(sd->obj, emission, "elm"); | ||
145 | sd->today_it = it; | ||
146 | } | ||
147 | |||
148 | static inline void | ||
149 | _enable(Efl_Ui_Calendar_Data *sd, | ||
150 | int it) | ||
151 | { | ||
152 | char emission[32]; | ||
153 | |||
154 | snprintf(emission, sizeof(emission), "cit_%d,enable", it); | ||
155 | elm_layout_signal_emit(sd->obj, emission, "elm"); | ||
156 | } | ||
157 | |||
158 | static inline void | ||
159 | _disable(Efl_Ui_Calendar_Data *sd, | ||
160 | int it) | ||
161 | { | ||
162 | char emission[32]; | ||
163 | |||
164 | snprintf(emission, sizeof(emission), "cit_%d,disable", it); | ||
165 | elm_layout_signal_emit(sd->obj, emission, "elm"); | ||
166 | } | ||
167 | |||
168 | static char * | ||
169 | _format_month_year(struct tm *date) | ||
170 | { | ||
171 | return eina_strftime(E_("%B %Y"), date); | ||
172 | } | ||
173 | |||
174 | static void | ||
175 | _set_month_year(Efl_Ui_Calendar_Data *sd) | ||
176 | { | ||
177 | char *buf; | ||
178 | |||
179 | sd->filling = EINA_TRUE; | ||
180 | |||
181 | buf = sd->format_func(&sd->shown_date); | ||
182 | |||
183 | if (buf) | ||
184 | { | ||
185 | elm_layout_text_set(sd->obj, "month_text", buf); | ||
186 | free(buf); | ||
187 | } | ||
188 | else elm_layout_text_set(sd->obj, "month_text", ""); | ||
189 | |||
190 | sd->filling = EINA_FALSE; | ||
191 | } | ||
192 | |||
193 | static char * | ||
194 | _access_info_cb(void *data EINA_UNUSED, Evas_Object *obj) | ||
195 | { | ||
196 | char *ret; | ||
197 | Eina_Strbuf *buf; | ||
198 | buf = eina_strbuf_new(); | ||
199 | |||
200 | eina_strbuf_append_printf(buf, "day %s", elm_widget_access_info_get(obj)); | ||
201 | |||
202 | ret = eina_strbuf_string_steal(buf); | ||
203 | eina_strbuf_free(buf); | ||
204 | return ret; | ||
205 | } | ||
206 | |||
207 | static void | ||
208 | _access_calendar_item_register(Evas_Object *obj) | ||
209 | { | ||
210 | unsigned int maxdays, i; | ||
211 | char day_s[13], pname[14]; | ||
212 | unsigned day = 0; | ||
213 | Evas_Object *ao; | ||
214 | |||
215 | EFL_UI_CALENDAR_DATA_GET(obj, sd); | ||
216 | |||
217 | maxdays = _maxdays_get(&sd->shown_date, 0); | ||
218 | for (i = 0; i < 42; i++) | ||
219 | { | ||
220 | if ((!day) && (i == sd->first_day_it)) day = 1; | ||
221 | if ((day) && (day <= maxdays)) | ||
222 | { | ||
223 | snprintf(pname, sizeof(pname), "cit_%d.access", i); | ||
224 | |||
225 | ao = _elm_access_edje_object_part_object_register | ||
226 | (obj, elm_layout_edje_get(obj), pname); | ||
227 | _elm_access_text_set(_elm_access_info_get(ao), | ||
228 | ELM_ACCESS_TYPE, E_("calendar item")); | ||
229 | _elm_access_callback_set(_elm_access_info_get(ao), | ||
230 | ELM_ACCESS_INFO, _access_info_cb, NULL); | ||
231 | |||
232 | snprintf(day_s, sizeof(day_s), "%d", (int) (day++)); | ||
233 | elm_widget_access_info_set(ao, (const char*)day_s); | ||
234 | } | ||
235 | else | ||
236 | { | ||
237 | snprintf(pname, sizeof(pname), "cit_%d.access", i); | ||
238 | _elm_access_edje_object_part_object_unregister | ||
239 | (obj, elm_layout_edje_get(obj), pname); | ||
240 | } | ||
241 | } | ||
242 | } | ||
243 | |||
244 | static void | ||
245 | _access_calendar_spinner_register(Evas_Object *obj) | ||
246 | { | ||
247 | Evas_Object *po; | ||
248 | Elm_Access_Info *ai; | ||
249 | EFL_UI_CALENDAR_DATA_GET(obj, sd); | ||
250 | |||
251 | if (!sd->dec_btn_month) | ||
252 | sd->dec_btn_month = _elm_access_edje_object_part_object_register | ||
253 | (obj, elm_layout_edje_get(obj), "left_bt"); | ||
254 | ai = _elm_access_info_get(sd->dec_btn_month); | ||
255 | _elm_access_text_set(ai, ELM_ACCESS_TYPE, E_("calendar decrement month button")); | ||
256 | |||
257 | if (!sd->inc_btn_month) | ||
258 | sd->inc_btn_month = _elm_access_edje_object_part_object_register | ||
259 | (obj, elm_layout_edje_get(obj), "right_bt"); | ||
260 | ai = _elm_access_info_get(sd->inc_btn_month); | ||
261 | _elm_access_text_set(ai, ELM_ACCESS_TYPE, E_("calendar increment month button")); | ||
262 | |||
263 | sd->month_access = _elm_access_edje_object_part_object_register | ||
264 | (obj, elm_layout_edje_get(obj), "text_month"); | ||
265 | ai = _elm_access_info_get(sd->month_access); | ||
266 | _elm_access_text_set(ai, ELM_ACCESS_TYPE, E_("calendar month")); | ||
267 | |||
268 | ai = _elm_access_info_get(sd->year_access); | ||
269 | _elm_access_text_set(ai, ELM_ACCESS_TYPE, E_("calendar year")); | ||
270 | |||
271 | po = (Evas_Object *)edje_object_part_object_get | ||
272 | (elm_layout_edje_get(obj), "month_text"); | ||
273 | evas_object_pass_events_set(po, EINA_FALSE); | ||
274 | } | ||
275 | |||
276 | static void | ||
277 | _access_calendar_register(Evas_Object *obj) | ||
278 | { | ||
279 | _access_calendar_spinner_register(obj); | ||
280 | _access_calendar_item_register(obj); | ||
281 | } | ||
282 | |||
283 | static void | ||
284 | _populate(Evas_Object *obj) | ||
285 | { | ||
286 | int maxdays, prev_month_maxdays, day, mon, yr, i; | ||
287 | char part[12], day_s[3]; | ||
288 | struct tm first_day; | ||
289 | |||
290 | EFL_UI_CALENDAR_DATA_GET(obj, sd); | ||
291 | |||
292 | elm_layout_freeze(obj); | ||
293 | |||
294 | sd->filling = EINA_FALSE; | ||
295 | if (sd->today_it > 0) _not_today(sd); | ||
296 | |||
297 | maxdays = _maxdays_get(&sd->shown_date, 0); | ||
298 | prev_month_maxdays = _maxdays_get(&sd->shown_date, -1); | ||
299 | mon = sd->shown_date.tm_mon; | ||
300 | yr = sd->shown_date.tm_year; | ||
301 | |||
302 | _set_month_year(sd); | ||
303 | sd->filling = EINA_TRUE; | ||
304 | |||
305 | /* Set days */ | ||
306 | day = 0; | ||
307 | first_day = sd->shown_date; | ||
308 | first_day.tm_mday = 1; | ||
309 | if (mktime(&first_day) == -1) | ||
310 | { | ||
311 | ERR("mktime can not give week day for this month properly. Please check year or month is proper."); | ||
312 | return; | ||
313 | } | ||
314 | |||
315 | // Layout of the calendar is changed for removing the unfilled last row. | ||
316 | if (first_day.tm_wday < (int)sd->first_week_day) | ||
317 | sd->first_day_it = first_day.tm_wday + ELM_DAY_LAST - sd->first_week_day; | ||
318 | else | ||
319 | sd->first_day_it = first_day.tm_wday - sd->first_week_day; | ||
320 | |||
321 | for (i = 0; i < 42; i++) | ||
322 | { | ||
323 | if ((!day) && (i == sd->first_day_it)) day = 1; | ||
324 | |||
325 | if ((day == sd->current_date.tm_mday) | ||
326 | && (mon == sd->current_date.tm_mon) | ||
327 | && (yr == sd->current_date.tm_year)) | ||
328 | _today(sd, i); | ||
329 | |||
330 | if (day == sd->date.tm_mday) | ||
331 | { | ||
332 | if ((sd->selected_it > -1) && (sd->selected_it != i)) | ||
333 | _unselect(obj, sd->selected_it); | ||
334 | |||
335 | if ((mon == sd->date.tm_mon) && (yr == sd->date.tm_year)) | ||
336 | _select(obj, i); | ||
337 | } | ||
338 | |||
339 | if ((day) && (day <= maxdays)) | ||
340 | { | ||
341 | if (((yr == sd->date_min.tm_year) && (mon == sd->date_min.tm_mon) && (day < sd->date_min.tm_mday)) | ||
342 | || ((yr == sd->date_max.tm_year) && (mon == sd->date_max.tm_mon) && (day > sd->date_max.tm_mday))) | ||
343 | _disable(sd, i); | ||
344 | else | ||
345 | _enable(sd, i); | ||
346 | |||
347 | snprintf(day_s, sizeof(day_s), "%d", day++); | ||
348 | } | ||
349 | else | ||
350 | { | ||
351 | _disable(sd, i); | ||
352 | |||
353 | if (day <= maxdays) | ||
354 | snprintf(day_s, sizeof(day_s), "%d", prev_month_maxdays - sd->first_day_it + i + 1); | ||
355 | else | ||
356 | snprintf(day_s, sizeof(day_s), "%d", i - sd->first_day_it - maxdays + 1); | ||
357 | } | ||
358 | |||
359 | snprintf(part, sizeof(part), "cit_%d.text", i); | ||
360 | elm_layout_text_set(obj, part, day_s); | ||
361 | } | ||
362 | |||
363 | // ACCESS | ||
364 | if ((_elm_config->access_mode != ELM_ACCESS_MODE_OFF)) | ||
365 | _access_calendar_item_register(obj); | ||
366 | |||
367 | sd->filling = EINA_FALSE; | ||
368 | |||
369 | elm_layout_thaw(obj); | ||
370 | edje_object_message_signal_process(elm_layout_edje_get(obj)); | ||
371 | } | ||
372 | |||
373 | static void | ||
374 | _set_headers(Evas_Object *obj) | ||
375 | { | ||
376 | static char part[] = "ch_0.text"; | ||
377 | int i; | ||
378 | struct tm *t; | ||
379 | time_t temp = 259200; // the first sunday since epoch | ||
380 | EFL_UI_CALENDAR_DATA_GET(obj, sd); | ||
381 | |||
382 | elm_layout_freeze(obj); | ||
383 | |||
384 | sd->filling = EINA_TRUE; | ||
385 | |||
386 | t = gmtime(&temp); | ||
387 | if (t) | ||
388 | { | ||
389 | t->tm_wday = 0; | ||
390 | for (i = 0; i < ELM_DAY_LAST; i++) | ||
391 | { | ||
392 | char *buf; | ||
393 | buf = eina_strftime("%a", t); | ||
394 | if (buf) | ||
395 | { | ||
396 | sd->weekdays[i] = eina_stringshare_add(buf); | ||
397 | free(buf); | ||
398 | } | ||
399 | else | ||
400 | { | ||
401 | /* If we failed getting day, get a default value */ | ||
402 | sd->weekdays[i] = _days_abbrev[i]; | ||
403 | WRN("Failed getting weekday name for '%s' from locale.", | ||
404 | _days_abbrev[i]); | ||
405 | } | ||
406 | t->tm_wday++; | ||
407 | } | ||
408 | } | ||
409 | |||
410 | for (i = 0; i < ELM_DAY_LAST; i++) | ||
411 | { | ||
412 | part[3] = i + '0'; | ||
413 | elm_layout_text_set(obj, part, sd->weekdays[(i + sd->first_week_day) % ELM_DAY_LAST]); | ||
414 | } | ||
415 | |||
416 | sd->filling = EINA_FALSE; | ||
417 | |||
418 | elm_layout_thaw(obj); | ||
419 | } | ||
420 | |||
421 | static void | ||
422 | _spinner_buttons_add(Evas_Object *obj, Efl_Ui_Calendar_Data *sd) | ||
423 | { | ||
424 | char left_buf[255] = { 0 }; | ||
425 | char right_buf[255] = { 0 }; | ||
426 | |||
427 | ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd); | ||
428 | |||
429 | snprintf(left_buf, sizeof(left_buf), "calendar/decrease/%s", elm_object_style_get(obj)); | ||
430 | snprintf(right_buf, sizeof(right_buf), "calendar/increase/%s", elm_object_style_get(obj)); | ||
431 | |||
432 | if (edje_object_part_exists(wd->resize_obj, EFL_UI_CALENDAR_BUTTON_LEFT)) | ||
433 | { | ||
434 | if (sd->dec_btn_month && efl_isa(sd->dec_btn_month, ELM_ACCESS_CLASS)) | ||
435 | { | ||
436 | _elm_access_edje_object_part_object_unregister | ||
437 | (obj, elm_layout_edje_get(obj), "left_bt"); | ||
438 | sd->dec_btn_month = NULL; | ||
439 | } | ||
440 | |||
441 | if (!sd->dec_btn_month) | ||
442 | { | ||
443 | sd->dec_btn_month = elm_button_add(obj); | ||
444 | elm_button_autorepeat_set(sd->dec_btn_month, EINA_TRUE); | ||
445 | elm_button_autorepeat_initial_timeout_set(sd->dec_btn_month, 0.5); | ||
446 | elm_button_autorepeat_gap_timeout_set(sd->dec_btn_month, 0.2); | ||
447 | evas_object_smart_callback_add(sd->dec_btn_month, "clicked", _button_widget_month_dec_start_click, obj); | ||
448 | evas_object_smart_callback_add(sd->dec_btn_month, "repeated", _button_widget_month_dec_start, obj); | ||
449 | } | ||
450 | |||
451 | elm_object_style_set(sd->dec_btn_month, left_buf); | ||
452 | elm_layout_content_set(obj, EFL_UI_CALENDAR_BUTTON_LEFT, sd->dec_btn_month); | ||
453 | } | ||
454 | else if (sd->dec_btn_month && !efl_isa(sd->dec_btn_month, ELM_ACCESS_CLASS)) | ||
455 | { | ||
456 | evas_object_del(sd->dec_btn_month); | ||
457 | sd->dec_btn_month = NULL; | ||
458 | } | ||
459 | |||
460 | if (edje_object_part_exists(wd->resize_obj, EFL_UI_CALENDAR_BUTTON_RIGHT)) | ||
461 | { | ||
462 | if (sd->inc_btn_month && efl_isa(sd->inc_btn_month, ELM_ACCESS_CLASS)) | ||
463 | { | ||
464 | _elm_access_edje_object_part_object_unregister | ||
465 | (obj, elm_layout_edje_get(obj), "right_bt"); | ||
466 | sd->inc_btn_month = NULL; | ||
467 | } | ||
468 | |||
469 | if (!sd->inc_btn_month) | ||
470 | { | ||
471 | sd->inc_btn_month = elm_button_add(obj); | ||
472 | elm_button_autorepeat_set(sd->inc_btn_month, EINA_TRUE); | ||
473 | elm_button_autorepeat_initial_timeout_set(sd->inc_btn_month, 0.5); | ||
474 | elm_button_autorepeat_gap_timeout_set(sd->inc_btn_month, 0.2); | ||
475 | evas_object_smart_callback_add(sd->inc_btn_month, "clicked", _button_widget_month_inc_start_click, obj); | ||
476 | evas_object_smart_callback_add(sd->inc_btn_month, "repeated", _button_widget_month_inc_start, obj); | ||
477 | } | ||
478 | |||
479 | elm_object_style_set(sd->inc_btn_month, right_buf); | ||
480 | elm_layout_content_set(obj, EFL_UI_CALENDAR_BUTTON_RIGHT, sd->inc_btn_month); | ||
481 | } | ||
482 | else if (sd->inc_btn_month && !efl_isa(sd->inc_btn_month, ELM_ACCESS_CLASS)) | ||
483 | { | ||
484 | evas_object_del(sd->inc_btn_month); | ||
485 | sd->inc_btn_month = NULL; | ||
486 | } | ||
487 | } | ||
488 | |||
489 | EOLIAN static Efl_Ui_Theme_Apply | ||
490 | _efl_ui_calendar_elm_widget_theme_apply(Eo *obj, Efl_Ui_Calendar_Data *sd) | ||
491 | { | ||
492 | Efl_Ui_Theme_Apply int_ret = EFL_UI_THEME_APPLY_FAILED; | ||
493 | |||
494 | int_ret = efl_ui_widget_theme_apply(efl_super(obj, MY_CLASS)); | ||
495 | if (!int_ret) return EFL_UI_THEME_APPLY_FAILED; | ||
496 | |||
497 | _spinner_buttons_add(obj, sd); | ||
498 | |||
499 | evas_object_smart_changed(obj); | ||
500 | return int_ret; | ||
501 | } | ||
502 | |||
503 | /* Set correct tm_wday and tm_yday after other fields changes*/ | ||
504 | static inline Eina_Bool | ||
505 | _fix_date(Efl_Ui_Calendar_Data *sd) | ||
506 | { | ||
507 | Eina_Bool fixed = EINA_FALSE; | ||
508 | |||
509 | if ((sd->date.tm_year < sd->date_min.tm_year) || | ||
510 | ((sd->date.tm_year == sd->date_min.tm_year) && | ||
511 | (sd->date.tm_mon < sd->date_min.tm_mon)) || | ||
512 | ((sd->date.tm_year == sd->date_min.tm_year) && | ||
513 | (sd->date.tm_mon == sd->date_min.tm_mon) && | ||
514 | (sd->date.tm_mday < sd->date_min.tm_mday))) | ||
515 | { | ||
516 | sd->date.tm_year = sd->shown_date.tm_year = sd->date_min.tm_year; | ||
517 | sd->date.tm_mon = sd->shown_date.tm_mon = sd->date_min.tm_mon; | ||
518 | sd->date.tm_mday = sd->shown_date.tm_mday = sd->date_min.tm_mday; | ||
519 | fixed = EINA_TRUE; | ||
520 | } | ||
521 | else if ((sd->date_max.tm_year != -1) && | ||
522 | ((sd->date.tm_year > sd->date_max.tm_year) || | ||
523 | ((sd->date.tm_year == sd->date_max.tm_year) && | ||
524 | (sd->date.tm_mon > sd->date_max.tm_mon)) || | ||
525 | ((sd->date.tm_year == sd->date_max.tm_year) && | ||
526 | (sd->date.tm_mon == sd->date_max.tm_mon) && | ||
527 | (sd->date.tm_mday > sd->date_max.tm_mday)))) | ||
528 | { | ||
529 | sd->date.tm_year = sd->shown_date.tm_year = sd->date_max.tm_year; | ||
530 | sd->date.tm_mon = sd->shown_date.tm_mon = sd->date_max.tm_mon; | ||
531 | sd->date.tm_mday = sd->shown_date.tm_mday = sd->date_max.tm_mday; | ||
532 | fixed = EINA_TRUE; | ||
533 | } | ||
534 | else | ||
535 | { | ||
536 | if (sd->date.tm_mon != sd->shown_date.tm_mon) | ||
537 | sd->date.tm_mon = sd->shown_date.tm_mon; | ||
538 | if (sd->date.tm_year != sd->shown_date.tm_year) | ||
539 | sd->date.tm_year = sd->shown_date.tm_year; | ||
540 | } | ||
541 | |||
542 | return fixed; | ||
543 | } | ||
544 | |||
545 | static Eina_Bool | ||
546 | _update_data(Evas_Object *obj, int delta) | ||
547 | { | ||
548 | struct tm time_check; | ||
549 | int maxdays; | ||
550 | |||
551 | EFL_UI_CALENDAR_DATA_GET(obj, sd); | ||
552 | |||
553 | /* check if it's a valid time. for 32 bits, year greater than 2037 is not */ | ||
554 | time_check = sd->shown_date; | ||
555 | time_check.tm_mon += delta; | ||
556 | |||
557 | if (mktime(&time_check) == -1) | ||
558 | { | ||
559 | ERR("mktime can not give week day for the next month. Please check what is wrong with udpate date."); | ||
560 | return EINA_FALSE; | ||
561 | } | ||
562 | |||
563 | sd->shown_date.tm_mon += delta; | ||
564 | |||
565 | if (delta < 0) | ||
566 | { | ||
567 | if (sd->shown_date.tm_year == sd->date_min.tm_year) | ||
568 | { | ||
569 | if (sd->shown_date.tm_mon < sd->date_min.tm_mon) | ||
570 | { | ||
571 | sd->shown_date.tm_mon = sd->date_min.tm_mon; | ||
572 | return EINA_FALSE; | ||
573 | } | ||
574 | } | ||
575 | else if (sd->shown_date.tm_mon < 0) | ||
576 | { | ||
577 | sd->shown_date.tm_mon = 11; | ||
578 | sd->shown_date.tm_year--; | ||
579 | } | ||
580 | } | ||
581 | else | ||
582 | { | ||
583 | if (sd->shown_date.tm_year == sd->date_max.tm_year) | ||
584 | { | ||
585 | if (sd->shown_date.tm_mon > sd->date_max.tm_mon) | ||
586 | { | ||
587 | sd->shown_date.tm_mon = sd->date_max.tm_mon; | ||
588 | return EINA_FALSE; | ||
589 | } | ||
590 | } | ||
591 | else if (sd->shown_date.tm_mon > 11) | ||
592 | { | ||
593 | sd->shown_date.tm_mon = 0; | ||
594 | sd->shown_date.tm_year++; | ||
595 | } | ||
596 | } | ||
597 | |||
598 | maxdays = _maxdays_get(&sd->shown_date, 0); | ||
599 | if (sd->date.tm_mday > maxdays) | ||
600 | sd->date.tm_mday = maxdays; | ||
601 | |||
602 | return EINA_TRUE; | ||
603 | } | ||
604 | |||
605 | static Eina_Bool | ||
606 | _spin_month_value(void *data) | ||
607 | { | ||
608 | EFL_UI_CALENDAR_DATA_GET(data, sd); | ||
609 | |||
610 | if (_update_data(data, sd->spin_speed)) | ||
611 | evas_object_smart_changed(data); | ||
612 | |||
613 | sd->interval = sd->interval / 1.05; | ||
614 | ecore_timer_interval_set(sd->spin_month, sd->interval); | ||
615 | |||
616 | return ECORE_CALLBACK_RENEW; | ||
617 | } | ||
618 | |||
619 | static void | ||
620 | _button_widget_month_inc_start_click(void *data, | ||
621 | Evas_Object *obj EINA_UNUSED, | ||
622 | void *event_info EINA_UNUSED) | ||
623 | { | ||
624 | EFL_UI_CALENDAR_DATA_GET(data, sd); | ||
625 | if (sd->month_repeated) | ||
626 | { | ||
627 | sd->month_repeated = EINA_FALSE; | ||
628 | return; | ||
629 | } | ||
630 | |||
631 | sd->interval = sd->first_interval; | ||
632 | sd->spin_speed = 1; | ||
633 | _spin_month_value(data); | ||
634 | } | ||
635 | |||
636 | static void | ||
637 | _button_widget_month_inc_start(void *data, | ||
638 | Evas_Object *obj EINA_UNUSED, | ||
639 | void *event_info EINA_UNUSED) | ||
640 | { | ||
641 | EFL_UI_CALENDAR_DATA_GET(data, sd); | ||
642 | |||
643 | sd->spin_speed = 1; | ||
644 | if (!sd->month_repeated) | ||
645 | sd->interval = sd->first_interval; | ||
646 | sd->month_repeated = EINA_TRUE; | ||
647 | _spin_month_value(data); | ||
648 | |||
649 | } | ||
650 | |||
651 | static void | ||
652 | _button_widget_month_dec_start_click(void *data, | ||
653 | Evas_Object *obj EINA_UNUSED, | ||
654 | void *event_info EINA_UNUSED) | ||
655 | { | ||
656 | EFL_UI_CALENDAR_DATA_GET(data, sd); | ||
657 | if (sd->month_repeated) | ||
658 | { | ||
659 | sd->month_repeated = EINA_FALSE; | ||
660 | return; | ||
661 | } | ||
662 | |||
663 | sd->interval = sd->first_interval; | ||
664 | sd->spin_speed = -1; | ||
665 | _spin_month_value(data); | ||
666 | } | ||
667 | |||
668 | static void | ||
669 | _button_widget_month_dec_start(void *data, | ||
670 | Evas_Object *obj EINA_UNUSED, | ||
671 | void *event_info EINA_UNUSED) | ||
672 | { | ||
673 | EFL_UI_CALENDAR_DATA_GET(data, sd); | ||
674 | |||
675 | sd->spin_speed = -1; | ||
676 | if (!sd->month_repeated) | ||
677 | sd->interval = sd->first_interval; | ||
678 | sd->month_repeated = EINA_TRUE; | ||
679 | _spin_month_value(data); | ||
680 | } | ||
681 | |||
682 | static int | ||
683 | _get_item_day(Evas_Object *obj, | ||
684 | int selected_it) | ||
685 | { | ||
686 | int day; | ||
687 | |||
688 | EFL_UI_CALENDAR_DATA_GET(obj, sd); | ||
689 | |||
690 | day = selected_it - sd->first_day_it + 1; | ||
691 | if ((day < 0) || (day > _maxdays_get(&sd->shown_date, 0))) | ||
692 | return 0; | ||
693 | |||
694 | if ((sd->shown_date.tm_year == sd->date_min.tm_year) | ||
695 | && (sd->shown_date.tm_mon == sd->date_min.tm_mon) | ||
696 | && (day < sd->date_min.tm_mday)) | ||
697 | { | ||
698 | return 0; | ||
699 | } | ||
700 | else if ((sd->shown_date.tm_year == sd->date_max.tm_year) | ||
701 | && (sd->shown_date.tm_mon == sd->date_max.tm_mon) | ||
702 | && (day > sd->date_max.tm_mday)) | ||
703 | { | ||
704 | return 0; | ||
705 | } | ||
706 | |||
707 | return day; | ||
708 | } | ||
709 | |||
710 | static void | ||
711 | _update_unfocused_it(Evas_Object *obj, int unfocused_it) | ||
712 | { | ||
713 | int day; | ||
714 | char emission[32]; | ||
715 | |||
716 | EFL_UI_CALENDAR_DATA_GET(obj, sd); | ||
717 | |||
718 | day = _get_item_day(obj, unfocused_it); | ||
719 | if (!day) | ||
720 | return; | ||
721 | |||
722 | sd->focused_it = -1; | ||
723 | |||
724 | snprintf(emission, sizeof(emission), "cit_%d,unfocused", unfocused_it); | ||
725 | elm_layout_signal_emit(obj, emission, "elm"); | ||
726 | } | ||
727 | |||
728 | static Eina_Bool | ||
729 | _update_focused_it(Evas_Object *obj, int focused_it) | ||
730 | { | ||
731 | int day; | ||
732 | char emission[32]; | ||
733 | |||
734 | EFL_UI_CALENDAR_DATA_GET(obj, sd); | ||
735 | |||
736 | day = _get_item_day(obj, focused_it); | ||
737 | if (!day) | ||
738 | return EINA_FALSE; | ||
739 | |||
740 | snprintf(emission, sizeof(emission), "cit_%d,unfocused", sd->focused_it); | ||
741 | elm_layout_signal_emit(obj, emission, "elm"); | ||
742 | |||
743 | sd->focused_it = focused_it; | ||
744 | |||
745 | snprintf(emission, sizeof(emission), "cit_%d,focused", sd->focused_it); | ||
746 | elm_layout_signal_emit(obj, emission, "elm"); | ||
747 | |||
748 | return EINA_TRUE; | ||
749 | } | ||
750 | |||
751 | static void | ||
752 | _update_sel_it(Evas_Object *obj, | ||
753 | int sel_it) | ||
754 | { | ||
755 | int day; | ||
756 | |||
757 | EFL_UI_CALENDAR_DATA_GET(obj, sd); | ||
758 | |||
759 | day = _get_item_day(obj, sel_it); | ||
760 | if (!day) | ||
761 | return; | ||
762 | |||
763 | _unselect(obj, sd->selected_it); | ||
764 | if (!sd->selected) | ||
765 | sd->selected = EINA_TRUE; | ||
766 | if (sd->focused_it) | ||
767 | _update_unfocused_it(obj, sd->focused_it); | ||
768 | |||
769 | sd->date.tm_mday = day; | ||
770 | _fix_date(sd); | ||
771 | _select(obj, sel_it); | ||
772 | efl_event_callback_legacy_call(obj, EFL_UI_CALENDAR_EVENT_CHANGED, NULL); | ||
773 | } | ||
774 | |||
775 | static void | ||
776 | _day_selected(void *data, | ||
777 | Evas_Object *obj EINA_UNUSED, | ||
778 | const char *emission EINA_UNUSED, | ||
779 | const char *source) | ||
780 | { | ||
781 | int sel_it; | ||
782 | |||
783 | sel_it = atoi(source); | ||
784 | |||
785 | _update_sel_it(data, sel_it); | ||
786 | } | ||
787 | |||
788 | static inline int | ||
789 | _time_to_next_day(struct tm *t) | ||
790 | { | ||
791 | return ((((24 - t->tm_hour) * 60) - t->tm_min) * 60) - t->tm_sec; | ||
792 | } | ||
793 | |||
794 | static Eina_Bool | ||
795 | _update_cur_date(void *data) | ||
796 | { | ||
797 | time_t current_date; | ||
798 | int t, day; | ||
799 | EFL_UI_CALENDAR_DATA_GET(data, sd); | ||
800 | |||
801 | if (sd->today_it > 0) _not_today(sd); | ||
802 | |||
803 | current_date = time(NULL); | ||
804 | localtime_r(¤t_date, &sd->current_date); | ||
805 | t = _time_to_next_day(&sd->current_date); | ||
806 | ecore_timer_interval_set(sd->update_timer, t); | ||
807 | |||
808 | if ((sd->current_date.tm_mon != sd->shown_date.tm_mon) || | ||
809 | (sd->current_date.tm_year != sd->shown_date.tm_year)) | ||
810 | return ECORE_CALLBACK_RENEW; | ||
811 | |||
812 | day = sd->current_date.tm_mday + sd->first_day_it - 1; | ||
813 | _today(sd, day); | ||
814 | |||
815 | return ECORE_CALLBACK_RENEW; | ||
816 | } | ||
817 | |||
818 | static Eina_Bool | ||
819 | _key_action_activate(Evas_Object *obj, const char *params EINA_UNUSED) | ||
820 | { | ||
821 | EFL_UI_CALENDAR_DATA_GET(obj, sd); | ||
822 | |||
823 | _update_sel_it(obj, sd->focused_it); | ||
824 | |||
825 | return EINA_TRUE; | ||
826 | } | ||
827 | |||
828 | EOLIAN static Eina_Bool | ||
829 | _efl_ui_calendar_elm_widget_on_focus_update(Eo *obj, Efl_Ui_Calendar_Data *sd, Elm_Object_Item *item EINA_UNUSED) | ||
830 | { | ||
831 | Eina_Bool int_ret = EINA_FALSE; | ||
832 | |||
833 | int_ret = efl_ui_widget_on_focus_update(efl_super(obj, MY_CLASS), NULL); | ||
834 | if (!int_ret) return EINA_FALSE; | ||
835 | |||
836 | // FIXME : Currently, focused item is same with selected item. | ||
837 | // After arrenging focus logic in this widget, we need to make | ||
838 | // focused item which is for indicating direction key input movement | ||
839 | // on the calendar widget. | ||
840 | if (elm_widget_focus_get(obj)) | ||
841 | _update_focused_it(obj, sd->selected_it); | ||
842 | else | ||
843 | _update_unfocused_it(obj, sd->focused_it); | ||
844 | |||
845 | return EINA_TRUE; | ||
846 | } | ||
847 | |||
848 | EOLIAN static void | ||
849 | _efl_ui_calendar_efl_canvas_group_group_calculate(Eo *obj, Efl_Ui_Calendar_Data *_pd EINA_UNUSED) | ||
850 | { | ||
851 | elm_layout_freeze(obj); | ||
852 | |||
853 | _set_headers(obj); | ||
854 | _populate(obj); | ||
855 | |||
856 | elm_layout_thaw(obj); | ||
857 | } | ||
858 | |||
859 | EOLIAN static void | ||
860 | _efl_ui_calendar_efl_object_destructor(Eo *obj, Efl_Ui_Calendar_Data *sd) | ||
861 | { | ||
862 | int i; | ||
863 | |||
864 | ecore_timer_del(sd->spin_month); | ||
865 | ecore_timer_del(sd->spin_year); | ||
866 | ecore_timer_del(sd->update_timer); | ||
867 | |||
868 | for (i = 0; i < ELM_DAY_LAST; i++) | ||
869 | eina_stringshare_del(sd->weekdays[i]); | ||
870 | |||
871 | efl_destructor(efl_super(obj, MY_CLASS)); | ||
872 | } | ||
873 | |||
874 | static void | ||
875 | _access_obj_process(Evas_Object *obj, Eina_Bool is_access) | ||
876 | { | ||
877 | int maxdays, day, i; | ||
878 | |||
879 | EFL_UI_CALENDAR_DATA_GET(obj, sd); | ||
880 | ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd); | ||
881 | |||
882 | if (is_access) | ||
883 | _access_calendar_register(obj); | ||
884 | else | ||
885 | { | ||
886 | day = 0; | ||
887 | maxdays = _maxdays_get(&sd->shown_date, 0); | ||
888 | for (i = 0; i < 42; i++) | ||
889 | { | ||
890 | if ((!day) && (i == sd->first_day_it)) day = 1; | ||
891 | if ((day) && (day <= maxdays)) | ||
892 | { | ||
893 | char pname[14]; | ||
894 | snprintf(pname, sizeof(pname), "cit_%d.access", i); | ||
895 | |||
896 | _elm_access_edje_object_part_object_unregister | ||
897 | (obj, elm_layout_edje_get(obj), pname); | ||
898 | } | ||
899 | } | ||
900 | |||
901 | if (sd->dec_btn_month && efl_isa(sd->dec_btn_month, ELM_ACCESS_CLASS)) | ||
902 | { | ||
903 | _elm_access_edje_object_part_object_unregister | ||
904 | (obj, elm_layout_edje_get(obj), "left_bt"); | ||
905 | sd->dec_btn_month = NULL; | ||
906 | } | ||
907 | if (sd->inc_btn_month && efl_isa(sd->inc_btn_month, ELM_ACCESS_CLASS)) | ||
908 | { | ||
909 | _elm_access_edje_object_part_object_unregister | ||
910 | (obj, elm_layout_edje_get(obj), "right_bt"); | ||
911 | sd->inc_btn_month = NULL; | ||
912 | } | ||
913 | if (sd->month_access) | ||
914 | _elm_access_edje_object_part_object_unregister | ||
915 | (obj, elm_layout_edje_get(obj), "month_text"); | ||
916 | } | ||
917 | } | ||
918 | |||
919 | EOLIAN static void | ||
920 | _efl_ui_calendar_elm_widget_on_access_update(Eo *obj EINA_UNUSED, Efl_Ui_Calendar_Data *_pd EINA_UNUSED, Eina_Bool acs) | ||
921 | { | ||
922 | _efl_ui_calendar_smart_focus_next_enable = acs; | ||
923 | _access_obj_process(obj, _efl_ui_calendar_smart_focus_next_enable); | ||
924 | } | ||
925 | |||
926 | static Eo * | ||
927 | _efl_ui_calendar_constructor_internal(Eo *obj, Efl_Ui_Calendar_Data *priv) | ||
928 | { | ||
929 | time_t current_date; | ||
930 | int t; | ||
931 | |||
932 | ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, NULL); | ||
933 | |||
934 | elm_widget_sub_object_parent_add(obj); | ||
935 | |||
936 | priv->first_interval = 0.85; | ||
937 | priv->date_min.tm_year = 2; | ||
938 | priv->date_min.tm_mon = 0; | ||
939 | priv->date_min.tm_mday = 1; | ||
940 | priv->date_max.tm_year = -1; | ||
941 | priv->date_max.tm_mon = 11; | ||
942 | priv->date_max.tm_mday = 31; | ||
943 | priv->today_it = -1; | ||
944 | priv->selected_it = -1; | ||
945 | priv->first_day_it = -1; | ||
946 | priv->format_func = _format_month_year; | ||
947 | |||
948 | edje_object_signal_callback_add | ||
949 | (wd->resize_obj, "elm,action,selected", "*", | ||
950 | _day_selected, obj); | ||
951 | |||
952 | current_date = time(NULL); | ||
953 | localtime_r(¤t_date, &priv->shown_date); | ||
954 | priv->current_date = priv->shown_date; | ||
955 | priv->date = priv->shown_date; | ||
956 | t = _time_to_next_day(&priv->current_date); | ||
957 | priv->update_timer = ecore_timer_add(t, _update_cur_date, obj); | ||
958 | |||
959 | elm_widget_can_focus_set(obj, EINA_TRUE); | ||
960 | |||
961 | if (!elm_layout_theme_set(obj, "calendar", "base", | ||
962 | elm_object_style_get(obj))) | ||
963 | CRI("Failed to set layout!"); | ||
964 | |||
965 | _spinner_buttons_add(obj, priv); | ||
966 | |||
967 | evas_object_smart_changed(obj); | ||
968 | |||
969 | // ACCESS | ||
970 | if ((_elm_config->access_mode != ELM_ACCESS_MODE_OFF)) | ||
971 | _access_calendar_spinner_register(obj); | ||
972 | |||
973 | return obj; | ||
974 | } | ||
975 | |||
976 | EOLIAN static Eo * | ||
977 | _efl_ui_calendar_efl_object_constructor(Eo *obj, Efl_Ui_Calendar_Data *sd) | ||
978 | { | ||
979 | obj = efl_constructor(efl_super(obj, MY_CLASS)); | ||
980 | sd->obj = obj; | ||
981 | evas_object_smart_callbacks_descriptions_set(obj, _smart_callbacks); | ||
982 | efl_access_role_set(obj, EFL_ACCESS_ROLE_DATE_EDITOR); | ||
983 | |||
984 | obj = _efl_ui_calendar_constructor_internal(obj, sd); | ||
985 | |||
986 | return obj; | ||
987 | } | ||
988 | |||
989 | EOLIAN static Eina_Bool | ||
990 | _efl_ui_calendar_date_min_set(Eo *obj, Efl_Ui_Calendar_Data *sd, Efl_Time min) | ||
991 | { | ||
992 | Eina_Bool upper = EINA_FALSE; | ||
993 | struct tm temp; | ||
994 | |||
995 | temp = min; | ||
996 | if (mktime(&temp) == -1) | ||
997 | { | ||
998 | ERR("mktime can not give week day for your minimum date. Please check the date."); | ||
999 | return EINA_FALSE; | ||
1000 | } | ||
1001 | |||
1002 | if ((sd->date_min.tm_year == min.tm_year) | ||
1003 | && (sd->date_min.tm_mon == min.tm_mon) | ||
1004 | && (sd->date_min.tm_mday == min.tm_mday)) | ||
1005 | return EINA_TRUE; | ||
1006 | |||
1007 | if (min.tm_year < 2) | ||
1008 | { | ||
1009 | sd->date_min.tm_year = 2; | ||
1010 | sd->date_min.tm_mon = 0; | ||
1011 | sd->date_min.tm_mday = 1; | ||
1012 | } | ||
1013 | else | ||
1014 | { | ||
1015 | if (sd->date_max.tm_year != -1) | ||
1016 | { | ||
1017 | if (min.tm_year > sd->date_max.tm_year) | ||
1018 | { | ||
1019 | upper = EINA_TRUE; | ||
1020 | } | ||
1021 | else if (min.tm_year == sd->date_max.tm_year) | ||
1022 | { | ||
1023 | if (min.tm_mon > sd->date_max.tm_mon) | ||
1024 | upper = EINA_TRUE; | ||
1025 | else if ((min.tm_mon == sd->date_max.tm_mon) && (min.tm_mday > sd->date_max.tm_mday)) | ||
1026 | upper = EINA_TRUE; | ||
1027 | } | ||
1028 | } | ||
1029 | |||
1030 | if (upper) | ||
1031 | { | ||
1032 | sd->date_min.tm_year = sd->date_max.tm_year; | ||
1033 | sd->date_min.tm_mon = sd->date_max.tm_mon; | ||
1034 | sd->date_min.tm_mday = sd->date_max.tm_mday; | ||
1035 | } | ||
1036 | else | ||
1037 | { | ||
1038 | sd->date_min.tm_year = min.tm_year; | ||
1039 | sd->date_min.tm_mon = min.tm_mon; | ||
1040 | sd->date_min.tm_mday = min.tm_mday; | ||
1041 | } | ||
1042 | } | ||
1043 | |||
1044 | _fix_date(sd); | ||
1045 | |||
1046 | evas_object_smart_changed(obj); | ||
1047 | |||
1048 | if (upper) | ||
1049 | { | ||
1050 | ERR("Your minimum date is greater than current maximum date."); | ||
1051 | return EINA_FALSE; | ||
1052 | } | ||
1053 | return EINA_TRUE; | ||
1054 | } | ||
1055 | |||
1056 | EOLIAN static Efl_Time | ||
1057 | _efl_ui_calendar_date_min_get(Eo *obj EINA_UNUSED, Efl_Ui_Calendar_Data *sd) | ||
1058 | { | ||
1059 | return sd->date_min; | ||
1060 | } | ||
1061 | |||
1062 | EOLIAN static Eina_Bool | ||
1063 | _efl_ui_calendar_date_max_set(Eo *obj, Efl_Ui_Calendar_Data *sd, Efl_Time max) | ||
1064 | { | ||
1065 | Eina_Bool lower = EINA_FALSE; | ||
1066 | struct tm temp; | ||
1067 | |||
1068 | temp = max; | ||
1069 | if (mktime(&temp) == -1) | ||
1070 | { | ||
1071 | ERR("mktime can not give week day for your maximum date. Please check the date."); | ||
1072 | return EINA_FALSE; | ||
1073 | } | ||
1074 | |||
1075 | if ((sd->date_max.tm_year == max.tm_year) | ||
1076 | && (sd->date_max.tm_mon == max.tm_mon) | ||
1077 | && (sd->date_max.tm_mday == max.tm_mday)) | ||
1078 | return EINA_TRUE; | ||
1079 | |||
1080 | if (max.tm_year < sd->date_min.tm_year) | ||
1081 | { | ||
1082 | lower = EINA_TRUE; | ||
1083 | } | ||
1084 | else if (max.tm_year == sd->date_min.tm_year) | ||
1085 | { | ||
1086 | if (max.tm_mon < sd->date_min.tm_mon) | ||
1087 | lower = EINA_TRUE; | ||
1088 | else if ((max.tm_mon == sd->date_min.tm_mon) && (max.tm_mday < sd->date_min.tm_mday)) | ||
1089 | lower = EINA_TRUE; | ||
1090 | } | ||
1091 | |||
1092 | if (lower) | ||
1093 | { | ||
1094 | sd->date_max.tm_year = sd->date_min.tm_year; | ||
1095 | sd->date_max.tm_mon = sd->date_min.tm_mon; | ||
1096 | sd->date_max.tm_mday = sd->date_min.tm_mday; | ||
1097 | } | ||
1098 | else | ||
1099 | { | ||
1100 | sd->date_max.tm_year = max.tm_year; | ||
1101 | sd->date_max.tm_mon = max.tm_mon; | ||
1102 | sd->date_max.tm_mday = max.tm_mday; | ||
1103 | } | ||
1104 | |||
1105 | _fix_date(sd); | ||
1106 | |||
1107 | evas_object_smart_changed(obj); | ||
1108 | |||
1109 | if (lower) | ||
1110 | { | ||
1111 | ERR("Your maximum date is less than current minimum date."); | ||
1112 | return EINA_FALSE; | ||
1113 | } | ||
1114 | return EINA_TRUE; | ||
1115 | } | ||
1116 | |||
1117 | EOLIAN static Efl_Time | ||
1118 | _efl_ui_calendar_date_max_get(Eo *obj EINA_UNUSED, Efl_Ui_Calendar_Data *sd) | ||
1119 | { | ||
1120 | return sd->date_max; | ||
1121 | } | ||
1122 | |||
1123 | EOLIAN static Eina_Bool | ||
1124 | _efl_ui_calendar_date_set(Eo *obj, Efl_Ui_Calendar_Data *sd, Efl_Time date) | ||
1125 | { | ||
1126 | Eina_Bool ret = EINA_TRUE; | ||
1127 | struct tm temp; | ||
1128 | |||
1129 | temp = date; | ||
1130 | if (mktime(&temp) == -1) | ||
1131 | { | ||
1132 | ERR("mktime can not give week day for your new date. Please check the date."); | ||
1133 | return EINA_FALSE; | ||
1134 | } | ||
1135 | |||
1136 | sd->date.tm_year = date.tm_year; | ||
1137 | sd->date.tm_mon = date.tm_mon; | ||
1138 | sd->date.tm_mday = date.tm_mday; | ||
1139 | if (!sd->selected) | ||
1140 | sd->selected = EINA_TRUE; | ||
1141 | |||
1142 | if (sd->date.tm_year != sd->shown_date.tm_year) | ||
1143 | sd->shown_date.tm_year = sd->date.tm_year; | ||
1144 | if (sd->date.tm_mon != sd->shown_date.tm_mon) | ||
1145 | sd->shown_date.tm_mon = sd->date.tm_mon; | ||
1146 | |||
1147 | ret = _fix_date(sd); | ||
1148 | |||
1149 | evas_object_smart_changed(obj); | ||
1150 | |||
1151 | if (!ret) | ||
1152 | ERR("The current date is greater than the maximum date or less than the minimum date."); | ||
1153 | |||
1154 | return ret; | ||
1155 | } | ||
1156 | |||
1157 | EOLIAN static Efl_Time | ||
1158 | _efl_ui_calendar_date_get(Eo *obj EINA_UNUSED, Efl_Ui_Calendar_Data *sd) | ||
1159 | { | ||
1160 | return sd->date; | ||
1161 | } | ||
1162 | |||
1163 | EOLIAN static void | ||
1164 | _efl_ui_calendar_format_function_set(Eo *obj EINA_UNUSED, Efl_Ui_Calendar_Data *sd, Efl_Ui_Calendar_Format_Cb format_function) | ||
1165 | { | ||
1166 | sd->format_func = format_function; | ||
1167 | } | ||
1168 | |||
1169 | EOLIAN static void | ||
1170 | _efl_ui_calendar_first_day_of_week_set(Eo *obj, Efl_Ui_Calendar_Data *sd, Efl_Ui_Calendar_Weekday day) | ||
1171 | { | ||
1172 | if (day >= EFL_UI_CALENDAR_WEEKDAY_LAST) return; | ||
1173 | if (sd->first_week_day != day) | ||
1174 | { | ||
1175 | sd->first_week_day = day; | ||
1176 | evas_object_smart_changed(obj); | ||
1177 | } | ||
1178 | } | ||
1179 | |||
1180 | EOLIAN static Efl_Ui_Calendar_Weekday | ||
1181 | _efl_ui_calendar_first_day_of_week_get(Eo *obj EINA_UNUSED, Efl_Ui_Calendar_Data *sd) | ||
1182 | { | ||
1183 | return sd->first_week_day; | ||
1184 | } | ||
1185 | |||
1186 | static void | ||
1187 | _efl_ui_calendar_class_constructor(Efl_Class *klass) | ||
1188 | { | ||
1189 | evas_smart_legacy_type_register(MY_CLASS_NAME, klass); | ||
1190 | |||
1191 | if (_elm_config->access_mode != ELM_ACCESS_MODE_OFF) | ||
1192 | _efl_ui_calendar_smart_focus_next_enable = EINA_TRUE; | ||
1193 | } | ||
1194 | |||
1195 | EOLIAN static const Elm_Atspi_Action* | ||
1196 | _efl_ui_calendar_elm_interface_atspi_widget_action_elm_actions_get(Eo *obj EINA_UNUSED, Efl_Ui_Calendar_Data *sd EINA_UNUSED) | ||
1197 | { | ||
1198 | static Elm_Atspi_Action atspi_actions[] = { | ||
1199 | { "activate", "activate", NULL, _key_action_activate}, | ||
1200 | { NULL, NULL, NULL, NULL } | ||
1201 | }; | ||
1202 | return &atspi_actions[0]; | ||
1203 | } | ||
1204 | |||
1205 | /* Standard widget overrides */ | ||
1206 | |||
1207 | ELM_WIDGET_KEY_DOWN_DEFAULT_IMPLEMENT(efl_ui_calendar, Efl_Ui_Calendar_Data) | ||
1208 | |||
1209 | /* Internal EO APIs and hidden overrides */ | ||
1210 | |||
1211 | #define EFL_UI_CALENDAR_EXTRA_OPS \ | ||
1212 | ELM_LAYOUT_SIZING_EVAL_OPS(efl_ui_calendar) | ||
1213 | |||
1214 | #include "efl_ui_calendar.eo.c" | ||
diff --git a/src/lib/elementary/efl_ui_calendar.eo b/src/lib/elementary/efl_ui_calendar.eo new file mode 100644 index 0000000000..6615fa90fb --- /dev/null +++ b/src/lib/elementary/efl_ui_calendar.eo | |||
@@ -0,0 +1,149 @@ | |||
1 | import efl_types; | ||
2 | |||
3 | type Efl_Ui_Calendar_Format_Cb: __undefined_type; [[Elementary calendar format callback type]] | ||
4 | |||
5 | enum Efl.Ui.Calendar.Weekday | ||
6 | { | ||
7 | [[A weekday | ||
8 | |||
9 | See also @Efl.Ui.Calendar.first_day_of_week.set. | ||
10 | ]] | ||
11 | sunday, [[Sunday weekday]] | ||
12 | monday, [[Monday weekday]] | ||
13 | tuesday, [[Tusday weekday]] | ||
14 | wednesday, [[Wednesday weekday]] | ||
15 | thursday, [[Thursday weekday]] | ||
16 | friday, [[Friday weekday]] | ||
17 | saturday, [[Saturday weekday]] | ||
18 | last [[Sentinel value to indicate last enum field during iteration]] | ||
19 | } | ||
20 | |||
21 | class Efl.Ui.Calendar (Efl.Ui.Layout, Efl.Ui.Focus.Composition, Elm.Interface.Atspi_Widget_Action) | ||
22 | { | ||
23 | [[Calendar widget | ||
24 | |||
25 | It helps applications to flexibly display a calendar with day of the week, | ||
26 | date, year and month. Applications are able to set specific dates to be | ||
27 | reported back, when selected, in the smart callbacks of the calendar widget. | ||
28 | ]] | ||
29 | methods { | ||
30 | @property first_day_of_week { | ||
31 | [[The first day of week to use on calendar widgets'.]] | ||
32 | set { | ||
33 | } | ||
34 | get { | ||
35 | } | ||
36 | values { | ||
37 | day: Efl.Ui.Calendar.Weekday(Efl.Ui.Calendar.Weekday.sunday); [[Weekday enum value, see @Elm.Calendar.Weekday]] | ||
38 | } | ||
39 | } | ||
40 | @property format_function { | ||
41 | set { | ||
42 | [[Set a function to format the string that will be used to display | ||
43 | month and year; | ||
44 | |||
45 | By default it uses strftime with "%B %Y" format string. | ||
46 | It should allocate the memory that will be used by the string, | ||
47 | that will be freed by the widget after usage. | ||
48 | A pointer to the string and a pointer to the time struct will be provided. | ||
49 | ]] | ||
50 | /* FIXME-doc | ||
51 | * Example: | ||
52 | * @code | ||
53 | * static char | ||
54 | * _format_month_year(struct tm *selected_time) | ||
55 | * { | ||
56 | * char buf[32]; | ||
57 | * if (!strftime(buf, sizeof(buf), "%B %Y", selected_time)) return NULL; | ||
58 | * return strdup(buf); | ||
59 | * } | ||
60 | * | ||
61 | * efl_ui_calendar_format_function_set(calendar, _format_month_year); | ||
62 | * @endcode | ||
63 | */ | ||
64 | } | ||
65 | values { | ||
66 | format_function: Efl_Ui_Calendar_Format_Cb; [[Function to set the month-year string given | ||
67 | the selected date.]] | ||
68 | } | ||
69 | } | ||
70 | @property date_min { | ||
71 | [[Minimum date on calendar.]] | ||
72 | set { | ||
73 | [[Set the minimum date on calendar. | ||
74 | |||
75 | Set the minimum date, changing the displayed month or year if needed. | ||
76 | Displayed day also to be disabled if it is smaller than minimum date. | ||
77 | If the minimum date is greater than current maximum date, the minimum | ||
78 | date would be changed to the maximum date with returning $false. | ||
79 | ]] | ||
80 | return: bool; [[$true, on success, $false otherwise]] | ||
81 | } | ||
82 | get { | ||
83 | [[Get the minimum date. | ||
84 | |||
85 | Default value is 1 JAN,1902. | ||
86 | ]] | ||
87 | } | ||
88 | values { | ||
89 | min: Efl.Time; [[Time structure containing the minmum date.]] | ||
90 | } | ||
91 | } | ||
92 | @property date_max { | ||
93 | [[Maximum date on calendar.]] | ||
94 | set { | ||
95 | [[Set the maximum date on calendar. | ||
96 | |||
97 | Set the maximum date, changing the displayed month or year if needed. | ||
98 | Displayed day also to be disabled if it is bigger than maximum date. | ||
99 | If the maximum date is less than current minimum date, the maximum date | ||
100 | would be changed to the minimum date with returning $false. | ||
101 | ]] | ||
102 | return: bool; [[$true, on success, $false otherwise]] | ||
103 | } | ||
104 | get { | ||
105 | [[Get the maximum date. | ||
106 | |||
107 | Default maximum year is -1. | ||
108 | Default maximum day and month are 31 and DEC. | ||
109 | |||
110 | If the maximum year is a negative value, it will be limited depending | ||
111 | on the platform architecture (year 2037 for 32 bits); | ||
112 | ]] | ||
113 | } | ||
114 | values { | ||
115 | max: Efl.Time; [[Time structure containing the maximum date.]] | ||
116 | } | ||
117 | } | ||
118 | @property date { | ||
119 | [[The selected date on calendar.]] | ||
120 | set { | ||
121 | [[Set the selected date. If the date is greater than the maximum date, | ||
122 | the date would be changed to the maximum date with returning $false. | ||
123 | In the opposite case with the minimum date, | ||
124 | this would give the same result. | ||
125 | ]] | ||
126 | return: bool; [[$true, on success, $false otherwise]] | ||
127 | } | ||
128 | get { | ||
129 | } | ||
130 | values { | ||
131 | date: Efl.Time; [[Time structure containing the selected date.]] | ||
132 | } | ||
133 | } | ||
134 | } | ||
135 | implements { | ||
136 | class.constructor; | ||
137 | Efl.Object.constructor; | ||
138 | Efl.Object.destructor; | ||
139 | Efl.Canvas.Group.group_calculate; | ||
140 | Elm.Widget.theme_apply; | ||
141 | Elm.Widget.on_access_update; | ||
142 | Elm.Widget.on_focus_update; | ||
143 | Elm.Widget.widget_event; | ||
144 | Elm.Interface.Atspi_Widget_Action.elm_actions { get; } | ||
145 | } | ||
146 | events { | ||
147 | changed; [[Emitted when the selected date in the calendar is changed]] | ||
148 | } | ||
149 | } | ||
diff --git a/src/lib/elementary/efl_ui_calendar.h b/src/lib/elementary/efl_ui_calendar.h new file mode 100644 index 0000000000..9c71188b47 --- /dev/null +++ b/src/lib/elementary/efl_ui_calendar.h | |||
@@ -0,0 +1,57 @@ | |||
1 | /** | ||
2 | * @defgroup Elm_Calendar Calendar | ||
3 | * @ingroup Elementary | ||
4 | * | ||
5 | * @image html calendar_inheritance_tree.png | ||
6 | * @image latex calendar_inheritance_tree.eps | ||
7 | * | ||
8 | * This is a calendar widget. It helps applications to flexibly | ||
9 | * display a calendar with day of the week, date, year and | ||
10 | * month. Applications are able to set specific dates to be reported | ||
11 | * back, when selected, in the smart callbacks of the calendar | ||
12 | * widget. The API of this widget lets the applications perform other | ||
13 | * functions, like: | ||
14 | * | ||
15 | * - placing marks on specific dates | ||
16 | * - setting the bounds for the calendar (minimum and maximum years) | ||
17 | * - setting the day names of the week (e.g. "Thu" or "Thursday") | ||
18 | * - setting the year and month format. | ||
19 | * | ||
20 | * This widget inherits from the @ref Layout one, so that all the | ||
21 | * functions acting on it also work for calendar objects. | ||
22 | * | ||
23 | * This widget emits the following signals, besides the ones sent from | ||
24 | * @ref Layout: | ||
25 | * - @c "changed" - emitted when the date in the calendar is changed. | ||
26 | * - @c "display,changed" - emitted when the current month displayed in the | ||
27 | * calendar is changed. | ||
28 | * - @c "focused" - When the calendar has received focus. (since 1.8) | ||
29 | * - @c "unfocused" - When the calendar has lost focus. (since 1.8) | ||
30 | * - @c "language,changed" - the program's language changed (since 1.9) | ||
31 | * | ||
32 | * Supported elm_object common APIs. | ||
33 | * @li @ref elm_object_signal_emit | ||
34 | * @li @ref elm_object_signal_callback_add | ||
35 | * @li @ref elm_object_signal_callback_del | ||
36 | * | ||
37 | * Here is some sample code using it: | ||
38 | * @li @ref calendar_example_01 | ||
39 | * @li @ref calendar_example_02 | ||
40 | * @li @ref calendar_example_03 | ||
41 | * @li @ref calendar_example_04 | ||
42 | * @li @ref calendar_example_05 | ||
43 | * @li @ref calendar_example_06 | ||
44 | */ | ||
45 | |||
46 | /** | ||
47 | * @addtogroup Elm_Calendar | ||
48 | * @{ | ||
49 | */ | ||
50 | |||
51 | #include "efl_ui_calendar_common.h" | ||
52 | #ifdef EFL_EO_API_SUPPORT | ||
53 | #include "efl_ui_calendar.eo.h" | ||
54 | #endif | ||
55 | /** | ||
56 | * @} | ||
57 | */ | ||
diff --git a/src/lib/elementary/efl_ui_calendar_common.h b/src/lib/elementary/efl_ui_calendar_common.h new file mode 100644 index 0000000000..70a95ad8f2 --- /dev/null +++ b/src/lib/elementary/efl_ui_calendar_common.h | |||
@@ -0,0 +1,21 @@ | |||
1 | /** | ||
2 | * @addtogroup Elm_Calendar | ||
3 | * | ||
4 | * @{ | ||
5 | */ | ||
6 | |||
7 | /** | ||
8 | * This callback type is used to format the string that will be used | ||
9 | * to display month and year. | ||
10 | * | ||
11 | * @param stime Struct representing time. | ||
12 | * @return String representing time that will be set to calendar's text. | ||
13 | * | ||
14 | * @see elm_calendar_format_function_set() | ||
15 | */ | ||
16 | typedef char * (*Efl_Ui_Calendar_Format_Cb)(struct tm *stime); | ||
17 | |||
18 | |||
19 | /** | ||
20 | * @} | ||
21 | */ | ||
diff --git a/src/lib/elementary/efl_ui_calendar_private.h b/src/lib/elementary/efl_ui_calendar_private.h new file mode 100644 index 0000000000..7ae261cfa9 --- /dev/null +++ b/src/lib/elementary/efl_ui_calendar_private.h | |||
@@ -0,0 +1,65 @@ | |||
1 | #ifndef EFL_UI_CALENDAR_PRIVATE_H | ||
2 | #define ELM_WIDGET_CALENDAR_H | ||
3 | |||
4 | #include "Elementary.h" | ||
5 | |||
6 | /* DO NOT USE THIS HEADER UNLESS YOU ARE PREPARED FOR BREAKING OF YOUR | ||
7 | * CODE. THIS IS ELEMENTARY'S INTERNAL WIDGET API (for now) AND IS NOT | ||
8 | * FINAL. CALL elm_widget_api_check(ELM_INTERNAL_API_VERSION) TO CHECK | ||
9 | * IT AT RUNTIME. | ||
10 | */ | ||
11 | |||
12 | /** | ||
13 | * @addtogroup Widget | ||
14 | * @{ | ||
15 | * | ||
16 | * @section elm-calendar-class The Elementary Calendar Class | ||
17 | * | ||
18 | * Elementary, besides having the @ref Calendar widget, exposes its | ||
19 | * foundation -- the Elementary Calendar Class -- in order to create other | ||
20 | * widgets which are a calendar with some more logic on top. | ||
21 | */ | ||
22 | |||
23 | /** | ||
24 | * Base layout smart data extended with calendar instance data. | ||
25 | */ | ||
26 | typedef struct _Efl_Ui_Calendar_Data Efl_Ui_Calendar_Data; | ||
27 | |||
28 | struct _Efl_Ui_Calendar_Data | ||
29 | { | ||
30 | Evas_Object *obj; // the object itself | ||
31 | double interval, first_interval; | ||
32 | int spin_speed; | ||
33 | int today_it, selected_it, focused_it; | ||
34 | Ecore_Timer *spin_month, *spin_year, *update_timer; | ||
35 | Efl_Ui_Calendar_Format_Cb format_func; | ||
36 | const char *weekdays[ELM_DAY_LAST]; | ||
37 | struct tm current_date, shown_date, date, date_min, date_max; | ||
38 | Evas_Object *inc_btn_month; | ||
39 | Evas_Object *dec_btn_month; | ||
40 | Evas_Object *month_access; | ||
41 | Evas_Object *inc_btn_year; | ||
42 | Evas_Object *dec_btn_year; | ||
43 | Evas_Object *year_access; | ||
44 | Eo *items[42]; | ||
45 | |||
46 | Efl_Ui_Calendar_Weekday first_week_day; | ||
47 | |||
48 | unsigned char first_day_it; | ||
49 | |||
50 | Eina_Bool selected : 1; | ||
51 | Eina_Bool double_spinners : 1; | ||
52 | Eina_Bool filling : 1; | ||
53 | Eina_Bool weekdays_set : 1; | ||
54 | Eina_Bool month_repeated : 1; | ||
55 | Eina_Bool year_repeated : 1; | ||
56 | }; | ||
57 | |||
58 | /** | ||
59 | * @} | ||
60 | */ | ||
61 | |||
62 | #define EFL_UI_CALENDAR_DATA_GET(o, sd) \ | ||
63 | Efl_Ui_Calendar_Data * sd = efl_data_scope_get(o, EFL_UI_CALENDAR_CLASS) | ||
64 | |||
65 | #endif | ||