tizen 2.4 release
[framework/uifw/elementary.git] / src / modules / datetime_input_ctxpopup / datetime_input_ctxpopup.c
1 #ifdef HAVE_CONFIG_H
2 #include "elementary_config.h"
3 #endif
4
5 #include <Elementary.h>
6 #include "elm_widget.h"
7 #include "elm_widget_datetime.h"
8
9 #define DATETIME_FIELD_COUNT    6
10 #define FIELD_FORMAT_LEN        3
11 #define DISKSELECTOR_MIN_ITEMS  4
12 #define BUFF_SIZE               1024
13
14 typedef struct _Ctxpopup_Module_Data Ctxpopup_Module_Data;
15 typedef struct _DiskItem_Data DiskItem_Data;
16
17 struct _Ctxpopup_Module_Data
18 {
19    Elm_Datetime_Module_Data mod_data;
20    Evas_Object *ctxpopup;
21 };
22
23 struct _DiskItem_Data
24 {
25    Ctxpopup_Module_Data *ctx_mod;
26    Elm_Datetime_Field_Type  sel_field_type;
27    unsigned int sel_field_value;
28 };
29
30 static void
31 _diskselector_item_free_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
32 {
33    if (data) free(data);
34 }
35
36 static void
37 _ctxpopup_dismissed_cb(void *data EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED )
38 {
39    Evas_Object *diskselector;
40
41    diskselector = elm_object_content_unset(obj);
42    if (diskselector) evas_object_del(diskselector);
43 }
44
45 static void
46 _datetime_resize_cb(void *data, Evas *e EINA_UNUSED,Evas_Object *obj EINA_UNUSED,
47                     void *event_info EINA_UNUSED)
48 {
49    Ctxpopup_Module_Data *ctx_mod;
50
51    ctx_mod = (Ctxpopup_Module_Data *)data;
52    if (!ctx_mod || !ctx_mod->ctxpopup) return;
53
54    evas_object_hide(ctx_mod->ctxpopup);
55 }
56
57 static void
58 _datetime_move_cb(void *data, Evas *e EINA_UNUSED,Evas_Object *obj EINA_UNUSED,
59                   void *event_info EINA_UNUSED)
60 {
61    Ctxpopup_Module_Data *ctx_mod;
62
63    ctx_mod = (Ctxpopup_Module_Data *)data;
64    if (!ctx_mod || !ctx_mod->ctxpopup) return;
65
66    evas_object_hide(ctx_mod->ctxpopup);
67 }
68
69 static void
70 _field_value_set(struct tm *tim, Elm_Datetime_Field_Type  field_type, int val)
71 {
72    if (field_type >= DATETIME_FIELD_COUNT - 1) return;
73
74    int *timearr[]= { &tim->tm_year, &tim->tm_mon, &tim->tm_mday, &tim->tm_hour, &tim->tm_min };
75    *timearr[field_type] = val;
76 }
77
78 static int
79 _field_value_get(struct tm *tim, Elm_Datetime_Field_Type  field_type)
80 {
81    if (field_type >= DATETIME_FIELD_COUNT - 1) return -1;
82
83    int *timearr[]= { &tim->tm_year, &tim->tm_mon, &tim->tm_mday, &tim->tm_hour, &tim->tm_min };
84    return (*timearr[field_type]);
85 }
86
87 static void
88 _diskselector_cb(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info)
89 {
90    DiskItem_Data *disk_data;
91    struct tm curr_time;
92    const char *fmt;
93
94    disk_data = (DiskItem_Data *)elm_object_item_data_get(event_info);
95    if (!disk_data || !(disk_data->ctx_mod)) return;
96
97    elm_datetime_value_get(disk_data->ctx_mod->mod_data.base, &curr_time);
98    fmt = disk_data->ctx_mod->mod_data.field_format_get(disk_data->ctx_mod->mod_data.base, disk_data->sel_field_type);
99    if ((disk_data->sel_field_type == ELM_DATETIME_HOUR) && ((!strncmp(fmt, "%I", FIELD_FORMAT_LEN)) ||
100         (!strncmp(fmt, "%l", FIELD_FORMAT_LEN))) && (curr_time.tm_hour >= 12))
101      disk_data->sel_field_value += 12;
102    _field_value_set(&curr_time, disk_data->sel_field_type, disk_data->sel_field_value);
103    elm_datetime_value_set(disk_data->ctx_mod->mod_data.base, &curr_time);
104    evas_object_hide(disk_data->ctx_mod->ctxpopup);
105 }
106
107 static void
108 _ampm_clicked_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
109 {
110    Ctxpopup_Module_Data *ctx_mod;
111    struct tm curr_time;
112
113    ctx_mod = (Ctxpopup_Module_Data *)data;
114    if (!ctx_mod) return;
115
116    elm_datetime_value_get(ctx_mod->mod_data.base, &curr_time);
117    if (curr_time.tm_hour >= 12) curr_time.tm_hour -= 12;
118    else curr_time.tm_hour += 12;
119    elm_datetime_value_set(ctx_mod->mod_data.base, &curr_time);
120 }
121
122 static void
123 _field_clicked_cb(void *data, Evas_Object *obj, void *event_info EINA_UNUSED)
124 {
125    Ctxpopup_Module_Data *ctx_mod;
126    Evas_Object *diskselector;
127    Elm_Object_Item *item;
128    DiskItem_Data *disk_data;
129    Elm_Datetime_Field_Type  field_type;
130    time_t t;
131    struct tm time1;
132    char buf[BUFF_SIZE], label[BUFF_SIZE];
133    const char *fmt;
134    int idx, min, max, val;
135    unsigned int display_item_num, text_len = 0;
136    Evas_Coord x = 0, y = 0, w = 0, h = 0, width;
137
138    ctx_mod = (Ctxpopup_Module_Data *)data;
139    if (!ctx_mod) return;
140
141    snprintf(buf, sizeof(buf), "datetime/%s", elm_object_style_get(obj));
142
143    if (!ctx_mod->ctxpopup)
144      {
145         ctx_mod->ctxpopup = elm_ctxpopup_add(obj);
146         elm_object_style_set(ctx_mod->ctxpopup, buf);
147         elm_ctxpopup_horizontal_set(ctx_mod->ctxpopup, EINA_TRUE);
148         evas_object_size_hint_weight_set(ctx_mod->ctxpopup, EVAS_HINT_EXPAND,
149                                          EVAS_HINT_EXPAND);
150         evas_object_size_hint_align_set(ctx_mod->ctxpopup, EVAS_HINT_FILL, 0.5);
151         evas_object_smart_callback_add(ctx_mod->ctxpopup, "dismissed",
152                                        _ctxpopup_dismissed_cb, ctx_mod);
153      }
154
155    elm_ctxpopup_hover_parent_set(ctx_mod->ctxpopup, elm_widget_top_get(obj));
156
157    // because of the diskselector behaviour, it is being recreated
158    diskselector = elm_diskselector_add(elm_widget_top_get(ctx_mod->mod_data.base));
159    evas_object_smart_callback_add(diskselector, "clicked", _diskselector_cb, NULL);
160    elm_object_style_set(diskselector, buf);
161    elm_object_content_set(ctx_mod->ctxpopup, diskselector);
162
163    t = time(NULL);
164    localtime_r(&t, &time1);
165
166    field_type = (Elm_Datetime_Field_Type )evas_object_data_get(obj, "_field_type");
167    fmt = ctx_mod->mod_data.field_format_get(ctx_mod->mod_data.base, field_type);
168    elm_datetime_value_get(ctx_mod->mod_data.base, &time1);
169    val = _field_value_get(&time1, field_type);
170    ctx_mod->mod_data.field_limit_get(ctx_mod->mod_data.base, field_type, &min, &max);
171
172    time1.tm_mday = 1;   // To avoid month wrapping, set the first day of the month to start with.
173
174    if ((field_type == ELM_DATETIME_HOUR) && ((!strncmp(fmt, "%I", FIELD_FORMAT_LEN)) ||
175         (!strncmp(fmt, "%l", FIELD_FORMAT_LEN))))
176      {
177         if (max >= 12) max -= 12;
178         if (val >= 12) val -= 12;
179         if (min >= 12) min -= 12;
180      }
181    for (idx = min; idx <= max; idx++)
182      {
183         _field_value_set(&time1, field_type, idx);
184         strftime(label, BUFF_SIZE, fmt, &time1);
185         if (strlen(label) > text_len) text_len = strlen(label);
186         if (idx == val)
187           {
188              item = elm_diskselector_item_append(diskselector, label, NULL, NULL, NULL);
189              elm_diskselector_item_selected_set(item, EINA_TRUE);
190           }
191         else
192           {
193              disk_data = (DiskItem_Data *) malloc (sizeof(DiskItem_Data));
194              if (!disk_data) return;
195
196              disk_data->ctx_mod = ctx_mod;
197              disk_data->sel_field_type = field_type;
198              disk_data->sel_field_value = idx;
199              item = elm_diskselector_item_append(diskselector, label, NULL, NULL, disk_data);
200              elm_object_item_del_cb_set(item, _diskselector_item_free_cb);
201           }
202      }
203    elm_diskselector_side_text_max_length_set(diskselector, text_len);
204
205    evas_object_geometry_get(obj, &x, &y, &w, &h);
206    evas_object_geometry_get(elm_widget_top_get(ctx_mod->mod_data.base), NULL, NULL, &width, NULL);
207    evas_object_size_hint_min_set(ctx_mod->ctxpopup, width, -1);
208    display_item_num = width / (w + elm_config_finger_size_get());
209    // always display even number of items to avoid autoselection
210    if (display_item_num % 2) display_item_num -= 1;
211    if (display_item_num < DISKSELECTOR_MIN_ITEMS)
212      display_item_num = DISKSELECTOR_MIN_ITEMS;
213    elm_diskselector_display_item_num_set(diskselector, display_item_num);
214    elm_diskselector_round_enabled_set(diskselector, EINA_TRUE);
215
216    elm_ctxpopup_direction_priority_set(ctx_mod->ctxpopup, ELM_CTXPOPUP_DIRECTION_DOWN,
217                                        ELM_CTXPOPUP_DIRECTION_UP, -1, -1);
218    evas_object_move(ctx_mod->ctxpopup, (x+w/2), (y+h));
219
220    // if the direction of Ctxpopup is upwards, move it to the top of datetime
221    if (elm_ctxpopup_direction_get (ctx_mod->ctxpopup) == ELM_CTXPOPUP_DIRECTION_UP)
222      {
223         elm_ctxpopup_direction_priority_set(ctx_mod->ctxpopup, ELM_CTXPOPUP_DIRECTION_UP,
224                                             ELM_CTXPOPUP_DIRECTION_DOWN, -1, -1);
225         evas_object_move(ctx_mod->ctxpopup, (x+w/2), y);
226      }
227    evas_object_show(ctx_mod->ctxpopup);
228 }
229
230 static void
231 _access_set(Evas_Object *obj, Elm_Datetime_Field_Type field_type)
232 {
233    const char* type = NULL;
234
235    switch (field_type)
236      {
237                  case ELM_DATETIME_YEAR:
238         type = "datetime field, year";
239         break;
240
241       case ELM_DATETIME_MONTH:
242         type = "datetime field, month";
243         break;
244
245           case ELM_DATETIME_DATE:
246         type = "datetime field, date";
247         break;
248
249       case ELM_DATETIME_HOUR:
250         type = "datetime field, hour";
251         break;
252
253       case ELM_DATETIME_MINUTE:
254         type = "datetime field, minute";
255         break;
256
257       case ELM_DATETIME_AMPM:
258         type = "datetime field, AM PM";
259         break;
260
261       default:
262         break;
263      }
264
265    _elm_access_text_set
266      (_elm_access_info_get(obj), ELM_ACCESS_TYPE, type);
267    _elm_access_callback_set
268      (_elm_access_info_get(obj), ELM_ACCESS_STATE, NULL, NULL);
269 }
270
271 // module fucns for the specific module type
272 EAPI void
273 field_value_display(Elm_Datetime_Module_Data *module_data, Evas_Object *obj)
274 {
275    Ctxpopup_Module_Data *ctx_mod;
276    Elm_Datetime_Field_Type  field_type;
277    struct tm tim;
278    char buf[BUFF_SIZE];
279    const char *fmt;
280
281    ctx_mod = (Ctxpopup_Module_Data *)module_data;
282    if (!ctx_mod || !obj) return;
283
284    elm_datetime_value_get(ctx_mod->mod_data.base, &tim);
285    field_type = (Elm_Datetime_Field_Type )evas_object_data_get(obj, "_field_type");
286    fmt = ctx_mod->mod_data.field_format_get(ctx_mod->mod_data.base, field_type);
287    buf[0] = 0;
288    strftime(buf, sizeof(buf), fmt, &tim);
289    if ((!buf[0]) && ((!strcmp(fmt, "%p")) || (!strcmp(fmt, "%P"))))
290      {
291         // yes BUFF_SIZE is more than 2 bytes!
292         if (tim.tm_hour < 12) strcpy(buf, "AM");
293         else strcpy(buf, "PM");
294      }
295    elm_object_text_set(obj, buf);
296 }
297
298 EAPI Evas_Object *
299 field_create(Elm_Datetime_Module_Data *module_data, Elm_Datetime_Field_Type  field_type)
300 {
301    Ctxpopup_Module_Data *ctx_mod;
302    Evas_Object *field_obj;
303
304    ctx_mod = (Ctxpopup_Module_Data *)module_data;
305    if (!ctx_mod) return NULL;
306
307    if (field_type == ELM_DATETIME_AMPM)
308      {
309         field_obj = elm_button_add(ctx_mod->mod_data.base);
310         evas_object_smart_callback_add(field_obj, "clicked", _ampm_clicked_cb, ctx_mod);
311      }
312    else
313      {
314         field_obj = elm_entry_add(ctx_mod->mod_data.base);
315         elm_entry_single_line_set(field_obj, EINA_TRUE);
316         elm_entry_editable_set(field_obj, EINA_FALSE);
317         elm_entry_input_panel_enabled_set(field_obj, EINA_FALSE);
318         elm_entry_context_menu_disabled_set(field_obj, EINA_TRUE);
319         evas_object_smart_callback_add(field_obj, "clicked", _field_clicked_cb, ctx_mod);
320      }
321    evas_object_data_set(field_obj, "_field_type", (void *)field_type);
322
323    // ACCESS
324    _access_set(field_obj, field_type);
325
326    return field_obj;
327 }
328
329 EAPI Elm_Datetime_Module_Data *
330 obj_hook(Evas_Object *obj)
331 {
332    Ctxpopup_Module_Data *ctx_mod;
333    ctx_mod = calloc(1, sizeof(Ctxpopup_Module_Data));
334    if (!ctx_mod) return NULL;
335
336    evas_object_event_callback_add(obj, EVAS_CALLBACK_RESIZE,
337                                   _datetime_resize_cb, ctx_mod);
338    evas_object_event_callback_add(obj, EVAS_CALLBACK_MOVE,
339                                   _datetime_move_cb, ctx_mod);
340
341    return ((Elm_Datetime_Module_Data*)ctx_mod);
342 }
343
344 EAPI void
345 obj_unhook(Elm_Datetime_Module_Data *module_data)
346 {
347    Ctxpopup_Module_Data *ctx_mod;
348
349    ctx_mod = (Ctxpopup_Module_Data *)module_data;
350    if (!ctx_mod) return;
351
352    if (ctx_mod->ctxpopup)
353      evas_object_del(ctx_mod->ctxpopup);
354
355    if (ctx_mod)
356      {
357           free(ctx_mod);
358           ctx_mod = NULL;
359       }
360 }
361
362 EAPI void
363 obj_hide(Elm_Datetime_Module_Data *module_data)
364 {
365    Ctxpopup_Module_Data *ctx_mod;
366
367    ctx_mod = (Ctxpopup_Module_Data *)module_data;
368    if (!ctx_mod) return;
369
370    if (ctx_mod->ctxpopup)
371      evas_object_hide(ctx_mod->ctxpopup);
372 }
373
374 // module api funcs needed
375 EAPI int
376 elm_modapi_init(void *m EINA_UNUSED)
377 {
378    return 1; // succeed always
379 }
380
381 EAPI int
382 elm_modapi_shutdown(void *m EINA_UNUSED)
383 {
384    return 1; // succeed always
385 }