fix win32 compilation
[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 #ifdef HAVE_CONFIG_H
28 # include <elementary_config.h>
29 #endif
30
31 #ifdef HAVE_EIO
32 # include <Eio.h>
33 #endif
34
35 #include <Elementary.h>
36 #include "elm_priv.h"
37
38 typedef struct _Widget_Data Widget_Data;
39
40 struct _Widget_Data
41 {
42    EINA_REFCOUNT;
43
44    Evas_Object *edje;
45    Evas_Object *filename_entry;
46    Evas_Object *path_entry;
47    Evas_Object *files_list;
48    Evas_Object *files_grid;
49    Evas_Object *up_button;
50    Evas_Object *home_button;
51
52    Evas_Object *ok_button;
53    Evas_Object *cancel_button;
54
55    const char  *path;
56    const char  *selection;
57    Ecore_Idler *sel_idler;
58
59    const char  *path_separator;
60
61 #ifdef HAVE_EIO
62    Eio_File    *current;
63 #endif
64
65    Elm_Fileselector_Mode mode;
66
67    Eina_Bool    only_folder : 1;
68    Eina_Bool    expand : 1;
69 };
70
71 struct sel_data
72 {
73    Evas_Object *fs;
74    const char  *path;
75 };
76
77 typedef struct _Widget_Request Widget_Request;
78 struct _Widget_Request
79 {
80    Widget_Data *wd;
81    Elm_Genlist_Item *parent;
82
83    Evas_Object *obj;
84    const char *path;
85    Eina_Bool first : 1;
86 };
87
88 typedef enum {
89   ELM_DIRECTORY = 0,
90   ELM_FILE_IMAGE = 1,
91   ELM_FILE_UNKNOW = 2,
92   ELM_FILE_LAST
93 } Elm_Fileselector_Type;
94
95 static Elm_Genlist_Item_Class list_itc[ELM_FILE_LAST] = {
96   { "default", { NULL, NULL, NULL, NULL, NULL }, NULL },
97   { "default", { NULL, NULL, NULL, NULL, NULL }, NULL },
98   { "default", { NULL, NULL, NULL, NULL, NULL }, NULL }
99 };
100 static Elm_Gengrid_Item_Class grid_itc[ELM_FILE_LAST] = {
101   { "default", { NULL, NULL, NULL, NULL } },
102   { "default", { NULL, NULL, NULL, NULL } },
103   { "default", { NULL, NULL, NULL, NULL } }
104 };
105
106 static const char *widtype = NULL;
107
108 static const char SIG_DIRECTORY_OPEN[] = "directory,open";
109 static const char SIG_DONE[] = "done";
110 static const char SIG_SELECTED[] = "selected";
111 static const Evas_Smart_Cb_Description _signals[] = {
112    {SIG_DIRECTORY_OPEN, "s"},
113    {SIG_DONE, "s"},
114    {SIG_SELECTED, "s"},
115    {NULL, NULL}
116 };
117
118 static void _populate(Evas_Object      *obj,
119                       const char       *path,
120                       Elm_Genlist_Item *parent);
121 static void _do_anchors(Evas_Object *obj,
122                         const char  *path);
123
124 /***  ELEMENTARY WIDGET  ***/
125 static void
126 _widget_data_free(Widget_Data *wd)
127 {
128    if (wd->path) eina_stringshare_del(wd->path);
129    if (wd->selection) eina_stringshare_del(wd->selection);
130    if (wd->sel_idler)
131      {
132         void *sd;
133
134         sd = ecore_idler_del(wd->sel_idler);
135         free(sd);
136      }
137    free(wd);
138 }
139
140 static void
141 _del_hook(Evas_Object *obj)
142 {
143    Widget_Data *wd;
144
145    wd = elm_widget_data_get(obj);
146    if (!wd) return;
147
148 #ifdef HAVE_EIO
149    if (wd->current)
150      eio_file_cancel(wd->current);
151 #endif
152
153    wd->files_list = NULL;
154    wd->files_grid = NULL;
155
156    EINA_REFCOUNT_UNREF(wd)
157      _widget_data_free(wd);
158 }
159
160 static void
161 _sizing_eval(Evas_Object *obj)
162 {
163    Widget_Data *wd = elm_widget_data_get(obj);
164    Evas_Coord minw = -1, minh = -1;
165    if (!wd) return;
166    elm_coords_finger_size_adjust(1, &minw, 1, &minh);
167    edje_object_size_min_restricted_calc(wd->edje, &minw, &minh, minw, minh);
168    evas_object_size_hint_min_set(obj, minw, minh);
169 }
170
171 static void
172 _mirrored_set(Evas_Object *obj, Eina_Bool rtl)
173 {
174    Widget_Data *wd = elm_widget_data_get(obj);
175    if (!wd) return;
176    elm_widget_mirrored_set(wd->cancel_button, rtl);
177    elm_widget_mirrored_set(wd->ok_button, rtl);
178    elm_widget_mirrored_set(wd->files_list, rtl);
179    elm_widget_mirrored_set(wd->up_button, rtl);
180    elm_widget_mirrored_set(wd->home_button, rtl);
181    edje_object_mirrored_set(wd->edje, rtl);
182 }
183
184 static void
185 _theme_hook(Evas_Object *obj)
186 {
187    Widget_Data *wd = elm_widget_data_get(obj);
188    const char *style = elm_widget_style_get(obj);
189    const char *data;
190    char buf[1024];
191
192    if (!wd) return;
193    _elm_widget_mirrored_reload(obj);
194
195    _elm_theme_object_set(obj, wd->edje, "fileselector", "base", style);
196
197    if (elm_object_disabled_get(obj))
198      edje_object_signal_emit(wd->edje, "elm,state,disabled", "elm");
199
200    data = edje_object_data_get(wd->edje, "path_separator");
201    if (data)
202      wd->path_separator = data;
203    else
204      wd->path_separator = "/";
205
206    if (!style) style = "default";
207    snprintf(buf, sizeof(buf), "fileselector/%s", style);
208
209 #define SWALLOW(part_name, object_ptn)                                \
210   if (object_ptn)                                                     \
211     {                                                                 \
212        elm_widget_style_set(object_ptn, buf);                         \
213        if (edje_object_part_swallow(wd->edje, part_name, object_ptn)) \
214          evas_object_show(object_ptn);                                \
215        else                                                           \
216          evas_object_hide(object_ptn);                                \
217     }
218    SWALLOW("elm.swallow.up", wd->up_button);
219    SWALLOW("elm.swallow.home", wd->home_button);
220
221    if (wd->mode == ELM_FILESELECTOR_LIST)
222      {
223         if (edje_object_part_swallow(wd->edje, "elm.swallow.files",
224                                      wd->files_list))
225           {
226              evas_object_show(wd->files_list);
227              evas_object_hide(wd->files_grid);
228           }
229         else
230           evas_object_hide(wd->files_list);
231      }
232    else
233      {
234         if (edje_object_part_swallow(wd->edje, "elm.swallow.files",
235                                      wd->files_grid))
236           {
237              evas_object_show(wd->files_grid);
238              evas_object_hide(wd->files_list);
239           }
240         else
241           evas_object_hide(wd->files_grid);
242      }
243
244    SWALLOW("elm.swallow.filename", wd->filename_entry);
245    SWALLOW("elm.swallow.path", wd->path_entry);
246
247    snprintf(buf, sizeof(buf), "fileselector/actions/%s", style);
248    SWALLOW("elm.swallow.cancel", wd->cancel_button);
249    SWALLOW("elm.swallow.ok", wd->ok_button);
250 #undef SWALLOW
251
252    edje_object_message_signal_process(wd->edje);
253    _mirrored_set(obj, elm_widget_mirrored_get(obj));
254    edje_object_scale_set
255      (wd->edje, elm_widget_scale_get(obj) * _elm_config->scale);
256    _sizing_eval(obj);
257 }
258
259 /***  GENLIST "MODEL"  ***/
260 static char *
261 _itc_label_get(void              *data,
262                Evas_Object *obj   __UNUSED__,
263                const char *source __UNUSED__)
264 {
265    return strdup(ecore_file_file_get(data)); /* NOTE this will be
266                                               * free() by the
267                                               * caller */
268 }
269
270 static Evas_Object *
271 _itc_icon_folder_get(void        *data __UNUSED__,
272                      Evas_Object *obj,
273                      const char  *source)
274 {
275    Evas_Object *ic;
276
277    if (strcmp(source, "elm.swallow.icon")) return NULL;
278
279    ic = elm_icon_add(obj);
280    elm_icon_standard_set(ic, "folder");
281
282    evas_object_size_hint_aspect_set(ic, EVAS_ASPECT_CONTROL_VERTICAL,
283                                     1, 1);
284    return ic;
285 }
286
287 static Evas_Object *
288 _itc_icon_image_get(void        *data,
289                     Evas_Object *obj,
290                     const char  *source)
291 {
292    const char *filename = data;
293    Evas_Object *ic;
294
295    if (strcmp(source, "elm.swallow.icon")) return NULL;
296
297    ic = elm_icon_add(obj);
298    elm_icon_standard_set(ic, "image");
299    elm_icon_thumb_set(ic, filename, NULL);
300
301    evas_object_size_hint_aspect_set(ic, EVAS_ASPECT_CONTROL_VERTICAL,
302                                     1, 1);
303    return ic;
304 }
305
306 static Evas_Object *
307 _itc_icon_file_get(void        *data __UNUSED__,
308                    Evas_Object *obj,
309                    const char  *source)
310 {
311    Evas_Object *ic;
312
313    if (strcmp(source, "elm.swallow.icon")) return NULL;
314
315    ic = elm_icon_add(obj);
316    elm_icon_standard_set(ic, "file");
317
318    evas_object_size_hint_aspect_set(ic, EVAS_ASPECT_CONTROL_VERTICAL,
319                                     1, 1);
320    return ic;
321 }
322
323 static Eina_Bool
324 _itc_state_get(void *data         __UNUSED__,
325                Evas_Object *obj   __UNUSED__,
326                const char *source __UNUSED__)
327 {
328    return EINA_FALSE;
329 }
330
331 static void
332 _itc_del(void            *data,
333          Evas_Object *obj __UNUSED__)
334 {
335    eina_stringshare_del(data);
336 }
337
338 static void
339 _expand_done(void            *data,
340              Evas_Object *obj __UNUSED__,
341              void            *event_info)
342 {
343    Elm_Genlist_Item *it = event_info;
344    const char *path = elm_genlist_item_data_get(it);
345    _populate(data, path, it);
346 }
347
348 static void
349 _contract_done(void *data       __UNUSED__,
350                Evas_Object *obj __UNUSED__,
351                void            *event_info)
352 {
353    Elm_Genlist_Item *it = event_info;
354    elm_genlist_item_subitems_clear(it);
355 }
356
357 static void
358 _expand_req(void *data       __UNUSED__,
359             Evas_Object *obj __UNUSED__,
360             void            *event_info)
361 {
362    Elm_Genlist_Item *it = event_info;
363    elm_genlist_item_expanded_set(it, 1);
364 }
365
366 static void
367 _contract_req(void *data       __UNUSED__,
368               Evas_Object *obj __UNUSED__,
369               void            *event_info)
370 {
371    Elm_Genlist_Item *it = event_info;
372    elm_genlist_item_expanded_set(it, 0);
373 }
374
375 /***  PRIVATES  ***/
376 static Eina_Bool
377 _sel_do(void *data)
378 {
379    struct sel_data *sd;
380    const char *path;
381    Widget_Data *wd;
382    const char *p;
383
384    sd = data;
385    wd = elm_widget_data_get(sd->fs);
386    path = sd->path;
387
388    if ((!wd->only_folder) && ecore_file_is_dir(path))
389      {
390         if (wd->expand && wd->mode == ELM_FILESELECTOR_LIST)
391           {
392              _do_anchors(sd->fs, path);
393              elm_entry_entry_set(wd->filename_entry, "");
394           }
395         else
396           {
397              /* keep a ref to path 'couse it will be destroyed by _populate */
398              p = eina_stringshare_add(path);
399              _populate(sd->fs, p, NULL);
400              eina_stringshare_del(p);
401           }
402         goto end;
403      }
404    else /* navigating through folders only or file is not a dir. */
405      {
406         if (wd->expand && wd->mode == ELM_FILESELECTOR_LIST)
407           _do_anchors(sd->fs, path);
408         else if (wd->only_folder)
409           {
410              /* keep a ref to path 'couse it will be destroyed by _populate */
411              p = eina_stringshare_add(path);
412              _populate(sd->fs, p, NULL);
413              eina_stringshare_del(p);
414           }
415         elm_entry_entry_set(wd->filename_entry,
416                                      ecore_file_file_get(path));
417      }
418
419    evas_object_smart_callback_call(sd->fs, SIG_SELECTED, (void *)path);
420
421 end:
422    wd->sel_idler = NULL;
423    free(sd);
424    return ECORE_CALLBACK_CANCEL;
425 }
426
427 static void
428 _sel(void            *data,
429      Evas_Object *obj __UNUSED__,
430      void            *event_info)
431 {
432    struct sel_data *sd;
433    Widget_Data *wd;
434    void *old_sd;
435    char *dir;
436
437    wd = elm_widget_data_get(data);
438    if (!wd) return;
439
440    sd = malloc(sizeof(*sd));
441    sd->fs = data;
442    sd->path = wd->mode == ELM_FILESELECTOR_LIST ?
443        elm_genlist_item_data_get(event_info) :
444        elm_gengrid_item_data_get(event_info);
445
446    if (!sd->path)
447      {
448         eina_stringshare_replace(&wd->path, "");
449         goto end;
450      }
451
452    dir = wd->only_folder ? strdup(sd->path) : ecore_file_dir_get(sd->path);
453    if (dir)
454      {
455         eina_stringshare_replace(&wd->path, dir);
456         free(dir);
457      }
458    else
459      {
460         eina_stringshare_replace(&wd->path, "");
461      }
462
463 end:
464    if (wd->sel_idler)
465      {
466         old_sd = ecore_idler_del(wd->sel_idler);
467         free(old_sd);
468      }
469    wd->sel_idler = ecore_idler_add(_sel_do, sd);
470 }
471
472 static void
473 _up(void            *data,
474     Evas_Object *obj __UNUSED__,
475     void *event_info __UNUSED__)
476 {
477    Evas_Object *fs = data;
478    char *parent;
479
480    Widget_Data *wd = elm_widget_data_get(fs);
481    if (!wd) return;
482    parent = ecore_file_dir_get(wd->path);
483    _populate(fs, parent, NULL);
484    free(parent);
485 }
486
487 static void
488 _home(void            *data,
489       Evas_Object *obj __UNUSED__,
490       void *event_info __UNUSED__)
491 {
492    Evas_Object *fs = data;
493    _populate(fs, getenv("HOME"), NULL);
494 }
495
496 static void
497 _ok(void            *data,
498     Evas_Object *obj __UNUSED__,
499     void *event_info __UNUSED__)
500 {
501    Evas_Object *fs = data;
502    evas_object_smart_callback_call(fs, SIG_DONE,
503                                    (void *)elm_fileselector_selected_get(fs));
504 }
505
506 static void
507 _canc(void            *data,
508       Evas_Object *obj __UNUSED__,
509       void *event_info __UNUSED__)
510 {
511    Evas_Object *fs = data;
512    evas_object_smart_callback_call(fs, SIG_DONE, NULL);
513 }
514
515 static void
516 _anchor_clicked(void            *data,
517                 Evas_Object *obj __UNUSED__,
518                 void            *event_info)
519 {
520    Evas_Object *fs = data;
521    Widget_Data *wd = elm_widget_data_get(fs);
522    Elm_Entry_Anchor_Info *info = event_info;
523    const char *p;
524    if (!wd) return;
525    // keep a ref to path 'couse it will be destroyed by _populate
526    p = eina_stringshare_add(info->name);
527    _populate(fs, p, NULL);
528    evas_object_smart_callback_call(data, SIG_SELECTED, (void *)p);
529    eina_stringshare_del(p);
530 }
531
532 static void
533 _do_anchors(Evas_Object *obj,
534             const char  *path)
535 {
536    Widget_Data *wd = elm_widget_data_get(obj);
537    char **tok, buf[PATH_MAX * 3];
538    int i, j;
539    if (!wd) return;
540    buf[0] = '\0';
541    tok = eina_str_split(path, "/", 0);
542    eina_strlcat(buf, "<a href=/>root</a>", sizeof(buf));
543    for (i = 0; tok[i]; i++)
544      {
545         if ((!tok[i]) || (!tok[i][0])) continue;
546         eina_strlcat(buf, wd->path_separator, sizeof(buf));
547         eina_strlcat(buf, "<a href=", sizeof(buf));
548         for (j = 0; j <= i; j++)
549           {
550              if (strlen(tok[j]) < 1) continue;
551              eina_strlcat(buf, "/", sizeof(buf));
552              eina_strlcat(buf, tok[j], sizeof(buf));
553           }
554         eina_strlcat(buf, ">", sizeof(buf));
555         eina_strlcat(buf, tok[i], sizeof(buf));
556         eina_strlcat(buf, "</a>", sizeof(buf));
557      }
558    free(tok[0]);
559    free(tok);
560
561    elm_entry_entry_set(wd->path_entry, buf);
562 }
563
564 #ifdef HAVE_EIO
565 static Eina_Bool
566 _filter_cb(void *data __UNUSED__, Eio_File *handler, const Eina_File_Direct_Info *info)
567 {
568    const char *filename;
569
570    if (info->path[info->name_start] == '.')
571      return EINA_FALSE;
572
573    filename = eina_stringshare_add(info->path);
574    eio_file_associate_direct_add(handler, "filename", filename, EINA_FREE_CB(eina_stringshare_del));
575
576    if (info->type == EINA_FILE_DIR)
577      {
578         eio_file_associate_direct_add(handler, "type/grid", &grid_itc[ELM_DIRECTORY], NULL);
579         eio_file_associate_direct_add(handler, "type/list", &list_itc[ELM_DIRECTORY], NULL);
580      }
581    else
582      {
583         if (evas_object_image_extension_can_load_get(info->path + info->name_start))
584           {
585              eio_file_associate_direct_add(handler, "type/grid", &grid_itc[ELM_FILE_IMAGE], NULL);
586              eio_file_associate_direct_add(handler, "type/list", &list_itc[ELM_FILE_IMAGE], NULL);
587           }
588         else
589           {
590              eio_file_associate_direct_add(handler, "type/grid", &grid_itc[ELM_FILE_UNKNOW], NULL);
591              eio_file_associate_direct_add(handler, "type/list", &list_itc[ELM_FILE_UNKNOW], NULL);
592           }
593      }
594
595    return EINA_TRUE;
596 }
597
598 static int
599 _file_grid_cmp(const void *a, const void *b)
600 {
601    const Elm_Gengrid_Item *ga = a;
602    const Elm_Gengrid_Item *gb = b;
603    const Elm_Gengrid_Item_Class *ca = elm_gengrid_item_item_class_get(ga);
604    const Elm_Gengrid_Item_Class *cb = elm_gengrid_item_item_class_get(gb);
605
606    if (ca == &grid_itc[ELM_DIRECTORY])
607      {
608         if (cb != &grid_itc[ELM_DIRECTORY])
609           return -1;
610      }
611
612    return strcoll(elm_gengrid_item_data_get(ga), elm_gengrid_item_data_get(gb));
613 }
614
615 static int
616 _file_list_cmp(const void *a, const void *b)
617 {
618    const Elm_Genlist_Item *la = a;
619    const Elm_Genlist_Item *lb = b;
620    const Elm_Genlist_Item_Class *ca = elm_genlist_item_item_class_get(la);
621    const Elm_Genlist_Item_Class *cb = elm_genlist_item_item_class_get(lb);
622
623    if (ca == &list_itc[ELM_DIRECTORY])
624      {
625         if (cb != &list_itc[ELM_DIRECTORY])
626           return -1;
627      }
628
629    return strcoll(elm_genlist_item_data_get(la), elm_genlist_item_data_get(lb));
630 }
631
632 static void
633 _signal_first(Widget_Request *wr)
634 {
635    if (!wr->first) return ;
636    evas_object_smart_callback_call(wr->obj, SIG_DIRECTORY_OPEN, (void *)wr->path);
637    if (!wr->parent)
638      {
639         elm_genlist_clear(wr->wd->files_list);
640         elm_gengrid_clear(wr->wd->files_grid);
641         eina_stringshare_replace(&wr->wd->path, wr->path);
642         _do_anchors(wr->obj, wr->path);
643      }
644
645    if (wr->wd->filename_entry) elm_entry_entry_set(wr->wd->filename_entry, "");
646
647    wr->first = EINA_FALSE;
648 }
649
650 static void
651 _main_cb(void *data, Eio_File *handler, const Eina_File_Direct_Info *info __UNUSED__)
652 {
653    Widget_Request *wr = data;
654
655    if (eio_file_check(handler))
656      return ;
657    if (!wr->wd->files_list || !wr->wd->files_grid || wr->wd->current != handler)
658      {
659         eio_file_cancel(handler);
660         return ;
661      }
662
663    _signal_first(wr);
664
665    if (wr->wd->mode == ELM_FILESELECTOR_LIST)
666      elm_genlist_item_direct_sorted_insert(wr->wd->files_list, eio_file_associate_find(handler, "type/list"),
667                                            eina_stringshare_ref(eio_file_associate_find(handler, "filename")),
668                                            wr->parent, ELM_GENLIST_ITEM_NONE, _file_list_cmp, NULL, NULL);
669    else if (wr->wd->mode == ELM_FILESELECTOR_GRID)
670      elm_gengrid_item_direct_sorted_insert(wr->wd->files_grid, eio_file_associate_find(handler, "type/grid"),
671                                            eina_stringshare_ref(eio_file_associate_find(handler, "filename")),
672                                            _file_grid_cmp, NULL, NULL);
673 }
674
675 static void
676 _widget_request_cleanup(Widget_Request *wr)
677 {
678    EINA_REFCOUNT_UNREF(wr->wd)
679      _widget_data_free(wr->wd);
680
681    eina_stringshare_del(wr->path);
682    free(wr);
683 }
684
685 static void
686 _done_cb(void *data, Eio_File *handler __UNUSED__)
687 {
688    Widget_Request *wr = data;
689
690    _signal_first(wr);
691
692 #ifdef HAVE_EIO
693    wr->wd->current = NULL;
694 #endif
695    _widget_request_cleanup(wr);
696 }
697
698 static void
699 _error_cb(void *data, Eio_File *handler, int error __UNUSED__)
700 {
701    Widget_Request *wr = data;
702
703 #ifdef HAVE_EIO
704    if (wr->wd->current == handler)
705      wr->wd->current = NULL;
706 #endif
707    _widget_request_cleanup(wr);
708 }
709
710 #endif
711
712 static void
713 _populate(Evas_Object      *obj,
714           const char       *path,
715           Elm_Genlist_Item *parent)
716 {
717    Widget_Data *wd = elm_widget_data_get(obj);
718 #ifdef HAVE_EIO
719    Widget_Request *wr;
720 #else
721    Eina_File_Direct_Info *file;
722    Eina_Iterator *it;
723    const char *real;
724    Eina_List *files = NULL, *dirs = NULL;
725 #endif
726
727    if (!wd) return;
728 #ifndef HAVE_EIO
729    if (!ecore_file_is_dir(path)) return ;
730    it = eina_file_stat_ls(path);
731    if (!it) return ;
732    evas_object_smart_callback_call(obj, SIG_DIRECTORY_OPEN, (void *)path);
733    if (!parent)
734      {
735         elm_genlist_clear(wd->files_list);
736         elm_gengrid_clear(wd->files_grid);
737         eina_stringshare_replace(&wd->path, path);
738         _do_anchors(obj, path);
739      }
740
741    if (wd->filename_entry) elm_entry_entry_set(wd->filename_entry, "");
742    EINA_ITERATOR_FOREACH(it, file)
743      {
744         const char *filename;
745
746         if (file->path[file->name_start] == '.')
747           continue ;
748
749         filename = eina_stringshare_add(file->path);
750         if (file->type == EINA_FILE_DIR)
751           dirs = eina_list_append(dirs, filename);
752         else if (!wd->only_folder)
753           files = eina_list_append(files, filename);
754      }
755    eina_iterator_free(it);
756
757    files = eina_list_sort(files, eina_list_count(files),
758                           EINA_COMPARE_CB(strcoll));
759    dirs = eina_list_sort(dirs, eina_list_count(dirs), EINA_COMPARE_CB(strcoll));
760    EINA_LIST_FREE(dirs, real)
761      {
762         if (wd->mode == ELM_FILESELECTOR_LIST)
763           elm_genlist_item_append(wd->files_list, &list_itc[ELM_DIRECTORY],
764                                   real, /* item data */
765                                   parent,
766                                   wd->expand ? ELM_GENLIST_ITEM_SUBITEMS :
767                                   ELM_GENLIST_ITEM_NONE,
768                                   NULL, NULL);
769         else if (wd->mode == ELM_FILESELECTOR_GRID)
770           elm_gengrid_item_append(wd->files_grid, &grid_itc[ELM_DIRECTORY],
771                                   real, /* item data */
772                                   NULL, NULL);
773      }
774
775    EINA_LIST_FREE(files, real)
776      {
777         Elm_Fileselector_Type type = evas_object_image_extension_can_load_fast_get(real) ?
778           ELM_FILE_IMAGE : ELM_FILE_UNKNOW;
779
780         if (wd->mode == ELM_FILESELECTOR_LIST)
781           elm_genlist_item_append(wd->files_list, &list_itc[type],
782                                   real, /* item data */
783                                   parent, ELM_GENLIST_ITEM_NONE,
784                                   NULL, NULL);
785         else if (wd->mode == ELM_FILESELECTOR_GRID)
786           elm_gengrid_item_append(wd->files_grid, &grid_itc[type],
787                                   real, /* item data */
788                                   NULL, NULL);
789      }
790 #else
791    if (wd->current)
792      eio_file_cancel(wd->current);
793    wr = malloc(sizeof (Widget_Request));
794    if (!wr) return ;
795    wr->wd = wd;
796    EINA_REFCOUNT_REF(wr->wd);
797    wr->parent = parent; /* FIXME: should we refcount the parent ? */
798    wr->obj = obj;
799    wr->path = eina_stringshare_add(path);
800    wr->first = EINA_TRUE;
801
802    wd->current = eio_file_stat_ls(path,
803                                   _filter_cb,
804                                   _main_cb,
805                                   _done_cb,
806                                   _error_cb,
807                                   wr);
808 #endif
809 }
810
811 /***  API  ***/
812
813 /**
814  * Add a new Fileselector object
815  *
816  * @param parent The parent object
817  * @return The new object or NULL if it cannot be created
818  *
819  * @ingroup Fileselector
820  */
821 EAPI Evas_Object *
822 elm_fileselector_add(Evas_Object *parent)
823 {
824    Evas *e;
825    Evas_Object *obj, *ic, *bt, *li, *en, *grid;
826    Widget_Data *wd;
827    unsigned int i;
828    int s;
829
830    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
831
832    EINA_REFCOUNT_INIT(wd);
833
834    ELM_SET_WIDTYPE(widtype, "fileselector");
835    elm_widget_type_set(obj, "fileselector");
836    elm_widget_sub_object_add(parent, obj);
837    elm_widget_data_set(obj, wd);
838    elm_widget_del_hook_set(obj, _del_hook);
839    elm_widget_theme_hook_set(obj, _theme_hook);
840    elm_widget_can_focus_set(obj, EINA_FALSE);
841
842    wd->expand = !!_elm_config->fileselector_expand_enable;
843
844    wd->edje = edje_object_add(e);
845    _elm_theme_object_set(obj, wd->edje, "fileselector", "base", "default");
846    elm_widget_resize_object_set(obj, wd->edje);
847
848    // up btn
849    ic = elm_icon_add(parent);
850    elm_icon_standard_set(ic, "arrow_up");
851    evas_object_size_hint_aspect_set(ic, EVAS_ASPECT_CONTROL_VERTICAL, 1, 1);
852    bt = elm_button_add(parent);
853    elm_widget_mirrored_automatic_set(bt, EINA_FALSE);
854    elm_button_icon_set(bt, ic);
855    elm_object_text_set(bt, E_("Up"));
856    evas_object_size_hint_align_set(bt, 0.0, 0.0);
857
858    evas_object_smart_callback_add(bt, "clicked", _up, obj);
859
860    elm_widget_sub_object_add(obj, bt);
861    wd->up_button = bt;
862
863    // home btn
864    ic = elm_icon_add(parent);
865    elm_icon_standard_set(ic, "home");
866    evas_object_size_hint_aspect_set(ic, EVAS_ASPECT_CONTROL_VERTICAL, 1, 1);
867    bt = elm_button_add(parent);
868    elm_widget_mirrored_automatic_set(bt, EINA_FALSE);
869    elm_button_icon_set(bt, ic);
870    elm_object_text_set(bt, E_("Home"));
871    evas_object_size_hint_align_set(bt, 0.0, 0.0);
872
873    evas_object_smart_callback_add(bt, "clicked", _home, obj);
874
875    elm_widget_sub_object_add(obj, bt);
876    wd->home_button = bt;
877
878    list_itc[ELM_DIRECTORY].func.icon_get = grid_itc[ELM_DIRECTORY].func.icon_get = _itc_icon_folder_get;
879    list_itc[ELM_FILE_IMAGE].func.icon_get = grid_itc[ELM_FILE_IMAGE].func.icon_get = _itc_icon_image_get;
880    list_itc[ELM_FILE_UNKNOW].func.icon_get = grid_itc[ELM_FILE_UNKNOW].func.icon_get = _itc_icon_file_get;
881
882    for (i = 0; i < ELM_FILE_LAST; ++i)
883      {
884         list_itc[i].func.label_get = grid_itc[i].func.label_get = _itc_label_get;
885         list_itc[i].func.state_get = grid_itc[i].func.state_get = _itc_state_get;
886         list_itc[i].func.del = grid_itc[i].func.del = _itc_del;
887      }
888
889    li = elm_genlist_add(parent);
890    elm_widget_mirrored_automatic_set(li, EINA_FALSE);
891    evas_object_size_hint_align_set(li, EVAS_HINT_FILL, EVAS_HINT_FILL);
892    evas_object_size_hint_weight_set(li, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
893    evas_object_size_hint_min_set(li, 100, 100);
894
895    grid = elm_gengrid_add(parent);
896    elm_widget_mirrored_automatic_set(grid, EINA_FALSE);
897    evas_object_size_hint_align_set(grid, EVAS_HINT_FILL, EVAS_HINT_FILL);
898    evas_object_size_hint_weight_set(grid, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
899
900    s = elm_finger_size_get() * 2;
901    elm_gengrid_item_size_set(grid, s, s);
902    elm_gengrid_align_set(grid, 0.0, 0.0);
903
904    evas_object_smart_callback_add(li, "selected", _sel, obj);
905    evas_object_smart_callback_add(li, "expand,request", _expand_req, obj);
906    evas_object_smart_callback_add(li, "contract,request", _contract_req, obj);
907    evas_object_smart_callback_add(li, "expanded", _expand_done, obj);
908    evas_object_smart_callback_add(li, "contracted", _contract_done, obj);
909
910    evas_object_smart_callback_add(grid, "selected", _sel, obj);
911
912    elm_widget_sub_object_add(obj, li);
913    elm_widget_sub_object_add(obj, grid);
914    wd->files_list = li;
915    wd->files_grid = grid;
916
917    // path entry
918    en = elm_entry_add(parent);
919    elm_entry_scrollable_set(en, EINA_TRUE);
920    elm_widget_mirrored_automatic_set(en, EINA_FALSE);
921    elm_entry_editable_set(en, EINA_FALSE);
922    elm_entry_single_line_set(en, EINA_TRUE);
923    elm_entry_line_wrap_set(en, ELM_WRAP_CHAR);
924    evas_object_size_hint_weight_set(en, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
925    evas_object_size_hint_align_set(en, EVAS_HINT_FILL, EVAS_HINT_FILL);
926
927    evas_object_smart_callback_add(en, "anchor,clicked", _anchor_clicked, obj);
928
929    elm_widget_sub_object_add(obj, en);
930    wd->path_entry = en;
931
932    // filename entry
933    en = elm_entry_add(parent);
934    elm_entry_scrollable_set(en, EINA_TRUE);
935    elm_widget_mirrored_automatic_set(en, EINA_FALSE);
936    elm_entry_editable_set(en, EINA_TRUE);
937    elm_entry_single_line_set(en, EINA_TRUE);
938    elm_entry_line_wrap_set(en, ELM_WRAP_CHAR);
939    evas_object_size_hint_weight_set(en, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
940    evas_object_size_hint_align_set(en, EVAS_HINT_FILL, EVAS_HINT_FILL);
941
942    elm_widget_sub_object_add(obj, en);
943    wd->filename_entry = en;
944
945    elm_fileselector_buttons_ok_cancel_set(obj, EINA_TRUE);
946    elm_fileselector_is_save_set(obj, EINA_FALSE);
947
948    _theme_hook(obj);
949
950    evas_object_smart_callbacks_descriptions_set(obj, _signals);
951    return obj;
952 }
953
954 /**
955  * This enables/disables the file name entry box where the user can
956  * type in a name for the file to be saved as.
957  *
958  * @param obj The fileselector object
959  * @param is_save If true, the fileselector is a save dialog
960  *
961  * @ingroup Fileselector
962  */
963 EAPI void
964 elm_fileselector_is_save_set(Evas_Object *obj,
965                              Eina_Bool    is_save)
966 {
967    ELM_CHECK_WIDTYPE(obj, widtype);
968    Widget_Data *wd = elm_widget_data_get(obj);
969    if (!wd) return;
970
971    elm_object_disabled_set(wd->filename_entry, !is_save);
972
973    if (is_save)
974      edje_object_signal_emit(wd->edje, "elm,state,save,on", "elm");
975    else
976      edje_object_signal_emit(wd->edje, "elm,state,save,off", "elm");
977 }
978
979 /**
980  * This returns whether the fileselector is a "save" type fileselector
981  *
982  * @param obj The fileselector object
983  * @return If true, the fileselector is a save type.
984  *
985  * @ingroup Fileselector
986  */
987 EAPI Eina_Bool
988 elm_fileselector_is_save_get(const Evas_Object *obj)
989 {
990    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
991    Widget_Data *wd = elm_widget_data_get(obj);
992    if (!wd) return EINA_FALSE;
993    return elm_object_disabled_get(wd->filename_entry);
994 }
995
996 /**
997  * This enables/disables folder-only view in the fileselector.
998  *
999  * @param obj The fileselector object
1000  * @param only If true, the fileselector will only display directories.
1001  * If false, files are displayed also.
1002  *
1003  * @ingroup Fileselector
1004  */
1005 EAPI void
1006 elm_fileselector_folder_only_set(Evas_Object *obj,
1007                                  Eina_Bool    only)
1008 {
1009    ELM_CHECK_WIDTYPE(obj, widtype);
1010    Widget_Data *wd = elm_widget_data_get(obj);
1011    if (!wd) return;
1012    if (wd->only_folder == only) return;
1013    wd->only_folder = !!only;
1014    if (wd->path) _populate(obj, wd->path, NULL);
1015 }
1016
1017 /**
1018  * This gets the state of file display in the fileselector.
1019  *
1020  * @param obj The fileselector object
1021  * @return If true, files are not being shown in the fileselector.
1022  * If false, files are being shown.
1023  *
1024  * @ingroup Fileselector
1025  */
1026 EAPI Eina_Bool
1027 elm_fileselector_folder_only_get(const Evas_Object *obj)
1028 {
1029    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
1030    Widget_Data *wd = elm_widget_data_get(obj);
1031    if (!wd) return EINA_FALSE;
1032    return wd->only_folder;
1033 }
1034
1035 /**
1036  * This enables/disables the ok,cancel buttons.
1037  *
1038  * @param obj The fileselector object
1039  * @param only If true, a box containing ok and cancel buttons is created.
1040  * If false, the box and the buttons are destroyed.
1041  *
1042  * @ingroup Fileselector
1043  */
1044 EAPI void
1045 elm_fileselector_buttons_ok_cancel_set(Evas_Object *obj,
1046                                        Eina_Bool    visible)
1047 {
1048    ELM_CHECK_WIDTYPE(obj, widtype);
1049    Widget_Data *wd = elm_widget_data_get(obj);
1050    Evas_Object *bt;
1051    if (!wd) return;
1052
1053    if (visible)
1054      {
1055         // cancel btn
1056         bt = elm_button_add(obj);
1057         elm_widget_mirrored_automatic_set(bt, EINA_FALSE);
1058         elm_object_text_set(bt, E_("Cancel"));
1059
1060         evas_object_smart_callback_add(bt, "clicked", _canc, obj);
1061
1062         elm_widget_sub_object_add(obj, bt);
1063         wd->cancel_button = bt;
1064
1065         // ok btn
1066         bt = elm_button_add(obj);
1067         elm_widget_mirrored_automatic_set(bt, EINA_FALSE);
1068         elm_object_text_set(bt, E_("OK"));
1069
1070         evas_object_smart_callback_add(bt, "clicked", _ok, obj);
1071
1072         elm_widget_sub_object_add(obj, bt);
1073         wd->ok_button = bt;
1074
1075         _theme_hook(obj);
1076      }
1077    else
1078      {
1079         evas_object_del(wd->cancel_button);
1080         wd->cancel_button = NULL;
1081         evas_object_del(wd->ok_button);
1082         wd->ok_button = NULL;
1083      }
1084 }
1085
1086 /**
1087  * This gets the state of the box containing ok and cancel buttons.
1088  *
1089  * @param obj The fileselector object
1090  * @return If true, the box exists.
1091  * If false, the box does not exist.
1092  *
1093  * @ingroup Fileselector
1094  */
1095 EAPI Eina_Bool
1096 elm_fileselector_buttons_ok_cancel_get(const Evas_Object *obj)
1097 {
1098    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
1099    Widget_Data *wd = elm_widget_data_get(obj);
1100    if (!wd) return EINA_FALSE;
1101    return wd->ok_button ? EINA_TRUE : EINA_FALSE;
1102 }
1103
1104 /**
1105  * This enables a tree view in the fileselector, <b>if in @c
1106  * ELM_FILESELECTOR_LIST mode</b>. If it's in other mode, the changes
1107  * made by this function will only be visible when one switches back
1108  * to list mode.
1109  *
1110  * @param obj The fileselector object
1111  * @param expand If true, tree view is enabled.
1112  * If false, tree view is disabled.
1113  *
1114  * In a tree view, arrows are created on the sides of directories,
1115  * allowing them to expand in place.
1116  *
1117  * @ingroup Fileselector
1118  */
1119 EAPI void
1120 elm_fileselector_expandable_set(Evas_Object *obj,
1121                                 Eina_Bool    expand)
1122 {
1123    ELM_CHECK_WIDTYPE(obj, widtype);
1124    Widget_Data *wd;
1125
1126    wd = elm_widget_data_get(obj);
1127    if (!wd) return;
1128
1129    wd->expand = !!expand;
1130
1131    if (wd->path) _populate(obj, wd->path, NULL);
1132 }
1133
1134 /**
1135  * This gets the state of tree view in the fileselector.
1136  *
1137  * @param obj The fileselector object
1138  * @return If true, tree view is enabled and folders will be expandable.
1139  * If false, tree view is disabled.
1140  *
1141  * @ingroup Fileselector
1142  */
1143 EAPI Eina_Bool
1144 elm_fileselector_expandable_get(const Evas_Object *obj)
1145 {
1146    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
1147    Widget_Data *wd = elm_widget_data_get(obj);
1148    if (!wd) return EINA_FALSE;
1149    return wd->expand;
1150 }
1151
1152 /**
1153  * This sets the path that the fileselector will display.
1154  *
1155  * @param obj The fileselector object
1156  * @param path The path of the fileselector
1157  *
1158  * @ingroup Fileselector
1159  */
1160 EAPI void
1161 elm_fileselector_path_set(Evas_Object *obj,
1162                           const char  *path)
1163 {
1164    ELM_CHECK_WIDTYPE(obj, widtype);
1165    _populate(obj, path, NULL);
1166 }
1167
1168 /**
1169  * This gets the path that the fileselector displays.
1170  *
1171  * @param obj The fileselector object
1172  * @return The path that the fileselector is displaying
1173  *
1174  * @ingroup Fileselector
1175  */
1176 EAPI const char *
1177 elm_fileselector_path_get(const Evas_Object *obj)
1178 {
1179    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
1180    Widget_Data *wd = elm_widget_data_get(obj);
1181    if (!wd) return NULL;
1182    return wd->path;
1183 }
1184
1185 /**
1186  * This sets the mode in which the fileselector will display files.
1187  *
1188  * @param obj The fileselector object
1189
1190  * @param mode The mode of the fileselector, being it one of @c
1191  * ELM_FILESELECTOR_LIST (default) or @c ELM_FILESELECTOR_GRID. The
1192  * first one, naturally, will display the files in a list. By using
1193  * elm_fileselector_expandable_set(), the user will trigger a tree
1194  * view for that list. The latter will make the widget to display its
1195  * entries in a grid form.
1196  *
1197  * @see elm_fileselector_expandable_set().
1198  *
1199  * @ingroup Fileselector
1200  */
1201 EAPI void
1202 elm_fileselector_mode_set(Evas_Object          *obj,
1203                           Elm_Fileselector_Mode mode)
1204 {
1205    ELM_CHECK_WIDTYPE(obj, widtype);
1206
1207    Widget_Data *wd = elm_widget_data_get(obj);
1208    if (!wd) return;
1209
1210    if (mode == wd->mode) return;
1211
1212    if (mode == ELM_FILESELECTOR_LIST)
1213      {
1214         if (edje_object_part_swallow(wd->edje, "elm.swallow.files",
1215                                      wd->files_list))
1216           {
1217              evas_object_show(wd->files_list);
1218              evas_object_hide(wd->files_grid);
1219           }
1220         else
1221           evas_object_hide(wd->files_list);
1222      }
1223    else
1224      {
1225         if (edje_object_part_swallow(wd->edje, "elm.swallow.files",
1226                                      wd->files_grid))
1227           {
1228              evas_object_show(wd->files_grid);
1229              evas_object_hide(wd->files_list);
1230           }
1231         else
1232           evas_object_hide(wd->files_grid);
1233      }
1234
1235    wd->mode = mode;
1236
1237    _populate(obj, wd->path, NULL);
1238 }
1239
1240 /**
1241  * This gets the mode in which the fileselector is displaying files.
1242  *
1243  * @param obj The fileselector object
1244  * @return The mode in which the fileselector is at
1245  *
1246  * @ingroup Fileselector
1247  */
1248 EAPI Elm_Fileselector_Mode
1249 elm_fileselector_mode_get(const Evas_Object *obj)
1250 {
1251    ELM_CHECK_WIDTYPE(obj, widtype) ELM_FILESELECTOR_LAST;
1252
1253    Widget_Data *wd = elm_widget_data_get(obj);
1254    if (!wd) return ELM_FILESELECTOR_LAST;
1255
1256    return wd->mode;
1257 }
1258
1259 /**
1260  * This gets the currently selected path in the file selector.
1261  *
1262  * @param obj The file selector object
1263  * @return The absolute path of the selected object in the fileselector
1264  *
1265  * @ingroup Fileselector
1266  */
1267 EAPI const char *
1268 elm_fileselector_selected_get(const Evas_Object *obj)
1269 {
1270    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
1271    Widget_Data *wd = elm_widget_data_get(obj);
1272    if (!wd) return NULL;
1273
1274    if (wd->filename_entry)
1275      {
1276         const char *name;
1277         char buf[PATH_MAX];
1278
1279         name = elm_entry_entry_get(wd->filename_entry);
1280         snprintf(buf, sizeof(buf), "%s/%s",
1281                  wd->only_folder ? ecore_file_dir_get(wd->path) : wd->path,
1282                  name);
1283         eina_stringshare_replace(&wd->selection, buf);
1284         return wd->selection;
1285      }
1286
1287    if (wd->mode == ELM_FILESELECTOR_LIST)
1288      {
1289         Elm_Genlist_Item *it;
1290         it = elm_genlist_selected_item_get(wd->files_list);
1291         if (it) return elm_genlist_item_data_get(it);
1292      }
1293    else
1294      {
1295         Elm_Gengrid_Item *it;
1296         it = elm_gengrid_selected_item_get(wd->files_grid);
1297         if (it) return elm_gengrid_item_data_get(it);
1298      }
1299
1300    return wd->path;
1301 }
1302
1303 /**
1304  * This sets the currently selected path in the file selector.
1305  *
1306  * @param obj The file selector object
1307  * @param path The path to a file or directory
1308  * @return @c EINA_TRUE on success, @c EINA_FALSE on failure. The
1309  * latter case occurs if the directory or file pointed to do not
1310  * exist.
1311  *
1312  * @ingroup Fileselector
1313  */
1314 EAPI Eina_Bool
1315 elm_fileselector_selected_set(Evas_Object *obj,
1316                               const char  *path)
1317 {
1318    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
1319    Widget_Data *wd = elm_widget_data_get(obj);
1320    if (!wd) return EINA_FALSE;
1321
1322    if (ecore_file_is_dir(path))
1323      _populate(obj, path, NULL);
1324    else
1325      {
1326         if (!ecore_file_exists(path))
1327           return EINA_FALSE;
1328
1329         _populate(obj, ecore_file_dir_get(path), NULL);
1330         if (wd->filename_entry)
1331           {
1332              elm_entry_entry_set(wd->filename_entry,
1333                                           ecore_file_file_get(path));
1334              eina_stringshare_replace(&wd->selection, path);
1335           }
1336      }
1337
1338    return EINA_TRUE;
1339 }
1340