Merge branch 'master' into svn_merge
[framework/uifw/elementary.git] / src / lib / elc_fileselector_button.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 /**
5  * @defgroup File_Selector_Button File Selector Button
6  *
7  * A button that, when clicked, creates an Elementary window (or inner
8  * window) with an Elementary File Selector within. When a file is
9  * chosen, the (inner) window is closed and the selected file is
10  * exposed as an evas_object_smart_callback_call() of the button.
11  *
12  * Signals that you can add callbacks for are:
13  *
14  * "file,chosen" - the user has selected a path, whose string pointer comes
15  *                 as event info
16  *
17  */
18
19 typedef struct _Widget_Data Widget_Data;
20
21 struct _Widget_Data
22 {
23    Evas_Object *self, *btn, *fs, *fsw;
24    const char  *window_title;
25    Evas_Coord   w, h;
26    struct
27    {
28       const char *path;
29       Eina_Bool   expandable : 1;
30       Eina_Bool   folder_only : 1;
31       Eina_Bool   is_save : 1;
32    } fsd;
33    Eina_Bool inwin_mode : 1;
34 };
35
36 #define DEFAULT_WINDOW_TITLE "Select a file"
37
38 static const char *widtype = NULL;
39
40 static void _del_hook(Evas_Object *obj);
41 static void _theme_hook(Evas_Object *obj);
42 static void _disable_hook(Evas_Object *obj);
43 static void _sizing_eval(Evas_Object *obj);
44 static void _changed_size_hints(void        *data,
45                                 Evas        *e,
46                                 Evas_Object *obj,
47                                 void        *event_info);
48 static void _on_focus_hook(void        *data,
49                            Evas_Object *obj);
50 static void _selection_done(void        *data,
51                             Evas_Object *obj,
52                             void        *event_info);
53 static void _activate(Widget_Data *wd);
54
55 static const char SIG_FILE_CHOSEN[] = "file,chosen";
56 static const Evas_Smart_Cb_Description _signals[] = {
57        {SIG_FILE_CHOSEN, "s"},
58        {NULL, NULL}
59 };
60
61 static void
62 _del_hook(Evas_Object *obj)
63 {
64    Evas_Object *win;
65    Widget_Data *wd;
66
67    wd = elm_widget_data_get(obj);
68    if (!wd) return;
69
70    if (wd->window_title) eina_stringshare_del(wd->window_title);
71    if (wd->fsd.path) eina_stringshare_del(wd->fsd.path);
72    if (wd->fs)
73      {
74         win = evas_object_data_del(obj, "win");
75         evas_object_del(win);
76      }
77    free(wd);
78 }
79
80 static void
81 _on_focus_hook(void *data   __UNUSED__,
82                Evas_Object *obj)
83 {
84    Widget_Data *wd = elm_widget_data_get(obj);
85    if (!wd) return;
86    if (elm_widget_focus_get(obj))
87      elm_widget_focus_steal(wd->btn);
88 }
89
90 static void
91 _mirrored_set(Evas_Object *obj, Eina_Bool rtl)
92 {
93    Widget_Data *wd = elm_widget_data_get(obj);
94    if (!wd) return;
95    elm_widget_mirrored_set(wd->btn, rtl);
96    elm_widget_mirrored_set(wd->fs, rtl);
97 }
98
99 static void
100 _theme_hook(Evas_Object *obj)
101 {
102    Widget_Data *wd = elm_widget_data_get(obj);
103    char buf[4096];
104    if (!wd) return;
105    _elm_widget_mirrored_reload(obj);
106    _mirrored_set(obj, elm_widget_mirrored_get(obj));
107
108    snprintf(buf, sizeof(buf), "fileselector_button/%s",
109             elm_widget_style_get(obj));
110    elm_object_style_set(wd->btn, buf);
111    _sizing_eval(obj);
112 }
113
114 static void
115 _disable_hook(Evas_Object *obj)
116 {
117    Widget_Data *wd = elm_widget_data_get(obj);
118    if (!wd) return;
119    elm_widget_disabled_set(wd->btn, elm_widget_disabled_get(obj));
120 }
121
122 static void
123 _sizing_eval(Evas_Object *obj)
124 {
125    Widget_Data *wd = elm_widget_data_get(obj);
126    Evas_Coord minw = -1, minh = -1, maxw = -1, maxh = -1;
127    if (!wd) return;
128    evas_object_size_hint_min_get(wd->btn, &minw, &minh);
129    evas_object_size_hint_max_get(wd->btn, &maxw, &maxh);
130    evas_object_size_hint_min_set(obj, minw, minh);
131    evas_object_size_hint_max_set(obj, maxw, maxh);
132 }
133
134 static void
135 _changed_size_hints(void            *data,
136                     Evas *e          __UNUSED__,
137                     Evas_Object *obj __UNUSED__,
138                     void *event_info __UNUSED__)
139 {
140    Widget_Data *wd = elm_widget_data_get(data);
141    if (!wd) return;
142    _sizing_eval(data);
143 }
144
145 static void
146 _activate_hook(Evas_Object *obj)
147 {
148    Widget_Data *wd;
149    wd = elm_widget_data_get(obj);
150    if (!wd) return;
151    _activate(wd);
152 }
153
154 static void
155 _button_clicked(void            *data,
156                 Evas_Object *obj __UNUSED__,
157                 void *event_info __UNUSED__)
158 {
159    _activate(data);
160 }
161
162 static Evas_Object *
163 _parent_win_get(Evas_Object *obj)
164 {
165    while ((obj) && (strcmp(elm_widget_type_get(obj), "win")))
166      obj = elm_object_parent_widget_get(obj);
167
168    return obj;
169 }
170
171 static Evas_Object *
172 _new_window_add(Widget_Data *wd)
173 {
174    Evas_Object *win, *bg;
175
176    win = elm_win_add(NULL, "fileselector_button", ELM_WIN_DIALOG_BASIC);
177    elm_win_title_set(win, wd->window_title);
178    elm_win_autodel_set(win, EINA_TRUE);
179
180    bg = elm_bg_add(win);
181    elm_win_resize_object_add(win, bg);
182    evas_object_size_hint_weight_set(bg, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
183    evas_object_show(bg);
184
185    evas_object_resize(win, wd->w, wd->h);
186    return win;
187 }
188
189 static void
190 _activate(Widget_Data *wd)
191 {
192    Eina_Bool is_inwin = EINA_FALSE;
193
194    if (wd->fs) return;
195
196    if (wd->inwin_mode)
197      {
198         wd->fsw = _parent_win_get(wd->self);
199
200         if (!wd->fsw)
201           wd->fsw = _new_window_add(wd);
202         else
203           {
204              wd->fsw = elm_win_inwin_add(wd->fsw);
205              is_inwin = EINA_TRUE;
206           }
207      }
208    else
209      wd->fsw = _new_window_add(wd);
210
211    wd->fs = elm_fileselector_add(wd->fsw);
212    elm_widget_mirrored_set(wd->fs, elm_widget_mirrored_get(wd->self));
213    elm_widget_mirrored_automatic_set(wd->fs, EINA_FALSE);
214    elm_fileselector_expandable_set(wd->fs, wd->fsd.expandable);
215    elm_fileselector_folder_only_set(wd->fs, wd->fsd.folder_only);
216    elm_fileselector_is_save_set(wd->fs, wd->fsd.is_save);
217    elm_fileselector_selected_set(wd->fs, wd->fsd.path);
218    evas_object_size_hint_weight_set(wd->fs, EVAS_HINT_EXPAND,
219                                     EVAS_HINT_EXPAND);
220    evas_object_size_hint_align_set(wd->fs, EVAS_HINT_FILL, EVAS_HINT_FILL);
221    evas_object_smart_callback_add(wd->fs, "done", _selection_done, wd);
222    evas_object_show(wd->fs);
223
224    if (is_inwin)
225      {
226         elm_win_inwin_content_set(wd->fsw, wd->fs);
227         elm_win_inwin_activate(wd->fsw);
228      }
229    else
230      {
231         elm_win_resize_object_add(wd->fsw, wd->fs);
232         evas_object_show(wd->fsw);
233      }
234 }
235
236 static void
237 _selection_done(void            *data,
238                 Evas_Object *obj __UNUSED__,
239                 void            *event_info)
240 {
241    const char *file = event_info;
242    Widget_Data *wd = data;
243    Evas_Object *del;
244    if (!wd) return;
245
246    if (file) eina_stringshare_replace(&wd->fsd.path, file);
247
248    del = wd->fsw;
249    wd->fs = NULL;
250    wd->fsw = NULL;
251    evas_object_del(del);
252
253    evas_object_smart_callback_call(wd->self, SIG_FILE_CHOSEN,
254                                    (void *)wd->fsd.path);
255 }
256
257 static void
258 _elm_fileselector_button_label_set(Evas_Object *obj, const char *item,
259                                   const char  *label)
260 {
261    ELM_CHECK_WIDTYPE(obj, widtype);
262    if (item && strcmp(item, "default")) return;
263    Widget_Data *wd = elm_widget_data_get(obj);
264    if (!wd) return;
265    elm_object_text_set(wd->btn, label);
266 }
267
268 static const char *
269 _elm_fileselector_button_label_get(const Evas_Object *obj, const char *item)
270 {
271    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
272    Widget_Data *wd = elm_widget_data_get(obj);
273    if (item && strcmp(item, "default")) return NULL;
274    if (!wd) return NULL;
275    return elm_object_text_get(wd->btn);
276 }
277
278 /**
279  * Add a new file selector button into the parent object.
280  *
281  * @param parent The parent object
282  * @return The new object or NULL if it cannot be created
283  *
284  * @ingroup File_Selector_Button
285  */
286 EAPI Evas_Object *
287 elm_fileselector_button_add(Evas_Object *parent)
288 {
289    Evas_Object *obj;
290    Evas *e;
291    Widget_Data *wd;
292
293    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
294
295    ELM_SET_WIDTYPE(widtype, "fileselector_button");
296    elm_widget_type_set(obj, "fileselector_button");
297    elm_widget_sub_object_add(parent, obj);
298    elm_widget_on_focus_hook_set(obj, _on_focus_hook, NULL);
299    elm_widget_data_set(obj, wd);
300    elm_widget_del_hook_set(obj, _del_hook);
301    elm_widget_theme_hook_set(obj, _theme_hook);
302    elm_widget_disable_hook_set(obj, _disable_hook);
303    elm_widget_can_focus_set(obj, EINA_TRUE);
304    elm_widget_activate_hook_set(obj, _activate_hook);
305    elm_widget_text_set_hook_set(obj, _elm_fileselector_button_label_set);
306    elm_widget_text_get_hook_set(obj, _elm_fileselector_button_label_get);
307
308    wd->self = obj;
309    wd->window_title = eina_stringshare_add(DEFAULT_WINDOW_TITLE);
310    if (getenv("HOME")) wd->fsd.path = eina_stringshare_add(getenv("HOME"));
311    else wd->fsd.path = eina_stringshare_add("/");
312    wd->fsd.expandable = _elm_config->fileselector_expand_enable;
313    wd->inwin_mode = _elm_config->inwin_dialogs_enable;
314    wd->w = 400;
315    wd->h = 400;
316
317    wd->btn = elm_button_add(parent);
318    elm_widget_mirrored_automatic_set(wd->btn, EINA_FALSE);
319    elm_widget_resize_object_set(obj, wd->btn);
320    evas_object_event_callback_add(wd->btn, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
321                                   _changed_size_hints, obj);
322    evas_object_smart_callback_add(wd->btn, "clicked", _button_clicked, wd);
323    elm_widget_sub_object_add(obj, wd->btn);
324
325    _theme_hook(obj);
326    evas_object_smart_callbacks_descriptions_set(obj, _signals);
327    return obj;
328 }
329
330 /**
331  * Set the label used in the file selector button.
332  *
333  * @param obj The button object
334  * @param label The text label text to be displayed on the button
335  *
336  * @ingroup File_Selector_Button
337  * @deprecated use elm_object_text_set() instead.
338  */
339 EAPI void
340 elm_fileselector_button_label_set(Evas_Object *obj,
341                                   const char  *label)
342 {
343    _elm_fileselector_button_label_set(obj, NULL, label);
344 }
345
346 /**
347  * Get the label used in the file selector button.
348  *
349  * @param obj The button object
350  * @return The button label
351  *
352  * @ingroup File_Selector_Button
353  * @deprecated use elm_object_text_set() instead.
354  */
355 EAPI const char *
356 elm_fileselector_button_label_get(const Evas_Object *obj)
357 {
358    return _elm_fileselector_button_label_get(obj, NULL);
359 }
360
361 /**
362  * Set the title of the file selector button's window.
363  *
364  * @param obj The button object
365  * @param title The title string
366  *
367  * Note that it will only take any effect if the fileselector button
368  * not at "inwin mode".
369  *
370  * @ingroup File_Selector_Button
371  */
372 EAPI void
373 elm_fileselector_button_window_title_set(Evas_Object *obj,
374                                          const char  *title)
375 {
376    ELM_CHECK_WIDTYPE(obj, widtype);
377    Widget_Data *wd = elm_widget_data_get(obj);
378
379    if (!wd) return;
380    eina_stringshare_replace(&wd->window_title, title);
381
382    if (wd->fsw)
383      elm_win_title_set(wd->fsw, wd->window_title);
384 }
385
386 /**
387  * Get the title of the file selector button's window.
388  *
389  * @param obj The button object
390  * @return Title of the file selector button's window
391  *
392  * @ingroup File_Selector_Button
393  */
394 EAPI const char *
395 elm_fileselector_button_window_title_get(const Evas_Object *obj)
396 {
397    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
398    Widget_Data *wd = elm_widget_data_get(obj);
399
400    if (!wd) return NULL;
401    return wd->window_title;
402 }
403
404 /**
405  * Set the size of the file selector button's window.
406  *
407  * @param obj The button object
408  * @param width The width
409  * @param height The height
410  *
411  * Note that it will only take any effect if the fileselector button not at
412  * "inwin mode". Default size for the window (when applicable) is 400x400.
413  *
414  * @ingroup File_Selector_Button
415  */
416 EAPI void
417 elm_fileselector_button_window_size_set(Evas_Object *obj,
418                                         Evas_Coord   width,
419                                         Evas_Coord   height)
420 {
421    ELM_CHECK_WIDTYPE(obj, widtype);
422    Widget_Data *wd = elm_widget_data_get(obj);
423
424    if (!wd) return;
425    wd->w = width;
426    wd->h = height;
427
428    if (wd->fsw)
429      evas_object_resize(wd->fsw, wd->w, wd->h);
430 }
431
432 /**
433  * Get the size of the file selector button's window.
434  *
435  * @param obj The button object
436  * @param width Pointer into which to store the width value
437  * @param height Pointer into which to store the height value
438  *
439  * @ingroup File_Selector_Button
440  */
441 EAPI void
442 elm_fileselector_button_window_size_get(const Evas_Object *obj,
443                                         Evas_Coord        *width,
444                                         Evas_Coord        *height)
445 {
446    ELM_CHECK_WIDTYPE(obj, widtype);
447    Widget_Data *wd = elm_widget_data_get(obj);
448
449    if (!wd) return;
450    if (width) *width = wd->w;
451    if (height) *height = wd->h;
452 }
453
454 /**
455  * Set the starting path of the file selector button's window.
456  *
457  * @param obj The button object
458  * @param path The path string
459  *
460  * It must be a <b>directory</b> path.
461  * Default path is "HOME" environment variable's value.
462  *
463  * @ingroup File_Selector_Button
464  */
465 EAPI void
466 elm_fileselector_button_path_set(Evas_Object *obj,
467                                  const char  *path)
468 {
469    ELM_CHECK_WIDTYPE(obj, widtype);
470    Widget_Data *wd = elm_widget_data_get(obj);
471
472    if (!wd) return;
473    eina_stringshare_replace(&wd->fsd.path, path);
474
475    if (wd->fs)
476      elm_fileselector_selected_set(wd->fs, wd->fsd.path);
477 }
478
479 /**
480  * Get the <b>last</b> path of the file selector button's window.
481  *
482  * @param obj The button object
483  *
484  * @ingroup File_Selector_Button
485  */
486 EAPI const char *
487 elm_fileselector_button_path_get(const Evas_Object *obj)
488 {
489    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
490    Widget_Data *wd = elm_widget_data_get(obj);
491    if (!wd) return NULL;
492    return wd->fsd.path;
493 }
494
495 /**
496  * Set whether the button's file selector is to present itself as an
497  * Elementary Generic List (which will expand its entries for nested
498  * directories) or as canonical list, which will be rendered again
499  * with the contents of each selected directory.
500  *
501  * @param obj The button object
502  * @param value The expandable flag
503  *
504  * @ingroup File_Selector_Button
505  */
506 EAPI void
507 elm_fileselector_button_expandable_set(Evas_Object *obj,
508                                        Eina_Bool    value)
509 {
510    ELM_CHECK_WIDTYPE(obj, widtype);
511    Widget_Data *wd = elm_widget_data_get(obj);
512
513    if (!wd) return;
514    wd->fsd.expandable = value;
515
516    if (wd->fs)
517      elm_fileselector_expandable_set(wd->fs, wd->fsd.expandable);
518 }
519
520 /**
521  * Get the button's file selector expandable flag.
522  *
523  * @param obj The button object
524  * @return value The expandable flag
525  *
526  * @ingroup File_Selector_Button
527  */
528 EAPI Eina_Bool
529 elm_fileselector_button_expandable_get(const Evas_Object *obj)
530 {
531    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
532    Widget_Data *wd = elm_widget_data_get(obj);
533
534    if (!wd) return EINA_FALSE;
535    return wd->fsd.expandable;
536 }
537
538 /**
539  * Set whether the button's file selector list is to display folders
540  * only or the directory contents, as well.
541  *
542  * @param obj The button object
543  * @param value The "folder only" flag
544  *
545  * @ingroup File_Selector_Button
546  */
547 EAPI void
548 elm_fileselector_button_folder_only_set(Evas_Object *obj,
549                                         Eina_Bool    value)
550 {
551    ELM_CHECK_WIDTYPE(obj, widtype);
552    Widget_Data *wd = elm_widget_data_get(obj);
553
554    if (!wd) return;
555    wd->fsd.folder_only = value;
556
557    if (wd->fs)
558      elm_fileselector_folder_only_set(wd->fs, wd->fsd.folder_only);
559 }
560
561 /**
562  * Get the button's file selector "folder only" flag.
563  *
564  * @param obj The button object
565  * @return value The "folder only" flag
566  *
567  * @ingroup File_Selector_Button
568  */
569 EAPI Eina_Bool
570 elm_fileselector_button_folder_only_get(const Evas_Object *obj)
571 {
572    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
573    Widget_Data *wd = elm_widget_data_get(obj);
574
575    if (!wd) return EINA_FALSE;
576    return wd->fsd.folder_only;
577 }
578
579 /**
580  * Set whether the button's file selector has an editable text entry
581  * which will hold its current selection.
582  *
583  * @param obj The button object
584  * @param value The "is save" flag
585  *
586  * @ingroup File_Selector_Button
587  */
588 EAPI void
589 elm_fileselector_button_is_save_set(Evas_Object *obj,
590                                     Eina_Bool    value)
591 {
592    ELM_CHECK_WIDTYPE(obj, widtype);
593    Widget_Data *wd = elm_widget_data_get(obj);
594
595    if (!wd) return;
596    wd->fsd.is_save = value;
597
598    if (wd->fs)
599      elm_fileselector_is_save_set(wd->fs, wd->fsd.is_save);
600 }
601
602 /**
603  * Get the button's file selector "is save" flag.
604  *
605  * @param obj The button object
606  * @return value The "is save" flag
607  *
608  * @ingroup File_Selector_Button
609  */
610 EAPI Eina_Bool
611 elm_fileselector_button_is_save_get(const Evas_Object *obj)
612 {
613    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
614    Widget_Data *wd = elm_widget_data_get(obj);
615
616    if (!wd) return EINA_FALSE;
617    return wd->fsd.is_save;
618 }
619
620 /**
621  * Set whether the button's file selector will raise an Elementary
622  * Inner Window, instead of a dedicated Elementary Window. By default,
623  * it won't.
624  *
625  * @param obj The button object
626  * @param value The "inwin mode" flag
627  *
628  * @ingroup File_Selector_Button
629  */
630 EAPI void
631 elm_fileselector_button_inwin_mode_set(Evas_Object *obj,
632                                        Eina_Bool    value)
633 {
634    ELM_CHECK_WIDTYPE(obj, widtype);
635    Widget_Data *wd = elm_widget_data_get(obj);
636
637    if (!wd) return;
638    wd->inwin_mode = value;
639 }
640
641 /**
642  * Get the button's file selector "inwin mode" flag.
643  *
644  * @param obj The button object
645  * @return value The "inwin mode" flag
646  *
647  * @ingroup File_Selector_Button
648  */
649 EAPI Eina_Bool
650 elm_fileselector_button_inwin_mode_get(const Evas_Object *obj)
651 {
652    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
653    Widget_Data *wd = elm_widget_data_get(obj);
654
655    if (!wd) return EINA_FALSE;
656    return wd->inwin_mode;
657 }
658
659 /**
660  * Set the icon used for the button
661  *
662  * Once the icon object is set, a previously set one will be deleted.
663  * If you want to keep that old content object, use the
664  * elm_fileselector_button_icon_unset() function.
665  *
666  * @param obj The button object
667  * @param icon  The icon object for the button
668  *
669  * @ingroup File_Selector_Button
670  */
671 EAPI void
672 elm_fileselector_button_icon_set(Evas_Object *obj,
673                                  Evas_Object *icon)
674 {
675    ELM_CHECK_WIDTYPE(obj, widtype);
676    Widget_Data *wd = elm_widget_data_get(obj);
677    if (!wd)
678      {
679         evas_object_del(icon);
680         return;
681      }
682    elm_button_icon_set(wd->btn, icon);
683 }
684
685 /**
686  * Get the icon used for the button
687  *
688  * @param obj The button object
689  * @return The icon object that is being used
690  *
691  * @ingroup File_Selector_Button
692  */
693 EAPI Evas_Object *
694 elm_fileselector_button_icon_get(const Evas_Object *obj)
695 {
696    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
697    Widget_Data *wd = elm_widget_data_get(obj);
698    if (!wd) return NULL;
699    return elm_button_icon_get(wd->btn);
700 }
701
702 /**
703  * Unset the icon used for the button
704  *
705  * Unparent and return the icon object which was set for this widget.
706  *
707  * @param obj The button object
708  * @return The icon object that was being used
709  *
710  * @ingroup File_Selector_Button
711  */
712 EAPI Evas_Object *
713 elm_fileselector_button_icon_unset(Evas_Object *obj)
714 {
715    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
716    Widget_Data *wd = elm_widget_data_get(obj);
717    if (!wd) return NULL;
718    return elm_button_icon_unset(wd->btn);
719 }
720