elementary/map - map supports language,changed
[framework/uifw/elementary.git] / src / lib / elc_fileselector.c
1 /*
2  * TODO:
3  *  - child elements focusing support
4  *  - user defined icon/label cb
5  *  - show/hide/add buttons ???
6  *  - show/hide hidden files
7  *  - double click to choose a file
8  *  - multi-selection
9  *  - make variable/function names that are sensible
10  *  - Filter support
11  */
12
13 #include <Elementary.h>
14 #include "elm_priv.h"
15 #include "elm_widget_fileselector.h"
16
17 EAPI const char ELM_FILESELECTOR_SMART_NAME[] = "elm_fileselector";
18
19 static Elm_Genlist_Item_Class *list_itc[ELM_FILE_LAST];
20 static Elm_Gengrid_Item_Class *grid_itc[ELM_FILE_LAST];
21
22 static const char SIG_DIRECTORY_OPEN[] = "directory,open";
23 static const char SIG_DONE[] = "done";
24 static const char SIG_SELECTED[] = "selected";
25 static const Evas_Smart_Cb_Description _smart_callbacks[] = {
26    {SIG_DIRECTORY_OPEN, "s"},
27    {SIG_DONE, "s"},
28    {SIG_SELECTED, "s"},
29    {NULL, NULL}
30 };
31
32 EVAS_SMART_SUBCLASS_NEW
33   (ELM_FILESELECTOR_SMART_NAME, _elm_fileselector,
34   Elm_Fileselector_Smart_Class, Elm_Layout_Smart_Class,
35   elm_layout_smart_class_get, _smart_callbacks);
36
37 /* final routine on deletion */
38 static void
39 _elm_fileselector_smart_del_do(Elm_Fileselector_Smart_Data *sd)
40 {
41    if (sd->path) eina_stringshare_del(sd->path);
42    if (sd->selection) eina_stringshare_del(sd->selection);
43    if (sd->sel_idler) free(ecore_idler_del(sd->sel_idler));
44
45    ELM_WIDGET_CLASS(_elm_fileselector_parent_sc)->base.del
46      (ELM_WIDGET_DATA(sd)->obj);
47 }
48
49 static void
50 _elm_fileselector_smart_sizing_eval(Evas_Object *obj)
51 {
52    Evas_Coord minw = -1, minh = -1;
53
54    ELM_FILESELECTOR_DATA_GET(obj, sd);
55
56    elm_coords_finger_size_adjust(1, &minw, 1, &minh);
57    edje_object_size_min_restricted_calc
58      (ELM_WIDGET_DATA(sd)->resize_obj, &minw, &minh, minw, minh);
59    evas_object_size_hint_min_set(obj, minw, minh);
60 }
61
62 static void
63 _mirrored_set(Evas_Object *obj, Eina_Bool rtl)
64 {
65    ELM_FILESELECTOR_DATA_GET(obj, sd);
66
67    elm_widget_mirrored_set(sd->cancel_button, rtl);
68    elm_widget_mirrored_set(sd->ok_button, rtl);
69    elm_widget_mirrored_set(sd->files_list, rtl);
70    elm_widget_mirrored_set(sd->up_button, rtl);
71    elm_widget_mirrored_set(sd->home_button, rtl);
72 }
73
74 static Eina_Bool
75 _elm_fileselector_smart_theme(Evas_Object *obj)
76 {
77    const char *style;
78    const char *data;
79    char buf[1024];
80
81    ELM_FILESELECTOR_DATA_GET(obj, sd);
82
83    if (!ELM_WIDGET_CLASS(_elm_fileselector_parent_sc)->theme(obj))
84      return EINA_FALSE;
85
86    style = elm_widget_style_get(obj);
87    _mirrored_set(obj, elm_widget_mirrored_get(obj));
88
89    data = edje_object_data_get
90        (ELM_WIDGET_DATA(sd)->resize_obj, "path_separator");
91    if (data) sd->path_separator = data;
92    else sd->path_separator = "/";
93
94    snprintf(buf, sizeof(buf), "fileselector/%s", style);
95
96 #define SWALLOW(part_name, object_ptn)                          \
97   if (object_ptn)                                               \
98     {                                                           \
99        elm_widget_style_set(object_ptn, buf);                   \
100        if (!elm_layout_content_set(obj, part_name, object_ptn)) \
101          evas_object_hide(object_ptn);                          \
102     }
103
104    SWALLOW("elm.swallow.up", sd->up_button);
105    SWALLOW("elm.swallow.home", sd->home_button);
106    SWALLOW("elm.swallow.spinner", sd->spinner);
107    elm_object_style_set(sd->spinner, "wheel");
108
109    if (sd->mode == ELM_FILESELECTOR_LIST)
110      {
111         if (elm_layout_content_set(obj, "elm.swallow.files", sd->files_list))
112           evas_object_hide(sd->files_grid);
113         else evas_object_hide(sd->files_list);
114      }
115    else
116      {
117         if (elm_layout_content_set(obj, "elm.swallow.files", sd->files_grid))
118           evas_object_hide(sd->files_list);
119         else evas_object_hide(sd->files_grid);
120      }
121
122    SWALLOW("elm.swallow.filename", sd->filename_entry);
123    SWALLOW("elm.swallow.path", sd->path_entry);
124
125    snprintf(buf, sizeof(buf), "fileselector/actions/%s", style);
126    SWALLOW("elm.swallow.cancel", sd->cancel_button);
127    SWALLOW("elm.swallow.ok", sd->ok_button);
128
129 #undef SWALLOW
130
131    edje_object_message_signal_process(ELM_WIDGET_DATA(sd)->resize_obj);
132
133    elm_layout_sizing_eval(obj);
134
135    return EINA_TRUE;
136 }
137
138 /***  GENLIST/GENGRID "MODEL"  ***/
139 static char *
140 _itc_text_get(void *data,
141               Evas_Object *obj   __UNUSED__,
142               const char *source __UNUSED__)
143 {
144    return elm_entry_utf8_to_markup
145             (ecore_file_file_get(data)); /* NOTE this will be free()'d by
146                                           * the caller */
147 }
148
149 static Evas_Object *
150 _itc_icon_folder_get(void *data __UNUSED__,
151                      Evas_Object *obj,
152                      const char *source)
153 {
154    Evas_Object *ic;
155
156    if (strcmp(source, "elm.swallow.icon")) return NULL;
157
158    ic = elm_icon_add(obj);
159    elm_icon_standard_set(ic, "folder");
160
161    evas_object_size_hint_aspect_set(ic, EVAS_ASPECT_CONTROL_VERTICAL, 1, 1);
162    return ic;
163 }
164
165 static Evas_Object *
166 _itc_icon_image_get(void *data,
167                     Evas_Object *obj,
168                     const char *source)
169 {
170    const char *filename = data;
171    Evas_Object *ic;
172
173    if (strcmp(source, "elm.swallow.icon")) return NULL;
174
175    ic = elm_icon_add(obj);
176    elm_icon_standard_set(ic, "image");
177    elm_icon_thumb_set(ic, filename, NULL);
178
179    evas_object_size_hint_aspect_set(ic, EVAS_ASPECT_CONTROL_VERTICAL, 1, 1);
180    return ic;
181 }
182
183 static Evas_Object *
184 _itc_icon_file_get(void *data __UNUSED__,
185                    Evas_Object *obj,
186                    const char *source)
187 {
188    Evas_Object *ic;
189
190    if (strcmp(source, "elm.swallow.icon")) return NULL;
191
192    ic = elm_icon_add(obj);
193    elm_icon_standard_set(ic, "file");
194
195    evas_object_size_hint_aspect_set(ic, EVAS_ASPECT_CONTROL_VERTICAL, 1, 1);
196    return ic;
197 }
198
199 static Eina_Bool
200 _itc_state_get(void *data         __UNUSED__,
201                Evas_Object *obj   __UNUSED__,
202                const char *source __UNUSED__)
203 {
204    return EINA_FALSE;
205 }
206
207 static void
208 _itc_del(void *data,
209          Evas_Object *obj __UNUSED__)
210 {
211    eina_stringshare_del(data);
212 }
213
214 static void
215 _anchors_do(Evas_Object *obj,
216             const char *path)
217 {
218    char **tok, buf[PATH_MAX * 3], *s;
219    int i, j;
220
221    ELM_FILESELECTOR_DATA_GET(obj, sd);
222
223    s = elm_entry_utf8_to_markup(path);
224    if (!s) return;
225
226    buf[0] = '\0';
227    tok = eina_str_split(s, "/", 0);
228    free(s);
229
230    eina_strlcat(buf, "<a href=/>root</a>", sizeof(buf));
231    for (i = 0; tok[i]; i++)
232      {
233         if ((!tok[i]) || (!tok[i][0])) continue;
234         eina_strlcat(buf, sd->path_separator, sizeof(buf));
235         eina_strlcat(buf, "<a href=", sizeof(buf));
236         for (j = 0; j <= i; j++)
237           {
238              if (strlen(tok[j]) < 1) continue;
239              eina_strlcat(buf, "/", sizeof(buf));
240              eina_strlcat(buf, tok[j], sizeof(buf));
241           }
242         eina_strlcat(buf, ">", sizeof(buf));
243         eina_strlcat(buf, tok[i], sizeof(buf));
244         eina_strlcat(buf, "</a>", sizeof(buf));
245      }
246    free(tok[0]);
247    free(tok);
248
249    elm_object_text_set(sd->path_entry, buf);
250 }
251
252 #ifdef HAVE_EIO
253 static Eina_Bool
254 _ls_filter_cb(void *data,
255               Eio_File *handler __UNUSED__,
256               const Eina_File_Direct_Info *info)
257 {
258    Listing_Request *lreq = data;
259
260    if (info->path[info->name_start] == '.')
261      return EINA_FALSE;
262
263    if (lreq->sd->only_folder && info->type != EINA_FILE_DIR)
264      return EINA_FALSE;
265
266    return EINA_TRUE;
267 }
268
269 static int
270 _file_grid_cmp(const void *a,
271                const void *b)
272 {
273    const Elm_Object_Item *ga = a;
274    const Elm_Object_Item *gb = b;
275    const Elm_Gengrid_Item_Class *ca = elm_gengrid_item_item_class_get(ga);
276    const Elm_Gengrid_Item_Class *cb = elm_gengrid_item_item_class_get(gb);
277
278    if (ca == grid_itc[ELM_DIRECTORY])
279      {
280         if (cb != grid_itc[ELM_DIRECTORY])
281           return -1;
282      }
283    else if (cb == grid_itc[ELM_DIRECTORY])
284      {
285         return 1;
286      }
287
288    return strcoll(elm_object_item_data_get(ga), elm_object_item_data_get(gb));
289 }
290
291 static int
292 _file_list_cmp(const void *a,
293                const void *b)
294 {
295    const Elm_Object_Item *la = a;
296    const Elm_Object_Item *lb = b;
297    const Elm_Genlist_Item_Class *ca = elm_genlist_item_item_class_get(la);
298    const Elm_Genlist_Item_Class *cb = elm_genlist_item_item_class_get(lb);
299
300    if (ca == list_itc[ELM_DIRECTORY])
301      {
302         if (cb != list_itc[ELM_DIRECTORY])
303           return -1;
304      }
305    else if (cb == list_itc[ELM_DIRECTORY])
306      {
307         return 1;
308      }
309
310    return strcoll(elm_object_item_data_get(la), elm_object_item_data_get(lb));
311 }
312
313 static void
314 _signal_first(Listing_Request *lreq)
315 {
316    if (!lreq->first) return;
317
318    evas_object_smart_callback_call
319      (lreq->obj, SIG_DIRECTORY_OPEN, (void *)lreq->path);
320
321    if (!lreq->parent_it)
322      {
323         elm_genlist_clear(lreq->sd->files_list);
324         elm_gengrid_clear(lreq->sd->files_grid);
325         eina_stringshare_replace(&lreq->sd->path, lreq->path);
326         _anchors_do(lreq->obj, lreq->path);
327      }
328
329    if (lreq->sd->filename_entry)
330      elm_object_text_set(lreq->sd->filename_entry, "");
331
332    lreq->first = EINA_FALSE;
333 }
334
335 static void
336 _ls_main_cb(void *data,
337             Eio_File *handler,
338             const Eina_File_Direct_Info *info)
339 {
340    Listing_Request *lreq = data;
341    int itcn = ELM_FILE_UNKNOW;
342
343    if (eio_file_check(handler)) return;
344
345    if (!lreq->sd->files_list || !lreq->sd->files_grid
346        || lreq->sd->current != handler)
347      {
348         eio_file_cancel(handler);
349         return;
350      }
351
352    _signal_first(lreq);
353
354    if (info->type == EINA_FILE_DIR)
355      itcn = ELM_DIRECTORY;
356    else
357      {
358         if (evas_object_image_extension_can_load_get
359               (info->path + info->name_start))
360           itcn = ELM_FILE_IMAGE;
361      }
362
363    if (lreq->sd->mode == ELM_FILESELECTOR_LIST)
364      elm_genlist_item_sorted_insert(lreq->sd->files_list, list_itc[itcn],
365                                     eina_stringshare_add(info->path),
366                                     lreq->parent_it,
367                                     ((lreq->sd->expand) && (itcn == ELM_DIRECTORY))
368                                     ? ELM_GENLIST_ITEM_TREE : ELM_GENLIST_ITEM_NONE,
369                                     _file_list_cmp, NULL, NULL);
370    else if (lreq->sd->mode == ELM_FILESELECTOR_GRID)
371      elm_gengrid_item_sorted_insert(lreq->sd->files_grid, grid_itc[itcn],
372                                     eina_stringshare_add(info->path),
373                                     _file_grid_cmp, NULL, NULL);
374 }
375
376 static void
377 _listing_request_cleanup(Listing_Request *lreq)
378 {
379    EINA_REFCOUNT_UNREF(lreq->sd) _elm_fileselector_smart_del_do(lreq->sd);
380
381    eina_stringshare_del(lreq->path);
382    free(lreq);
383 }
384
385 static void
386 _ls_done_cb(void *data, Eio_File *handler __UNUSED__)
387 {
388    Listing_Request *lreq = data;
389
390    _signal_first(lreq);
391    elm_progressbar_pulse(lreq->sd->spinner, EINA_FALSE);
392    elm_layout_signal_emit(lreq->obj, "elm,action,spinner,hide", "elm");
393
394    lreq->sd->current = NULL;
395    _listing_request_cleanup(lreq);
396 }
397
398 static void
399 _ls_error_cb(void *data, Eio_File *handler, int error __UNUSED__)
400 {
401    Listing_Request *lreq = data;
402
403    elm_progressbar_pulse(lreq->sd->spinner, EINA_FALSE);
404    elm_layout_signal_emit(lreq->obj, "elm,action,spinner,hide", "elm");
405
406    if (lreq->sd->current == handler)
407      lreq->sd->current = NULL;
408    _listing_request_cleanup(lreq);
409 }
410
411 #endif
412
413 static void
414 _populate(Evas_Object *obj,
415           const char *path,
416           Elm_Object_Item *parent_it)
417 {
418    ELM_FILESELECTOR_DATA_GET(obj, sd);
419
420 #ifdef HAVE_EIO
421    Listing_Request *lreq;
422 #else
423    Eina_List *files = NULL, *dirs = NULL;
424    Eina_File_Direct_Info *file;
425    Eina_Iterator *it;
426    const char *entry;
427 #endif
428
429 #ifndef HAVE_EIO /* synchronous listing path */
430    if (!ecore_file_is_dir(path)) return;
431
432    it = eina_file_stat_ls(path);
433    if (!it) return;
434
435    evas_object_smart_callback_call(obj, SIG_DIRECTORY_OPEN, (void *)path);
436    if (!parent_it)
437      {
438         elm_genlist_clear(sd->files_list);
439         elm_gengrid_clear(sd->files_grid);
440         eina_stringshare_replace(&sd->path, path);
441         _anchors_do(obj, path);
442      }
443
444    if (sd->filename_entry) elm_object_text_set(sd->filename_entry, "");
445    EINA_ITERATOR_FOREACH(it, file)
446      {
447         const char *filename;
448
449         if (file->path[file->name_start] == '.') continue;
450
451         filename = eina_stringshare_add(file->path);
452         if (file->type == EINA_FILE_DIR)
453           dirs = eina_list_append(dirs, filename);
454         else if (!sd->only_folder)
455           files = eina_list_append(files, filename);
456      }
457    eina_iterator_free(it);
458
459    files = eina_list_sort
460        (files, eina_list_count(files), EINA_COMPARE_CB(strcoll));
461
462    dirs = eina_list_sort
463        (dirs, eina_list_count(dirs), EINA_COMPARE_CB(strcoll));
464    EINA_LIST_FREE (dirs, entry)
465      {
466         if (sd->mode == ELM_FILESELECTOR_LIST)
467           elm_genlist_item_append(sd->files_list, list_itc[ELM_DIRECTORY],
468                                   entry, /* item data */
469                                   parent_it,
470                                   (sd->expand)
471                                   ? ELM_GENLIST_ITEM_TREE : ELM_GENLIST_ITEM_NONE,
472                                   NULL, NULL);
473         else if (sd->mode == ELM_FILESELECTOR_GRID)
474           elm_gengrid_item_append(sd->files_grid, grid_itc[ELM_DIRECTORY],
475                                   entry, /* item data */
476                                   NULL, NULL);
477      }
478
479    EINA_LIST_FREE (files, entry)
480      {
481         Elm_Fileselector_Type type =
482           evas_object_image_extension_can_load_fast_get(entry) ?
483           ELM_FILE_IMAGE : ELM_FILE_UNKNOW;
484
485         if (sd->mode == ELM_FILESELECTOR_LIST)
486           elm_genlist_item_append(sd->files_list, list_itc[type],
487                                   entry, /* item data */
488                                   parent_it, ELM_GENLIST_ITEM_NONE,
489                                   NULL, NULL);
490         else if (sd->mode == ELM_FILESELECTOR_GRID)
491           elm_gengrid_item_append(sd->files_grid, grid_itc[type],
492                                   entry, /* item data */
493                                   NULL, NULL);
494      }
495
496 #else /* asynchronous listing path */
497    if (sd->expand && sd->current) return;
498
499    if (sd->current) eio_file_cancel(sd->current);
500
501    lreq = malloc(sizeof (Listing_Request));
502    if (!lreq) return;
503
504    lreq->sd = sd;
505    EINA_REFCOUNT_REF(lreq->sd);
506
507    lreq->parent_it = parent_it; /* FIXME: should we refcount the parent_it ? */
508    lreq->obj = obj;
509    lreq->path = eina_stringshare_add(path);
510    lreq->first = EINA_TRUE;
511
512    sd->current = eio_file_stat_ls(path, _ls_filter_cb, _ls_main_cb,
513                                   _ls_done_cb, _ls_error_cb, lreq);
514    elm_progressbar_pulse(sd->spinner, EINA_TRUE);
515    elm_layout_signal_emit(lreq->obj, "elm,action,spinner,show", "elm");
516 #endif
517 }
518
519 static void
520 _on_list_expanded(void *data,
521                   Evas_Object *obj __UNUSED__,
522                   void *event_info)
523 {
524    Elm_Object_Item *it = event_info;
525    const char *path = elm_object_item_data_get(it);
526
527    _populate(data, path, it);
528 }
529
530 static void
531 _on_list_contracted(void *data       __UNUSED__,
532                     Evas_Object *obj __UNUSED__,
533                     void *event_info)
534 {
535    Elm_Object_Item *it = event_info;
536
537    elm_genlist_item_subitems_clear(it);
538 }
539
540 static void
541 _on_list_expand_req(void *data       __UNUSED__,
542                     Evas_Object *obj __UNUSED__,
543                     void *event_info)
544 {
545    Elm_Object_Item *it = event_info;
546
547    elm_genlist_item_expanded_set(it, EINA_TRUE);
548 }
549
550 static void
551 _on_list_contract_req(void *data       __UNUSED__,
552                       Evas_Object *obj __UNUSED__,
553                       void *event_info)
554 {
555    Elm_Object_Item *it = event_info;
556
557    elm_genlist_item_expanded_set(it, EINA_FALSE);
558 }
559
560 static Eina_Bool
561 _sel_do(void *data)
562 {
563    struct sel_data *sdata = data;
564    const char *path;
565    const char *p;
566
567    ELM_FILESELECTOR_DATA_GET(sdata->fs, sd);
568    path = sdata->path;
569
570    if ((!sd->only_folder) && ecore_file_is_dir(path))
571      {
572         if (sd->expand && sd->mode == ELM_FILESELECTOR_LIST)
573           {
574              _anchors_do(sdata->fs, path);
575              elm_object_text_set(sd->filename_entry, "");
576           }
577         else
578           {
579              /* keep a ref to path 'couse it will be destroyed by _populate */
580              p = eina_stringshare_add(path);
581              _populate(sdata->fs, p, NULL);
582              eina_stringshare_del(p);
583           }
584         goto end;
585      }
586    else /* navigating through folders only or file is not a dir. */
587      {
588         char *s;
589
590         if (sd->expand && sd->mode == ELM_FILESELECTOR_LIST)
591           _anchors_do(sdata->fs, path);
592         else if (sd->only_folder)
593           {
594              /* keep a ref to path 'couse it will be destroyed by _populate */
595              p = eina_stringshare_add(path);
596              _populate(sdata->fs, p, NULL);
597              eina_stringshare_del(p);
598           }
599
600         s = elm_entry_utf8_to_markup(ecore_file_file_get(path));
601         if (s)
602           {
603              elm_object_text_set(sd->filename_entry, s);
604              free(s);
605           }
606         else elm_object_text_set(sd->filename_entry, "");
607      }
608
609    evas_object_smart_callback_call(sdata->fs, SIG_SELECTED, (void *)path);
610
611 end:
612    sd->sel_idler = NULL;
613    free(sdata);
614    return ECORE_CALLBACK_CANCEL;
615 }
616
617 static void
618 _on_item_selected(void *data,
619                   Evas_Object *obj __UNUSED__,
620                   void *event_info)
621 {
622    //This event_info could be a list or gengrid item
623    Elm_Object_Item *it = event_info;
624    struct sel_data *sdata;
625    void *old_sd;
626    char *dir;
627
628    ELM_FILESELECTOR_DATA_GET(data, sd);
629
630    sdata = malloc(sizeof(*sdata));
631    sdata->fs = data;
632    sdata->path = elm_object_item_data_get(it);
633
634    if (!sdata->path)
635      {
636         eina_stringshare_replace(&sd->path, "");
637         goto end;
638      }
639
640    dir = sd->only_folder ? strdup(sdata->path) :
641      ecore_file_dir_get(sdata->path);
642    if (dir)
643      {
644         eina_stringshare_replace(&sd->path, dir);
645         free(dir);
646      }
647    else
648      {
649         eina_stringshare_replace(&sd->path, "");
650      }
651
652 end:
653    if (sd->sel_idler)
654      {
655         old_sd = ecore_idler_del(sd->sel_idler);
656         free(old_sd);
657      }
658    sd->sel_idler = ecore_idler_add(_sel_do, sdata);
659 }
660
661 static void
662 _on_dir_up(void *data,
663            Evas_Object *obj __UNUSED__,
664            void *event_info __UNUSED__)
665 {
666    Evas_Object *fs = data;
667    char *parent;
668
669    ELM_FILESELECTOR_DATA_GET(fs, sd);
670
671    parent = ecore_file_dir_get(sd->path);
672    _populate(fs, parent, NULL);
673    free(parent);
674 }
675
676 static void
677 _home(void *data,
678       Evas_Object *obj __UNUSED__,
679       void *event_info __UNUSED__)
680 {
681    Evas_Object *fs = data;
682
683    _populate(fs, getenv("HOME"), NULL);
684 }
685
686 static void
687 _ok(void *data,
688     Evas_Object *obj __UNUSED__,
689     void *event_info __UNUSED__)
690 {
691    Evas_Object *fs = data;
692
693    evas_object_smart_callback_call
694      (fs, SIG_DONE, (void *)elm_fileselector_selected_get(fs));
695 }
696
697 static void
698 _canc(void *data,
699       Evas_Object *obj __UNUSED__,
700       void *event_info __UNUSED__)
701 {
702    Evas_Object *fs = data;
703
704    evas_object_smart_callback_call(fs, SIG_DONE, NULL);
705 }
706
707 static void
708 _anchor_clicked(void *data,
709                 Evas_Object *obj __UNUSED__,
710                 void *event_info)
711 {
712    Elm_Entry_Anchor_Info *info = event_info;
713    Evas_Object *fs = data;
714    const char *p;
715
716    // keep a ref to path 'couse it will be destroyed by _populate
717    p = eina_stringshare_add(info->name);
718    _populate(fs, p, NULL);
719    evas_object_smart_callback_call(data, SIG_SELECTED, (void *)p);
720    eina_stringshare_del(p);
721 }
722
723 static void
724 _elm_fileselector_smart_add(Evas_Object *obj)
725 {
726    Evas_Object *ic, *bt, *li, *en, *grid, *pb;
727    unsigned int i;
728    int s;
729
730    EVAS_SMART_DATA_ALLOC(obj, Elm_Fileselector_Smart_Data);
731    EINA_REFCOUNT_INIT(priv);
732
733    ELM_WIDGET_CLASS(_elm_fileselector_parent_sc)->base.add(obj);
734
735    elm_widget_can_focus_set(obj, EINA_FALSE);
736
737    priv->expand = !!_elm_config->fileselector_expand_enable;
738
739    elm_layout_theme_set
740      (obj, "fileselector", "base", elm_widget_style_get(obj));
741
742    // up btn
743    ic = elm_icon_add(obj);
744    elm_icon_standard_set(ic, "arrow_up");
745    evas_object_size_hint_aspect_set(ic, EVAS_ASPECT_CONTROL_VERTICAL, 1, 1);
746    bt = elm_button_add(obj);
747    elm_widget_mirrored_automatic_set(bt, EINA_FALSE);
748    elm_object_part_content_set(bt, "icon", ic);
749    elm_object_domain_translatable_text_set(bt, PACKAGE, N_("Up"));
750    evas_object_size_hint_align_set(bt, 0.0, 0.0);
751    evas_object_smart_callback_add(bt, "clicked", _on_dir_up, obj);
752
753    elm_widget_sub_object_add(obj, bt);
754
755    priv->up_button = bt;
756
757    // home btn
758    ic = elm_icon_add(obj);
759    elm_icon_standard_set(ic, "home");
760    evas_object_size_hint_aspect_set(ic, EVAS_ASPECT_CONTROL_VERTICAL, 1, 1);
761    bt = elm_button_add(obj);
762    elm_widget_mirrored_automatic_set(bt, EINA_FALSE);
763    elm_object_part_content_set(bt, "icon", ic);
764    elm_object_domain_translatable_text_set(bt, PACKAGE, N_("Home"));
765    evas_object_size_hint_align_set(bt, 0.0, 0.0);
766    evas_object_smart_callback_add(bt, "clicked", _home, obj);
767
768    elm_widget_sub_object_add(obj, bt);
769    priv->home_button = bt;
770
771    // spinner
772    pb = elm_progressbar_add(obj);
773    elm_widget_sub_object_add(obj, pb);
774    priv->spinner = pb;
775
776    for (i = 0; i < ELM_FILE_LAST; ++i)
777      {
778         list_itc[i] = elm_genlist_item_class_new();
779         grid_itc[i] = elm_gengrid_item_class_new();
780
781         list_itc[i]->item_style = "default";
782         list_itc[i]->func.text_get = grid_itc[i]->func.text_get =
783             _itc_text_get;
784         list_itc[i]->func.state_get = grid_itc[i]->func.state_get =
785             _itc_state_get;
786         list_itc[i]->func.del = grid_itc[i]->func.del = _itc_del;
787      }
788
789    list_itc[ELM_DIRECTORY]->func.content_get =
790      grid_itc[ELM_DIRECTORY]->func.content_get = _itc_icon_folder_get;
791    list_itc[ELM_FILE_IMAGE]->func.content_get =
792      grid_itc[ELM_FILE_IMAGE]->func.content_get = _itc_icon_image_get;
793    list_itc[ELM_FILE_UNKNOW]->func.content_get =
794      grid_itc[ELM_FILE_UNKNOW]->func.content_get = _itc_icon_file_get;
795
796    li = elm_genlist_add(obj);
797    elm_widget_mirrored_automatic_set(li, EINA_FALSE);
798    evas_object_size_hint_align_set(li, EVAS_HINT_FILL, EVAS_HINT_FILL);
799    evas_object_size_hint_weight_set(li, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
800    evas_object_size_hint_min_set(li, 100, 100);
801
802    grid = elm_gengrid_add(obj);
803    elm_widget_mirrored_automatic_set(grid, EINA_FALSE);
804    evas_object_size_hint_align_set(grid, EVAS_HINT_FILL, EVAS_HINT_FILL);
805    evas_object_size_hint_weight_set(grid, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
806
807    // XXX: will fail for dynamic finger size changing
808    s = elm_config_finger_size_get() * 2;
809    elm_gengrid_item_size_set(grid, s, s);
810    elm_gengrid_align_set(grid, 0.0, 0.0);
811
812    evas_object_smart_callback_add(li, "selected", _on_item_selected, obj);
813    evas_object_smart_callback_add
814      (li, "expand,request", _on_list_expand_req, obj);
815    evas_object_smart_callback_add
816      (li, "contract,request", _on_list_contract_req, obj);
817    evas_object_smart_callback_add(li, "expanded", _on_list_expanded, obj);
818    evas_object_smart_callback_add(li, "contracted", _on_list_contracted, obj);
819    evas_object_smart_callback_add(grid, "selected", _on_item_selected, obj);
820
821    elm_widget_sub_object_add(obj, li);
822    elm_widget_sub_object_add(obj, grid);
823    priv->files_list = li;
824    priv->files_grid = grid;
825
826    // path entry
827    en = elm_entry_add(obj);
828    elm_entry_scrollable_set(en, EINA_TRUE);
829    elm_widget_mirrored_automatic_set(en, EINA_FALSE);
830    elm_entry_editable_set(en, EINA_FALSE);
831    elm_entry_single_line_set(en, EINA_TRUE);
832    elm_entry_line_wrap_set(en, ELM_WRAP_CHAR);
833    evas_object_size_hint_weight_set(en, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
834    evas_object_size_hint_align_set(en, EVAS_HINT_FILL, EVAS_HINT_FILL);
835
836    evas_object_smart_callback_add(en, "anchor,clicked", _anchor_clicked, obj);
837
838    elm_widget_sub_object_add(obj, en);
839    priv->path_entry = en;
840
841    // filename entry
842    en = elm_entry_add(obj);
843    elm_entry_scrollable_set(en, EINA_TRUE);
844    elm_widget_mirrored_automatic_set(en, EINA_FALSE);
845    elm_entry_editable_set(en, EINA_TRUE);
846    elm_entry_single_line_set(en, EINA_TRUE);
847    elm_entry_line_wrap_set(en, ELM_WRAP_CHAR);
848    evas_object_size_hint_weight_set(en, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
849    evas_object_size_hint_align_set(en, EVAS_HINT_FILL, EVAS_HINT_FILL);
850
851    elm_widget_sub_object_add(obj, en);
852    priv->filename_entry = en;
853
854    elm_fileselector_buttons_ok_cancel_set(obj, EINA_TRUE);
855    elm_fileselector_is_save_set(obj, EINA_FALSE);
856
857    _elm_fileselector_smart_theme(obj);
858 }
859
860 static void
861 _elm_fileselector_smart_del(Evas_Object *obj)
862 {
863    int i;
864
865    ELM_FILESELECTOR_DATA_GET(obj, sd);
866
867    for (i = 0; i < ELM_FILE_LAST; ++i)
868      {
869         elm_genlist_item_class_free(list_itc[i]);
870         elm_gengrid_item_class_free(grid_itc[i]);
871      }
872
873 #ifdef HAVE_EIO
874    if (sd->current) eio_file_cancel(sd->current);
875 #endif
876
877    sd->files_list = NULL;
878    sd->files_grid = NULL;
879
880    /* this one matching EINA_REFCOUNT_INIT() */
881    EINA_REFCOUNT_UNREF(sd) _elm_fileselector_smart_del_do(sd);
882 }
883
884 static void
885 _elm_fileselector_smart_set_user(Elm_Fileselector_Smart_Class *sc)
886 {
887    ELM_WIDGET_CLASS(sc)->base.add = _elm_fileselector_smart_add;
888    ELM_WIDGET_CLASS(sc)->base.del = _elm_fileselector_smart_del;
889
890    ELM_WIDGET_CLASS(sc)->theme = _elm_fileselector_smart_theme;
891
892    /* not a 'focus chain manager' */
893    ELM_WIDGET_CLASS(sc)->focus_next = NULL;
894    ELM_WIDGET_CLASS(sc)->focus_direction = NULL;
895
896    ELM_LAYOUT_CLASS(sc)->sizing_eval = _elm_fileselector_smart_sizing_eval;
897 }
898
899 EAPI const Elm_Fileselector_Smart_Class *
900 elm_fileselector_smart_class_get(void)
901 {
902    static Elm_Fileselector_Smart_Class _sc =
903      ELM_FILESELECTOR_SMART_CLASS_INIT_NAME_VERSION
904        (ELM_FILESELECTOR_SMART_NAME);
905    static const Elm_Fileselector_Smart_Class *class = NULL;
906    Evas_Smart_Class *esc = (Evas_Smart_Class *)&_sc;
907
908    if (class)
909      return class;
910
911    _elm_fileselector_smart_set(&_sc);
912    esc->callbacks = _smart_callbacks;
913    class = &_sc;
914
915    return class;
916 }
917
918 EAPI Evas_Object *
919 elm_fileselector_add(Evas_Object *parent)
920 {
921    Evas_Object *obj;
922
923    EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL);
924
925    obj = elm_widget_add(_elm_fileselector_smart_class_new(), parent);
926    if (!obj) return NULL;
927
928    if (!elm_widget_sub_object_add(parent, obj))
929      ERR("could not add %p as sub object of %p", obj, parent);
930
931    return obj;
932 }
933
934 EAPI void
935 elm_fileselector_is_save_set(Evas_Object *obj,
936                              Eina_Bool is_save)
937 {
938    ELM_FILESELECTOR_CHECK(obj);
939    ELM_FILESELECTOR_DATA_GET(obj, sd);
940
941    elm_object_disabled_set(sd->filename_entry, !is_save);
942
943    if (is_save) elm_layout_signal_emit(obj, "elm,state,save,on", "elm");
944    else elm_layout_signal_emit(obj, "elm,state,save,off", "elm");
945 }
946
947 EAPI Eina_Bool
948 elm_fileselector_is_save_get(const Evas_Object *obj)
949 {
950    ELM_FILESELECTOR_CHECK(obj) EINA_FALSE;
951    ELM_FILESELECTOR_DATA_GET(obj, sd);
952
953    return !elm_object_disabled_get(sd->filename_entry);
954 }
955
956 EAPI void
957 elm_fileselector_folder_only_set(Evas_Object *obj,
958                                  Eina_Bool only)
959 {
960    ELM_FILESELECTOR_CHECK(obj);
961    ELM_FILESELECTOR_DATA_GET(obj, sd);
962
963    if (sd->only_folder == only) return;
964
965    sd->only_folder = !!only;
966    if (sd->path) _populate(obj, sd->path, NULL);
967 }
968
969 EAPI Eina_Bool
970 elm_fileselector_folder_only_get(const Evas_Object *obj)
971 {
972    ELM_FILESELECTOR_CHECK(obj) EINA_FALSE;
973    ELM_FILESELECTOR_DATA_GET(obj, sd);
974
975    return sd->only_folder;
976 }
977
978 EAPI void
979 elm_fileselector_buttons_ok_cancel_set(Evas_Object *obj,
980                                        Eina_Bool visible)
981 {
982    Evas_Object *bt;
983
984    ELM_FILESELECTOR_CHECK(obj);
985    ELM_FILESELECTOR_DATA_GET(obj, sd);
986
987    if (visible)
988      {
989         // cancel btn
990         bt = elm_button_add(obj);
991         elm_widget_mirrored_automatic_set(bt, EINA_FALSE);
992         elm_object_domain_translatable_text_set(bt, PACKAGE, N_("Cancel"));
993
994         evas_object_smart_callback_add(bt, "clicked", _canc, obj);
995
996         sd->cancel_button = bt;
997
998         // ok btn
999         bt = elm_button_add(obj);
1000         elm_widget_mirrored_automatic_set(bt, EINA_FALSE);
1001         elm_object_domain_translatable_text_set(bt, PACKAGE, N_("OK"));
1002
1003         evas_object_smart_callback_add(bt, "clicked", _ok, obj);
1004
1005         sd->ok_button = bt;
1006
1007         _elm_fileselector_smart_theme(obj);
1008      }
1009    else
1010      {
1011         evas_object_del(sd->cancel_button);
1012         sd->cancel_button = NULL;
1013         evas_object_del(sd->ok_button);
1014         sd->ok_button = NULL;
1015      }
1016 }
1017
1018 EAPI Eina_Bool
1019 elm_fileselector_buttons_ok_cancel_get(const Evas_Object *obj)
1020 {
1021    ELM_FILESELECTOR_CHECK(obj) EINA_FALSE;
1022    ELM_FILESELECTOR_DATA_GET(obj, sd);
1023
1024    return sd->ok_button ? EINA_TRUE : EINA_FALSE;
1025 }
1026
1027 EAPI void
1028 elm_fileselector_expandable_set(Evas_Object *obj,
1029                                 Eina_Bool expand)
1030 {
1031    ELM_FILESELECTOR_CHECK(obj);
1032    ELM_FILESELECTOR_DATA_GET(obj, sd);
1033
1034    sd->expand = !!expand;
1035
1036    if (sd->path) _populate(obj, sd->path, NULL);
1037 }
1038
1039 EAPI Eina_Bool
1040 elm_fileselector_expandable_get(const Evas_Object *obj)
1041 {
1042    ELM_FILESELECTOR_CHECK(obj) EINA_FALSE;
1043    ELM_FILESELECTOR_DATA_GET(obj, sd);
1044
1045    return sd->expand;
1046 }
1047
1048 EAPI void
1049 elm_fileselector_path_set(Evas_Object *obj,
1050                           const char *_path)
1051 {
1052    char *path;
1053
1054    ELM_FILESELECTOR_CHECK(obj);
1055
1056    path = ecore_file_realpath(_path);
1057    _populate(obj, path, NULL);
1058    free(path);
1059 }
1060
1061 EAPI const char *
1062 elm_fileselector_path_get(const Evas_Object *obj)
1063 {
1064    ELM_FILESELECTOR_CHECK(obj) NULL;
1065    ELM_FILESELECTOR_DATA_GET(obj, sd);
1066
1067    return sd->path;
1068 }
1069
1070 EAPI void
1071 elm_fileselector_mode_set(Evas_Object *obj,
1072                           Elm_Fileselector_Mode mode)
1073 {
1074    ELM_FILESELECTOR_CHECK(obj);
1075
1076    ELM_FILESELECTOR_DATA_GET(obj, sd);
1077
1078    if (mode == sd->mode) return;
1079
1080    evas_object_hide(elm_layout_content_unset(obj, "elm.swallow.files"));
1081
1082    if (mode == ELM_FILESELECTOR_LIST)
1083      elm_layout_content_set(obj, "elm.swallow.files", sd->files_list);
1084    else elm_layout_content_set(obj, "elm.swallow.files", sd->files_grid);
1085
1086    sd->mode = mode;
1087
1088    _populate(obj, sd->path, NULL);
1089 }
1090
1091 EAPI Elm_Fileselector_Mode
1092 elm_fileselector_mode_get(const Evas_Object *obj)
1093 {
1094    ELM_FILESELECTOR_CHECK(obj) ELM_FILESELECTOR_LAST;
1095    ELM_FILESELECTOR_DATA_GET(obj, sd);
1096
1097    return sd->mode;
1098 }
1099
1100 EAPI const char *
1101 elm_fileselector_selected_get(const Evas_Object *obj)
1102 {
1103    ELM_FILESELECTOR_CHECK(obj) NULL;
1104    ELM_FILESELECTOR_DATA_GET(obj, sd);
1105
1106    if (!sd->path) return NULL;
1107
1108    if (sd->filename_entry)
1109      {
1110         char buf[PATH_MAX];
1111         const char *name;
1112         char *dir, *s;
1113
1114         if (ecore_file_is_dir(sd->path))
1115           dir = strdup(sd->path);
1116         else
1117           dir = ecore_file_dir_get(sd->path);
1118         name = elm_object_text_get(sd->filename_entry);
1119         if (name)
1120           {
1121              s = elm_entry_markup_to_utf8(name);
1122              if (s)
1123                {
1124                   snprintf(buf, sizeof(buf), "%s/%s", dir, s);
1125                   free(s);
1126                }
1127              else snprintf(buf, sizeof(buf), "%s", dir);
1128           }
1129         else snprintf(buf, sizeof(buf), "%s", dir);
1130
1131         eina_stringshare_replace(&sd->selection, buf);
1132
1133         if (dir) free(dir);
1134         return sd->selection;
1135      }
1136
1137    if (sd->mode == ELM_FILESELECTOR_LIST)
1138      {
1139         Elm_Object_Item *gl_it = elm_genlist_selected_item_get(sd->files_list);
1140
1141         if (gl_it) return elm_object_item_data_get(gl_it);
1142      }
1143    else
1144      {
1145         Elm_Object_Item *gg_it = elm_gengrid_selected_item_get(sd->files_grid);
1146
1147         if (gg_it) return elm_object_item_data_get(gg_it);
1148      }
1149
1150    return sd->path;
1151 }
1152
1153 EAPI Eina_Bool
1154 elm_fileselector_selected_set(Evas_Object *obj,
1155                               const char *_path)
1156 {
1157    Eina_Bool ret = EINA_TRUE;
1158    char *path;
1159
1160    ELM_FILESELECTOR_CHECK(obj) EINA_FALSE;
1161    ELM_FILESELECTOR_DATA_GET(obj, sd);
1162
1163    path = ecore_file_realpath(_path);
1164
1165    if (ecore_file_is_dir(path)) _populate(obj, path, NULL);
1166    else
1167      {
1168         if (!ecore_file_exists(path))
1169           {
1170              ret = EINA_FALSE;
1171              goto clean_up;
1172           }
1173
1174         _populate(obj, ecore_file_dir_get(path), NULL);
1175         if (sd->filename_entry)
1176           {
1177              char *s;
1178
1179              s = elm_entry_utf8_to_markup(ecore_file_file_get(path));
1180              if (s)
1181                {
1182                   elm_object_text_set(sd->filename_entry, s);
1183                   free(s);
1184                }
1185              else elm_object_text_set(sd->filename_entry, "");
1186
1187              eina_stringshare_replace(&sd->selection, path);
1188           }
1189      }
1190
1191 clean_up:
1192    free(path);
1193    return ret;
1194 }