elm genlist: Fixed genlist crash issue when huge amount of items are expanded/contrac...
[framework/uifw/elementary.git] / src / lib / elc_fileselector_button.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 typedef struct _Widget_Data Widget_Data;
5
6 struct _Widget_Data
7 {
8    Evas_Object *self, *btn, *fs, *fsw;
9    const char  *window_title;
10    Evas_Coord   w, h;
11    struct
12    {
13       const char *path;
14       Eina_Bool   expandable : 1;
15       Eina_Bool   folder_only : 1;
16       Eina_Bool   is_save : 1;
17    } fsd;
18    Eina_Bool inwin_mode : 1;
19 };
20
21 #define DEFAULT_WINDOW_TITLE "Select a file"
22
23 static const char *widtype = NULL;
24
25 static void _del_pre_hook(Evas_Object *obj);
26 static void _del_hook(Evas_Object *obj);
27 static void _theme_hook(Evas_Object *obj);
28 static void _disable_hook(Evas_Object *obj);
29 static void _sizing_eval(Evas_Object *obj);
30 static void _changed_size_hints(void        *data,
31                                 Evas        *e,
32                                 Evas_Object *obj,
33                                 void        *event_info);
34 static void _button_clicked(void        *data,
35                             Evas_Object *obj,
36                             void        *event_info);
37 static void _on_focus_hook(void        *data,
38                            Evas_Object *obj);
39 static void _selection_done(void        *data,
40                             Evas_Object *obj,
41                             void        *event_info);
42 static void _activate(Widget_Data *wd);
43
44 static const char SIG_FILE_CHOSEN[] = "file,chosen";
45 static const Evas_Smart_Cb_Description _signals[] = {
46        {SIG_FILE_CHOSEN, "s"},
47        {NULL, NULL}
48 };
49
50 static void
51 _del_pre_hook(Evas_Object *obj)
52 {
53    Widget_Data *wd = elm_widget_data_get(obj);
54    if (!wd) return;
55    evas_object_event_callback_del_full(wd->btn, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
56                                        _changed_size_hints, obj);
57    evas_object_smart_callback_del(wd->btn, "clicked", _button_clicked);
58 }
59
60 static void
61 _del_hook(Evas_Object *obj)
62 {
63    Evas_Object *win;
64    Widget_Data *wd;
65
66    wd = elm_widget_data_get(obj);
67    if (!wd) return;
68
69    if (wd->window_title) eina_stringshare_del(wd->window_title);
70    if (wd->fsd.path) eina_stringshare_del(wd->fsd.path);
71    if (wd->fs)
72      {
73         win = evas_object_data_del(obj, "win");
74         evas_object_del(win);
75      }
76    free(wd);
77 }
78
79 static void
80 _on_focus_hook(void *data   __UNUSED__,
81                Evas_Object *obj)
82 {
83    Widget_Data *wd = elm_widget_data_get(obj);
84    if (!wd) return;
85    if (elm_widget_focus_get(obj))
86      elm_widget_focus_steal(wd->btn);
87 }
88
89 static void
90 _mirrored_set(Evas_Object *obj, Eina_Bool rtl)
91 {
92    Widget_Data *wd = elm_widget_data_get(obj);
93    if (!wd) return;
94    elm_widget_mirrored_set(wd->btn, rtl);
95    elm_widget_mirrored_set(wd->fs, rtl);
96 }
97
98 static void
99 _theme_hook(Evas_Object *obj)
100 {
101    Widget_Data *wd = elm_widget_data_get(obj);
102    char buf[4096];
103    if (!wd) return;
104    _elm_widget_mirrored_reload(obj);
105    _mirrored_set(obj, elm_widget_mirrored_get(obj));
106
107    snprintf(buf, sizeof(buf), "fileselector_button/%s",
108             elm_widget_style_get(obj));
109    elm_object_style_set(wd->btn, buf);
110    _sizing_eval(obj);
111 }
112
113 static void
114 _disable_hook(Evas_Object *obj)
115 {
116    Widget_Data *wd = elm_widget_data_get(obj);
117    if (!wd) return;
118    elm_widget_disabled_set(wd->btn, elm_widget_disabled_get(obj));
119 }
120
121 static void
122 _sizing_eval(Evas_Object *obj)
123 {
124    Widget_Data *wd = elm_widget_data_get(obj);
125    Evas_Coord minw = -1, minh = -1, maxw = -1, maxh = -1;
126    if (!wd) return;
127    evas_object_size_hint_min_get(wd->btn, &minw, &minh);
128    evas_object_size_hint_max_get(wd->btn, &maxw, &maxh);
129    evas_object_size_hint_min_set(obj, minw, minh);
130    evas_object_size_hint_max_set(obj, maxw, maxh);
131 }
132
133 static void
134 _changed_size_hints(void            *data,
135                     Evas *e          __UNUSED__,
136                     Evas_Object *obj __UNUSED__,
137                     void *event_info __UNUSED__)
138 {
139    Widget_Data *wd = elm_widget_data_get(data);
140    if (!wd) return;
141    _sizing_eval(data);
142 }
143
144 static void
145 _activate_hook(Evas_Object *obj)
146 {
147    Widget_Data *wd;
148    wd = elm_widget_data_get(obj);
149    if (!wd) return;
150    _activate(wd);
151 }
152
153 static void
154 _button_clicked(void            *data,
155                 Evas_Object *obj __UNUSED__,
156                 void *event_info __UNUSED__)
157 {
158    _activate(data);
159 }
160
161 static Evas_Object *
162 _parent_win_get(Evas_Object *obj)
163 {
164    while ((obj) && (strcmp(elm_widget_type_get(obj), "win")))
165      obj = elm_object_parent_widget_get(obj);
166
167    return obj;
168 }
169
170 static Evas_Object *
171 _new_window_add(Widget_Data *wd)
172 {
173    Evas_Object *win, *bg;
174
175    win = elm_win_add(NULL, "fileselector_button", ELM_WIN_DIALOG_BASIC);
176    elm_win_title_set(win, wd->window_title);
177    elm_win_autodel_set(win, EINA_TRUE);
178
179    bg = elm_bg_add(win);
180    elm_win_resize_object_add(win, bg);
181    evas_object_size_hint_weight_set(bg, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
182    evas_object_show(bg);
183
184    evas_object_resize(win, wd->w, wd->h);
185    return win;
186 }
187
188 static void
189 _activate(Widget_Data *wd)
190 {
191    Eina_Bool is_inwin = EINA_FALSE;
192
193    if (wd->fs) return;
194
195    if (wd->inwin_mode)
196      {
197         wd->fsw = _parent_win_get(wd->self);
198
199         if (!wd->fsw)
200           wd->fsw = _new_window_add(wd);
201         else
202           {
203              wd->fsw = elm_win_inwin_add(wd->fsw);
204              is_inwin = EINA_TRUE;
205           }
206      }
207    else
208      wd->fsw = _new_window_add(wd);
209
210    wd->fs = elm_fileselector_add(wd->fsw);
211    elm_widget_mirrored_set(wd->fs, elm_widget_mirrored_get(wd->self));
212    elm_widget_mirrored_automatic_set(wd->fs, EINA_FALSE);
213    elm_fileselector_expandable_set(wd->fs, wd->fsd.expandable);
214    elm_fileselector_folder_only_set(wd->fs, wd->fsd.folder_only);
215    elm_fileselector_is_save_set(wd->fs, wd->fsd.is_save);
216    elm_fileselector_selected_set(wd->fs, wd->fsd.path);
217    evas_object_size_hint_weight_set(wd->fs, EVAS_HINT_EXPAND,
218                                     EVAS_HINT_EXPAND);
219    evas_object_size_hint_align_set(wd->fs, EVAS_HINT_FILL, EVAS_HINT_FILL);
220    evas_object_smart_callback_add(wd->fs, "done", _selection_done, wd);
221    evas_object_show(wd->fs);
222
223    if (is_inwin)
224      {
225         elm_win_inwin_content_set(wd->fsw, wd->fs);
226         elm_win_inwin_activate(wd->fsw);
227      }
228    else
229      {
230         elm_win_resize_object_add(wd->fsw, wd->fs);
231         evas_object_show(wd->fsw);
232      }
233 }
234
235 static void
236 _selection_done(void            *data,
237                 Evas_Object *obj __UNUSED__,
238                 void            *event_info)
239 {
240    const char *file = event_info;
241    Widget_Data *wd = data;
242    Evas_Object *del;
243    if (!wd) return;
244
245    if (file) eina_stringshare_replace(&wd->fsd.path, file);
246
247    del = wd->fsw;
248    wd->fs = NULL;
249    wd->fsw = NULL;
250    evas_object_del(del);
251
252    evas_object_smart_callback_call(wd->self, SIG_FILE_CHOSEN,
253                                    (void *)wd->fsd.path);
254 }
255
256 static void
257 _elm_fileselector_button_label_set(Evas_Object *obj, const char *item,
258                                   const char  *label)
259 {
260    ELM_CHECK_WIDTYPE(obj, widtype);
261    if (item && strcmp(item, "default")) return;
262    Widget_Data *wd = elm_widget_data_get(obj);
263    if (!wd) return;
264    elm_object_text_set(wd->btn, label);
265 }
266
267 static const char *
268 _elm_fileselector_button_label_get(const Evas_Object *obj, const char *item)
269 {
270    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
271    Widget_Data *wd = elm_widget_data_get(obj);
272    if (item && strcmp(item, "default")) return NULL;
273    if (!wd) return NULL;
274    return elm_object_text_get(wd->btn);
275 }
276
277 static void
278 _content_set_hook(Evas_Object *obj, const char *part, Evas_Object *content)
279 {
280    ELM_CHECK_WIDTYPE(obj, widtype);
281    Widget_Data *wd = elm_widget_data_get(obj);
282    if (!wd) return;
283    elm_object_part_content_set(wd->btn, part, content);
284 }
285
286 static Evas_Object *
287 _content_get_hook(const Evas_Object *obj, const char *part)
288 {
289    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
290    Widget_Data *wd = elm_widget_data_get(obj);
291    if (!wd) return NULL;
292    return elm_object_part_content_get(wd->btn, part);
293 }
294
295 static Evas_Object *
296 _content_unset_hook(Evas_Object *obj, const char *part)
297 {
298    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
299    Widget_Data *wd = elm_widget_data_get(obj);
300    if (!wd) return NULL;
301    return elm_object_part_content_unset(wd->btn, part);
302 }
303
304 EAPI Evas_Object *
305 elm_fileselector_button_add(Evas_Object *parent)
306 {
307    Evas_Object *obj;
308    Evas *e;
309    Widget_Data *wd;
310
311    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
312
313    ELM_SET_WIDTYPE(widtype, "fileselector_button");
314    elm_widget_type_set(obj, "fileselector_button");
315    elm_widget_sub_object_add(parent, obj);
316    elm_widget_on_focus_hook_set(obj, _on_focus_hook, NULL);
317    elm_widget_data_set(obj, wd);
318    elm_widget_del_pre_hook_set(obj, _del_pre_hook);
319    elm_widget_del_hook_set(obj, _del_hook);
320    elm_widget_theme_hook_set(obj, _theme_hook);
321    elm_widget_disable_hook_set(obj, _disable_hook);
322    elm_widget_can_focus_set(obj, EINA_TRUE);
323    elm_widget_activate_hook_set(obj, _activate_hook);
324    elm_widget_text_set_hook_set(obj, _elm_fileselector_button_label_set);
325    elm_widget_text_get_hook_set(obj, _elm_fileselector_button_label_get);
326    elm_widget_content_set_hook_set(obj, _content_set_hook);
327    elm_widget_content_get_hook_set(obj, _content_get_hook);
328    elm_widget_content_unset_hook_set(obj, _content_unset_hook);
329
330    wd->self = obj;
331    wd->window_title = eina_stringshare_add(DEFAULT_WINDOW_TITLE);
332    if (getenv("HOME")) wd->fsd.path = eina_stringshare_add(getenv("HOME"));
333    else wd->fsd.path = eina_stringshare_add("/");
334    wd->fsd.expandable = _elm_config->fileselector_expand_enable;
335    wd->inwin_mode = _elm_config->inwin_dialogs_enable;
336    wd->w = 400;
337    wd->h = 400;
338
339    wd->btn = elm_button_add(parent);
340    elm_widget_mirrored_automatic_set(wd->btn, EINA_FALSE);
341    elm_widget_resize_object_set(obj, wd->btn);
342    evas_object_event_callback_add(wd->btn, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
343                                   _changed_size_hints, obj);
344    evas_object_smart_callback_add(wd->btn, "clicked", _button_clicked, wd);
345    elm_widget_sub_object_add(obj, wd->btn);
346
347    _theme_hook(obj);
348    evas_object_smart_callbacks_descriptions_set(obj, _signals);
349    return obj;
350 }
351
352
353 EAPI void
354 elm_fileselector_button_window_title_set(Evas_Object *obj,
355                                          const char  *title)
356 {
357    ELM_CHECK_WIDTYPE(obj, widtype);
358    Widget_Data *wd = elm_widget_data_get(obj);
359
360    if (!wd) return;
361    eina_stringshare_replace(&wd->window_title, title);
362
363    if (wd->fsw)
364      elm_win_title_set(wd->fsw, wd->window_title);
365 }
366
367 EAPI const char *
368 elm_fileselector_button_window_title_get(const Evas_Object *obj)
369 {
370    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
371    Widget_Data *wd = elm_widget_data_get(obj);
372
373    if (!wd) return NULL;
374    return wd->window_title;
375 }
376
377 EAPI void
378 elm_fileselector_button_window_size_set(Evas_Object *obj,
379                                         Evas_Coord   width,
380                                         Evas_Coord   height)
381 {
382    ELM_CHECK_WIDTYPE(obj, widtype);
383    Widget_Data *wd = elm_widget_data_get(obj);
384
385    if (!wd) return;
386    wd->w = width;
387    wd->h = height;
388
389    if (wd->fsw)
390      evas_object_resize(wd->fsw, wd->w, wd->h);
391 }
392
393 EAPI void
394 elm_fileselector_button_window_size_get(const Evas_Object *obj,
395                                         Evas_Coord        *width,
396                                         Evas_Coord        *height)
397 {
398    ELM_CHECK_WIDTYPE(obj, widtype);
399    Widget_Data *wd = elm_widget_data_get(obj);
400
401    if (!wd) return;
402    if (width) *width = wd->w;
403    if (height) *height = wd->h;
404 }
405
406 EAPI void
407 elm_fileselector_button_path_set(Evas_Object *obj,
408                                  const char  *path)
409 {
410    ELM_CHECK_WIDTYPE(obj, widtype);
411    Widget_Data *wd = elm_widget_data_get(obj);
412
413    if (!wd) return;
414    eina_stringshare_replace(&wd->fsd.path, path);
415
416    if (wd->fs)
417      elm_fileselector_selected_set(wd->fs, wd->fsd.path);
418 }
419
420 EAPI const char *
421 elm_fileselector_button_path_get(const Evas_Object *obj)
422 {
423    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
424    Widget_Data *wd = elm_widget_data_get(obj);
425    if (!wd) return NULL;
426    return wd->fsd.path;
427 }
428
429 EAPI void
430 elm_fileselector_button_expandable_set(Evas_Object *obj,
431                                        Eina_Bool    value)
432 {
433    ELM_CHECK_WIDTYPE(obj, widtype);
434    Widget_Data *wd = elm_widget_data_get(obj);
435
436    if (!wd) return;
437    wd->fsd.expandable = value;
438
439    if (wd->fs)
440      elm_fileselector_expandable_set(wd->fs, wd->fsd.expandable);
441 }
442
443 EAPI Eina_Bool
444 elm_fileselector_button_expandable_get(const Evas_Object *obj)
445 {
446    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
447    Widget_Data *wd = elm_widget_data_get(obj);
448
449    if (!wd) return EINA_FALSE;
450    return wd->fsd.expandable;
451 }
452
453 EAPI void
454 elm_fileselector_button_folder_only_set(Evas_Object *obj,
455                                         Eina_Bool    value)
456 {
457    ELM_CHECK_WIDTYPE(obj, widtype);
458    Widget_Data *wd = elm_widget_data_get(obj);
459
460    if (!wd) return;
461    wd->fsd.folder_only = value;
462
463    if (wd->fs)
464      elm_fileselector_folder_only_set(wd->fs, wd->fsd.folder_only);
465 }
466
467 EAPI Eina_Bool
468 elm_fileselector_button_folder_only_get(const Evas_Object *obj)
469 {
470    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
471    Widget_Data *wd = elm_widget_data_get(obj);
472
473    if (!wd) return EINA_FALSE;
474    return wd->fsd.folder_only;
475 }
476
477 EAPI void
478 elm_fileselector_button_is_save_set(Evas_Object *obj,
479                                     Eina_Bool    value)
480 {
481    ELM_CHECK_WIDTYPE(obj, widtype);
482    Widget_Data *wd = elm_widget_data_get(obj);
483
484    if (!wd) return;
485    wd->fsd.is_save = value;
486
487    if (wd->fs)
488      elm_fileselector_is_save_set(wd->fs, wd->fsd.is_save);
489 }
490
491 EAPI Eina_Bool
492 elm_fileselector_button_is_save_get(const Evas_Object *obj)
493 {
494    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
495    Widget_Data *wd = elm_widget_data_get(obj);
496
497    if (!wd) return EINA_FALSE;
498    return wd->fsd.is_save;
499 }
500
501 EAPI void
502 elm_fileselector_button_inwin_mode_set(Evas_Object *obj,
503                                        Eina_Bool    value)
504 {
505    ELM_CHECK_WIDTYPE(obj, widtype);
506    Widget_Data *wd = elm_widget_data_get(obj);
507
508    if (!wd) return;
509    wd->inwin_mode = value;
510 }
511
512 EAPI Eina_Bool
513 elm_fileselector_button_inwin_mode_get(const Evas_Object *obj)
514 {
515    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
516    Widget_Data *wd = elm_widget_data_get(obj);
517
518    if (!wd) return EINA_FALSE;
519    return wd->inwin_mode;
520 }