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