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