elementary: use Eina_File.
[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    Eina_File_Direct_Info *file;
497    Eina_Iterator *it;
498    const char *real;
499    Eina_List *files = NULL, *dirs = NULL;
500
501    if ((!wd) || (!ecore_file_is_dir(path))) return;
502    it = eina_file_stat_ls(path);
503    if (!it) return ;
504    evas_object_smart_callback_call(obj, SIG_DIRECTORY_OPEN, (void *)path);
505    if (!parent)
506      {
507         elm_genlist_clear(wd->files_list);
508         elm_gengrid_clear(wd->files_grid);
509         eina_stringshare_replace(&wd->path, path);
510         _do_anchors(obj, path);
511      }
512
513    if (wd->filename_entry) elm_entry_entry_set(wd->filename_entry, "");
514    EINA_ITERATOR_FOREACH(it, file)
515      {
516         const char *filename;
517
518         if (file->path[file->name_start] == '.')
519           continue ;
520
521         filename = eina_stringshare_add(file->path);
522         if (file->type == EINA_FILE_DIR)
523           dirs = eina_list_append(dirs, filename);
524         else if (!wd->only_folder)
525           files = eina_list_append(files, filename);
526      }
527    eina_iterator_free(it);
528
529    files = eina_list_sort(files, eina_list_count(files),
530                           EINA_COMPARE_CB(strcoll));
531    dirs = eina_list_sort(dirs, eina_list_count(dirs), EINA_COMPARE_CB(strcoll));
532    EINA_LIST_FREE(dirs, real)
533      {
534         if (wd->mode == ELM_FILESELECTOR_LIST)
535           elm_genlist_item_append(wd->files_list, &list_itc,
536                                   real, /* item data */
537                                   parent,
538                                   wd->expand ? ELM_GENLIST_ITEM_SUBITEMS :
539                                   ELM_GENLIST_ITEM_NONE,
540                                   NULL, NULL);
541         else if (wd->mode == ELM_FILESELECTOR_GRID)
542           elm_gengrid_item_append(wd->files_grid, &grid_itc,
543                                   real, /* item data */
544                                   NULL, NULL);
545      }
546
547    EINA_LIST_FREE(files, real)
548      {
549         if (wd->mode == ELM_FILESELECTOR_LIST)
550           elm_genlist_item_append(wd->files_list, &list_itc,
551                                   real, /* item data */
552                                   parent, ELM_GENLIST_ITEM_NONE,
553                                   NULL, NULL);
554         else if (wd->mode == ELM_FILESELECTOR_GRID)
555           elm_gengrid_item_append(wd->files_grid, &grid_itc,
556                                   real, /* item data */
557                                   NULL, NULL);
558      }
559 }
560
561 /***  API  ***/
562
563 /**
564  * Add a new Fileselector object
565  *
566  * @param parent The parent object
567  * @return The new object or NULL if it cannot be created
568  *
569  * @ingroup Fileselector
570  */
571 EAPI Evas_Object *
572 elm_fileselector_add(Evas_Object *parent)
573 {
574    Evas *e;
575    Evas_Object *obj, *ic, *bt, *li, *en, *grid;
576    Widget_Data *wd;
577    int s;
578
579    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
580
581    ELM_SET_WIDTYPE(widtype, "fileselector");
582    elm_widget_type_set(obj, "fileselector");
583    elm_widget_sub_object_add(parent, obj);
584    elm_widget_data_set(obj, wd);
585    elm_widget_del_hook_set(obj, _del_hook);
586    elm_widget_theme_hook_set(obj, _theme_hook);
587    elm_widget_can_focus_set(obj, EINA_FALSE);
588
589    wd->expand = !!_elm_config->fileselector_expand_enable;
590
591    wd->edje = edje_object_add(e);
592    _elm_theme_object_set(obj, wd->edje, "fileselector", "base", "default");
593    elm_widget_resize_object_set(obj, wd->edje);
594
595    // up btn
596    ic = elm_icon_add(parent);
597    elm_icon_standard_set(ic, "arrow_up");
598    evas_object_size_hint_aspect_set(ic, EVAS_ASPECT_CONTROL_VERTICAL, 1, 1);
599    bt = elm_button_add(parent);
600    elm_widget_mirrored_automatic_set(bt, EINA_FALSE);
601    elm_button_icon_set(bt, ic);
602    elm_button_label_set(bt, E_("Up"));
603    evas_object_size_hint_align_set(bt, 0.0, 0.0);
604
605    evas_object_smart_callback_add(bt, "clicked", _up, obj);
606
607    elm_widget_sub_object_add(obj, bt);
608    wd->up_button = bt;
609
610    // home btn
611    ic = elm_icon_add(parent);
612    elm_icon_standard_set(ic, "home");
613    evas_object_size_hint_aspect_set(ic, EVAS_ASPECT_CONTROL_VERTICAL, 1, 1);
614    bt = elm_button_add(parent);
615    elm_widget_mirrored_automatic_set(bt, EINA_FALSE);
616    elm_button_icon_set(bt, ic);
617    elm_button_label_set(bt, E_("Home"));
618    evas_object_size_hint_align_set(bt, 0.0, 0.0);
619
620    evas_object_smart_callback_add(bt, "clicked", _home, obj);
621
622    elm_widget_sub_object_add(obj, bt);
623    wd->home_button = bt;
624
625    list_itc.item_style = grid_itc.item_style = "default";
626    list_itc.func.label_get = grid_itc.func.label_get = _itc_label_get;
627    list_itc.func.icon_get = grid_itc.func.icon_get = _itc_icon_get;
628    list_itc.func.state_get = grid_itc.func.state_get = _itc_state_get;
629    list_itc.func.del = grid_itc.func.del = _itc_del;
630
631    li = elm_genlist_add(parent);
632    elm_widget_mirrored_automatic_set(li, EINA_FALSE);
633    evas_object_size_hint_align_set(li, EVAS_HINT_FILL, EVAS_HINT_FILL);
634    evas_object_size_hint_weight_set(li, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
635    evas_object_size_hint_min_set(li, 100, 100);
636
637    grid = elm_gengrid_add(parent);
638    elm_widget_mirrored_automatic_set(grid, EINA_FALSE);
639    evas_object_size_hint_align_set(grid, EVAS_HINT_FILL, EVAS_HINT_FILL);
640    evas_object_size_hint_weight_set(grid, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
641
642    s = elm_finger_size_get() * 2;
643    elm_gengrid_item_size_set(grid, s, s);
644    elm_gengrid_align_set(grid, 0.0, 0.0);
645
646    evas_object_smart_callback_add(li, "selected", _sel, obj);
647    evas_object_smart_callback_add(li, "expand,request", _expand_req, obj);
648    evas_object_smart_callback_add(li, "contract,request", _contract_req, obj);
649    evas_object_smart_callback_add(li, "expanded", _expand_done, obj);
650    evas_object_smart_callback_add(li, "contracted", _contract_done, obj);
651
652    evas_object_smart_callback_add(grid, "selected", _sel, obj);
653
654    elm_widget_sub_object_add(obj, li);
655    elm_widget_sub_object_add(obj, grid);
656    wd->files_list = li;
657    wd->files_grid = grid;
658
659    // path entry
660    en = elm_entry_add(parent);
661    elm_entry_scrollable_set(en, EINA_TRUE);
662    elm_widget_mirrored_automatic_set(en, EINA_FALSE);
663    elm_entry_editable_set(en, EINA_FALSE);
664    elm_entry_single_line_set(en, EINA_TRUE);
665    elm_entry_line_wrap_set(en, ELM_WRAP_CHAR);
666    evas_object_size_hint_weight_set(en, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
667    evas_object_size_hint_align_set(en, EVAS_HINT_FILL, EVAS_HINT_FILL);
668
669    evas_object_smart_callback_add(en, "anchor,clicked", _anchor_clicked, obj);
670
671    elm_widget_sub_object_add(obj, en);
672    wd->path_entry = en;
673
674    // filename entry
675    en = elm_entry_add(parent);
676    elm_entry_scrollable_set(en, EINA_TRUE);
677    elm_widget_mirrored_automatic_set(en, EINA_FALSE);
678    elm_entry_editable_set(en, EINA_TRUE);
679    elm_entry_single_line_set(en, EINA_TRUE);
680    elm_entry_line_wrap_set(en, ELM_WRAP_CHAR);
681    evas_object_size_hint_weight_set(en, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
682    evas_object_size_hint_align_set(en, EVAS_HINT_FILL, EVAS_HINT_FILL);
683
684    elm_widget_sub_object_add(obj, en);
685    wd->filename_entry = en;
686
687    elm_fileselector_buttons_ok_cancel_set(obj, EINA_TRUE);
688    elm_fileselector_is_save_set(obj, EINA_FALSE);
689
690    _theme_hook(obj);
691
692    evas_object_smart_callbacks_descriptions_set(obj, _signals);
693    return obj;
694 }
695
696 /**
697  * This enables/disables the file name entry box where the user can
698  * type in a name for the file to be saved as.
699  *
700  * @param obj The fileselector object
701  * @param is_save If true, the fileselector is a save dialog
702  *
703  * @ingroup Fileselector
704  */
705 EAPI void
706 elm_fileselector_is_save_set(Evas_Object *obj,
707                              Eina_Bool    is_save)
708 {
709    ELM_CHECK_WIDTYPE(obj, widtype);
710    Widget_Data *wd = elm_widget_data_get(obj);
711    if (!wd) return;
712
713    elm_object_disabled_set(wd->filename_entry, !is_save);
714
715    if (is_save)
716      edje_object_signal_emit(wd->edje, "elm,state,save,on", "elm");
717    else
718      edje_object_signal_emit(wd->edje, "elm,state,save,off", "elm");
719 }
720
721 /**
722  * This returns whether the fileselector is a "save" type fileselector
723  *
724  * @param obj The fileselector object
725  * @return If true, the fileselector is a save type.
726  *
727  * @ingroup Fileselector
728  */
729 EAPI Eina_Bool
730 elm_fileselector_is_save_get(const Evas_Object *obj)
731 {
732    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
733    Widget_Data *wd = elm_widget_data_get(obj);
734    if (!wd) return EINA_FALSE;
735    return elm_object_disabled_get(wd->filename_entry);
736 }
737
738 /**
739  * This enables/disables folder-only view in the fileselector.
740  *
741  * @param obj The fileselector object
742  * @param only If true, the fileselector will only display directories.
743  * If false, files are displayed also.
744  *
745  * @ingroup Fileselector
746  */
747 EAPI void
748 elm_fileselector_folder_only_set(Evas_Object *obj,
749                                  Eina_Bool    only)
750 {
751    ELM_CHECK_WIDTYPE(obj, widtype);
752    Widget_Data *wd = elm_widget_data_get(obj);
753    if (!wd) return;
754    if (wd->only_folder == only) return;
755    wd->only_folder = !!only;
756    if (wd->path) _populate(obj, wd->path, NULL);
757 }
758
759 /**
760  * This gets the state of file display in the fileselector.
761  *
762  * @param obj The fileselector object
763  * @return If true, files are not being shown in the fileselector.
764  * If false, files are being shown.
765  *
766  * @ingroup Fileselector
767  */
768 EAPI Eina_Bool
769 elm_fileselector_folder_only_get(const Evas_Object *obj)
770 {
771    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
772    Widget_Data *wd = elm_widget_data_get(obj);
773    if (!wd) return EINA_FALSE;
774    return wd->only_folder;
775 }
776
777 /**
778  * This enables/disables the ok,cancel buttons.
779  *
780  * @param obj The fileselector object
781  * @param only If true, a box containing ok and cancel buttons is created.
782  * If false, the box and the buttons are destroyed.
783  *
784  * @ingroup Fileselector
785  */
786 EAPI void
787 elm_fileselector_buttons_ok_cancel_set(Evas_Object *obj,
788                                        Eina_Bool    visible)
789 {
790    ELM_CHECK_WIDTYPE(obj, widtype);
791    Widget_Data *wd = elm_widget_data_get(obj);
792    Evas_Object *bt;
793    if (!wd) return;
794
795    if (visible)
796      {
797         // cancel btn
798         bt = elm_button_add(obj);
799         elm_widget_mirrored_automatic_set(bt, EINA_FALSE);
800         elm_button_label_set(bt, E_("Cancel"));
801
802         evas_object_smart_callback_add(bt, "clicked", _canc, obj);
803
804         elm_widget_sub_object_add(obj, bt);
805         wd->cancel_button = bt;
806
807         // ok btn
808         bt = elm_button_add(obj);
809         elm_widget_mirrored_automatic_set(bt, EINA_FALSE);
810         elm_button_label_set(bt, E_("OK"));
811
812         evas_object_smart_callback_add(bt, "clicked", _ok, obj);
813
814         elm_widget_sub_object_add(obj, bt);
815         wd->ok_button = bt;
816
817         _theme_hook(obj);
818      }
819    else
820      {
821         evas_object_del(wd->cancel_button);
822         wd->cancel_button = NULL;
823         evas_object_del(wd->ok_button);
824         wd->ok_button = NULL;
825      }
826 }
827
828 /**
829  * This gets the state of the box containing ok and cancel buttons.
830  *
831  * @param obj The fileselector object
832  * @return If true, the box exists.
833  * If false, the box does not exist.
834  *
835  * @ingroup Fileselector
836  */
837 EAPI Eina_Bool
838 elm_fileselector_buttons_ok_cancel_get(const Evas_Object *obj)
839 {
840    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
841    Widget_Data *wd = elm_widget_data_get(obj);
842    if (!wd) return EINA_FALSE;
843    return wd->ok_button ? EINA_TRUE : EINA_FALSE;
844 }
845
846 /**
847  * This enables a tree view in the fileselector, <b>if in @c
848  * ELM_FILESELECTOR_LIST mode</b>. If it's in other mode, the changes
849  * made by this function will only be visible when one switches back
850  * to list mode.
851  *
852  * @param obj The fileselector object
853  * @param expand If true, tree view is enabled.
854  * If false, tree view is disabled.
855  *
856  * In a tree view, arrows are created on the sides of directories,
857  * allowing them to expand in place.
858  *
859  * @ingroup Fileselector
860  */
861 EAPI void
862 elm_fileselector_expandable_set(Evas_Object *obj,
863                                 Eina_Bool    expand)
864 {
865    ELM_CHECK_WIDTYPE(obj, widtype);
866    Widget_Data *wd;
867
868    wd = elm_widget_data_get(obj);
869    if (!wd) return;
870
871    wd->expand = !!expand;
872
873    if (wd->path) _populate(obj, wd->path, NULL);
874 }
875
876 /**
877  * This gets the state of tree view in the fileselector.
878  *
879  * @param obj The fileselector object
880  * @return If true, tree view is enabled and folders will be expandable.
881  * If false, tree view is disabled.
882  *
883  * @ingroup Fileselector
884  */
885 EAPI Eina_Bool
886 elm_fileselector_expandable_get(const Evas_Object *obj)
887 {
888    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
889    Widget_Data *wd = elm_widget_data_get(obj);
890    if (!wd) return EINA_FALSE;
891    return wd->expand;
892 }
893
894 /**
895  * This sets the path that the fileselector will display.
896  *
897  * @param obj The fileselector object
898  * @param path The path of the fileselector
899  *
900  * @ingroup Fileselector
901  */
902 EAPI void
903 elm_fileselector_path_set(Evas_Object *obj,
904                           const char  *path)
905 {
906    ELM_CHECK_WIDTYPE(obj, widtype);
907    _populate(obj, path, NULL);
908 }
909
910 /**
911  * This gets the path that the fileselector displays.
912  *
913  * @param obj The fileselector object
914  * @return The path that the fileselector is displaying
915  *
916  * @ingroup Fileselector
917  */
918 EAPI const char *
919 elm_fileselector_path_get(const Evas_Object *obj)
920 {
921    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
922    Widget_Data *wd = elm_widget_data_get(obj);
923    if (!wd) return NULL;
924    return wd->path;
925 }
926
927 /**
928  * This sets the mode in which the fileselector will display files.
929  *
930  * @param obj The fileselector object
931
932  * @param mode The mode of the fileselector, being it one of @c
933  * ELM_FILESELECTOR_LIST (default) or @c ELM_FILESELECTOR_GRID. The
934  * first one, naturally, will display the files in a list. By using
935  * elm_fileselector_expandable_set(), the user will trigger a tree
936  * view for that list. The latter will make the widget to display its
937  * entries in a grid form.
938  *
939  * @see elm_fileselector_expandable_set().
940  *
941  * @ingroup Fileselector
942  */
943 EAPI void
944 elm_fileselector_mode_set(Evas_Object          *obj,
945                           Elm_Fileselector_Mode mode)
946 {
947    ELM_CHECK_WIDTYPE(obj, widtype);
948
949    Widget_Data *wd = elm_widget_data_get(obj);
950    if (!wd) return;
951
952    if (mode == wd->mode) return;
953
954    if (mode == ELM_FILESELECTOR_LIST)
955      {
956         if (edje_object_part_swallow(wd->edje, "elm.swallow.files",
957                                      wd->files_list))
958           {
959              evas_object_show(wd->files_list);
960              evas_object_hide(wd->files_grid);
961           }
962         else
963           evas_object_hide(wd->files_list);
964      }
965    else
966      {
967         if (edje_object_part_swallow(wd->edje, "elm.swallow.files",
968                                      wd->files_grid))
969           {
970              evas_object_show(wd->files_grid);
971              evas_object_hide(wd->files_list);
972           }
973         else
974           evas_object_hide(wd->files_grid);
975      }
976
977    wd->mode = mode;
978
979    _populate(obj, wd->path, NULL);
980 }
981
982 /**
983  * This gets the mode in which the fileselector is displaying files.
984  *
985  * @param obj The fileselector object
986  * @return The mode in which the fileselector is at
987  *
988  * @ingroup Fileselector
989  */
990 EAPI Elm_Fileselector_Mode
991 elm_fileselector_mode_get(const Evas_Object *obj)
992 {
993    ELM_CHECK_WIDTYPE(obj, widtype) ELM_FILESELECTOR_LAST;
994
995    Widget_Data *wd = elm_widget_data_get(obj);
996    if (!wd) return ELM_FILESELECTOR_LAST;
997
998    return wd->mode;
999 }
1000
1001 /**
1002  * This gets the currently selected path in the file selector.
1003  *
1004  * @param obj The file selector object
1005  * @return The absolute path of the selected object in the fileselector
1006  *
1007  * @ingroup Fileselector
1008  */
1009 EAPI const char *
1010 elm_fileselector_selected_get(const Evas_Object *obj)
1011 {
1012    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
1013    Widget_Data *wd = elm_widget_data_get(obj);
1014    if (!wd) return NULL;
1015
1016    if (wd->filename_entry)
1017      {
1018         const char *name;
1019         char buf[PATH_MAX];
1020
1021         name = elm_entry_entry_get(wd->filename_entry);
1022         snprintf(buf, sizeof(buf), "%s/%s",
1023                  wd->only_folder ? ecore_file_dir_get(wd->path) : wd->path,
1024                  name);
1025         eina_stringshare_replace(&wd->selection, buf);
1026         return wd->selection;
1027      }
1028
1029    if (wd->mode == ELM_FILESELECTOR_LIST)
1030      {
1031         Elm_Genlist_Item *it;
1032         it = elm_genlist_selected_item_get(wd->files_list);
1033         if (it) return elm_genlist_item_data_get(it);
1034      }
1035    else
1036      {
1037         Elm_Gengrid_Item *it;
1038         it = elm_gengrid_selected_item_get(wd->files_grid);
1039         if (it) return elm_gengrid_item_data_get(it);
1040      }
1041
1042    return wd->path;
1043 }
1044
1045 /**
1046  * This sets the currently selected path in the file selector.
1047  *
1048  * @param obj The file selector object
1049  * @param path The path to a file or directory
1050  * @return @c EINA_TRUE on success, @c EINA_FALSE on failure. The
1051  * latter case occurs if the directory or file pointed to do not
1052  * exist.
1053  *
1054  * @ingroup Fileselector
1055  */
1056 EAPI Eina_Bool
1057 elm_fileselector_selected_set(Evas_Object *obj,
1058                               const char  *path)
1059 {
1060    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
1061    Widget_Data *wd = elm_widget_data_get(obj);
1062    if (!wd) return EINA_FALSE;
1063
1064    if (ecore_file_is_dir(path))
1065      _populate(obj, path, NULL);
1066    else
1067      {
1068         if (!ecore_file_exists(path))
1069           return EINA_FALSE;
1070
1071         _populate(obj, ecore_file_dir_get(path), NULL);
1072         if (wd->filename_entry)
1073           {
1074              elm_entry_entry_set(wd->filename_entry,
1075                                           ecore_file_file_get(path));
1076              eina_stringshare_replace(&wd->selection, path);
1077           }
1078      }
1079
1080    return EINA_TRUE;
1081 }
1082