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