elementary: add elm_icon_thumb_set.
[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              static const char *extensions[][2] = {
231                { ".jpg", "image" },
232                { ".png", "image" },
233                { ".gif", "image" },
234                { ".jpeg", "image" },
235                { NULL, NULL }
236              };
237              int len, i;
238              Eina_Bool found = EINA_FALSE;
239
240              len = eina_stringshare_strlen(filename);
241
242              for (i = 0; extensions[i][0]; ++i)
243                {
244                   int lext;
245
246                   lext = strlen(extensions[i][0]);
247                   if (len < lext) continue;
248                   if (!strcasecmp(filename + len - lext, extensions[i][0]))
249                     {
250                        found = EINA_TRUE;
251                        elm_icon_standard_set(ic, extensions[i][1]);
252                        elm_icon_thumb_set(ic, filename, NULL);
253                     }
254                }
255
256              if (!found)
257                elm_icon_standard_set(ic, "file");
258           }
259
260         evas_object_size_hint_aspect_set(ic, EVAS_ASPECT_CONTROL_VERTICAL,
261                                          1, 1);
262         evas_object_show(ic);
263         return ic;
264      }
265    return NULL;
266 }
267
268 static Eina_Bool
269 _itc_state_get(void *data         __UNUSED__,
270                Evas_Object *obj   __UNUSED__,
271                const char *source __UNUSED__)
272 {
273    return EINA_FALSE;
274 }
275
276 static void
277 _itc_del(void            *data,
278          Evas_Object *obj __UNUSED__)
279 {
280    eina_stringshare_del(data);
281 }
282
283 static void
284 _expand_done(void            *data,
285              Evas_Object *obj __UNUSED__,
286              void            *event_info)
287 {
288    Elm_Genlist_Item *it = event_info;
289    const char *path = elm_genlist_item_data_get(it);
290    _populate(data, path, it);
291 }
292
293 static void
294 _contract_done(void *data       __UNUSED__,
295                Evas_Object *obj __UNUSED__,
296                void            *event_info)
297 {
298    Elm_Genlist_Item *it = event_info;
299    elm_genlist_item_subitems_clear(it);
300 }
301
302 static void
303 _expand_req(void *data       __UNUSED__,
304             Evas_Object *obj __UNUSED__,
305             void            *event_info)
306 {
307    Elm_Genlist_Item *it = event_info;
308    elm_genlist_item_expanded_set(it, 1);
309 }
310
311 static void
312 _contract_req(void *data       __UNUSED__,
313               Evas_Object *obj __UNUSED__,
314               void            *event_info)
315 {
316    Elm_Genlist_Item *it = event_info;
317    elm_genlist_item_expanded_set(it, 0);
318 }
319
320 /***  PRIVATES  ***/
321 static Eina_Bool
322 _sel_do(void *data)
323 {
324    struct sel_data *sd;
325    const char *path;
326    Widget_Data *wd;
327    const char *p;
328
329    sd = data;
330    wd = elm_widget_data_get(sd->fs);
331    path = sd->path;
332
333    if ((!wd->only_folder) && ecore_file_is_dir(path))
334      {
335         if (wd->expand && wd->mode == ELM_FILESELECTOR_LIST)
336           {
337              _do_anchors(sd->fs, path);
338              elm_scrolled_entry_entry_set(wd->filename_entry, "");
339           }
340         else
341           {
342              /* keep a ref to path 'couse it will be destroyed by _populate */
343              p = eina_stringshare_add(path);
344              _populate(sd->fs, p, NULL);
345              eina_stringshare_del(p);
346           }
347         goto end;
348      }
349    else /* navigating through folders only or file is not a dir. */
350      {
351         if (wd->expand && wd->mode == ELM_FILESELECTOR_LIST)
352           _do_anchors(sd->fs, path);
353         else if (wd->only_folder)
354           {
355              /* keep a ref to path 'couse it will be destroyed by _populate */
356              p = eina_stringshare_add(path);
357              _populate(sd->fs, p, NULL);
358              eina_stringshare_del(p);
359           }
360         elm_scrolled_entry_entry_set(wd->filename_entry,
361                                      ecore_file_file_get(path));
362      }
363
364    evas_object_smart_callback_call(sd->fs, SIG_SELECTED, (void *)path);
365
366 end:
367    wd->sel_idler = NULL;
368    free(sd);
369    return ECORE_CALLBACK_CANCEL;
370 }
371
372 static void
373 _sel(void            *data,
374      Evas_Object *obj __UNUSED__,
375      void            *event_info)
376 {
377    struct sel_data *sd;
378    Widget_Data *wd;
379    void *old_sd;
380    char *dir;
381
382    wd = elm_widget_data_get(data);
383    if (!wd) return;
384
385    sd = malloc(sizeof(*sd));
386    sd->fs = data;
387    sd->path = wd->mode == ELM_FILESELECTOR_LIST ?
388        elm_genlist_item_data_get(event_info) :
389        elm_gengrid_item_data_get(event_info);
390
391    if (!sd->path)
392      {
393         eina_stringshare_replace(&wd->path, "");
394         goto end;
395      }
396
397    dir = wd->only_folder ? strdup(sd->path) : ecore_file_dir_get(sd->path);
398    if (dir)
399      {
400         eina_stringshare_replace(&wd->path, dir);
401         free(dir);
402      }
403    else
404      {
405         eina_stringshare_replace(&wd->path, "");
406      }
407
408 end:
409    if (wd->sel_idler)
410      {
411         old_sd = ecore_idler_del(wd->sel_idler);
412         free(old_sd);
413      }
414    wd->sel_idler = ecore_idler_add(_sel_do, sd);
415 }
416
417 static void
418 _up(void            *data,
419     Evas_Object *obj __UNUSED__,
420     void *event_info __UNUSED__)
421 {
422    Evas_Object *fs = data;
423    char *parent;
424
425    Widget_Data *wd = elm_widget_data_get(fs);
426    if (!wd) return;
427    parent = ecore_file_dir_get(wd->path);
428    _populate(fs, parent, NULL);
429    free(parent);
430 }
431
432 static void
433 _home(void            *data,
434       Evas_Object *obj __UNUSED__,
435       void *event_info __UNUSED__)
436 {
437    Evas_Object *fs = data;
438    _populate(fs, getenv("HOME"), NULL);
439 }
440
441 static void
442 _ok(void            *data,
443     Evas_Object *obj __UNUSED__,
444     void *event_info __UNUSED__)
445 {
446    Evas_Object *fs = data;
447    evas_object_smart_callback_call(fs, SIG_DONE,
448                                    (void *)elm_fileselector_selected_get(fs));
449 }
450
451 static void
452 _canc(void            *data,
453       Evas_Object *obj __UNUSED__,
454       void *event_info __UNUSED__)
455 {
456    Evas_Object *fs = data;
457    evas_object_smart_callback_call(fs, SIG_DONE, NULL);
458 }
459
460 static void
461 _anchor_clicked(void            *data,
462                 Evas_Object *obj __UNUSED__,
463                 void            *event_info)
464 {
465    Evas_Object *fs = data;
466    Widget_Data *wd = elm_widget_data_get(fs);
467    Elm_Entry_Anchor_Info *info = event_info;
468    const char *p;
469    if (!wd) return;
470    // keep a ref to path 'couse it will be destroyed by _populate
471    p = eina_stringshare_add(info->name);
472    _populate(fs, p, NULL);
473    evas_object_smart_callback_call(data, SIG_SELECTED, (void *)p);
474    eina_stringshare_del(p);
475 }
476
477 static void
478 _do_anchors(Evas_Object *obj,
479             const char  *path)
480 {
481    Widget_Data *wd = elm_widget_data_get(obj);
482    char **tok, buf[PATH_MAX * 3];
483    int i, j;
484    if (!wd) return;
485    buf[0] = '\0';
486    tok = eina_str_split(path, "/", 0);
487    eina_strlcat(buf, "<a href=/>root</a>", sizeof(buf));
488    for (i = 0; tok[i]; i++)
489      {
490         if ((!tok[i]) || (!tok[i][0])) continue;
491         eina_strlcat(buf, wd->path_separator, sizeof(buf));
492         eina_strlcat(buf, "<a href=", sizeof(buf));
493         for (j = 0; j <= i; j++)
494           {
495              if (strlen(tok[j]) < 1) continue;
496              eina_strlcat(buf, "/", sizeof(buf));
497              eina_strlcat(buf, tok[j], sizeof(buf));
498           }
499         eina_strlcat(buf, ">", sizeof(buf));
500         eina_strlcat(buf, tok[i], sizeof(buf));
501         eina_strlcat(buf, "</a>", sizeof(buf));
502      }
503    free(tok[0]);
504    free(tok);
505
506    elm_scrolled_entry_entry_set(wd->path_entry, buf);
507 }
508
509 static void
510 _populate(Evas_Object      *obj,
511           const char       *path,
512           Elm_Genlist_Item *parent)
513 {
514    Widget_Data *wd = elm_widget_data_get(obj);
515    DIR *dir;
516    struct dirent *dp;
517    char buf[PATH_MAX];
518    char *real;
519    Eina_List *files = NULL, *dirs = NULL, *l;
520
521    if ((!wd) || (!ecore_file_is_dir(path))) return;
522    dir = opendir(path);
523    if (!dir) return;
524    evas_object_smart_callback_call(obj, SIG_DIRECTORY_OPEN, (void *)path);
525    if (!parent)
526      {
527         elm_genlist_clear(wd->files_list);
528         elm_gengrid_clear(wd->files_grid);
529         eina_stringshare_replace(&wd->path, path);
530         _do_anchors(obj, path);
531      }
532
533    if (wd->filename_entry) elm_scrolled_entry_entry_set(wd->filename_entry, "");
534    while ((dp = readdir(dir)))
535      {
536         if (dp->d_name[0] == '.') continue;  // TODO make this configurable
537
538         snprintf(buf, sizeof(buf), "%s/%s", path, dp->d_name);
539         real = ecore_file_realpath(buf); /* TODO: this will resolv
540                                           * symlinks...I dont like
541                                           * it*/
542         if (ecore_file_is_dir(real))
543           dirs = eina_list_append(dirs, real);
544         else if (!wd->only_folder)
545           files = eina_list_append(files, real);
546      }
547    closedir(dir);
548
549    files = eina_list_sort(files, eina_list_count(files),
550                           EINA_COMPARE_CB(strcoll));
551    dirs = eina_list_sort(dirs, eina_list_count(dirs), EINA_COMPARE_CB(strcoll));
552    EINA_LIST_FOREACH(dirs, l, real)
553      {
554         if (wd->mode == ELM_FILESELECTOR_LIST)
555           elm_genlist_item_append(wd->files_list, &list_itc,
556                                   eina_stringshare_add(real), /* item data */
557                                   parent,
558                                   wd->expand ? ELM_GENLIST_ITEM_SUBITEMS :
559                                   ELM_GENLIST_ITEM_NONE,
560                                   NULL, NULL);
561         else if (wd->mode == ELM_FILESELECTOR_GRID)
562           elm_gengrid_item_append(wd->files_grid, &grid_itc,
563                                   eina_stringshare_add(real), /* item data */
564                                   NULL, NULL);
565
566         free(real);
567      }
568    eina_list_free(dirs);
569
570    EINA_LIST_FOREACH(files, l, real)
571      {
572         if (wd->mode == ELM_FILESELECTOR_LIST)
573           elm_genlist_item_append(wd->files_list, &list_itc,
574                                   eina_stringshare_add(real), /* item data */
575                                   parent, ELM_GENLIST_ITEM_NONE,
576                                   NULL, NULL);
577         else if (wd->mode == ELM_FILESELECTOR_GRID)
578           elm_gengrid_item_append(wd->files_grid, &grid_itc,
579                                   eina_stringshare_add(real), /* item data */
580                                   NULL, NULL);
581         free(real);
582      }
583    eina_list_free(files);
584 }
585
586 /***  API  ***/
587
588 /**
589  * Add a new Fileselector object
590  *
591  * @param parent The parent object
592  * @return The new object or NULL if it cannot be created
593  *
594  * @ingroup Fileselector
595  */
596 EAPI Evas_Object *
597 elm_fileselector_add(Evas_Object *parent)
598 {
599    Evas *e;
600    Evas_Object *obj, *ic, *bt, *li, *en, *grid;
601    Widget_Data *wd;
602    int s;
603
604    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
605
606    ELM_SET_WIDTYPE(widtype, "fileselector");
607    elm_widget_type_set(obj, "fileselector");
608    elm_widget_sub_object_add(parent, obj);
609    elm_widget_data_set(obj, wd);
610    elm_widget_del_hook_set(obj, _del_hook);
611    elm_widget_theme_hook_set(obj, _theme_hook);
612    elm_widget_can_focus_set(obj, EINA_FALSE);
613
614    wd->expand = !!_elm_config->fileselector_expand_enable;
615
616    wd->edje = edje_object_add(e);
617    _elm_theme_object_set(obj, wd->edje, "fileselector", "base", "default");
618    elm_widget_resize_object_set(obj, wd->edje);
619
620    // up btn
621    ic = elm_icon_add(parent);
622    elm_icon_standard_set(ic, "arrow_up");
623    evas_object_size_hint_aspect_set(ic, EVAS_ASPECT_CONTROL_VERTICAL, 1, 1);
624    bt = elm_button_add(parent);
625    elm_widget_mirrored_automatic_set(bt, EINA_FALSE);
626    elm_button_icon_set(bt, ic);
627    elm_button_label_set(bt, E_("Up"));
628    evas_object_size_hint_align_set(bt, 0.0, 0.0);
629
630    evas_object_smart_callback_add(bt, "clicked", _up, obj);
631
632    elm_widget_sub_object_add(obj, bt);
633    wd->up_button = bt;
634
635    // home btn
636    ic = elm_icon_add(parent);
637    elm_icon_standard_set(ic, "home");
638    evas_object_size_hint_aspect_set(ic, EVAS_ASPECT_CONTROL_VERTICAL, 1, 1);
639    bt = elm_button_add(parent);
640    elm_widget_mirrored_automatic_set(bt, EINA_FALSE);
641    elm_button_icon_set(bt, ic);
642    elm_button_label_set(bt, E_("Home"));
643    evas_object_size_hint_align_set(bt, 0.0, 0.0);
644
645    evas_object_smart_callback_add(bt, "clicked", _home, obj);
646
647    elm_widget_sub_object_add(obj, bt);
648    wd->home_button = bt;
649
650    list_itc.item_style = grid_itc.item_style = "default";
651    list_itc.func.label_get = grid_itc.func.label_get = _itc_label_get;
652    list_itc.func.icon_get = grid_itc.func.icon_get = _itc_icon_get;
653    list_itc.func.state_get = grid_itc.func.state_get = _itc_state_get;
654    list_itc.func.del = grid_itc.func.del = _itc_del;
655
656    li = elm_genlist_add(parent);
657    elm_widget_mirrored_automatic_set(li, EINA_FALSE);
658    evas_object_size_hint_align_set(li, EVAS_HINT_FILL, EVAS_HINT_FILL);
659    evas_object_size_hint_weight_set(li, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
660    evas_object_size_hint_min_set(li, 100, 100);
661
662    grid = elm_gengrid_add(parent);
663    elm_widget_mirrored_automatic_set(grid, EINA_FALSE);
664    evas_object_size_hint_align_set(grid, EVAS_HINT_FILL, EVAS_HINT_FILL);
665    evas_object_size_hint_weight_set(grid, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
666
667    s = elm_finger_size_get() * 2;
668    elm_gengrid_item_size_set(grid, s, s);
669    elm_gengrid_align_set(grid, 0.0, 0.0);
670
671    evas_object_smart_callback_add(li, "selected", _sel, obj);
672    evas_object_smart_callback_add(li, "expand,request", _expand_req, obj);
673    evas_object_smart_callback_add(li, "contract,request", _contract_req, obj);
674    evas_object_smart_callback_add(li, "expanded", _expand_done, obj);
675    evas_object_smart_callback_add(li, "contracted", _contract_done, obj);
676
677    evas_object_smart_callback_add(grid, "selected", _sel, obj);
678
679    elm_widget_sub_object_add(obj, li);
680    elm_widget_sub_object_add(obj, grid);
681    wd->files_list = li;
682    wd->files_grid = grid;
683
684    // path entry
685    en = elm_scrolled_entry_add(parent);
686    elm_widget_mirrored_automatic_set(en, EINA_FALSE);
687    elm_scrolled_entry_editable_set(en, EINA_FALSE);
688    elm_scrolled_entry_single_line_set(en, EINA_TRUE);
689    elm_scrolled_entry_line_wrap_set(en, ELM_WRAP_CHAR);
690    evas_object_size_hint_weight_set(en, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
691    evas_object_size_hint_align_set(en, EVAS_HINT_FILL, EVAS_HINT_FILL);
692
693    evas_object_smart_callback_add(en, "anchor,clicked", _anchor_clicked, obj);
694
695    elm_widget_sub_object_add(obj, en);
696    wd->path_entry = en;
697
698    // filename entry
699    en = elm_scrolled_entry_add(parent);
700    elm_widget_mirrored_automatic_set(en, EINA_FALSE);
701    elm_scrolled_entry_editable_set(en, EINA_TRUE);
702    elm_scrolled_entry_single_line_set(en, EINA_TRUE);
703    elm_scrolled_entry_line_wrap_set(en, ELM_WRAP_CHAR);
704    evas_object_size_hint_weight_set(en, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
705    evas_object_size_hint_align_set(en, EVAS_HINT_FILL, EVAS_HINT_FILL);
706
707    elm_widget_sub_object_add(obj, en);
708    wd->filename_entry = en;
709
710    elm_fileselector_buttons_ok_cancel_set(obj, EINA_TRUE);
711    elm_fileselector_is_save_set(obj, EINA_FALSE);
712
713    _theme_hook(obj);
714
715    evas_object_smart_callbacks_descriptions_set(obj, _signals);
716    return obj;
717 }
718
719 /**
720  * This enables/disables the file name entry box where the user can
721  * type in a name for the file to be saved as.
722  *
723  * @param obj The fileselector object
724  * @param is_save If true, the fileselector is a save dialog
725  *
726  * @ingroup Fileselector
727  */
728 EAPI void
729 elm_fileselector_is_save_set(Evas_Object *obj,
730                              Eina_Bool    is_save)
731 {
732    ELM_CHECK_WIDTYPE(obj, widtype);
733    Widget_Data *wd = elm_widget_data_get(obj);
734    if (!wd) return;
735
736    elm_object_disabled_set(wd->filename_entry, !is_save);
737
738    if (is_save)
739      edje_object_signal_emit(wd->edje, "elm,state,save,on", "elm");
740    else
741      edje_object_signal_emit(wd->edje, "elm,state,save,off", "elm");
742 }
743
744 /**
745  * This returns whether the fileselector is a "save" type fileselector
746  *
747  * @param obj The fileselector object
748  * @return If true, the fileselector is a save type.
749  *
750  * @ingroup Fileselector
751  */
752 EAPI Eina_Bool
753 elm_fileselector_is_save_get(const Evas_Object *obj)
754 {
755    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
756    Widget_Data *wd = elm_widget_data_get(obj);
757    if (!wd) return EINA_FALSE;
758    return elm_object_disabled_get(wd->filename_entry);
759 }
760
761 /**
762  * This enables/disables folder-only view in the fileselector.
763  *
764  * @param obj The fileselector object
765  * @param only If true, the fileselector will only display directories.
766  * If false, files are displayed also.
767  *
768  * @ingroup Fileselector
769  */
770 EAPI void
771 elm_fileselector_folder_only_set(Evas_Object *obj,
772                                  Eina_Bool    only)
773 {
774    ELM_CHECK_WIDTYPE(obj, widtype);
775    Widget_Data *wd = elm_widget_data_get(obj);
776    if (!wd) return;
777    if (wd->only_folder == only) return;
778    wd->only_folder = !!only;
779    if (wd->path) _populate(obj, wd->path, NULL);
780 }
781
782 /**
783  * This gets the state of file display in the fileselector.
784  *
785  * @param obj The fileselector object
786  * @return If true, files are not being shown in the fileselector.
787  * If false, files are being shown.
788  *
789  * @ingroup Fileselector
790  */
791 EAPI Eina_Bool
792 elm_fileselector_folder_only_get(const Evas_Object *obj)
793 {
794    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
795    Widget_Data *wd = elm_widget_data_get(obj);
796    if (!wd) return EINA_FALSE;
797    return wd->only_folder;
798 }
799
800 /**
801  * This enables/disables the ok,cancel buttons.
802  *
803  * @param obj The fileselector object
804  * @param only If true, a box containing ok and cancel buttons is created.
805  * If false, the box and the buttons are destroyed.
806  *
807  * @ingroup Fileselector
808  */
809 EAPI void
810 elm_fileselector_buttons_ok_cancel_set(Evas_Object *obj,
811                                        Eina_Bool    visible)
812 {
813    ELM_CHECK_WIDTYPE(obj, widtype);
814    Widget_Data *wd = elm_widget_data_get(obj);
815    Evas_Object *bt;
816    if (!wd) return;
817
818    if (visible)
819      {
820         // cancel btn
821         bt = elm_button_add(obj);
822         elm_widget_mirrored_automatic_set(bt, EINA_FALSE);
823         elm_button_label_set(bt, E_("Cancel"));
824
825         evas_object_smart_callback_add(bt, "clicked", _canc, obj);
826
827         elm_widget_sub_object_add(obj, bt);
828         wd->cancel_button = bt;
829
830         // ok btn
831         bt = elm_button_add(obj);
832         elm_widget_mirrored_automatic_set(bt, EINA_FALSE);
833         elm_button_label_set(bt, E_("OK"));
834
835         evas_object_smart_callback_add(bt, "clicked", _ok, obj);
836
837         elm_widget_sub_object_add(obj, bt);
838         wd->ok_button = bt;
839
840         _theme_hook(obj);
841      }
842    else
843      {
844         evas_object_del(wd->cancel_button);
845         wd->cancel_button = NULL;
846         evas_object_del(wd->ok_button);
847         wd->ok_button = NULL;
848      }
849 }
850
851 /**
852  * This gets the state of the box containing ok and cancel buttons.
853  *
854  * @param obj The fileselector object
855  * @return If true, the box exists.
856  * If false, the box does not exist.
857  *
858  * @ingroup Fileselector
859  */
860 EAPI Eina_Bool
861 elm_fileselector_buttons_ok_cancel_get(const Evas_Object *obj)
862 {
863    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
864    Widget_Data *wd = elm_widget_data_get(obj);
865    if (!wd) return EINA_FALSE;
866    return wd->ok_button ? EINA_TRUE : EINA_FALSE;
867 }
868
869 /**
870  * This enables a tree view in the fileselector, <b>if in @c
871  * ELM_FILESELECTOR_LIST mode</b>. If it's in other mode, the changes
872  * made by this function will only be visible when one switches back
873  * to list mode.
874  *
875  * @param obj The fileselector object
876  * @param expand If true, tree view is enabled.
877  * If false, tree view is disabled.
878  *
879  * In a tree view, arrows are created on the sides of directories,
880  * allowing them to expand in place.
881  *
882  * @ingroup Fileselector
883  */
884 EAPI void
885 elm_fileselector_expandable_set(Evas_Object *obj,
886                                 Eina_Bool    expand)
887 {
888    ELM_CHECK_WIDTYPE(obj, widtype);
889    Widget_Data *wd;
890
891    wd = elm_widget_data_get(obj);
892    if (!wd) return;
893
894    wd->expand = !!expand;
895
896    if (wd->path) _populate(obj, wd->path, NULL);
897 }
898
899 /**
900  * This gets the state of tree view in the fileselector.
901  *
902  * @param obj The fileselector object
903  * @return If true, tree view is enabled and folders will be expandable.
904  * If false, tree view is disabled.
905  *
906  * @ingroup Fileselector
907  */
908 EAPI Eina_Bool
909 elm_fileselector_expandable_get(const Evas_Object *obj)
910 {
911    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
912    Widget_Data *wd = elm_widget_data_get(obj);
913    if (!wd) return EINA_FALSE;
914    return wd->expand;
915 }
916
917 /**
918  * This sets the path that the fileselector will display.
919  *
920  * @param obj The fileselector object
921  * @param path The path of the fileselector
922  *
923  * @ingroup Fileselector
924  */
925 EAPI void
926 elm_fileselector_path_set(Evas_Object *obj,
927                           const char  *path)
928 {
929    ELM_CHECK_WIDTYPE(obj, widtype);
930    _populate(obj, path, NULL);
931 }
932
933 /**
934  * This gets the path that the fileselector displays.
935  *
936  * @param obj The fileselector object
937  * @return The path that the fileselector is displaying
938  *
939  * @ingroup Fileselector
940  */
941 EAPI const char *
942 elm_fileselector_path_get(const Evas_Object *obj)
943 {
944    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
945    Widget_Data *wd = elm_widget_data_get(obj);
946    if (!wd) return NULL;
947    return wd->path;
948 }
949
950 /**
951  * This sets the mode in which the fileselector will display files.
952  *
953  * @param obj The fileselector object
954
955  * @param mode The mode of the fileselector, being it one of @c
956  * ELM_FILESELECTOR_LIST (default) or @c ELM_FILESELECTOR_GRID. The
957  * first one, naturally, will display the files in a list. By using
958  * elm_fileselector_expandable_set(), the user will trigger a tree
959  * view for that list. The latter will make the widget to display its
960  * entries in a grid form.
961  *
962  * @see elm_fileselector_expandable_set().
963  *
964  * @ingroup Fileselector
965  */
966 EAPI void
967 elm_fileselector_mode_set(Evas_Object          *obj,
968                           Elm_Fileselector_Mode mode)
969 {
970    ELM_CHECK_WIDTYPE(obj, widtype);
971
972    Widget_Data *wd = elm_widget_data_get(obj);
973    if (!wd) return;
974
975    if (mode == wd->mode) return;
976
977    if (mode == ELM_FILESELECTOR_LIST)
978      {
979         if (edje_object_part_swallow(wd->edje, "elm.swallow.files",
980                                      wd->files_list))
981           {
982              evas_object_show(wd->files_list);
983              evas_object_hide(wd->files_grid);
984           }
985         else
986           evas_object_hide(wd->files_list);
987      }
988    else
989      {
990         if (edje_object_part_swallow(wd->edje, "elm.swallow.files",
991                                      wd->files_grid))
992           {
993              evas_object_show(wd->files_grid);
994              evas_object_hide(wd->files_list);
995           }
996         else
997           evas_object_hide(wd->files_grid);
998      }
999
1000    wd->mode = mode;
1001
1002    _populate(obj, wd->path, NULL);
1003 }
1004
1005 /**
1006  * This gets the mode in which the fileselector is displaying files.
1007  *
1008  * @param obj The fileselector object
1009  * @return The mode in which the fileselector is at
1010  *
1011  * @ingroup Fileselector
1012  */
1013 EAPI Elm_Fileselector_Mode
1014 elm_fileselector_mode_get(const Evas_Object *obj)
1015 {
1016    ELM_CHECK_WIDTYPE(obj, widtype) ELM_FILESELECTOR_LAST;
1017
1018    Widget_Data *wd = elm_widget_data_get(obj);
1019    if (!wd) return ELM_FILESELECTOR_LAST;
1020
1021    return wd->mode;
1022 }
1023
1024 /**
1025  * This gets the currently selected path in the file selector.
1026  *
1027  * @param obj The file selector object
1028  * @return The absolute path of the selected object in the fileselector
1029  *
1030  * @ingroup Fileselector
1031  */
1032 EAPI const char *
1033 elm_fileselector_selected_get(const Evas_Object *obj)
1034 {
1035    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
1036    Widget_Data *wd = elm_widget_data_get(obj);
1037    if (!wd) return NULL;
1038
1039    if (wd->filename_entry)
1040      {
1041         const char *name;
1042         char buf[PATH_MAX];
1043
1044         name = elm_scrolled_entry_entry_get(wd->filename_entry);
1045         snprintf(buf, sizeof(buf), "%s/%s",
1046                  wd->only_folder ? ecore_file_dir_get(wd->path) : wd->path,
1047                  name);
1048         eina_stringshare_replace(&wd->selection, buf);
1049         return wd->selection;
1050      }
1051
1052    if (wd->mode == ELM_FILESELECTOR_LIST)
1053      {
1054         Elm_Genlist_Item *it;
1055         it = elm_genlist_selected_item_get(wd->files_list);
1056         if (it) return elm_genlist_item_data_get(it);
1057      }
1058    else
1059      {
1060         Elm_Gengrid_Item *it;
1061         it = elm_gengrid_selected_item_get(wd->files_grid);
1062         if (it) return elm_gengrid_item_data_get(it);
1063      }
1064
1065    return wd->path;
1066 }
1067
1068 /**
1069  * This sets the currently selected path in the file selector.
1070  *
1071  * @param obj The file selector object
1072  * @param path The path to a file or directory
1073  * @return @c EINA_TRUE on success, @c EINA_FALSE on failure. The
1074  * latter case occurs if the directory or file pointed to do not
1075  * exist.
1076  *
1077  * @ingroup Fileselector
1078  */
1079 EAPI Eina_Bool
1080 elm_fileselector_selected_set(Evas_Object *obj,
1081                               const char  *path)
1082 {
1083    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
1084    Widget_Data *wd = elm_widget_data_get(obj);
1085    if (!wd) return EINA_FALSE;
1086
1087    if (ecore_file_is_dir(path))
1088      _populate(obj, path, NULL);
1089    else
1090      {
1091         if (!ecore_file_exists(path))
1092           return EINA_FALSE;
1093
1094         _populate(obj, ecore_file_dir_get(path), NULL);
1095         if (wd->filename_entry)
1096           {
1097              elm_scrolled_entry_entry_set(wd->filename_entry,
1098                                           ecore_file_file_get(path));
1099              eina_stringshare_replace(&wd->selection, path);
1100           }
1101      }
1102
1103    return EINA_TRUE;
1104 }
1105