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