2 * @defgroup Fileselector Fileselector
5 * A fileselector is a widget that allows a user to navigate through a tree
6 * of files. It contains buttons for Home(~) and Up(..) as well as cancel/ok
7 * buttons to confirm/cancel a selection. This widget is currently very much
10 * Signals that you can add callbacks for are:
12 * "selected" - the user clicks on a file
13 * "directory,open" - the list is populated with a new content. event_info is a directory.
14 * "done" - the user clicks on the ok or cancel button
17 #include <Elementary.h>
20 typedef struct _Widget_Data Widget_Data;
24 Evas_Object *vbox, *entry, *entry2, *list, *scr2;
26 const char *selection;
27 Eina_Bool only_folder;
29 Ecore_Idler *sel_idler;
45 Elm_Genlist_Item_Class itc;
47 static const char *widtype = NULL;
49 static const char SIG_DIRECTORY_OPEN[]= "directory,open";
50 static const char SIG_DONE[] = "done";
51 static const char SIG_SELECTED[] = "selected";
52 static const Evas_Smart_Cb_Description _signals[] = {
53 {SIG_DIRECTORY_OPEN, "s"},
59 static void _populate(Evas_Object *obj, const char *path, Elm_Genlist_Item *parent);
60 static void _do_anchors(Evas_Object *obj, const char *path);
62 /*** ELEMENTARY WIDGET ***/
64 _del_hook(Evas_Object *obj)
69 wd = elm_widget_data_get(obj);
72 if (wd->path) eina_stringshare_del(wd->path);
73 if (wd->selection) eina_stringshare_del(wd->selection);
76 sd = ecore_idler_del(wd->sel_idler);
83 _sizing_eval(Evas_Object *obj)
85 Widget_Data *wd = elm_widget_data_get(obj);
86 Evas_Coord minw = -1, minh = -1;
88 evas_object_size_hint_min_get(wd->vbox, &minw, &minh);
89 evas_object_size_hint_min_set(obj, minw, minh);
90 // printf("***** SIZING EVAL [min %d %d] *************\n", minw, minh);
93 /*** GENLIST "MODEL" ***/
95 _itc_label_get(const void *data, Evas_Object *obj __UNUSED__, const char *source __UNUSED__)
97 //~ printf("LABEL_GET: %s\n", (char*) data);
98 return strdup(ecore_file_file_get(data)); // NOTE this will be free() by the caller
102 _itc_icon_get(const void *data, Evas_Object *obj, const char *source)
106 //~ printf("ICON GET for %s (source: %s)\n", (char*)data, source);
107 if (!strcmp(source, "elm.swallow.icon"))
109 ic = elm_icon_add(obj);
110 if (ecore_file_is_dir((char*)data))
111 elm_icon_standard_set(ic, "folder");
113 elm_icon_standard_set(ic, "file");
114 evas_object_size_hint_aspect_set(ic, EVAS_ASPECT_CONTROL_VERTICAL, 1, 1);
115 evas_object_show(ic);
122 _itc_state_get(const void *data __UNUSED__, Evas_Object *obj __UNUSED__, const char *source __UNUSED__)
128 _itc_del(const void *data, Evas_Object *obj __UNUSED__)
130 //~ printf("DEL DATA [%s]\n", (char*)data);
131 eina_stringshare_del(data);
135 _expand_done(void *data, Evas_Object *obj __UNUSED__, void *event_info)
137 Elm_Genlist_Item *it = event_info;
138 const char *path = elm_genlist_item_data_get(it);
139 // printf("EXPAND %s\n", path);
140 _populate(data, path, it);
144 _contract_done(void *data __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info)
146 Elm_Genlist_Item *it = event_info;
147 // const char *path = elm_genlist_item_data_get(it);
148 // printf("CONTRACT %s\n", path);
149 elm_genlist_item_subitems_clear(it);
153 _expand_req(void *data __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info)
155 Elm_Genlist_Item *it = event_info;
156 elm_genlist_item_expanded_set(it, 1);
160 _contract_req(void *data __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info)
162 Elm_Genlist_Item *it = event_info;
163 elm_genlist_item_expanded_set(it, 0);
176 wd = elm_widget_data_get(sd->fs);
179 if (ecore_file_is_dir(path))
181 // printf("SELECTED DIR: %s\n", path);
184 _do_anchors(sd->fs, path);
185 if (wd->entry2) elm_entry_entry_set(wd->entry2, "");
189 // keep a ref to path 'couse it will be destroyed by _populate
190 p = eina_stringshare_add(path);
191 _populate(sd->fs, p, NULL);
192 eina_stringshare_del(p);
198 // printf("SELECTED FILE: %s\n", path);
200 elm_entry_entry_set(wd->entry2, ecore_file_file_get(path));
203 evas_object_smart_callback_call(sd->fs, SIG_SELECTED, (void*)path);
206 wd->sel_idler = NULL;
208 return ECORE_CALLBACK_CANCEL;
212 _sel(void *data, Evas_Object *obj __UNUSED__, void *event_info)
218 wd = elm_widget_data_get(data);
221 sd = malloc(sizeof(*sd));
223 sd->path = elm_genlist_item_data_get(event_info);
227 old_sd = ecore_idler_del(wd->sel_idler);
230 wd->sel_idler = ecore_idler_add(_sel_do, sd);
234 _up(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
236 Evas_Object *fs = data;
237 Widget_Data *wd = elm_widget_data_get(fs);
239 char *parent = ecore_file_dir_get(wd->path);
240 _populate(fs, parent, NULL);
245 _home(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
247 Evas_Object *fs = data;
248 _populate(fs, getenv("HOME"), NULL);
252 _ok(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
254 Evas_Object *fs = data;
255 evas_object_smart_callback_call(fs, SIG_DONE,
256 (void*)elm_fileselector_selected_get(fs));
260 _canc(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
262 Evas_Object *fs = data;
263 evas_object_smart_callback_call(fs, SIG_DONE, NULL);
267 _anchor_clicked(void *data, Evas_Object *obj __UNUSED__, void *event_info)
269 Evas_Object *fs = data;
270 Widget_Data *wd = elm_widget_data_get(fs);
271 Elm_Entry_Anchor_Info *info = event_info;
274 //~ printf("ANCHOR CLICKED %s\n", info->name);
275 // keep a ref to path 'couse it will be destroyed by _populate
276 p = eina_stringshare_add(info->name);
277 _populate(fs, p, NULL);
278 evas_object_smart_callback_call(data, SIG_SELECTED, (void*)p);
279 eina_stringshare_del(p);
283 _do_anchors(Evas_Object *obj, const char *path)
285 Widget_Data *wd = elm_widget_data_get(obj);
286 char **tok, buf[PATH_MAX*3];
289 //~ printf("ANCHORIZE...\n");
291 tok = eina_str_split(path, "/", 0);
292 for (i = 0; tok[i]; i++)
294 if (strlen(tok[i]) < 1) continue;
295 //~ printf("TOK: %s\n", tok[i]);
296 eina_strlcat(buf, "/<a href=", sizeof(buf));
297 for (j = 0; j <= i; j++)
299 if (strlen(tok[j]) < 1) continue;
300 //~ printf("REV: %s\n",tok[j]);
301 eina_strlcat(buf, "/", sizeof(buf));
302 eina_strlcat(buf, tok[j], sizeof(buf));
304 eina_strlcat(buf, ">", sizeof(buf));
305 eina_strlcat(buf, tok[i], sizeof(buf));
306 eina_strlcat(buf, "</a>", sizeof(buf));
311 //~ printf("ANCHOR: %s\n", buf);
312 elm_entry_entry_set(wd->entry, buf);
316 _populate(Evas_Object *obj, const char *path, Elm_Genlist_Item *parent)
318 Widget_Data *wd = elm_widget_data_get(obj);
323 Eina_List *files = NULL, *dirs = NULL, *l;
325 if ((!wd) || (!ecore_file_is_dir(path))) return;
328 evas_object_smart_callback_call(obj, SIG_DIRECTORY_OPEN, (void*)path);
331 elm_genlist_clear(wd->list);
332 eina_stringshare_replace(&wd->path, path);
333 _do_anchors(obj, path);
336 if (wd->entry2) elm_entry_entry_set(wd->entry2, "");
337 while ((dp = readdir(dir)))
339 if (dp->d_name[0] == '.') continue; // TODO make this configurable
341 snprintf(buf, sizeof(buf), "%s/%s", path, dp->d_name);
342 real = ecore_file_realpath(buf); //TODO this will resolv symlinks...I dont like it
343 if (ecore_file_is_dir(real))
344 dirs = eina_list_append(dirs, real);
345 else if(!wd->only_folder)
346 files = eina_list_append(files, real);
350 files = eina_list_sort(files, eina_list_count(files), EINA_COMPARE_CB(strcoll));
351 dirs = eina_list_sort(dirs, eina_list_count(dirs), EINA_COMPARE_CB(strcoll));
352 EINA_LIST_FOREACH(dirs, l, real)
354 //~ printf("DIR: %s\n", real);
355 elm_genlist_item_append(wd->list, &itc,
356 eina_stringshare_add(real), /* item data */
358 wd->expand ? ELM_GENLIST_ITEM_SUBITEMS :
359 ELM_GENLIST_ITEM_NONE,
363 eina_list_free(dirs);
365 EINA_LIST_FOREACH(files, l, real)
367 //~ printf("FILE: %s [%p]\n", real, wd->list);
368 elm_genlist_item_append(wd->list, &itc,
369 eina_stringshare_add(real), /* item data */
370 parent, ELM_GENLIST_ITEM_NONE,
374 eina_list_free(files);
379 * Add a new Fileselector object
381 * @param parent The parent object
382 * @return The new object or NULL if it cannot be created
384 * @ingroup Fileselector
387 elm_fileselector_add(Evas_Object *parent)
389 Evas_Object *obj, *ic, *bt, *box;
393 wd = ELM_NEW(Widget_Data);
394 wd->expand = EINA_FALSE;
395 obj = elm_widget_add(evas_object_evas_get(parent));
396 ELM_SET_WIDTYPE(widtype, "fileselector");
397 elm_widget_type_set(obj, "fileselector");
398 elm_widget_sub_object_add(parent, obj);
399 elm_widget_data_set(obj, wd);
400 elm_widget_del_hook_set(obj, _del_hook);
402 // TODO Do we need a bg object? a frame?
404 wd->vbox = elm_box_add(parent);
405 evas_object_size_hint_weight_set(wd->vbox, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
406 elm_widget_resize_object_set(obj, wd->vbox);
407 evas_object_show(wd->vbox);
410 box = elm_box_add(parent);
411 elm_box_horizontal_set(box, 1);
412 elm_widget_sub_object_add(obj, box);
413 elm_box_pack_end(wd->vbox, box);
414 evas_object_size_hint_align_set(box, 0.0, 0.0);
415 evas_object_show(box);
418 ic = elm_icon_add(parent);
419 elm_icon_standard_set(ic, "arrow_up");
420 evas_object_size_hint_aspect_set(ic, EVAS_ASPECT_CONTROL_VERTICAL, 1, 1);
421 bt = elm_button_add(parent);
422 elm_button_icon_set(bt, ic);
423 elm_button_label_set(bt, "Up");
424 evas_object_size_hint_align_set(bt, 0.0, 0.0);
425 elm_widget_sub_object_add(obj, bt);
426 elm_box_pack_end(box, bt);
427 evas_object_smart_callback_add(bt, "clicked", _up, obj);
428 evas_object_show(bt);
431 ic = elm_icon_add(parent);
432 elm_icon_standard_set(ic, "home");
433 evas_object_size_hint_aspect_set(ic, EVAS_ASPECT_CONTROL_VERTICAL, 1, 1);
434 bt = elm_button_add(parent);
435 elm_button_icon_set(bt, ic);
436 elm_button_label_set(bt, "Home");
437 evas_object_size_hint_align_set(bt, 0.0, 0.0);
438 elm_widget_sub_object_add(obj, bt);
439 elm_box_pack_end(box, bt);
440 evas_object_smart_callback_add(bt, "clicked", _home, obj);
441 evas_object_show(bt);
444 itc.item_style = "default";
445 itc.func.label_get = _itc_label_get;
446 itc.func.icon_get = _itc_icon_get;
447 itc.func.state_get = _itc_state_get;
448 itc.func.del = _itc_del;
450 wd->list = elm_genlist_add(parent);
451 evas_object_size_hint_align_set(wd->list, EVAS_HINT_FILL, EVAS_HINT_FILL);
452 evas_object_size_hint_weight_set(wd->list, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
453 evas_object_size_hint_min_set(wd->list, 100, 100);
454 elm_widget_sub_object_add(obj, wd->list);
455 elm_box_pack_end(wd->vbox, wd->list);
456 evas_object_show(wd->list);
458 evas_object_smart_callback_add(wd->list, "selected", _sel, obj);
459 evas_object_smart_callback_add(wd->list, "expand,request", _expand_req, obj);
460 evas_object_smart_callback_add(wd->list, "contract,request", _contract_req, obj);
461 evas_object_smart_callback_add(wd->list, "expanded", _expand_done, obj);
462 evas_object_smart_callback_add(wd->list, "contracted", _contract_done, obj);
465 wd->entry = elm_entry_add(parent);
466 elm_widget_sub_object_add(obj, wd->entry);
467 elm_entry_editable_set(wd->entry, 0);
468 elm_entry_single_line_set(wd->entry, EINA_FALSE);
469 elm_entry_line_char_wrap_set(wd->entry, EINA_TRUE);
470 evas_object_size_hint_weight_set(wd->entry, EVAS_HINT_EXPAND, 0.0);
471 evas_object_size_hint_align_set(wd->entry, EVAS_HINT_FILL, 0.0);
472 elm_box_pack_end(wd->vbox, wd->entry);
473 evas_object_show(wd->entry);
474 evas_object_smart_callback_add(wd->entry, "anchor,clicked", _anchor_clicked, obj);
476 // name entry scroller
477 wd->scr2 = elm_scroller_add(parent);
478 elm_scroller_content_min_limit(wd->scr2, 0, 1);
479 elm_scroller_policy_set(wd->scr2, ELM_SCROLLER_POLICY_OFF, ELM_SCROLLER_POLICY_OFF);
480 evas_object_size_hint_weight_set(wd->scr2, EVAS_HINT_EXPAND, 0.0);
481 evas_object_size_hint_align_set(wd->scr2, EVAS_HINT_FILL, EVAS_HINT_FILL);
482 elm_box_pack_end(wd->vbox, wd->scr2);
483 evas_object_show(wd->scr2);
485 elm_fileselector_buttons_ok_cancel_set(obj, 1);
487 // Is this the right way to show sub-objs ?? or use the show/hide cbs ??
488 //~ evas_object_event_callback_add(obj, EVAS_CALLBACK_SHOW, _show, obj);
489 //~ evas_object_event_callback_add(obj, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
490 //~ _changed_size_hints, obj);
493 // TODO: convert Elementary to subclassing of Evas_Smart_Class
494 // TODO: and save some bytes, making descriptions per-class and not instance!
495 evas_object_smart_callbacks_descriptions_set(obj, _signals);
500 * This enables/disables the file name entry box where the user can
501 * type in a name for the file to be saved as.
503 * @param obj The fileselector object
504 * @param is_save If true, the fileselector is a save dialog
506 * @ingroup Fileselector
509 elm_fileselector_is_save_set(Evas_Object *obj, Eina_Bool is_save)
511 ELM_CHECK_WIDTYPE(obj, widtype);
512 Widget_Data *wd = elm_widget_data_get(obj);
516 if (wd->entry2) return;
517 wd->entry2 = elm_entry_add(elm_widget_parent_get(obj));
518 elm_widget_sub_object_add(obj, wd->entry2);
519 elm_entry_editable_set(wd->entry2, 1);
520 elm_entry_single_line_set(wd->entry2, EINA_TRUE);
521 evas_object_size_hint_weight_set(wd->entry2, EVAS_HINT_EXPAND, 0.0);
522 evas_object_size_hint_align_set(wd->entry2, EVAS_HINT_FILL, 0.0);
523 elm_scroller_content_set(wd->scr2, wd->entry2);
524 evas_object_show(wd->entry2);
528 evas_object_del(wd->entry2);
534 * This returns whether the fileselector is a "save" type fileselector
536 * @param obj The fileselector object
537 * @return If true, the fileselector is a save type.
539 * @ingroup Fileselector
542 elm_fileselector_is_save_get(const Evas_Object *obj)
544 ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
545 Widget_Data *wd = elm_widget_data_get(obj);
546 if (!wd) return EINA_FALSE;
547 return wd->entry2 ? EINA_TRUE : EINA_FALSE;
552 * This enables/disables folder-only view in the fileselector.
554 * @param obj The fileselector object
555 * @param only If true, the fileselector will only display directories.
556 * If false, files are displayed also.
558 * @ingroup Fileselector
561 elm_fileselector_folder_only_set(Evas_Object *obj, Eina_Bool only)
563 ELM_CHECK_WIDTYPE(obj, widtype);
564 Widget_Data *wd = elm_widget_data_get(obj);
566 if (wd->only_folder == only) return;
567 wd->only_folder = only;
572 * This gets the state of file display in the fileselector.
574 * @param obj The fileselector object
575 * @return If true, files are not being shown in the fileselector.
576 * If false, files are being shown.
578 * @ingroup Fileselector
581 elm_fileselector_folder_only_get(const Evas_Object *obj)
583 ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
584 Widget_Data *wd = elm_widget_data_get(obj);
585 if (!wd) return EINA_FALSE;
586 return wd->only_folder;
591 * This enables/disables the file name entry box where the user can
592 * type in the name of a file to be selected.
594 * @param obj The fileselector object
595 * @param only If true, a box containing ok and cancel buttons is created.
596 * If false, the box and the buttons are destroyed.
598 * @ingroup Fileselector
601 elm_fileselector_buttons_ok_cancel_set(Evas_Object *obj, Eina_Bool only)
603 ELM_CHECK_WIDTYPE(obj, widtype);
604 Widget_Data *wd = elm_widget_data_get(obj);
605 Evas_Object *box, *bt;
609 if (wd->buttons.bx) return;
611 box = elm_box_add(obj);
612 wd->buttons.bx = box;
613 elm_box_horizontal_set(box, 1);
614 elm_widget_sub_object_add(obj, box);
615 elm_box_pack_end(wd->vbox, box);
616 evas_object_show(box);
619 bt = elm_button_add(obj);
620 wd->buttons.cancel = bt;
621 elm_button_label_set(bt, "Cancel");
622 elm_widget_sub_object_add(obj, bt);
623 elm_box_pack_end(box, bt);
624 evas_object_smart_callback_add(bt, "clicked", _canc, obj);
625 evas_object_show(bt);
628 bt = elm_button_add(obj);
630 elm_button_label_set(bt, "OK");
631 elm_widget_sub_object_add(obj, bt);
632 elm_box_pack_end(box, bt);
633 evas_object_smart_callback_add(bt, "clicked", _ok, obj);
634 evas_object_show(bt);
638 evas_object_del(wd->buttons.bx);
639 evas_object_del(wd->buttons.ok);
640 evas_object_del(wd->buttons.cancel);
641 wd->buttons.bx = NULL;
647 * This gets the state of the box containing ok and cancel buttons.
649 * @param obj The fileselector object
650 * @return If true, the box exists.
651 * If false, the box does not exist.
653 * @ingroup Fileselector
656 elm_fileselector_buttons_ok_cancel_get(const Evas_Object *obj)
658 ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
659 Widget_Data *wd = elm_widget_data_get(obj);
660 if (!wd) return EINA_FALSE;
661 return wd->buttons.bx ? EINA_TRUE : EINA_FALSE;
666 * This enables tree view in the fileselector. Arrows are created on the
667 * sides of directories, allowing them to expand in place.
669 * @param obj The fileselector object
670 * @param expand If true, tree view is enabled.
671 * If false, tree view is disabled.
673 * @ingroup Fileselector
676 elm_fileselector_expandable_set(Evas_Object *obj, Eina_Bool expand)
678 ELM_CHECK_WIDTYPE(obj, widtype);
681 wd = elm_widget_data_get(obj);
688 * This gets the state of tree view in the fileselector.
690 * @param obj The fileselector object
691 * @return If true, tree view is enabled and folders will be expandable.
692 * If false, tree view is disabled.
694 * @ingroup Fileselector
697 elm_fileselector_expandable_get(const Evas_Object *obj)
699 ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
700 Widget_Data *wd = elm_widget_data_get(obj);
701 if (!wd) return EINA_FALSE;
706 * This sets the path that the fileselector will display.
708 * @param obj The fileselector object
709 * @param path The path of the fileselector
711 * @ingroup Fileselector
714 elm_fileselector_path_set(Evas_Object *obj, const char *path)
716 _populate(obj, path, NULL);
720 * This gets the path that the fileselector displays.
722 * @param obj The fileselector object
723 * @return The path that the fileselector is displaying
725 * @ingroup Fileselector
728 elm_fileselector_path_get(const Evas_Object *obj)
730 ELM_CHECK_WIDTYPE(obj, widtype) NULL;
731 Widget_Data *wd = elm_widget_data_get(obj);
732 if (!wd) return NULL;
737 * This gets the currently selected path in the file selector.
739 * @param obj The file selector object
740 * @return The absolute path of the selected object in the fileselector
742 * @ingroup Fileselector
745 elm_fileselector_selected_get(const Evas_Object *obj)
747 ELM_CHECK_WIDTYPE(obj, widtype) NULL;
748 Widget_Data *wd = elm_widget_data_get(obj);
749 Elm_Genlist_Item *it;
750 if (!wd) return NULL;
756 name = elm_entry_entry_get(wd->entry2);
758 snprintf(buf, sizeof(buf), "%s/%s", wd->path, name);
759 eina_stringshare_replace(&wd->selection, buf);
760 return wd->selection;
763 it = elm_genlist_selected_item_get(wd->list);
764 if (it) return elm_genlist_item_data_get(it);
770 * This sets the currently selected path in the file selector.
772 * @param obj The file selector object
773 * @param path The path to a file or directory
774 * @return @c EINA_TRUE on success, @c EINA_FALSE on failure. The
775 * latter case occurs if the directory or file pointed to do not
778 * @ingroup Fileselector
781 elm_fileselector_selected_set(Evas_Object *obj, const char *path)
783 ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
784 Widget_Data *wd = elm_widget_data_get(obj);
785 if (!wd) return EINA_FALSE;
787 if (ecore_file_is_dir(path)) _populate(obj, path, NULL);
790 if (!ecore_file_exists(path)) return EINA_FALSE;
792 _populate(obj, ecore_file_dir_get(path), NULL);
795 elm_entry_entry_set(wd->entry2, ecore_file_file_get(path));
796 eina_stringshare_replace(&wd->selection, path);