Page Example

File Manager Sample Overview

Mobile native

The File Manager sample demonstrates how to implement a complex view using EFL UI components and containers.

It uses UI components, such as elm_conformant, elm_naviframe, and elm_layout for the view management, containers, such as elm_popup, elm_genlist, and elm_box for UI component management inside the view. And UI components, such as elm_button, elm_image, and elm_progress for the content inside view.

The following figure illustrates the main view of the File Manager sample application, its wireframe structure, and the UI component tree.

Figure: File Manager main views

File Manager main views

File Manager main views

Implementation

To manage files:

  1. Create a window:
    1. The win_create() function creates the window which consists of an indicator (elm_conformant), a view manager (elm_naviframe), and a background (elm_bg).

      window_obj *win_create()
      {
         window_obj *obj = calloc(1, sizeof(window_obj));
         RETVM_IF(!obj, NULL, "Cannot allocate memory");
      
         obj->win = elm_win_add(NULL, "File Manager", ELM_WIN_BASIC);
         elm_win_conformant_set(obj->win, EINA_TRUE);
         evas_object_show(obj->win);
      
         obj->conform = elm_conformant_add(obj->win);
         evas_object_size_hint_weight_set(obj->conform, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
         elm_win_resize_object_add(obj->win, obj->conform);
         evas_object_show(obj->conform);
      
         obj->bg = elm_bg_add(obj->conform);
         evas_object_size_hint_weight_set(obj->bg, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
         evas_object_show(obj->bg);
      
         elm_object_part_content_set(obj->conform, "elm.swallow.bg", obj->bg);
      
         return obj;
      }
      
    2. The _app_add_naviframe() function adds the view manager (elm_naviframe) to the window. Naviframe is created using the ui_utils_navi_add() function.

      static Evas_Object* 
      _app_add_naviframe(app_data *app)
      {
         Evas_Object *naviframe = NULL;
         Evas_Object *parent = win_get_host_layout(app->win);
         if (parent)
         {
            naviframe = ui_utils_navi_add(parent, _app_naviframe_backbutton_cb, app);
            if (naviframe)
            {
               win_set_layout(app->win, naviframe);
            }
         }
      
         return naviframe;
      }
      
    3. The ui_utils_navi_add() function creates and adds the view manager (elm_naviframe) to the parent. In the following example, the parent is the window (elm_win).

      Evas_Object* 
      ui_utils_navi_add(Evas_Object *parent, Eext_Event_Cb back_cb, void *cb_data)
      {
         RETVM_IF(!parent, NULL, "Parent is NULL");
      
         Evas_Object *navi = elm_naviframe_add(parent);
         eext_object_event_callback_add(navi, EEXT_CALLBACK_BACK, back_cb, cb_data);
         elm_naviframe_prev_btn_auto_pushed_set(navi, EINA_FALSE);
      
         evas_object_show(navi);
      
         return navi;
      }
      
  2. Create the main view:
    1. The main_view_add() function creates the main layout and UI components as genlist and control bar. Then layout inserts to naviframe.
      int 
      main_view_add(app_data *app, Evas_Object *parent)
      {
         RETVM_IF(!app, RESULT_TYPE_INVALID_ARG, "App object is NULL");
         RETVM_IF(!parent, RESULT_TYPE_INVALID_ARG, "Parent object is NULL");
      
         app->status.is_mainview = EINA_TRUE;
      
         main_view_data *data = calloc(1, sizeof(main_view_data));
         RETVM_IF(!data, RESULT_TYPE_FAIL_ALLOCATE_MEMORY, "Failed to allocate memory");
      
         data->view.app = app;
         data->view.navi = parent;
      
         data->storage_list = NULL;
         data->view.is_root = EINA_FALSE;
      
         data->view.navi_layout = ui_utils_layout_add(data->view.navi, _main_view_del_cb, data);
         if (!data->view.navi_layout)
         {
            ERR("Failed to create layout");
            _main_view_destroy(data);
      
            return RESULT_TYPE_FAIL;
         }
      
         elm_layout_file_set(data->view.navi_layout, ui_utils_get_resource(FM_LAYOUT_EDJ), "navi_layout");
      
         int result = navigator_add_view(data->view.app->navigator, FM_MAIN_VIEW_TITLE, &data->view);
         if (result != RESULT_TYPE_OK)
         {
            ERR("Failed to add view to naviframe");
            evas_object_del(data->view.navi_layout);
      
            return result;
         }
      
         result = _main_view_create_widgets(data);
         if (result != RESULT_TYPE_OK)
         {
            ERR("Failed to create widgets");
            evas_object_del(data->view.navi_layout);
      
            return result;
         }
      
         result = _main_view_fill(data);
         if (result != RESULT_TYPE_OK)
         {
            ERR("Failed to initialize main view");
            evas_object_del(data->view.navi_layout);
      
            return result;
         }
      
         elm_object_item_data_set(data->view.navi_item, data);
      
         return RESULT_TYPE_OK;
      }
      
    2. Figure: File Manager main view layout

      File Manager main view layout

    3. The ui_utils_genlist_add() function creates the genlist:
      Evas_Object* 
      ui_utils_genlist_add(Evas_Object *parent, Evas_Object_Event_Cb destroy_cb, void *cb_data)
      {
         Evas_Object *genlist = elm_genlist_add(parent);
         RETVM_IF(!genlist, NULL, "Genlist is NULL");
      
         evas_object_size_hint_weight_set(genlist, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
         evas_object_size_hint_align_set(genlist, EVAS_HINT_FILL, EVAS_HINT_FILL);
      
         evas_object_event_callback_add(genlist, EVAS_CALLBACK_FREE, destroy_cb, cb_data);
      
         evas_object_show(genlist);
      
         return genlist;
      }
      
    4. The _main_view_fill() function gets content using the fs_manager_get_storage_list() function and sets to the genlist using the genlist_widget_content_set() function.
      static int 
      _main_view_fill(main_view_data *data)
      {
         int res = fs_manager_get_storage_list(data->view.app->manager, &data->storage_list);
         RETVM_IF(res != RESULT_TYPE_OK, res, "Failed to get storage list");
      
         genlist_widget_content_set(data->genlist_widget, data->storage_list);
      
         return RESULT_TYPE_OK;
      }
      
    5. The genlist_widget_content_set() function sets the content from the list to the genlist.
      void 
      genlist_widget_content_set(genlist_widget *widget, const Eina_List *file_list)
      {
         RETM_IF(!widget, "Widget is NULL");
      
         const Eina_List *list = NULL;
         void *item = NULL;
      
         _genlist_widget_set_items_style(widget);
      
         EINA_LIST_FOREACH(file_list, list, item)
         {
            Elm_Object_Item *node_item = elm_genlist_item_append(widget->genlist, &itc, item, NULL,
                  ELM_GENLIST_ITEM_NONE, _genlist_widget_item_sel_cb, widget);
      
            elm_object_item_data_set(node_item, item);
         }
      }
      
  3. Create the storage view:
    1. The list_view_add() function creates and shows the storage view. It creates the layout and UI components as navigation path, genlist, and control bar. Then the layout is inserted to naviframe.
      int 
      list_view_add(app_data *app, Evas_Object *parent, const char *path, const char *dir_name)
      {
         RETVM_IF(!app, RESULT_TYPE_INVALID_ARG, "App object is NULL");
         RETVM_IF(!parent, RESULT_TYPE_INVALID_ARG, "Parent object is NULL");
         RETVM_IF(!path, RESULT_TYPE_INVALID_ARG, "Path is NULL");
      
         app->status.is_mainview = EINA_FALSE;
      
         list_view_data *data = calloc(1, sizeof(list_view_data));
         RETVM_IF(!data, RESULT_TYPE_FAIL_ALLOCATE_MEMORY, "Failed to allocate memory");
      
         data->list_view.app = app;
         data->list_view.navi = parent;
         data->list_view.curr_path = (dir_name) ? common_util_strconcat(path, "/", dir_name, NULL)
                                     : strdup(path);
         data->list_view.is_root = model_utils_is_root_path(data->list_view.curr_path);
      
         data->file_list = NULL;
      
         data->list_view.navi_layout = ui_utils_layout_add(data->list_view.navi, _list_view_del_cb, data);
         if (!data->list_view.navi_layout)
         {
            ERR("Failed to create Layout");
            _list_view_destroy(data);
      
            return RESULT_TYPE_FAIL;
         }
      
         elm_layout_file_set(data->list_view.navi_layout, ui_utils_get_resource(FM_LAYOUT_EDJ), "navi_layout");
      
         int result = fs_manager_get_file_list(data->list_view.app->manager, data->list_view.curr_path, &data->file_list);
         if (result != RESULT_TYPE_OK)
         {
            ERR("Failed to get file list");
            evas_object_del(data->list_view.navi_layout);
      
            return result;
         }
      
         const char *title = ui_utils_title_get(data->list_view.curr_path);
         result = navigator_add_view(data->list_view.app->navigator, title, &data->list_view);
         if (result != RESULT_TYPE_OK)
         {
            ERR("Failed to add view to naviframe");
            evas_object_del(data->list_view.navi_layout);
      
            return result;
         }
      
         result = _list_view_create_widgets(data);
         if (result != RESULT_TYPE_OK)
         {
            ERR("Failed to create widgets");
            evas_object_del(data->list_view.navi_layout);
      
            return result;
         }
      
         Eina_List *path_list = NULL;
         int res = navi_path_storage_get_path_list(app->path_storage, &path_list);
         RETVM_IF(res != RESULT_TYPE_OK, res, "Failed to get folders list for navigation widget");
         navi_path_widget_content_set(data->navi_path_wgt, path_list);
      
         elm_object_item_data_set(data->list_view.navi_item, data);
      
         return RESULT_TYPE_OK;
      }
      

      Figure: File Manager storage view layout

      File Manager storage view layout

    2. The navi_path_widget_add() function adds the navigation path component to the view. The UI component consists of the layout (elm_layout), table (elm_table), and scroller (elm_scroller).
      navi_path_widget *navi_path_widget_add(view_data *view)
      {
         RETVM_IF(!view, NULL, "View object is NULL");
      
         navi_path_widget *widget = calloc(1, sizeof(navi_path_widget));
         RETVM_IF(!widget, NULL, "Failed to allocate memory");
      
         widget->table_size = 0;
         widget->view = view;
      
         widget->navi_path_layout = ui_utils_genlist_add(widget->view->navi_layout, _navi_path_widget_delete_cb, widget);
         if (!widget->navi_path_layout)
         {
            ERR("Layout is NULL");
            _navi_path_widget_destroy(widget);
      
            return NULL;
         }
      
         evas_object_size_hint_weight_set(widget->navi_path_layout, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
         evas_object_size_hint_align_set(widget->navi_path_layout, EVAS_HINT_FILL, EVAS_HINT_FILL);
      
         elm_layout_file_set(widget->navi_path_layout, ui_utils_get_resource(FM_LAYOUT_EDJ), "path_info");
      
         evas_object_show(widget->navi_path_layout);
      
         widget->navi_path_scroller = elm_scroller_add(widget->navi_path_layout);
         if (!widget->navi_path_scroller)
         {
            ERR("Scroller is NULL");
            evas_object_del(widget->navi_path_layout);
      
            return NULL;
         }
      
         elm_scroller_bounce_set(widget->navi_path_scroller, EINA_TRUE, EINA_FALSE);
         elm_scroller_policy_set(widget->navi_path_scroller, ELM_SCROLLER_POLICY_OFF, ELM_SCROLLER_POLICY_OFF);
      
         evas_object_size_hint_weight_set(widget->navi_path_scroller, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
         evas_object_size_hint_align_set(widget->navi_path_scroller, EVAS_HINT_FILL, EVAS_HINT_FILL);
         evas_object_show(widget->navi_path_scroller);
      
         widget->navi_path_table = elm_table_add(widget->navi_path_layout);
         if (!widget->navi_path_table)
         {
            ERR("Table is NULL");
            evas_object_del(widget->navi_path_layout);
      
            return NULL;
         }
      
         evas_object_size_hint_weight_set(widget->navi_path_table, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
         evas_object_size_hint_align_set(widget->navi_path_table, EVAS_HINT_FILL, EVAS_HINT_FILL);
         evas_object_show(widget->navi_path_table);
      
         elm_object_content_set(widget->navi_path_scroller, widget->navi_path_table);
         elm_object_part_content_set(widget->navi_path_layout, "info", widget->navi_path_scroller);
      
         elm_object_part_content_set(widget->view->navi_layout, "header_box", widget->navi_path_layout);
      
         return widget;
      }
      
  4. Edit files.

    The following figure illustrates the main functionalities of the File Manager.

    Figure: File Manager functionalities

    File Manager functionalities

    1. After clicking the Edit Mode button, the genlist_widget_update() and ctrl_bar_widget_mode_update() functions are called.

    2. The genlist_widget_update() function updates the style of the genlist items. If it is in the edit mode, checkboxes and the Select All genlist is shown.

      int 
      genlist_widget_update(genlist_widget *widget)
      {
         RETVM_IF(!widget->view, RESULT_TYPE_INVALID_ARG, "View object is NULL");
      
         app_data *app = widget->view->app;
      
         _genlist_widget_set_items_style(widget);
         _genlist_widget_reset_all_checkboxes(widget);
      
         if (app->status.curr_mode == MODE_EDIT)
         {
            elm_layout_signal_emit(widget->view->navi_layout, "show_select_all", "");
         }
         else
         {
            elm_layout_signal_emit(widget->view->navi_layout, "hide_select_all", "");
         }
      
         return RESULT_TYPE_OK;
      }
      
    3. The ctrl_bar_widget_mode_update() function updates the buttons in the toolbar. In the edit mode, the toolbar shows the delete, copy, move, and exit functionalities.

      int 
      ctrl_bar_widget_mode_update(ctrl_bar_widget *widget)
      {
         RETVM_IF(!widget, RESULT_TYPE_FAIL, "Widget object is NULL");
         RETVM_IF(!widget->view, RESULT_TYPE_FAIL, "View object is NULL");
         RETVM_IF(!widget->ctrl_bar, RESULT_TYPE_FAIL, "Ctrl bar object is NULL");
      
         _ctrl_bar_widget_add_update_buttons(widget);
      
         return RESULT_TYPE_OK;
      }
      
      static void 
      _ctrl_bar_widget_add_update_buttons(ctrl_bar_widget *widget)
      {
         if (app->status.curr_mode == MODE_DEFAULT)
         {
            Elm_Object_Item *edit_item = elm_toolbar_item_append(widget->ctrl_bar, NULL, FM_TOOLBAR_TEXT_EDIT, _ctrl_bar_widget_edit_btn_cb, widget);
            elm_toolbar_item_append(widget->ctrl_bar, NULL, FM_TOOLBAR_TEXT_MORE, _ctrl_bar_widget_more_btn_cb, widget);
            elm_toolbar_item_append(widget->ctrl_bar, NULL, FM_TOOLBAR_TEXT_EXIT, _ctrl_bar_widget_exit_btn_cb, widget);
      
            if (list_view_items_count_get(widget->view) < 1)
            {
               elm_object_item_disabled_set(edit_item, EINA_TRUE);
            }
         }
         else if (app->status.curr_mode == MODE_EDIT)
         {
            elm_toolbar_item_append(widget->ctrl_bar, NULL, FM_TOOLBAR_TEXT_DELETE, _ctrl_bar_widget_delete_btn_cb, widget);
            elm_toolbar_item_append(widget->ctrl_bar, NULL, FM_TOOLBAR_TEXT_MOVE, _ctrl_bar_widget_move_btn_cb, widget);
            elm_toolbar_item_append(widget->ctrl_bar, NULL, FM_TOOLBAR_TEXT_COPY, _ctrl_bar_widget_copy_btn_cb, widget);
            elm_toolbar_item_append(widget->ctrl_bar, NULL, FM_TOOLBAR_TEXT_CANCEL, _ctrl_bar_widget_cancel_btn_cb, widget);
         }
      }
      
  5. Create a new folder:
    1. The _popup_new_folder_type_create() function creates a popup with an edit field and the OK and Cancel buttons. It sets the text and callback functions.

      static int 
      _popup_new_folder_type_create(view_data *view)
      {
         Evas_Object *popup = _popup_new(view->navi, NULL, view);
         RETVM_IF(!popup, RESULT_TYPE_INVALID_ARG, "Failed to create popup");
      
         elm_object_part_text_set(popup, "title,text", POPUP_TEXT_TITLE_NEW_FOLDER);
      
         int res = _popup_editfield_add(popup);
         RETVM_IF(res != RESULT_TYPE_OK, res, "Failed to add editfield");
      
         res = _popup_buttons_add(popup, _popup_create_folder_ok_cb, _popup_cancel_cb);
         RETVM_IF(res != RESULT_TYPE_OK, res, "Failed to add buttons");
      
         return RESULT_TYPE_OK;
      }
      
    2. The _popup_new() function creates a popup (elm_popup) and sets its text and style.

      static Evas_Object* 
      _popup_new(Evas_Object *parent, const char *text, const void *data)
      {
         Evas_Object *popup = elm_popup_add(parent);
         RETVM_IF(!popup, NULL, "Failed to create popup");
      
         eext_object_event_callback_add(popup, EEXT_CALLBACK_BACK, _popup_back_button_cb, NULL);
      
         elm_object_style_set(popup, "default");
         elm_object_text_set(popup, text);
      
         evas_object_data_set(popup, "view", data);
         evas_object_show(popup);
      
         return popup;
      }
      
    3. The _popup_editfield_add() creates the edit field from the entry (elm_entry), sets its theme and text.

      static int 
      _popup_editfield_add(Evas_Object *popup)
      {
         Evas_Object *entry = elm_entry_add(popup);
         RETVM_IF(!entry, RESULT_TYPE_INVALID_ARG, "Failed to create entry");
      
         elm_layout_theme_set(entry, "entry", "base-single", "editfield");
         evas_object_size_hint_weight_set(entry, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
         elm_entry_scrollable_set(entry, EINA_TRUE);
         elm_entry_single_line_set(entry, EINA_TRUE);
      
         elm_object_part_text_set(entry, "elm.guide", POPUP_TEXT_EMPTY_EDIT_FIELD);
         elm_object_part_text_set(entry, "elm.text", POPUP_TEXT_NEW_FOLDER);
         elm_object_content_set(popup, entry);
      
         elm_entry_cursor_end_set(entry);
         elm_object_focus_set(entry, EINA_TRUE);
      
         return RESULT_TYPE_OK;
      }
      
  6. Copy operation:
    1. The _ctrl_bar_widget_copy_btn_cb() function is called when the Copy button is clicked. This function copies the selected items using the list_view_copy_items() function then updates the storage list using the list_view_update() function and shows the popup.

      static void 
      _ctrl_bar_widget_copy_btn_cb(void *data, Evas_Object *obj, void *eventInfo)
      {
         RETM_IF(!data, "Data is NULL");
      
         ctrl_bar_widget *widget = data;
         widget->view->app->status.curr_mode = MODE_DEFAULT;
      
         int res = list_view_copy_items(widget->view);
         RETM_IF(res != RESULT_TYPE_OK, "Failed to copy file");
      
         res = list_view_update(widget->view->navi_item, UPDATE_TYPE_GENLIST | UPDATE_TYPE_CTRL_BAR_MODE);
         RETM_IF(res != RESULT_TYPE_OK, "Failed to update view");
      
         res = popup_create(widget->view, POPUP_TYPE_COPY_MOVE);
         RETM_IF(res != RESULT_TYPE_OK, "Failed to create popup");
      }
      
    2. The list_view_copy_items() function copies selected files to the clipboard using the clipboard_add_data() function and sets the operation type using the clipboard_set_operation() function.

      int 
      list_view_copy_items(view_data *view)
      {
         RETVM_IF(!view, RESULT_TYPE_INVALID_ARG, "View object is NULL");
      
         list_view_data *list_data = (list_view_data *)view;
         app_data *app = list_data->list_view.app;
      
         int res = clipboard_add_data(app->clipboard, list_data->file_list);
         RETVM_IF(res != RESULT_TYPE_OK, res, "Failed to copy files");
      
         res = clipboard_set_operation(app->clipboard, OPERATION_TYPE_COPY);
         RETVM_IF(res != RESULT_TYPE_OK, res, "Failed to set operation");
      
         return RESULT_TYPE_OK;
      }
      
  7. Model functionality:
    • The main model part functionalities provided by the fs_manager and fs_operation methods.

      The provided fs_manager API is used by the GUI elements to get data the for UI component: list of available storages, list of files and subfolder for specified folder; make copy, move, delete operations on files, and folders.

      To get a list of available storages, use the fs_manager_get_storage_list() function.

      int 
      fs_manager_get_storage_list(fs_manager *manager, Eina_List **storage_list)
      {
         RETVM_IF(!manager, RESULT_TYPE_INVALID_ARG, "File manager is NULL");
         if (manager->is_locked)
         {
            ERR("File manager is busy");
      
            return RESULT_TYPE_BUSY;
         }
      
         bool is_supported = false;
      
         int res = _fs_manager_is_mmc_supported(&is_supported);
         if (res != RESULT_TYPE_OK)
         {
            return res;
         }
      
         if (is_supported)
         {
            storage_info *const pNode_internal = calloc(1, sizeof(node_info));
            pNode_internal->root_name = strdup(FM_MEMORY_LABEL);
            pNode_internal->root_path = strdup(FM_MEMORY_FOLDER);
            pNode_internal->type = STORAGE_TYPE_MMC;
      
            *storage_list = eina_list_append(*storage_list, pNode_internal);
         }
      
         storage_info *const pNode_device = calloc(1, sizeof(node_info));
         pNode_device->root_name = strdup(FM_PHONE_LABEL);
         pNode_device->root_path = strdup(FM_PHONE_FOLDER);
         pNode_device->type = STORAGE_TYPE_PHONE;
      
         *storage_list = eina_list_append(*storage_list, pNode_device);
         *storage_list = eina_list_sort(*storage_list, eina_list_count(*storage_list), _fs_manager_sort_by_name_cb);
      
         return RESULT_TYPE_OK;
      }
      
    • To check whether an SD card is mounted, the _fs_manager_is_mmc_supported() function is used where the storage_get_external_memory_size() function from the storage API is called. The total size of the external storage is counted. If the size is 0, no SD card is mounted.

      For getting a list of the files and subfolders in the current folder, the fs_manager_get_file_list() function is called. Inside this, the model_utils_read_dir() function is called. It gets 2 Eina_List containers with the current folder's subfolders and files. Using the eina_list_sort() function sorts the lists. The order can be defined using the _fs_manager_sort_by_name_cb() function and merged into the result list using the eina_list_merge() function.

      int 
      fs_manager_get_file_list(fs_manager *manager, const char *dir_path, Eina_List **file_list)
      {
         Eina_List *dirs = NULL;
         Eina_List *files = NULL;
         ret = model_utils_read_dir(dir_path, &dirs, &files);
         if (ret != RESULT_TYPE_OK)
         {
            ERR("Failed to read dir '%s'", dir_path);
      
            return ret;
         }
         dirs = eina_list_sort(dirs, eina_list_count(dirs), _fs_manager_sort_by_name_cb);
         files = eina_list_sort(files, eina_list_count(files), _fs_manager_sort_by_name_cb);
         *file_list = eina_list_merge(dirs, files);
      
         return RESULT_TYPE_OK;
      }
      
    • The folder content getter, the model_utils_read_dir() function uses the Linux readdir_r() function to read data from folders.

      int 
      model_utils_read_dir(const char *dir_path, Eina_List **dir_list, Eina_List **file_list)
      {
         struct dirent ent_struct;
         struct dirent *ent = NULL;
         while ((readdir_r(pDir, &ent_struct, &ent) == 0) && ent)
         {
            int skip = (!ent->d_name ||
                     (strncmp(ent->d_name, ".", 1) == 0) ||
                     (strncmp(ent->d_name, "..", 2) == 0));
      
            skip = skip || ((ent->d_type != DT_DIR) && (ent->d_type != DT_REG));
      
            skip = skip || ((ent->d_type == DT_DIR) &&
                  (strcmp(dir_path, FM_PHONE_FOLDER) == 0) &&
                  (strcmp(ent->d_name, FM_DEBUG_FOLDER) == 0));
      
            node_info *const pNode = skip ? NULL : calloc(1, sizeof(node_info));
      
            if (pNode)
            {
               pNode->parent_path = strdup(dir_path);
               pNode->name = strdup(ent->d_name);
               pNode->is_selected = EINA_FALSE;
      
               if (ent->d_type == DT_DIR)
               {
                  pNode->type = FILE_TYPE_DIR;
               }
               else
               {
                  model_utils_get_file_category(ent->d_name, &(pNode->type));
               }
      
               if (pNode->type == FILE_TYPE_DIR)
               {
                  *dir_list = eina_list_append(*dir_list, pNode);
               }
               else
               {
                  *file_list = eina_list_append(*file_list, pNode);
               }
            }
         }
      
         closedir(pDir);
      
         return RESULT_TYPE_OK;
      }
      
    • To create a new folder in the file system, use the fs_manager_create_folder() function that uses the Linux mkdir() method.

      int 
      fs_manager_create_folder(fs_manager *manager, const char *dir)
      {
         RETVM_IF(!manager, RESULT_TYPE_INVALID_ARG, "File manager is NULL");
         RETVM_IF(!dir, RESULT_TYPE_INVALID_ARG, "Directory path is NULL");
         if (mkdir(dir, DIR_MODE) < 0)
         {
            ERR("Failed to create folder '%s'", dir);
      
            return RESULT_TYPE_FAIL;
         }
      
         return RESULT_TYPE_OK;
      }
      
    • To create a copy, or move or delete an operation, the fs_manager provides the fs_manager_copy_files(), fs_manager_move_files(), and fs_manager_delete_files() functions. All of them call the private _fs_manager_generate_operation() function. In this method, the fs_operation instance is created.

      static int 
      _fs_manager_generate_operation(fs_manager *manager, Eina_List *source_list, const char *dest_path,
                                     operation_type oper_type, fs_manager_complete_cb_func func, void *data)
      {
         manager->user_cb_func = func;
         manager->user_cb_data = data;
      
         manager->operation = fs_operation_create();
         if (!manager->operation)
         {
            _fs_manager_clear_data(manager);
            ERR("Failed to allocate memory for file operation");
      
            return RESULT_TYPE_FAIL_ALLOCATE_MEMORY;
         }
      
         int result = fs_operation_set_data(manager->operation, source_list, dest_path, oper_type);
         if (result != RESULT_TYPE_OK)
         {
            _fs_manager_clear_data(manager);
            ERR("Failed to set operation data");
      
            return result;
         }
      
         fs_operation_cb_data *cb_data = calloc(1, sizeof(fs_operation_cb_data));
         if (!cb_data)
         {
            _fs_manager_clear_data(manager);
            ERR("Failed to allocate memory for callback operation data");
      
            return RESULT_TYPE_FAIL_ALLOCATE_MEMORY;
         }
      
         cb_data->manager = manager;
         cb_data->result = RESULT_TYPE_FAIL;
      
         // Lock file system manager
         manager->is_locked = EINA_TRUE;
      
         result = fs_operation_execute(manager->operation, _on_operation_completed, cb_data);
         if (result != RESULT_TYPE_OK)
         {
            free(cb_data);
            _fs_manager_clear_data(manager);
            manager->is_locked = EINA_FALSE;
            ERR("Failed to execute operation");
         }
      
         return result;
      }
      
    • Every file operation that is needed to be done runs separately from the main loop thread. This functionality is provided by the fs_operation API. The fs_manager creates the fs_operation instance, sets the operation data, completes the callback data, and runs this operation by calling the fs_operation_execute() function where a new instance of the ecore_thread is created.

      int 
      fs_operation_execute(fs_operation *operation, fs_operation_cb_func cb_func, fs_operation_cb_data *cb_data)
      {
         RETVM_IF(!operation, RESULT_TYPE_INVALID_ARG, "Operation object is NULL");
         RETVM_IF(!operation->source_list, RESULT_TYPE_FAIL,"File list not set");
         RETVM_IF(operation->oper_type == OPERATION_TYPE_NONE, RESULT_TYPE_FAIL,"Type of operation not set");
         RETVM_IF(!operation->dst_path && (operation->oper_type != OPERATION_TYPE_DELETE),
                  RESULT_TYPE_FAIL, "Destination path not set");
      
         operation->cb_func = cb_func;
         operation->cb_data = cb_data;
      
         operation->exec_thread = ecore_thread_feedback_run(_fs_operation_run, NULL,
                                                            _fs_operation_end, NULL, 
                                                            (void*)operation, EINA_TRUE);
      
         RETVM_IF(!operation->exec_thread, RESULT_TYPE_FAIL, "Failed to create thread");
      
         return RESULT_TYPE_OK;
      }
      
    • The _fs_operation_run() function is the thread function that runs in the separate thread. The operation is executed in this function file. The result of the operation is set to the callback data.

      void 
      _fs_operation_run(void *data, Ecore_Thread *thread)
      {
         fs_operation *operation = data;
         int res = RESULT_TYPE_FAIL;
      
         switch (operation->oper_type)
         {
            case OPERATION_TYPE_COPY:
               res = _fs_operation_copy(operation);
               break;
            case OPERATION_TYPE_MOVE:
               res = _fs_operation_move(operation);
               break;
            case OPERATION_TYPE_DELETE:
               res = _fs_operation_delete(operation);
               break;
            default:
               ERR("Operation type not set");
      
               return;
               break;
         }
      
         if (operation->is_canceled)
         {
            return;
         }
      
         if (operation->cb_data)
         {
            operation->cb_data->result = res;
         }
      }
      
    • The _fs_operation_end() function runs after the _fs_operation_run() is finished and it runs in the main loop. In this method, the operation callback function is called and the operation result is returned to the operation caller.

      void 
      _fs_operation_end(void *data, Ecore_Thread *thread)
      {
         fs_operation *operation = data;
         _fs_operation_delete_exec_thread(operation);
         if (operation->is_canceled)
         {
            fs_operation_destroy(operation);
      
            return;
         }
      
         if (operation->cb_func)
         {
            operation->cb_func(operation->cb_data);
         }
      }