elementary: use evas_object_image_extension_can_load_fast_get.
[framework/uifw/elementary.git] / src / lib / elc_fileselector.c
1 /**
2  * @defgroup Fileselector Fileselector
3  *
4  * A fileselector is a widget that allows a user to navigate through a
5  * tree of files.  It contains buttons for Home(~) and Up(..) as well
6  * as cancel/ok buttons to confirm/cancel a selection.  This widget is
7  * currently very much in progress.
8  *
9  * TODO
10  * child elements focusing support
11  * userdefined icon/label cb
12  * show/hide/add buttons ???
13  * show/Hide hidden files
14  * double click to choose a file
15  * multiselection
16  * make variable/function names that are sensible
17  * Filter support
18  *
19  * Signals that you can add callbacks for are:
20  *
21  * "selected" - the user clicks on a file
22  * "directory,open" - the list is populated with new content.
23  *                    event_info is a directory.
24  * "done" - the user clicks on the ok or cancel button
25  */
26
27 #include <Elementary.h>
28 #include "elm_priv.h"
29
30 typedef struct _Widget_Data Widget_Data;
31
32 struct _Widget_Data
33 {
34    Evas_Object *edje;
35    Evas_Object *filename_entry;
36    Evas_Object *path_entry;
37    Evas_Object *files_list;
38    Evas_Object *files_grid;
39    Evas_Object *up_button;
40    Evas_Object *home_button;
41
42    Evas_Object *ok_button;
43    Evas_Object *cancel_button;
44
45    const char  *path;
46    const char  *selection;
47    Ecore_Idler *sel_idler;
48
49    const char  *path_separator;
50
51    Elm_Fileselector_Mode mode;
52
53    Eina_Bool    only_folder : 1;
54    Eina_Bool    expand : 1;
55 };
56
57 struct sel_data
58 {
59    Evas_Object *fs;
60    const char  *path;
61 };
62
63 Elm_Genlist_Item_Class list_itc;
64 Elm_Gengrid_Item_Class grid_itc;
65
66 static const char *widtype = NULL;
67
68 static const char SIG_DIRECTORY_OPEN[] = "directory,open";
69 static const char SIG_DONE[] = "done";
70 static const char SIG_SELECTED[] = "selected";
71 static const Evas_Smart_Cb_Description _signals[] = {
72    {SIG_DIRECTORY_OPEN, "s"},
73    {SIG_DONE, "s"},
74    {SIG_SELECTED, "s"},
75    {NULL, NULL}
76 };
77
78 static void _populate(Evas_Object      *obj,
79                       const char       *path,
80                       Elm_Genlist_Item *parent);
81 static void _do_anchors(Evas_Object *obj,
82                         const char  *path);
83
84 /***  ELEMENTARY WIDGET  ***/
85 static void
86 _del_hook(Evas_Object *obj)
87 {
88    Widget_Data *wd;
89    void *sd;
90
91    wd = elm_widget_data_get(obj);
92    if (!wd) return;
93
94    if (wd->path) eina_stringshare_del(wd->path);
95    if (wd->selection) eina_stringshare_del(wd->selection);
96    if (wd->sel_idler)
97      {
98         sd = ecore_idler_del(wd->sel_idler);
99         free(sd);
100      }
101    free(wd);
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;
109    if (!wd) return;
110    elm_coords_finger_size_adjust(1, &minw, 1, &minh);
111    edje_object_size_min_restricted_calc(wd->edje, &minw, &minh, minw, minh);
112    evas_object_size_hint_min_set(obj, minw, minh);
113 }
114
115 static void
116 _mirrored_set(Evas_Object *obj, Eina_Bool rtl)
117 {
118    Widget_Data *wd = elm_widget_data_get(obj);
119    if (!wd) return;
120    elm_widget_mirrored_set(wd->cancel_button, rtl);
121    elm_widget_mirrored_set(wd->ok_button, rtl);
122    elm_widget_mirrored_set(wd->files_list, rtl);
123    elm_widget_mirrored_set(wd->up_button, rtl);
124    elm_widget_mirrored_set(wd->home_button, rtl);
125    edje_object_mirrored_set(wd->edje, rtl);
126 }
127
128 static void
129 _theme_hook(Evas_Object *obj)
130 {
131    Widget_Data *wd = elm_widget_data_get(obj);
132    const char *style = elm_widget_style_get(obj);
133    const char *data;
134    char buf[1024];
135
136    if (!wd) return;
137    _elm_widget_mirrored_reload(obj);
138
139    _elm_theme_object_set(obj, wd->edje, "fileselector", "base", style);
140
141    if (elm_object_disabled_get(obj))
142      edje_object_signal_emit(wd->edje, "elm,state,disabled", "elm");
143
144    data = edje_object_data_get(wd->edje, "path_separator");
145    if (data)
146      wd->path_separator = data;
147    else
148      wd->path_separator = "/";
149
150    if (!style) style = "default";
151    snprintf(buf, sizeof(buf), "fileselector/%s", style);
152
153 #define SWALLOW(part_name, object_ptn)                                \
154   if (object_ptn)                                                     \
155     {                                                                 \
156        elm_widget_style_set(object_ptn, buf);                         \
157        if (edje_object_part_swallow(wd->edje, part_name, object_ptn)) \
158          evas_object_show(object_ptn);                                \
159        else                                                           \
160          evas_object_hide(object_ptn);                                \
161     }
162    SWALLOW("elm.swallow.up", wd->up_button);
163    SWALLOW("elm.swallow.home", wd->home_button);
164
165    if (wd->mode == ELM_FILESELECTOR_LIST)
166      {
167         if (edje_object_part_swallow(wd->edje, "elm.swallow.files",
168                                      wd->files_list))
169           {
170              evas_object_show(wd->files_list);
171              evas_object_hide(wd->files_grid);
172           }
173         else
174           evas_object_hide(wd->files_list);
175      }
176    else
177      {
178         if (edje_object_part_swallow(wd->edje, "elm.swallow.files",
179                                      wd->files_grid))
180           {
181              evas_object_show(wd->files_grid);
182              evas_object_hide(wd->files_list);
183           }
184         else
185           evas_object_hide(wd->files_grid);
186      }
187
188    SWALLOW("elm.swallow.filename", wd->filename_entry);
189    SWALLOW("elm.swallow.path", wd->path_entry);
190
191    snprintf(buf, sizeof(buf), "fileselector/actions/%s", style);
192    SWALLOW("elm.swallow.cancel", wd->cancel_button);
193    SWALLOW("elm.swallow.ok", wd->ok_button);
194 #undef SWALLOW
195
196    edje_object_message_signal_process(wd->edje);
197    _mirrored_set(obj, elm_widget_mirrored_get(obj));
198    edje_object_scale_set
199      (wd->edje, elm_widget_scale_get(obj) * _elm_config->scale);
200    _sizing_eval(obj);
201 }
202
203 /***  GENLIST "MODEL"  ***/
204 static char *
205 _itc_label_get(void              *data,
206                Evas_Object *obj   __UNUSED__,
207                const char *source __UNUSED__)
208 {
209    return strdup(ecore_file_file_get(data)); /* NOTE this will be
210                                               * free() by the
211                                               * caller */
212 }
213
214 static Evas_Object *
215 _itc_icon_get(void        *data,
216               Evas_Object *obj,
217               const char  *source)
218 {
219    Evas_Object *ic;
220
221    if (!strcmp(source, "elm.swallow.icon"))
222      {
223         const char *filename = data;
224
225         ic = elm_icon_add(obj);
226         if (ecore_file_is_dir((char *)data))
227           elm_icon_standard_set(ic, "folder");
228         else
229           {
230              if (evas_object_image_extension_can_load_fast_get(filename))
231                {
232                   elm_icon_standard_set(ic, "image");
233                   elm_icon_thumb_set(ic, filename, NULL);
234                }
235              else
236                {
237                   elm_icon_standard_set(ic, "file");
238                }
239           }
240
241         evas_object_size_hint_aspect_set(ic, EVAS_ASPECT_CONTROL_VERTICAL,
242                                          1, 1);
243         evas_object_show(ic);
244         return ic;
245      }
246    return NULL;
247 }
248
249 static Eina_Bool
250 _itc_state_get(void *data         __UNUSED__,
251                Evas_Object *obj   __UNUSED__,
252                const char *source __UNUSED__)
253 {
254    return EINA_FALSE;
255 }
256
257 static void
258 _itc_del(void            *data,
259          Evas_Object *obj __UNUSED__)
260 {
261    eina_stringshare_del(data);
262 }
263
264 static void
265 _expand_done(void            *data,
266              Evas_Object *obj __UNUSED__,
267              void            *event_info)
268 {
269    Elm_Genlist_Item *it = event_info;
270    const char *path = elm_genlist_item_data_get(it);
271    _populate(data, path, it);
272 }
273
274 static void
275 _contract_done(void *data       __UNUSED__,
276                Evas_Object *obj __UNUSED__,
277                void            *event_info)
278 {
279    Elm_Genlist_Item *it = event_info;
280    elm_genlist_item_subitems_clear(it);
281 }
282
283 static void
284 _expand_req(void *data       __UNUSED__,
285             Evas_Object *obj __UNUSED__,
286             void            *event_info)
287 {
288    Elm_Genlist_Item *it = event_info;
289    elm_genlist_item_expanded_set(it, 1);
290 }
291
292 static void
293 _contract_req(void *data       __UNUSED__,
294               Evas_Object *obj __UNUSED__,
295               void            *event_info)
296 {
297    Elm_Genlist_Item *it = event_info;
298    elm_genlist_item_expanded_set(it, 0);
299 }
300
301 /***  PRIVATES  ***/
302 static Eina_Bool
303 _sel_do(void *data)
304 {
305    struct sel_data *sd;
306    const char *path;
307    Widget_Data *wd;
308    const char *p;
309
310    sd = data;
311    wd = elm_widget_data_get(sd->fs);
312    path = sd->path;
313
314    if ((!wd->only_folder) && ecore_file_is_dir(path))
315      {
316         if (wd->expand && wd->mode == ELM_FILESELECTOR_LIST)
317           {
318              _do_anchors(sd->fs, path);
319              elm_scrolled_entry_entry_set(wd->filename_entry, "");
320           }
321         else
322           {
323              /* keep a ref to path 'couse it will be destroyed by _populate */
324              p = eina_stringshare_add(path);
325              _populate(sd->fs, p, NULL);
326              eina_stringshare_del(p);
327           }
328         goto end;
329      }
330    else /* navigating through folders only or file is not a dir. */
331      {
332         if (wd->expand && wd->mode == ELM_FILESELECTOR_LIST)
333           _do_anchors(sd->fs, path);
334         else if (wd->only_folder)
335           {
336              /* keep a ref to path 'couse it will be destroyed by _populate */
337              p = eina_stringshare_add(path);
338              _populate(sd->fs, p, NULL);
339              eina_stringshare_del(p);
340           }
341         elm_scrolled_entry_entry_set(wd->filename_entry,
342                                      ecore_file_file_get(path));
343      }
344
345    evas_object_smart_callback_call(sd->fs, SIG_SELECTED, (void *)path);
346
347 end:
348    wd->sel_idler = NULL;
349    free(sd);
350    return ECORE_CALLBACK_CANCEL;
351 }
352
353 static void
354 _sel(void            *data,
355      Evas_Object *obj __UNUSED__,
356      void            *event_info)
357 {
358    struct sel_data *sd;
359    Widget_Data *wd;
360    void *old_sd;
361    char *dir;
362
363    wd = elm_widget_data_get(data);
364    if (!wd) return;
365
366    sd = malloc(sizeof(*sd));
367    sd->fs = data;
368    sd->path = wd->mode == ELM_FILESELECTOR_LIST ?
369        elm_genlist_item_data_get(event_info) :
370        elm_gengrid_item_data_get(event_info);
371
372    if (!sd->path)
373      {
374         eina_stringshare_replace(&wd->path, "");
375         goto end;
376      }
377
378    dir = wd->only_folder ? strdup(sd->path) : ecore_file_dir_get(sd->path);
379    if (dir)
380      {
381         eina_stringshare_replace(&wd->path, dir);
382         free(dir);
383      }
384    else
385      {
386         eina_stringshare_replace(&wd->path, "");
387      }
388
389 end:
390    if (wd->sel_idler)
391      {
392         old_sd = ecore_idler_del(wd->sel_idler);
393         free(old_sd);
394      }
395    wd->sel_idler = ecore_idler_add(_sel_do, sd);
396 }
397
398 static void
399 _up(void            *data,
400     Evas_Object *obj __UNUSED__,
401     void *event_info __UNUSED__)
402 {
403    Evas_Object *fs = data;
404    char *parent;
405
406    Widget_Data *wd = elm_widget_data_get(fs);
407    if (!wd) return;
408    parent = ecore_file_dir_get(wd->path);
409    _populate(fs, parent, NULL);
410    free(parent);
411 }
412
413 static void
414 _home(void            *data,
415       Evas_Object *obj __UNUSED__,
416       void *event_info __UNUSED__)
417 {
418    Evas_Object *fs = data;
419    _populate(fs, getenv("HOME"), NULL);
420 }
421
422 static void
423 _ok(void            *data,
424     Evas_Object *obj __UNUSED__,
425     void *event_info __UNUSED__)
426 {
427    Evas_Object *fs = data;
428    evas_object_smart_callback_call(fs, SIG_DONE,
429                                    (void *)elm_fileselector_selected_get(fs));
430 }
431
432 static void
433 _canc(void            *data,
434       Evas_Object *obj __UNUSED__,
435       void *event_info __UNUSED__)
436 {
437    Evas_Object *fs = data;
438    evas_object_smart_callback_call(fs, SIG_DONE, NULL);
439 }
440
441 static void
442 _anchor_clicked(void            *data,
443                 Evas_Object *obj __UNUSED__,
444                 void            *event_info)
445 {
446    Evas_Object *fs = data;
447    Widget_Data *wd = elm_widget_data_get(fs);
448    Elm_Entry_Anchor_Info *info = event_info;
449    const char *p;
450    if (!wd) return;
451    // keep a ref to path 'couse it will be destroyed by _populate
452    p = eina_stringshare_add(info->name);
453    _populate(fs, p, NULL);
454    evas_object_smart_callback_call(data, SIG_SELECTED, (void *)p);
455    eina_stringshare_del(p);
456 }
457
458 static void
459 _do_anchors(Evas_Object *obj,
460             const char  *path)
461 {
462    Widget_Data *wd = elm_widget_data_get(obj);
463    char **tok, buf[PATH_MAX * 3];
464    int i, j;
465    if (!wd) return;
466    buf[0] = '\0';
467    tok = eina_str_split(path, "/", 0);
468    eina_strlcat(buf, "<a href=/>root</a>", sizeof(buf));
469    for (i = 0; tok[i]; i++)
470      {
471         if ((!tok[i]) || (!tok[i][0])) continue;
472         eina_strlcat(buf, wd->path_separator, sizeof(buf));
473         eina_strlcat(buf, "<a href=", sizeof(buf));
474         for (j = 0; j <= i; j++)
475           {
476              if (strlen(tok[j]) < 1) continue;
477              eina_strlcat(buf, "/", sizeof(buf));
478              eina_strlcat(buf, tok[j], sizeof(buf));
479           }
480         eina_strlcat(buf, ">", sizeof(buf));
481         eina_strlcat(buf, tok[i], sizeof(buf));
482         eina_strlcat(buf, "</a>", sizeof(buf));
483      }
484    free(tok[0]);
485    free(tok);
486
487    elm_scrolled_entry_entry_set(wd->path_entry, buf);
488 }
489
490 static void
491 _populate(Evas_Object      *obj,
492           const char       *path,
493           Elm_Genlist_Item *parent)
494 {
495    Widget_Data *wd = elm_widget_data_get(obj);
496    DIR *dir;
497    struct dirent *dp;
498    char buf[PATH_MAX];
499    char *real;
500    Eina_List *files = NULL, *dirs = NULL, *l;
501
502    if ((!wd) || (!ecore_file_is_dir(path))) return;
503    dir = opendir(path);
504    if (!dir) return;
505    evas_object_smart_callback_call(obj, SIG_DIRECTORY_OPEN, (void *)path);
506    if (!parent)
507      {
508         elm_genlist_clear(wd->files_list);
509         elm_gengrid_clear(wd->files_grid);
510         eina_stringshare_replace(&wd->path, path);
511         _do_anchors(obj, path);
512      }
513
514    if (wd->filename_entry) elm_scrolled_entry_entry_set(wd->filename_entry, "");
515    while ((dp = readdir(dir)))
516      {
517         if (dp->d_name[0] == '.') continue;  // TODO make this configurable
518
519         snprintf(buf, sizeof(buf), "%s/%s", path, dp->d_name);
520         real = ecore_file_realpath(buf); /* TODO: this will resolv
521                                           * symlinks...I dont like
522                                           * it*/
523         if (ecore_file_is_dir(real))
524           dirs = eina_list_append(dirs, real);
525         else if (!wd->only_folder)
526           files = eina_list_append(files, real);
527      }
528    closedir(dir);
529
530    files = eina_list_sort(files, eina_list_count(files),
531                           EINA_COMPARE_CB(strcoll));
532    dirs = eina_list_sort(dirs, eina_list_count(dirs), EINA_COMPARE_CB(strcoll));
533    EINA_LIST_FOREACH(dirs, l, real)
534      {
535         if (wd->mode == ELM_FILESELECTOR_LIST)
536           elm_genlist_item_append(wd->files_list, &list_itc,
537                                   eina_stringshare_add(real), /* item data */
538                                   parent,
539                                   wd->expand ? ELM_GENLIST_ITEM_SUBITEMS :
540                                   ELM_GENLIST_ITEM_NONE,
541                                   NULL, NULL);
542         else if (wd->mode == ELM_FILESELECTOR_GRID)
543           elm_gengrid_item_append(wd->files_grid, &grid_itc,
544                                   eina_stringshare_add(real), /* item data */
545                                   NULL, NULL);
546
547         free(real);
548      }
549    eina_list_free(dirs);
550
551    EINA_LIST_FOREACH(files, l, real)
552      {
553         if (wd->mode == ELM_FILESELECTOR_LIST)
554           elm_genlist_item_append(wd->files_list, &list_itc,
555                                   eina_stringshare_add(real), /* item data */
556                                   parent, ELM_GENLIST_ITEM_NONE,
557                                   NULL, NULL);
558         else if (wd->mode == ELM_FILESELECTOR_GRID)
559           elm_gengrid_item_append(wd->files_grid, &grid_itc,
560                                   eina_stringshare_add(real), /* item data */
561                                   NULL, NULL);
562         free(real);
563      }
564    eina_list_free(files);
565 }
566
567 /***  API  ***/
568
569 /**
570  * Add a new Fileselector object
571  *
572  * @param parent The parent object
573  * @return The new object or NULL if it cannot be created
574  *
575  * @ingroup Fileselector
576  */
577 EAPI Evas_Object *
578 elm_fileselector_add(Evas_Object *parent)
579 {
580    Evas *e;
581    Evas_Object *obj, *ic, *bt, *li, *en, *grid;
582    Widget_Data *wd;
583    int s;
584
585    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
586
587    ELM_SET_WIDTYPE(widtype, "fileselector");
588    elm_widget_type_set(obj, "fileselector");
589    elm_widget_sub_object_add(parent, obj);
590    elm_widget_data_set(obj, wd);
591    elm_widget_del_hook_set(obj, _del_hook);
592    elm_widget_theme_hook_set(obj, _theme_hook);
593    elm_widget_can_focus_set(obj, EINA_FALSE);
594
595    wd->expand = !!_elm_config->fileselector_expand_enable;
596
597    wd->edje = edje_object_add(e);
598    _elm_theme_object_set(obj, wd->edje, "fileselector", "base", "default");
599    elm_widget_resize_object_set(obj, wd->edje);
600
601    // up btn
602    ic = elm_icon_add(parent);
603    elm_icon_standard_set(ic, "arrow_up");
604    evas_object_size_hint_aspect_set(ic, EVAS_ASPECT_CONTROL_VERTICAL, 1, 1);
605    bt = elm_button_add(parent);
606    elm_widget_mirrored_automatic_set(bt, EINA_FALSE);
607    elm_button_icon_set(bt, ic);
608    elm_button_label_set(bt, E_("Up"));
609    evas_object_size_hint_align_set(bt, 0.0, 0.0);
610
611    evas_object_smart_callback_add(bt, "clicked", _up, obj);
612
613    elm_widget_sub_object_add(obj, bt);
614    wd->up_button = bt;
615
616    // home btn
617    ic = elm_icon_add(parent);
618    elm_icon_standard_set(ic, "home");
619    evas_object_size_hint_aspect_set(ic, EVAS_ASPECT_CONTROL_VERTICAL, 1, 1);
620    bt = elm_button_add(parent);
621    elm_widget_mirrored_automatic_set(bt, EINA_FALSE);
622    elm_button_icon_set(bt, ic);
623    elm_button_label_set(bt, E_("Home"));
624    evas_object_size_hint_align_set(bt, 0.0, 0.0);
625
626    evas_object_smart_callback_add(bt, "clicked", _home, obj);
627
628    elm_widget_sub_object_add(obj, bt);
629    wd->home_button = bt;
630
631    list_itc.item_style = grid_itc.item_style = "default";
632    list_itc.func.label_get = grid_itc.func.label_get = _itc_label_get;
633    list_itc.func.icon_get = grid_itc.func.icon_get = _itc_icon_get;
634    list_itc.func.state_get = grid_itc.func.state_get = _itc_state_get;
635    list_itc.func.del = grid_itc.func.del = _itc_del;
636
637    li = elm_genlist_add(parent);
638    elm_widget_mirrored_automatic_set(li, EINA_FALSE);
639    evas_object_size_hint_align_set(li, EVAS_HINT_FILL, EVAS_HINT_FILL);
640    evas_object_size_hint_weight_set(li, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
641    evas_object_size_hint_min_set(li, 100, 100);
642
643    grid = elm_gengrid_add(parent);
644    elm_widget_mirrored_automatic_set(grid, EINA_FALSE);
645    evas_object_size_hint_align_set(grid, EVAS_HINT_FILL, EVAS_HINT_FILL);
646    evas_object_size_hint_weight_set(grid, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
647
648    s = elm_finger_size_get() * 2;
649    elm_gengrid_item_size_set(grid, s, s);
650    elm_gengrid_align_set(grid, 0.0, 0.0);
651
652    evas_object_smart_callback_add(li, "selected", _sel, obj);
653    evas_object_smart_callback_add(li, "expand,request", _expand_req, obj);
654    evas_object_smart_callback_add(li, "contract,request", _contract_req, obj);
655    evas_object_smart_callback_add(li, "expanded", _expand_done, obj);
656    evas_object_smart_callback_add(li, "contracted", _contract_done, obj);
657
658    evas_object_smart_callback_add(grid, "selected", _sel, obj);
659
660    elm_widget_sub_object_add(obj, li);
661    elm_widget_sub_object_add(obj, grid);
662    wd->files_list = li;
663    wd->files_grid = grid;
664
665    // path entry
666    en = elm_scrolled_entry_add(parent);
667    elm_widget_mirrored_automatic_set(en, EINA_FALSE);
668    elm_scrolled_entry_editable_set(en, EINA_FALSE);
669    elm_scrolled_entry_single_line_set(en, EINA_TRUE);
670    elm_scrolled_entry_line_wrap_set(en, ELM_WRAP_CHAR);
671    evas_object_size_hint_weight_set(en, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
672    evas_object_size_hint_align_set(en, EVAS_HINT_FILL, EVAS_HINT_FILL);
673
674    evas_object_smart_callback_add(en, "anchor,clicked", _anchor_clicked, obj);
675
676    elm_widget_sub_object_add(obj, en);
677    wd->path_entry = en;
678
679    // filename entry
680    en = elm_scrolled_entry_add(parent);
681    elm_widget_mirrored_automatic_set(en, EINA_FALSE);
682    elm_scrolled_entry_editable_set(en, EINA_TRUE);
683    elm_scrolled_entry_single_line_set(en, EINA_TRUE);
684    elm_scrolled_entry_line_wrap_set(en, ELM_WRAP_CHAR);
685    evas_object_size_hint_weight_set(en, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
686    evas_object_size_hint_align_set(en, EVAS_HINT_FILL, EVAS_HINT_FILL);
687
688    elm_widget_sub_object_add(obj, en);
689    wd->filename_entry = en;
690
691    elm_fileselector_buttons_ok_cancel_set(obj, EINA_TRUE);
692    elm_fileselector_is_save_set(obj, EINA_FALSE);
693
694    _theme_hook(obj);
695
696    evas_object_smart_callbacks_descriptions_set(obj, _signals);
697    return obj;
698 }
699
700 /**
701  * This enables/disables the file name entry box where the user can
702  * type in a name for the file to be saved as.
703  *
704  * @param obj The fileselector object
705  * @param is_save If true, the fileselector is a save dialog
706  *
707  * @ingroup Fileselector
708  */
709 EAPI void
710 elm_fileselector_is_save_set(Evas_Object *obj,
711                              Eina_Bool    is_save)
712 {
713    ELM_CHECK_WIDTYPE(obj, widtype);
714    Widget_Data *wd = elm_widget_data_get(obj);
715    if (!wd) return;
716
717    elm_object_disabled_set(wd->filename_entry, !is_save);
718
719    if (is_save)
720      edje_object_signal_emit(wd->edje, "elm,state,save,on", "elm");
721    else
722      edje_object_signal_emit(wd->edje, "elm,state,save,off", "elm");
723 }
724
725 /**
726  * This returns whether the fileselector is a "save" type fileselector
727  *
728  * @param obj The fileselector object
729  * @return If true, the fileselector is a save type.
730  *
731  * @ingroup Fileselector
732  */
733 EAPI Eina_Bool
734 elm_fileselector_is_save_get(const Evas_Object *obj)
735 {
736    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
737    Widget_Data *wd = elm_widget_data_get(obj);
738    if (!wd) return EINA_FALSE;
739    return elm_object_disabled_get(wd->filename_entry);
740 }
741
742 /**
743  * This enables/disables folder-only view in the fileselector.
744  *
745  * @param obj The fileselector object
746  * @param only If true, the fileselector will only display directories.
747  * If false, files are displayed also.
748  *
749  * @ingroup Fileselector
750  */
751 EAPI void
752 elm_fileselector_folder_only_set(Evas_Object *obj,
753                                  Eina_Bool    only)
754 {
755    ELM_CHECK_WIDTYPE(obj, widtype);
756    Widget_Data *wd = elm_widget_data_get(obj);
757    if (!wd) return;
758    if (wd->only_folder == only) return;
759    wd->only_folder = !!only;
760    if (wd->path) _populate(obj, wd->path, NULL);
761 }
762
763 /**
764  * This gets the state of file display in the fileselector.
765  *
766  * @param obj The fileselector object
767  * @return If true, files are not being shown in the fileselector.
768  * If false, files are being shown.
769  *
770  * @ingroup Fileselector
771  */
772 EAPI Eina_Bool
773 elm_fileselector_folder_only_get(const Evas_Object *obj)
774 {
775    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
776    Widget_Data *wd = elm_widget_data_get(obj);
777    if (!wd) return EINA_FALSE;
778    return wd->only_folder;
779 }
780
781 /**
782  * This enables/disables the ok,cancel buttons.
783  *
784  * @param obj The fileselector object
785  * @param only If true, a box containing ok and cancel buttons is created.
786  * If false, the box and the buttons are destroyed.
787  *
788  * @ingroup Fileselector
789  */
790 EAPI void
791 elm_fileselector_buttons_ok_cancel_set(Evas_Object *obj,
792                                        Eina_Bool    visible)
793 {
794    ELM_CHECK_WIDTYPE(obj, widtype);
795    Widget_Data *wd = elm_widget_data_get(obj);
796    Evas_Object *bt;
797    if (!wd) return;
798
799    if (visible)
800      {
801         // cancel btn
802         bt = elm_button_add(obj);
803         elm_widget_mirrored_automatic_set(bt, EINA_FALSE);
804         elm_button_label_set(bt, E_("Cancel"));
805
806         evas_object_smart_callback_add(bt, "clicked", _canc, obj);
807
808         elm_widget_sub_object_add(obj, bt);
809         wd->cancel_button = bt;
810
811         // ok btn
812         bt = elm_button_add(obj);
813         elm_widget_mirrored_automatic_set(bt, EINA_FALSE);
814         elm_button_label_set(bt, E_("OK"));
815
816         evas_object_smart_callback_add(bt, "clicked", _ok, obj);
817
818         elm_widget_sub_object_add(obj, bt);
819         wd->ok_button = bt;
820
821         _theme_hook(obj);
822      }
823    else
824      {
825         evas_object_del(wd->cancel_button);
826         wd->cancel_button = NULL;
827         evas_object_del(wd->ok_button);
828         wd->ok_button = NULL;
829      }
830 }
831
832 /**
833  * This gets the state of the box containing ok and cancel buttons.
834  *
835  * @param obj The fileselector object
836  * @return If true, the box exists.
837  * If false, the box does not exist.
838  *
839  * @ingroup Fileselector
840  */
841 EAPI Eina_Bool
842 elm_fileselector_buttons_ok_cancel_get(const Evas_Object *obj)
843 {
844    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
845    Widget_Data *wd = elm_widget_data_get(obj);
846    if (!wd) return EINA_FALSE;
847    return wd->ok_button ? EINA_TRUE : EINA_FALSE;
848 }
849
850 /**
851  * This enables a tree view in the fileselector, <b>if in @c
852  * ELM_FILESELECTOR_LIST mode</b>. If it's in other mode, the changes
853  * made by this function will only be visible when one switches back
854  * to list mode.
855  *
856  * @param obj The fileselector object
857  * @param expand If true, tree view is enabled.
858  * If false, tree view is disabled.
859  *
860  * In a tree view, arrows are created on the sides of directories,
861  * allowing them to expand in place.
862  *
863  * @ingroup Fileselector
864  */
865 EAPI void
866 elm_fileselector_expandable_set(Evas_Object *obj,
867                                 Eina_Bool    expand)
868 {
869    ELM_CHECK_WIDTYPE(obj, widtype);
870    Widget_Data *wd;
871
872    wd = elm_widget_data_get(obj);
873    if (!wd) return;
874
875    wd->expand = !!expand;
876
877    if (wd->path) _populate(obj, wd->path, NULL);
878 }
879
880 /**
881  * This gets the state of tree view in the fileselector.
882  *
883  * @param obj The fileselector object
884  * @return If true, tree view is enabled and folders will be expandable.
885  * If false, tree view is disabled.
886  *
887  * @ingroup Fileselector
888  */
889 EAPI Eina_Bool
890 elm_fileselector_expandable_get(const Evas_Object *obj)
891 {
892    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
893    Widget_Data *wd = elm_widget_data_get(obj);
894    if (!wd) return EINA_FALSE;
895    return wd->expand;
896 }
897
898 /**
899  * This sets the path that the fileselector will display.
900  *
901  * @param obj The fileselector object
902  * @param path The path of the fileselector
903  *
904  * @ingroup Fileselector
905  */
906 EAPI void
907 elm_fileselector_path_set(Evas_Object *obj,
908                           const char  *path)
909 {
910    ELM_CHECK_WIDTYPE(obj, widtype);
911    _populate(obj, path, NULL);
912 }
913
914 /**
915  * This gets the path that the fileselector displays.
916  *
917  * @param obj The fileselector object
918  * @return The path that the fileselector is displaying
919  *
920  * @ingroup Fileselector
921  */
922 EAPI const char *
923 elm_fileselector_path_get(const Evas_Object *obj)
924 {
925    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
926    Widget_Data *wd = elm_widget_data_get(obj);
927    if (!wd) return NULL;
928    return wd->path;
929 }
930
931 /**
932  * This sets the mode in which the fileselector will display files.
933  *
934  * @param obj The fileselector object
935
936  * @param mode The mode of the fileselector, being it one of @c
937  * ELM_FILESELECTOR_LIST (default) or @c ELM_FILESELECTOR_GRID. The
938  * first one, naturally, will display the files in a list. By using
939  * elm_fileselector_expandable_set(), the user will trigger a tree
940  * view for that list. The latter will make the widget to display its
941  * entries in a grid form.
942  *
943  * @see elm_fileselector_expandable_set().
944  *
945  * @ingroup Fileselector
946  */
947 EAPI void
948 elm_fileselector_mode_set(Evas_Object          *obj,
949                           Elm_Fileselector_Mode mode)
950 {
951    ELM_CHECK_WIDTYPE(obj, widtype);
952
953    Widget_Data *wd = elm_widget_data_get(obj);
954    if (!wd) return;
955
956    if (mode == wd->mode) return;
957
958    if (mode == ELM_FILESELECTOR_LIST)
959      {
960         if (edje_object_part_swallow(wd->edje, "elm.swallow.files",
961                                      wd->files_list))
962           {
963              evas_object_show(wd->files_list);
964              evas_object_hide(wd->files_grid);
965           }
966         else
967           evas_object_hide(wd->files_list);
968      }
969    else
970      {
971         if (edje_object_part_swallow(wd->edje, "elm.swallow.files",
972                                      wd->files_grid))
973           {
974              evas_object_show(wd->files_grid);
975              evas_object_hide(wd->files_list);
976           }
977         else
978           evas_object_hide(wd->files_grid);
979      }
980
981    wd->mode = mode;
982
983    _populate(obj, wd->path, NULL);
984 }
985
986 /**
987  * This gets the mode in which the fileselector is displaying files.
988  *
989  * @param obj The fileselector object
990  * @return The mode in which the fileselector is at
991  *
992  * @ingroup Fileselector
993  */
994 EAPI Elm_Fileselector_Mode
995 elm_fileselector_mode_get(const Evas_Object *obj)
996 {
997    ELM_CHECK_WIDTYPE(obj, widtype) ELM_FILESELECTOR_LAST;
998
999    Widget_Data *wd = elm_widget_data_get(obj);
1000    if (!wd) return ELM_FILESELECTOR_LAST;
1001
1002    return wd->mode;
1003 }
1004
1005 /**
1006  * This gets the currently selected path in the file selector.
1007  *
1008  * @param obj The file selector object
1009  * @return The absolute path of the selected object in the fileselector
1010  *
1011  * @ingroup Fileselector
1012  */
1013 EAPI const char *
1014 elm_fileselector_selected_get(const Evas_Object *obj)
1015 {
1016    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
1017    Widget_Data *wd = elm_widget_data_get(obj);
1018    if (!wd) return NULL;
1019
1020    if (wd->filename_entry)
1021      {
1022         const char *name;
1023         char buf[PATH_MAX];
1024
1025         name = elm_scrolled_entry_entry_get(wd->filename_entry);
1026         snprintf(buf, sizeof(buf), "%s/%s",
1027                  wd->only_folder ? ecore_file_dir_get(wd->path) : wd->path,
1028                  name);
1029         eina_stringshare_replace(&wd->selection, buf);
1030         return wd->selection;
1031      }
1032
1033    if (wd->mode == ELM_FILESELECTOR_LIST)
1034      {
1035         Elm_Genlist_Item *it;
1036         it = elm_genlist_selected_item_get(wd->files_list);
1037         if (it) return elm_genlist_item_data_get(it);
1038      }
1039    else
1040      {
1041         Elm_Gengrid_Item *it;
1042         it = elm_gengrid_selected_item_get(wd->files_grid);
1043         if (it) return elm_gengrid_item_data_get(it);
1044      }
1045
1046    return wd->path;
1047 }
1048
1049 /**
1050  * This sets the currently selected path in the file selector.
1051  *
1052  * @param obj The file selector object
1053  * @param path The path to a file or directory
1054  * @return @c EINA_TRUE on success, @c EINA_FALSE on failure. The
1055  * latter case occurs if the directory or file pointed to do not
1056  * exist.
1057  *
1058  * @ingroup Fileselector
1059  */
1060 EAPI Eina_Bool
1061 elm_fileselector_selected_set(Evas_Object *obj,
1062                               const char  *path)
1063 {
1064    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
1065    Widget_Data *wd = elm_widget_data_get(obj);
1066    if (!wd) return EINA_FALSE;
1067
1068    if (ecore_file_is_dir(path))
1069      _populate(obj, path, NULL);
1070    else
1071      {
1072         if (!ecore_file_exists(path))
1073           return EINA_FALSE;
1074
1075         _populate(obj, ecore_file_dir_get(path), NULL);
1076         if (wd->filename_entry)
1077           {
1078              elm_scrolled_entry_entry_set(wd->filename_entry,
1079                                           ecore_file_file_get(path));
1080              eina_stringshare_replace(&wd->selection, path);
1081           }
1082      }
1083
1084    return EINA_TRUE;
1085 }
1086