big move forward. scrolled entry and entry merge into entry. entry now
[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_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_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_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_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_entry_add(parent);
667    elm_entry_scrollable_set(en, EINA_TRUE);
668    elm_widget_mirrored_automatic_set(en, EINA_FALSE);
669    elm_entry_editable_set(en, EINA_FALSE);
670    elm_entry_single_line_set(en, EINA_TRUE);
671    elm_entry_line_wrap_set(en, ELM_WRAP_CHAR);
672    evas_object_size_hint_weight_set(en, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
673    evas_object_size_hint_align_set(en, EVAS_HINT_FILL, EVAS_HINT_FILL);
674
675    evas_object_smart_callback_add(en, "anchor,clicked", _anchor_clicked, obj);
676
677    elm_widget_sub_object_add(obj, en);
678    wd->path_entry = en;
679
680    // filename entry
681    en = elm_entry_add(parent);
682    elm_entry_scrollable_set(en, EINA_TRUE);
683    elm_widget_mirrored_automatic_set(en, EINA_FALSE);
684    elm_entry_editable_set(en, EINA_TRUE);
685    elm_entry_single_line_set(en, EINA_TRUE);
686    elm_entry_line_wrap_set(en, ELM_WRAP_CHAR);
687    evas_object_size_hint_weight_set(en, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
688    evas_object_size_hint_align_set(en, EVAS_HINT_FILL, EVAS_HINT_FILL);
689
690    elm_widget_sub_object_add(obj, en);
691    wd->filename_entry = en;
692
693    elm_fileselector_buttons_ok_cancel_set(obj, EINA_TRUE);
694    elm_fileselector_is_save_set(obj, EINA_FALSE);
695
696    _theme_hook(obj);
697
698    evas_object_smart_callbacks_descriptions_set(obj, _signals);
699    return obj;
700 }
701
702 /**
703  * This enables/disables the file name entry box where the user can
704  * type in a name for the file to be saved as.
705  *
706  * @param obj The fileselector object
707  * @param is_save If true, the fileselector is a save dialog
708  *
709  * @ingroup Fileselector
710  */
711 EAPI void
712 elm_fileselector_is_save_set(Evas_Object *obj,
713                              Eina_Bool    is_save)
714 {
715    ELM_CHECK_WIDTYPE(obj, widtype);
716    Widget_Data *wd = elm_widget_data_get(obj);
717    if (!wd) return;
718
719    elm_object_disabled_set(wd->filename_entry, !is_save);
720
721    if (is_save)
722      edje_object_signal_emit(wd->edje, "elm,state,save,on", "elm");
723    else
724      edje_object_signal_emit(wd->edje, "elm,state,save,off", "elm");
725 }
726
727 /**
728  * This returns whether the fileselector is a "save" type fileselector
729  *
730  * @param obj The fileselector object
731  * @return If true, the fileselector is a save type.
732  *
733  * @ingroup Fileselector
734  */
735 EAPI Eina_Bool
736 elm_fileselector_is_save_get(const Evas_Object *obj)
737 {
738    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
739    Widget_Data *wd = elm_widget_data_get(obj);
740    if (!wd) return EINA_FALSE;
741    return elm_object_disabled_get(wd->filename_entry);
742 }
743
744 /**
745  * This enables/disables folder-only view in the fileselector.
746  *
747  * @param obj The fileselector object
748  * @param only If true, the fileselector will only display directories.
749  * If false, files are displayed also.
750  *
751  * @ingroup Fileselector
752  */
753 EAPI void
754 elm_fileselector_folder_only_set(Evas_Object *obj,
755                                  Eina_Bool    only)
756 {
757    ELM_CHECK_WIDTYPE(obj, widtype);
758    Widget_Data *wd = elm_widget_data_get(obj);
759    if (!wd) return;
760    if (wd->only_folder == only) return;
761    wd->only_folder = !!only;
762    if (wd->path) _populate(obj, wd->path, NULL);
763 }
764
765 /**
766  * This gets the state of file display in the fileselector.
767  *
768  * @param obj The fileselector object
769  * @return If true, files are not being shown in the fileselector.
770  * If false, files are being shown.
771  *
772  * @ingroup Fileselector
773  */
774 EAPI Eina_Bool
775 elm_fileselector_folder_only_get(const Evas_Object *obj)
776 {
777    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
778    Widget_Data *wd = elm_widget_data_get(obj);
779    if (!wd) return EINA_FALSE;
780    return wd->only_folder;
781 }
782
783 /**
784  * This enables/disables the ok,cancel buttons.
785  *
786  * @param obj The fileselector object
787  * @param only If true, a box containing ok and cancel buttons is created.
788  * If false, the box and the buttons are destroyed.
789  *
790  * @ingroup Fileselector
791  */
792 EAPI void
793 elm_fileselector_buttons_ok_cancel_set(Evas_Object *obj,
794                                        Eina_Bool    visible)
795 {
796    ELM_CHECK_WIDTYPE(obj, widtype);
797    Widget_Data *wd = elm_widget_data_get(obj);
798    Evas_Object *bt;
799    if (!wd) return;
800
801    if (visible)
802      {
803         // cancel btn
804         bt = elm_button_add(obj);
805         elm_widget_mirrored_automatic_set(bt, EINA_FALSE);
806         elm_button_label_set(bt, E_("Cancel"));
807
808         evas_object_smart_callback_add(bt, "clicked", _canc, obj);
809
810         elm_widget_sub_object_add(obj, bt);
811         wd->cancel_button = bt;
812
813         // ok btn
814         bt = elm_button_add(obj);
815         elm_widget_mirrored_automatic_set(bt, EINA_FALSE);
816         elm_button_label_set(bt, E_("OK"));
817
818         evas_object_smart_callback_add(bt, "clicked", _ok, obj);
819
820         elm_widget_sub_object_add(obj, bt);
821         wd->ok_button = bt;
822
823         _theme_hook(obj);
824      }
825    else
826      {
827         evas_object_del(wd->cancel_button);
828         wd->cancel_button = NULL;
829         evas_object_del(wd->ok_button);
830         wd->ok_button = NULL;
831      }
832 }
833
834 /**
835  * This gets the state of the box containing ok and cancel buttons.
836  *
837  * @param obj The fileselector object
838  * @return If true, the box exists.
839  * If false, the box does not exist.
840  *
841  * @ingroup Fileselector
842  */
843 EAPI Eina_Bool
844 elm_fileselector_buttons_ok_cancel_get(const Evas_Object *obj)
845 {
846    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
847    Widget_Data *wd = elm_widget_data_get(obj);
848    if (!wd) return EINA_FALSE;
849    return wd->ok_button ? EINA_TRUE : EINA_FALSE;
850 }
851
852 /**
853  * This enables a tree view in the fileselector, <b>if in @c
854  * ELM_FILESELECTOR_LIST mode</b>. If it's in other mode, the changes
855  * made by this function will only be visible when one switches back
856  * to list mode.
857  *
858  * @param obj The fileselector object
859  * @param expand If true, tree view is enabled.
860  * If false, tree view is disabled.
861  *
862  * In a tree view, arrows are created on the sides of directories,
863  * allowing them to expand in place.
864  *
865  * @ingroup Fileselector
866  */
867 EAPI void
868 elm_fileselector_expandable_set(Evas_Object *obj,
869                                 Eina_Bool    expand)
870 {
871    ELM_CHECK_WIDTYPE(obj, widtype);
872    Widget_Data *wd;
873
874    wd = elm_widget_data_get(obj);
875    if (!wd) return;
876
877    wd->expand = !!expand;
878
879    if (wd->path) _populate(obj, wd->path, NULL);
880 }
881
882 /**
883  * This gets the state of tree view in the fileselector.
884  *
885  * @param obj The fileselector object
886  * @return If true, tree view is enabled and folders will be expandable.
887  * If false, tree view is disabled.
888  *
889  * @ingroup Fileselector
890  */
891 EAPI Eina_Bool
892 elm_fileselector_expandable_get(const Evas_Object *obj)
893 {
894    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
895    Widget_Data *wd = elm_widget_data_get(obj);
896    if (!wd) return EINA_FALSE;
897    return wd->expand;
898 }
899
900 /**
901  * This sets the path that the fileselector will display.
902  *
903  * @param obj The fileselector object
904  * @param path The path of the fileselector
905  *
906  * @ingroup Fileselector
907  */
908 EAPI void
909 elm_fileselector_path_set(Evas_Object *obj,
910                           const char  *path)
911 {
912    ELM_CHECK_WIDTYPE(obj, widtype);
913    _populate(obj, path, NULL);
914 }
915
916 /**
917  * This gets the path that the fileselector displays.
918  *
919  * @param obj The fileselector object
920  * @return The path that the fileselector is displaying
921  *
922  * @ingroup Fileselector
923  */
924 EAPI const char *
925 elm_fileselector_path_get(const Evas_Object *obj)
926 {
927    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
928    Widget_Data *wd = elm_widget_data_get(obj);
929    if (!wd) return NULL;
930    return wd->path;
931 }
932
933 /**
934  * This sets the mode in which the fileselector will display files.
935  *
936  * @param obj The fileselector object
937
938  * @param mode The mode of the fileselector, being it one of @c
939  * ELM_FILESELECTOR_LIST (default) or @c ELM_FILESELECTOR_GRID. The
940  * first one, naturally, will display the files in a list. By using
941  * elm_fileselector_expandable_set(), the user will trigger a tree
942  * view for that list. The latter will make the widget to display its
943  * entries in a grid form.
944  *
945  * @see elm_fileselector_expandable_set().
946  *
947  * @ingroup Fileselector
948  */
949 EAPI void
950 elm_fileselector_mode_set(Evas_Object          *obj,
951                           Elm_Fileselector_Mode mode)
952 {
953    ELM_CHECK_WIDTYPE(obj, widtype);
954
955    Widget_Data *wd = elm_widget_data_get(obj);
956    if (!wd) return;
957
958    if (mode == wd->mode) return;
959
960    if (mode == ELM_FILESELECTOR_LIST)
961      {
962         if (edje_object_part_swallow(wd->edje, "elm.swallow.files",
963                                      wd->files_list))
964           {
965              evas_object_show(wd->files_list);
966              evas_object_hide(wd->files_grid);
967           }
968         else
969           evas_object_hide(wd->files_list);
970      }
971    else
972      {
973         if (edje_object_part_swallow(wd->edje, "elm.swallow.files",
974                                      wd->files_grid))
975           {
976              evas_object_show(wd->files_grid);
977              evas_object_hide(wd->files_list);
978           }
979         else
980           evas_object_hide(wd->files_grid);
981      }
982
983    wd->mode = mode;
984
985    _populate(obj, wd->path, NULL);
986 }
987
988 /**
989  * This gets the mode in which the fileselector is displaying files.
990  *
991  * @param obj The fileselector object
992  * @return The mode in which the fileselector is at
993  *
994  * @ingroup Fileselector
995  */
996 EAPI Elm_Fileselector_Mode
997 elm_fileselector_mode_get(const Evas_Object *obj)
998 {
999    ELM_CHECK_WIDTYPE(obj, widtype) ELM_FILESELECTOR_LAST;
1000
1001    Widget_Data *wd = elm_widget_data_get(obj);
1002    if (!wd) return ELM_FILESELECTOR_LAST;
1003
1004    return wd->mode;
1005 }
1006
1007 /**
1008  * This gets the currently selected path in the file selector.
1009  *
1010  * @param obj The file selector object
1011  * @return The absolute path of the selected object in the fileselector
1012  *
1013  * @ingroup Fileselector
1014  */
1015 EAPI const char *
1016 elm_fileselector_selected_get(const Evas_Object *obj)
1017 {
1018    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
1019    Widget_Data *wd = elm_widget_data_get(obj);
1020    if (!wd) return NULL;
1021
1022    if (wd->filename_entry)
1023      {
1024         const char *name;
1025         char buf[PATH_MAX];
1026
1027         name = elm_entry_entry_get(wd->filename_entry);
1028         snprintf(buf, sizeof(buf), "%s/%s",
1029                  wd->only_folder ? ecore_file_dir_get(wd->path) : wd->path,
1030                  name);
1031         eina_stringshare_replace(&wd->selection, buf);
1032         return wd->selection;
1033      }
1034
1035    if (wd->mode == ELM_FILESELECTOR_LIST)
1036      {
1037         Elm_Genlist_Item *it;
1038         it = elm_genlist_selected_item_get(wd->files_list);
1039         if (it) return elm_genlist_item_data_get(it);
1040      }
1041    else
1042      {
1043         Elm_Gengrid_Item *it;
1044         it = elm_gengrid_selected_item_get(wd->files_grid);
1045         if (it) return elm_gengrid_item_data_get(it);
1046      }
1047
1048    return wd->path;
1049 }
1050
1051 /**
1052  * This sets the currently selected path in the file selector.
1053  *
1054  * @param obj The file selector object
1055  * @param path The path to a file or directory
1056  * @return @c EINA_TRUE on success, @c EINA_FALSE on failure. The
1057  * latter case occurs if the directory or file pointed to do not
1058  * exist.
1059  *
1060  * @ingroup Fileselector
1061  */
1062 EAPI Eina_Bool
1063 elm_fileselector_selected_set(Evas_Object *obj,
1064                               const char  *path)
1065 {
1066    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
1067    Widget_Data *wd = elm_widget_data_get(obj);
1068    if (!wd) return EINA_FALSE;
1069
1070    if (ecore_file_is_dir(path))
1071      _populate(obj, path, NULL);
1072    else
1073      {
1074         if (!ecore_file_exists(path))
1075           return EINA_FALSE;
1076
1077         _populate(obj, ecore_file_dir_get(path), NULL);
1078         if (wd->filename_entry)
1079           {
1080              elm_entry_entry_set(wd->filename_entry,
1081                                           ecore_file_file_get(path));
1082              eina_stringshare_replace(&wd->selection, path);
1083           }
1084      }
1085
1086    return EINA_TRUE;
1087 }
1088