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